1*eda14cbcSMatt Macy /* 2*eda14cbcSMatt Macy * CDDL HEADER START 3*eda14cbcSMatt Macy * 4*eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5*eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6*eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7*eda14cbcSMatt Macy * 8*eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*eda14cbcSMatt Macy * or http://www.opensolaris.org/os/licensing. 10*eda14cbcSMatt Macy * See the License for the specific language governing permissions 11*eda14cbcSMatt Macy * and limitations under the License. 12*eda14cbcSMatt Macy * 13*eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14*eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16*eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17*eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18*eda14cbcSMatt Macy * 19*eda14cbcSMatt Macy * CDDL HEADER END 20*eda14cbcSMatt Macy */ 21*eda14cbcSMatt Macy 22*eda14cbcSMatt Macy /* 23*eda14cbcSMatt Macy * Copyright (c) 2017, Intle Corporation. All rights reserved. 24*eda14cbcSMatt Macy */ 25*eda14cbcSMatt Macy 26*eda14cbcSMatt Macy #include <errno.h> 27*eda14cbcSMatt Macy #include <getopt.h> 28*eda14cbcSMatt Macy #include <stdio.h> 29*eda14cbcSMatt Macy #include <stdlib.h> 30*eda14cbcSMatt Macy #include <strings.h> 31*eda14cbcSMatt Macy #include <unistd.h> 32*eda14cbcSMatt Macy #include <fcntl.h> 33*eda14cbcSMatt Macy #include <dirent.h> 34*eda14cbcSMatt Macy #include <stddef.h> 35*eda14cbcSMatt Macy #include <libintl.h> 36*eda14cbcSMatt Macy #include <sys/stat.h> 37*eda14cbcSMatt Macy #include <sys/types.h> 38*eda14cbcSMatt Macy #include <sys/list.h> 39*eda14cbcSMatt Macy #include <sys/zfs_project.h> 40*eda14cbcSMatt Macy 41*eda14cbcSMatt Macy #include "zfs_util.h" 42*eda14cbcSMatt Macy #include "zfs_projectutil.h" 43*eda14cbcSMatt Macy 44*eda14cbcSMatt Macy typedef struct zfs_project_item { 45*eda14cbcSMatt Macy list_node_t zpi_list; 46*eda14cbcSMatt Macy char zpi_name[0]; 47*eda14cbcSMatt Macy } zfs_project_item_t; 48*eda14cbcSMatt Macy 49*eda14cbcSMatt Macy static void 50*eda14cbcSMatt Macy zfs_project_item_alloc(list_t *head, const char *name) 51*eda14cbcSMatt Macy { 52*eda14cbcSMatt Macy zfs_project_item_t *zpi; 53*eda14cbcSMatt Macy 54*eda14cbcSMatt Macy zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1); 55*eda14cbcSMatt Macy strcpy(zpi->zpi_name, name); 56*eda14cbcSMatt Macy list_insert_tail(head, zpi); 57*eda14cbcSMatt Macy } 58*eda14cbcSMatt Macy 59*eda14cbcSMatt Macy static int 60*eda14cbcSMatt Macy zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc, 61*eda14cbcSMatt Macy struct stat *st) 62*eda14cbcSMatt Macy { 63*eda14cbcSMatt Macy int ret; 64*eda14cbcSMatt Macy 65*eda14cbcSMatt Macy ret = stat(name, st); 66*eda14cbcSMatt Macy if (ret) { 67*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("failed to stat %s: %s\n"), 68*eda14cbcSMatt Macy name, strerror(errno)); 69*eda14cbcSMatt Macy return (ret); 70*eda14cbcSMatt Macy } 71*eda14cbcSMatt Macy 72*eda14cbcSMatt Macy if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { 73*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("only support project quota on " 74*eda14cbcSMatt Macy "regular file or directory\n")); 75*eda14cbcSMatt Macy return (-1); 76*eda14cbcSMatt Macy } 77*eda14cbcSMatt Macy 78*eda14cbcSMatt Macy if (!S_ISDIR(st->st_mode)) { 79*eda14cbcSMatt Macy if (zpc->zpc_dironly) { 80*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 81*eda14cbcSMatt Macy "'-d' option on non-dir target %s\n"), name); 82*eda14cbcSMatt Macy return (-1); 83*eda14cbcSMatt Macy } 84*eda14cbcSMatt Macy 85*eda14cbcSMatt Macy if (zpc->zpc_recursive) { 86*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 87*eda14cbcSMatt Macy "'-r' option on non-dir target %s\n"), name); 88*eda14cbcSMatt Macy return (-1); 89*eda14cbcSMatt Macy } 90*eda14cbcSMatt Macy } 91*eda14cbcSMatt Macy 92*eda14cbcSMatt Macy return (0); 93*eda14cbcSMatt Macy } 94*eda14cbcSMatt Macy 95*eda14cbcSMatt Macy static int 96*eda14cbcSMatt Macy zfs_project_load_projid(const char *name, zfs_project_control_t *zpc) 97*eda14cbcSMatt Macy { 98*eda14cbcSMatt Macy zfsxattr_t fsx; 99*eda14cbcSMatt Macy int ret, fd; 100*eda14cbcSMatt Macy 101*eda14cbcSMatt Macy fd = open(name, O_RDONLY | O_NOCTTY); 102*eda14cbcSMatt Macy if (fd < 0) { 103*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("failed to open %s: %s\n"), 104*eda14cbcSMatt Macy name, strerror(errno)); 105*eda14cbcSMatt Macy return (fd); 106*eda14cbcSMatt Macy } 107*eda14cbcSMatt Macy 108*eda14cbcSMatt Macy ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); 109*eda14cbcSMatt Macy if (ret) 110*eda14cbcSMatt Macy (void) fprintf(stderr, 111*eda14cbcSMatt Macy gettext("failed to get xattr for %s: %s\n"), 112*eda14cbcSMatt Macy name, strerror(errno)); 113*eda14cbcSMatt Macy else 114*eda14cbcSMatt Macy zpc->zpc_expected_projid = fsx.fsx_projid; 115*eda14cbcSMatt Macy 116*eda14cbcSMatt Macy close(fd); 117*eda14cbcSMatt Macy return (ret); 118*eda14cbcSMatt Macy } 119*eda14cbcSMatt Macy 120*eda14cbcSMatt Macy static int 121*eda14cbcSMatt Macy zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) 122*eda14cbcSMatt Macy { 123*eda14cbcSMatt Macy zfsxattr_t fsx; 124*eda14cbcSMatt Macy int ret, fd; 125*eda14cbcSMatt Macy 126*eda14cbcSMatt Macy fd = open(name, O_RDONLY | O_NOCTTY); 127*eda14cbcSMatt Macy if (fd < 0) { 128*eda14cbcSMatt Macy if (errno == ENOENT && zpc->zpc_ignore_noent) 129*eda14cbcSMatt Macy return (0); 130*eda14cbcSMatt Macy 131*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("failed to open %s: %s\n"), 132*eda14cbcSMatt Macy name, strerror(errno)); 133*eda14cbcSMatt Macy return (fd); 134*eda14cbcSMatt Macy } 135*eda14cbcSMatt Macy 136*eda14cbcSMatt Macy ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); 137*eda14cbcSMatt Macy if (ret) { 138*eda14cbcSMatt Macy (void) fprintf(stderr, 139*eda14cbcSMatt Macy gettext("failed to get xattr for %s: %s\n"), 140*eda14cbcSMatt Macy name, strerror(errno)); 141*eda14cbcSMatt Macy goto out; 142*eda14cbcSMatt Macy } 143*eda14cbcSMatt Macy 144*eda14cbcSMatt Macy switch (zpc->zpc_op) { 145*eda14cbcSMatt Macy case ZFS_PROJECT_OP_LIST: 146*eda14cbcSMatt Macy (void) printf("%5u %c %s\n", fsx.fsx_projid, 147*eda14cbcSMatt Macy (fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name); 148*eda14cbcSMatt Macy goto out; 149*eda14cbcSMatt Macy case ZFS_PROJECT_OP_CHECK: 150*eda14cbcSMatt Macy if (fsx.fsx_projid == zpc->zpc_expected_projid && 151*eda14cbcSMatt Macy fsx.fsx_xflags & ZFS_PROJINHERIT_FL) 152*eda14cbcSMatt Macy goto out; 153*eda14cbcSMatt Macy 154*eda14cbcSMatt Macy if (!zpc->zpc_newline) { 155*eda14cbcSMatt Macy char c = '\0'; 156*eda14cbcSMatt Macy 157*eda14cbcSMatt Macy (void) printf("%s%c", name, c); 158*eda14cbcSMatt Macy goto out; 159*eda14cbcSMatt Macy } 160*eda14cbcSMatt Macy 161*eda14cbcSMatt Macy if (fsx.fsx_projid != zpc->zpc_expected_projid) 162*eda14cbcSMatt Macy (void) printf("%s - project ID is not set properly " 163*eda14cbcSMatt Macy "(%u/%u)\n", name, fsx.fsx_projid, 164*eda14cbcSMatt Macy (uint32_t)zpc->zpc_expected_projid); 165*eda14cbcSMatt Macy 166*eda14cbcSMatt Macy if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) 167*eda14cbcSMatt Macy (void) printf("%s - project inherit flag is not set\n", 168*eda14cbcSMatt Macy name); 169*eda14cbcSMatt Macy 170*eda14cbcSMatt Macy goto out; 171*eda14cbcSMatt Macy case ZFS_PROJECT_OP_CLEAR: 172*eda14cbcSMatt Macy if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) && 173*eda14cbcSMatt Macy (zpc->zpc_keep_projid || 174*eda14cbcSMatt Macy fsx.fsx_projid == ZFS_DEFAULT_PROJID)) 175*eda14cbcSMatt Macy goto out; 176*eda14cbcSMatt Macy 177*eda14cbcSMatt Macy fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL; 178*eda14cbcSMatt Macy if (!zpc->zpc_keep_projid) 179*eda14cbcSMatt Macy fsx.fsx_projid = ZFS_DEFAULT_PROJID; 180*eda14cbcSMatt Macy break; 181*eda14cbcSMatt Macy case ZFS_PROJECT_OP_SET: 182*eda14cbcSMatt Macy if (fsx.fsx_projid == zpc->zpc_expected_projid && 183*eda14cbcSMatt Macy (!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) 184*eda14cbcSMatt Macy goto out; 185*eda14cbcSMatt Macy 186*eda14cbcSMatt Macy fsx.fsx_projid = zpc->zpc_expected_projid; 187*eda14cbcSMatt Macy if (zpc->zpc_set_flag) 188*eda14cbcSMatt Macy fsx.fsx_xflags |= ZFS_PROJINHERIT_FL; 189*eda14cbcSMatt Macy break; 190*eda14cbcSMatt Macy default: 191*eda14cbcSMatt Macy ASSERT(0); 192*eda14cbcSMatt Macy break; 193*eda14cbcSMatt Macy } 194*eda14cbcSMatt Macy 195*eda14cbcSMatt Macy ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx); 196*eda14cbcSMatt Macy if (ret) 197*eda14cbcSMatt Macy (void) fprintf(stderr, 198*eda14cbcSMatt Macy gettext("failed to set xattr for %s: %s\n"), 199*eda14cbcSMatt Macy name, strerror(errno)); 200*eda14cbcSMatt Macy 201*eda14cbcSMatt Macy out: 202*eda14cbcSMatt Macy close(fd); 203*eda14cbcSMatt Macy return (ret); 204*eda14cbcSMatt Macy } 205*eda14cbcSMatt Macy 206*eda14cbcSMatt Macy static int 207*eda14cbcSMatt Macy zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc, 208*eda14cbcSMatt Macy list_t *head) 209*eda14cbcSMatt Macy { 210*eda14cbcSMatt Macy char fullname[PATH_MAX]; 211*eda14cbcSMatt Macy struct dirent *ent; 212*eda14cbcSMatt Macy DIR *dir; 213*eda14cbcSMatt Macy int ret = 0; 214*eda14cbcSMatt Macy 215*eda14cbcSMatt Macy dir = opendir(name); 216*eda14cbcSMatt Macy if (dir == NULL) { 217*eda14cbcSMatt Macy if (errno == ENOENT && zpc->zpc_ignore_noent) 218*eda14cbcSMatt Macy return (0); 219*eda14cbcSMatt Macy 220*eda14cbcSMatt Macy ret = -errno; 221*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("failed to opendir %s: %s\n"), 222*eda14cbcSMatt Macy name, strerror(errno)); 223*eda14cbcSMatt Macy return (ret); 224*eda14cbcSMatt Macy } 225*eda14cbcSMatt Macy 226*eda14cbcSMatt Macy /* Non-top item, ignore the case of being removed or renamed by race. */ 227*eda14cbcSMatt Macy zpc->zpc_ignore_noent = B_TRUE; 228*eda14cbcSMatt Macy errno = 0; 229*eda14cbcSMatt Macy while (!ret && (ent = readdir(dir)) != NULL) { 230*eda14cbcSMatt Macy /* skip "." and ".." */ 231*eda14cbcSMatt Macy if (strcmp(ent->d_name, ".") == 0 || 232*eda14cbcSMatt Macy strcmp(ent->d_name, "..") == 0) 233*eda14cbcSMatt Macy continue; 234*eda14cbcSMatt Macy 235*eda14cbcSMatt Macy if (strlen(ent->d_name) + strlen(name) >= 236*eda14cbcSMatt Macy sizeof (fullname) + 1) { 237*eda14cbcSMatt Macy errno = ENAMETOOLONG; 238*eda14cbcSMatt Macy break; 239*eda14cbcSMatt Macy } 240*eda14cbcSMatt Macy 241*eda14cbcSMatt Macy sprintf(fullname, "%s/%s", name, ent->d_name); 242*eda14cbcSMatt Macy ret = zfs_project_handle_one(fullname, zpc); 243*eda14cbcSMatt Macy if (!ret && zpc->zpc_recursive && ent->d_type == DT_DIR) 244*eda14cbcSMatt Macy zfs_project_item_alloc(head, fullname); 245*eda14cbcSMatt Macy } 246*eda14cbcSMatt Macy 247*eda14cbcSMatt Macy if (errno && !ret) { 248*eda14cbcSMatt Macy ret = -errno; 249*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("failed to readdir %s: %s\n"), 250*eda14cbcSMatt Macy name, strerror(errno)); 251*eda14cbcSMatt Macy } 252*eda14cbcSMatt Macy 253*eda14cbcSMatt Macy closedir(dir); 254*eda14cbcSMatt Macy return (ret); 255*eda14cbcSMatt Macy } 256*eda14cbcSMatt Macy 257*eda14cbcSMatt Macy int 258*eda14cbcSMatt Macy zfs_project_handle(const char *name, zfs_project_control_t *zpc) 259*eda14cbcSMatt Macy { 260*eda14cbcSMatt Macy zfs_project_item_t *zpi; 261*eda14cbcSMatt Macy struct stat st; 262*eda14cbcSMatt Macy list_t head; 263*eda14cbcSMatt Macy int ret; 264*eda14cbcSMatt Macy 265*eda14cbcSMatt Macy ret = zfs_project_sanity_check(name, zpc, &st); 266*eda14cbcSMatt Macy if (ret) 267*eda14cbcSMatt Macy return (ret); 268*eda14cbcSMatt Macy 269*eda14cbcSMatt Macy if ((zpc->zpc_op == ZFS_PROJECT_OP_SET || 270*eda14cbcSMatt Macy zpc->zpc_op == ZFS_PROJECT_OP_CHECK) && 271*eda14cbcSMatt Macy zpc->zpc_expected_projid == ZFS_INVALID_PROJID) { 272*eda14cbcSMatt Macy ret = zfs_project_load_projid(name, zpc); 273*eda14cbcSMatt Macy if (ret) 274*eda14cbcSMatt Macy return (ret); 275*eda14cbcSMatt Macy } 276*eda14cbcSMatt Macy 277*eda14cbcSMatt Macy zpc->zpc_ignore_noent = B_FALSE; 278*eda14cbcSMatt Macy ret = zfs_project_handle_one(name, zpc); 279*eda14cbcSMatt Macy if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly || 280*eda14cbcSMatt Macy (!zpc->zpc_recursive && 281*eda14cbcSMatt Macy zpc->zpc_op != ZFS_PROJECT_OP_LIST && 282*eda14cbcSMatt Macy zpc->zpc_op != ZFS_PROJECT_OP_CHECK)) 283*eda14cbcSMatt Macy return (ret); 284*eda14cbcSMatt Macy 285*eda14cbcSMatt Macy list_create(&head, sizeof (zfs_project_item_t), 286*eda14cbcSMatt Macy offsetof(zfs_project_item_t, zpi_list)); 287*eda14cbcSMatt Macy zfs_project_item_alloc(&head, name); 288*eda14cbcSMatt Macy while ((zpi = list_remove_head(&head)) != NULL) { 289*eda14cbcSMatt Macy if (!ret) 290*eda14cbcSMatt Macy ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head); 291*eda14cbcSMatt Macy free(zpi); 292*eda14cbcSMatt Macy } 293*eda14cbcSMatt Macy 294*eda14cbcSMatt Macy return (ret); 295*eda14cbcSMatt Macy } 296