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