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