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