]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/project.c
xfs_quota: add -d option to limit directory depth
[thirdparty/xfsprogs-dev.git] / quota / project.c
CommitLineData
5aead01d 1/*
da23017d
NS
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
5aead01d 4 *
da23017d
NS
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
5aead01d
NS
7 * published by the Free Software Foundation.
8 *
da23017d
NS
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
5aead01d 13 *
da23017d
NS
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
5aead01d
NS
17 */
18
19#include <xfs/command.h>
20#include <xfs/input.h>
21#include "init.h"
22#include "quota.h"
23
24static cmdinfo_t project_cmd;
25static prid_t prid;
544f6ba0 26static int recurse_depth = -1;
5aead01d
NS
27
28enum {
29 CHECK_PROJECT = 0x1,
30 SETUP_PROJECT = 0x2,
31 CLEAR_PROJECT = 0x4,
32};
33
366127f7 34#define EXCLUDED_FILE_TYPES(x) \
b9d2d4a2 35 (S_ISCHR((x)) \
366127f7
DD
36 || S_ISBLK((x)) \
37 || S_ISFIFO((x)) \
38 || S_ISLNK((x)) \
b9d2d4a2 39 || S_ISSOCK((x)))
366127f7 40
5aead01d
NS
41static void
42project_help(void)
43{
44 printf(_(
45"\n"
46" list projects or setup a project tree for tree quota management\n"
47"\n"
48" Example:\n"
49" 'project -c logfiles'\n"
50" (match project 'logfiles' to a directory, and setup the directory tree)\n"
51"\n"
52" Without arguments, report all projects found in the /etc/projects file.\n"
53" The project quota mechanism in XFS can be used to implement a form of\n"
54" directory tree quota, where a specified directory and all of the files\n"
55" and subdirectories below it (i.e. a tree) can be restricted to using a\n"
56" subset of the available space in the filesystem.\n"
57"\n"
58" A managed tree must be setup initially using the -c option with a project.\n"
59" The specified project name or identifier is matched to one or more trees\n"
60" defined in /etc/projects, and these trees are then recursively descended\n"
61" to mark the affected inodes as being part of that tree - which sets inode\n"
62" flags and the project identifier on every file.\n"
63" Once this has been done, new files created in the tree will automatically\n"
64" be accounted to the tree based on their project identifier. An attempt to\n"
65" create a hard link to a file in the tree will only succeed if the project\n"
66" identifier matches the project identifer for the tree. The xfs_io utility\n"
67" can be used to set the project ID for an arbitrary file, but this can only\n"
68" be done by a privileged user.\n"
69"\n"
70" A previously setup tree can be cleared from project quota control through\n"
71" use of the -C option, which will recursively descend the tree, clearing\n"
72" the affected inodes from project quota control.\n"
73"\n"
74" The -c option can be used to check whether a tree is setup, it reports\n"
75" nothing if the tree is correct, otherwise it reports the paths of inodes\n"
76" which do not have the project ID of the rest of the tree, or if the inode\n"
77" flag is not set.\n"
78"\n"
544f6ba0
AM
79" The -d <depth> option allows to descend at most <depth> levels of directories\n"
80" below the command line arguments. -d 0 means only apply the actions\n"
81" to the top level of the projects. -d -1 means no recursion limit (default).\n"
82"\n"
5aead01d
NS
83" The /etc/projid and /etc/projects file formats are simple, and described\n"
84" on the xfs_quota man page.\n"
85"\n"));
86}
87
88static int
89check_project(
90 const char *path,
91 const struct stat *stat,
366127f7 92 int flag,
5aead01d
NS
93 struct FTW *data)
94{
95 struct fsxattr fsx;
5aead01d
NS
96 int fd;
97
544f6ba0
AM
98 if (recurse_depth >= 0 && data->level > recurse_depth)
99 return -1;
100
366127f7 101 if (flag == FTW_NS ){
e3210fd8 102 exitcode = 1;
366127f7
DD
103 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
104 return 0;
105 }
106 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
107 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
108 return 0;
109 }
110
e3210fd8
AM
111 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
112 exitcode = 1;
5aead01d
NS
113 fprintf(stderr, _("%s: cannot open %s: %s\n"),
114 progname, path, strerror(errno));
e3210fd8
AM
115 } else if ((xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
116 exitcode = 1;
5aead01d
NS
117 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
118 progname, path, strerror(errno));
e3210fd8 119 } else {
764b1982 120 if (fsx.fsx_projid != prid)
5aead01d
NS
121 printf(_("%s - project identifier is not set"
122 " (inode=%u, tree=%u)\n"),
2a1888c5 123 path, fsx.fsx_projid, (unsigned int)prid);
5aead01d
NS
124 if (!(fsx.fsx_xflags & XFS_XFLAG_PROJINHERIT))
125 printf(_("%s - project inheritance flag is not set\n"),
126 path);
127 }
128 if (fd != -1)
129 close(fd);
130 return 0;
131}
132
133static int
134clear_project(
135 const char *path,
136 const struct stat *stat,
366127f7 137 int flag,
5aead01d
NS
138 struct FTW *data)
139{
140 struct fsxattr fsx;
141 int fd;
142
544f6ba0
AM
143 if (recurse_depth >= 0 && data->level > recurse_depth)
144 return -1;
145
366127f7 146 if (flag == FTW_NS ){
e3210fd8 147 exitcode = 1;
366127f7
DD
148 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
149 return 0;
150 }
151 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
152 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
153 return 0;
154 }
155
5aead01d 156 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 157 exitcode = 1;
5aead01d
NS
158 fprintf(stderr, _("%s: cannot open %s: %s\n"),
159 progname, path, strerror(errno));
160 return 0;
161 } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 162 exitcode = 1;
5aead01d
NS
163 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
164 progname, path, strerror(errno));
165 close(fd);
166 return 0;
167 }
168
764b1982 169 fsx.fsx_projid = 0;
5aead01d 170 fsx.fsx_xflags &= ~XFS_XFLAG_PROJINHERIT;
e3210fd8
AM
171 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
172 exitcode = 1;
764b1982 173 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
5aead01d 174 progname, path, strerror(errno));
e3210fd8 175 }
5aead01d
NS
176 close(fd);
177 return 0;
178}
179
180static int
181setup_project(
182 const char *path,
183 const struct stat *stat,
366127f7 184 int flag,
5aead01d
NS
185 struct FTW *data)
186{
187 struct fsxattr fsx;
188 int fd;
189
544f6ba0
AM
190 if (recurse_depth >= 0 && data->level > recurse_depth)
191 return -1;
192
366127f7 193 if (flag == FTW_NS ){
e3210fd8 194 exitcode = 1;
366127f7
DD
195 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
196 return 0;
197 }
198 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
199 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
200 return 0;
201 }
202
5aead01d 203 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 204 exitcode = 1;
5aead01d
NS
205 fprintf(stderr, _("%s: cannot open %s: %s\n"),
206 progname, path, strerror(errno));
207 return 0;
208 } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 209 exitcode = 1;
5aead01d
NS
210 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
211 progname, path, strerror(errno));
212 close(fd);
213 return 0;
214 }
215
764b1982 216 fsx.fsx_projid = prid;
5aead01d 217 fsx.fsx_xflags |= XFS_XFLAG_PROJINHERIT;
e3210fd8
AM
218 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
219 exitcode = 1;
764b1982 220 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
5aead01d 221 progname, path, strerror(errno));
e3210fd8 222 }
5aead01d
NS
223 close(fd);
224 return 0;
225}
226
227static void
228project_operations(
229 char *project,
230 char *dir,
231 int type)
232{
233 switch (type) {
234 case CHECK_PROJECT:
235 printf(_("Checking project %s (path %s)...\n"), project, dir);
544f6ba0 236 nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
237 break;
238 case SETUP_PROJECT:
239 printf(_("Setting up project %s (path %s)...\n"), project, dir);
544f6ba0 240 nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
241 break;
242 case CLEAR_PROJECT:
243 printf(_("Clearing project %s (path %s)...\n"), project, dir);
544f6ba0 244 nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
245 break;
246 }
247}
248
249static void
250project(
251 char *project,
252 int type)
253{
254 fs_cursor_t cursor;
255 fs_path_t *path;
256 int count = 0;
257
258 fs_cursor_initialise(NULL, FS_PROJECT_PATH, &cursor);
259 while ((path = fs_cursor_next_entry(&cursor))) {
260 if (prid != path->fs_prid)
261 continue;
262 project_operations(project, path->fs_dir, type);
263 count++;
264 }
265
544f6ba0
AM
266 printf(_("Processed %d %s paths for project %s with recursion depth %s (%d)\n"),
267 count, projects_file, project,
268 recurse_depth < 0 ? _("infinite") : _("limited"), recurse_depth);
5aead01d
NS
269}
270
271static int
272project_f(
273 int argc,
274 char **argv)
275{
276 int c, type = 0;
277
544f6ba0 278 while ((c = getopt(argc, argv, "cd:sC")) != EOF) {
5aead01d
NS
279 switch (c) {
280 case 'c':
281 type = CHECK_PROJECT;
282 break;
544f6ba0
AM
283 case 'd':
284 recurse_depth = atoi(optarg);
285 if (recurse_depth < 0)
286 recurse_depth = -1;
287 break;
5aead01d
NS
288 case 's':
289 type = SETUP_PROJECT;
290 break;
291 case 'C':
292 type = CLEAR_PROJECT;
293 break;
294 default:
295 return command_usage(&project_cmd);
296 }
297 }
298
299 if (argc == optind)
300 return command_usage(&project_cmd);
301
302 /* no options - just check the given projects */
303 if (!type)
304 type = CHECK_PROJECT;
305
306 setprfiles();
307 if (access(projects_file, F_OK) != 0) {
e3210fd8 308 exitcode = 1;
5aead01d
NS
309 fprintf(stderr, _("projects file \"%s\" doesn't exist\n"),
310 projects_file);
311 return 0;
312 }
313
314 while (argc > optind) {
315 prid = prid_from_string(argv[optind]);
e3210fd8
AM
316 if (prid == -1) {
317 exitcode = 1;
5aead01d
NS
318 fprintf(stderr, _("%s - no such project in %s\n"),
319 argv[optind], projects_file);
e3210fd8 320 } else
5aead01d
NS
321 project(argv[optind], type);
322 optind++;
323 }
324
325 return 0;
326}
327
328void
329project_init(void)
330{
331 project_cmd.name = _("project");
332 project_cmd.altname = _("tree");
333 project_cmd.cfunc = project_f;
544f6ba0 334 project_cmd.args = _("[-c|-s|-C|-d <depth>] project ...");
5aead01d
NS
335 project_cmd.argmin = 1;
336 project_cmd.argmax = -1;
337 project_cmd.oneline = _("check, setup or clear project quota trees");
338 project_cmd.help = project_help;
339
340 if (expert)
341 add_command(&project_cmd);
342}