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