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