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