]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/project.c
1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2005 Silicon Graphics, Inc.
12 static cmdinfo_t project_cmd
;
14 static int recurse_depth
= -1;
22 #define EXCLUDED_FILE_TYPES(x) \
34 " list projects or setup a project tree for tree quota management\n"
37 " 'project -c logfiles'\n"
38 " (match project 'logfiles' to a directory, and setup the directory tree)\n"
40 " Without arguments, report all projects found in the /etc/projects file.\n"
41 " The project quota mechanism in XFS can be used to implement a form of\n"
42 " directory tree quota, where a specified directory and all of the files\n"
43 " and subdirectories below it (i.e. a tree) can be restricted to using a\n"
44 " subset of the available space in the filesystem.\n"
46 " A managed tree must be setup initially using the -s option with a project.\n"
47 " The specified project name or identifier is matched to one or more trees\n"
48 " defined in /etc/projects, and these trees are then recursively descended\n"
49 " to mark the affected inodes as being part of that tree - which sets inode\n"
50 " flags and the project identifier on every file.\n"
51 " Once this has been done, new files created in the tree will automatically\n"
52 " be accounted to the tree based on their project identifier. An attempt to\n"
53 " create a hard link to a file in the tree will only succeed if the project\n"
54 " identifier matches the project identifier for the tree. The xfs_io utility\n"
55 " can be used to set the project ID for an arbitrary file, but this can only\n"
56 " be done by a privileged user.\n"
58 " A previously setup tree can be cleared from project quota control through\n"
59 " use of the -C option, which will recursively descend the tree, clearing\n"
60 " the affected inodes from project quota control.\n"
62 " The -c option can be used to check whether a tree is setup, it reports\n"
63 " nothing if the tree is correct, otherwise it reports the paths of inodes\n"
64 " which do not have the project ID of the rest of the tree, or if the inode\n"
67 " The -p <path> option can be used to manually specify project path without\n"
68 " need to create /etc/projects file. This option can be used multiple times\n"
69 " to specify multiple paths. When using this option only one projid/name can\n"
70 " be specified at command line. Note that /etc/projects is also used if exists.\n"
72 " The -d <depth> option allows to descend at most <depth> levels of directories\n"
73 " below the command line arguments. -d 0 means only apply the actions\n"
74 " to the top level of the projects. -d -1 means no recursion limit (default).\n"
76 " The /etc/projid and /etc/projects file formats are simple, and described\n"
77 " on the xfs_quota man page.\n"
84 const struct stat
*stat
,
91 if (recurse_depth
>= 0 && data
->level
> recurse_depth
)
96 fprintf(stderr
, _("%s: cannot stat file %s\n"), progname
, path
);
99 if (EXCLUDED_FILE_TYPES(stat
->st_mode
)) {
100 fprintf(stderr
, _("%s: skipping special file %s\n"), progname
, path
);
104 if ((fd
= open(path
, O_RDONLY
|O_NOCTTY
)) == -1) {
106 fprintf(stderr
, _("%s: cannot open %s: %s\n"),
107 progname
, path
, strerror(errno
));
108 } else if ((xfsctl(path
, fd
, FS_IOC_FSGETXATTR
, &fsx
)) < 0) {
110 fprintf(stderr
, _("%s: cannot get flags on %s: %s\n"),
111 progname
, path
, strerror(errno
));
113 if (fsx
.fsx_projid
!= prid
)
114 printf(_("%s - project identifier is not set"
115 " (inode=%u, tree=%u)\n"),
116 path
, fsx
.fsx_projid
, (unsigned int)prid
);
117 if (!(fsx
.fsx_xflags
& FS_XFLAG_PROJINHERIT
) && S_ISDIR(stat
->st_mode
))
118 printf(_("%s - project inheritance flag is not set\n"),
129 const struct stat
*stat
,
136 if (recurse_depth
>= 0 && data
->level
> recurse_depth
)
139 if (flag
== FTW_NS
){
141 fprintf(stderr
, _("%s: cannot stat file %s\n"), progname
, path
);
144 if (EXCLUDED_FILE_TYPES(stat
->st_mode
)) {
145 fprintf(stderr
, _("%s: skipping special file %s\n"), progname
, path
);
149 if ((fd
= open(path
, O_RDONLY
|O_NOCTTY
)) == -1) {
151 fprintf(stderr
, _("%s: cannot open %s: %s\n"),
152 progname
, path
, strerror(errno
));
154 } else if (xfsctl(path
, fd
, FS_IOC_FSGETXATTR
, &fsx
) < 0) {
156 fprintf(stderr
, _("%s: cannot get flags on %s: %s\n"),
157 progname
, path
, strerror(errno
));
163 fsx
.fsx_xflags
&= ~FS_XFLAG_PROJINHERIT
;
164 if (xfsctl(path
, fd
, FS_IOC_FSSETXATTR
, &fsx
) < 0) {
166 fprintf(stderr
, _("%s: cannot clear project on %s: %s\n"),
167 progname
, path
, strerror(errno
));
176 const struct stat
*stat
,
183 if (recurse_depth
>= 0 && data
->level
> recurse_depth
)
186 if (flag
== FTW_NS
){
188 fprintf(stderr
, _("%s: cannot stat file %s\n"), progname
, path
);
191 if (EXCLUDED_FILE_TYPES(stat
->st_mode
)) {
192 fprintf(stderr
, _("%s: skipping special file %s\n"), progname
, path
);
196 if ((fd
= open(path
, O_RDONLY
|O_NOCTTY
)) == -1) {
198 fprintf(stderr
, _("%s: cannot open %s: %s\n"),
199 progname
, path
, strerror(errno
));
201 } else if (xfsctl(path
, fd
, FS_IOC_FSGETXATTR
, &fsx
) < 0) {
203 fprintf(stderr
, _("%s: cannot get flags on %s: %s\n"),
204 progname
, path
, strerror(errno
));
209 fsx
.fsx_projid
= prid
;
210 fsx
.fsx_xflags
|= FS_XFLAG_PROJINHERIT
;
211 if (xfsctl(path
, fd
, FS_IOC_FSSETXATTR
, &fsx
) < 0) {
213 fprintf(stderr
, _("%s: cannot set project on %s: %s\n"),
214 progname
, path
, strerror(errno
));
228 printf(_("Checking project %s (path %s)...\n"), project
, dir
);
229 nftw(dir
, check_project
, 100, FTW_PHYS
|FTW_MOUNT
);
232 printf(_("Setting up project %s (path %s)...\n"), project
, dir
);
233 nftw(dir
, setup_project
, 100, FTW_PHYS
|FTW_MOUNT
);
236 printf(_("Clearing project %s (path %s)...\n"), project
, dir
);
237 nftw(dir
, clear_project
, 100, FTW_PHYS
|FTW_MOUNT
);
251 fs_cursor_initialise(NULL
, FS_PROJECT_PATH
, &cursor
);
252 while ((path
= fs_cursor_next_entry(&cursor
))) {
253 if (prid
!= path
->fs_prid
&& path
->fs_prid
!= -1)
255 project_operations(project
, path
->fs_dir
, type
);
259 printf(_("Processed %d (%s and cmdline) paths for project %s with "
260 "recursion depth %s (%d).\n"),
261 count
, projects_file
, project
,
262 recurse_depth
< 0 ? _("infinite") : _("limited"), recurse_depth
);
270 int c
, type
= 0, ispath
= 0;
272 while ((c
= getopt(argc
, argv
, "cd:p:sC")) != EOF
) {
275 type
= CHECK_PROJECT
;
278 recurse_depth
= atoi(optarg
);
279 if (recurse_depth
< 0)
284 fs_table_insert_project_path(optarg
, -1);
287 type
= SETUP_PROJECT
;
290 type
= CLEAR_PROJECT
;
293 return command_usage(&project_cmd
);
298 return command_usage(&project_cmd
);
300 /* no options - just check the given projects */
302 type
= CHECK_PROJECT
;
305 if (!ispath
&& access(projects_file
, F_OK
) != 0) {
307 fprintf(stderr
, _("projects file \"%s\" doesn't exist\n"),
312 if (ispath
&& argc
- optind
> 1) {
314 fprintf(stderr
, _("%s: only one projid/name can be specified "
315 "when using -p <path>, %d found.\n"),
316 progname
, argc
- optind
);
320 while (argc
> optind
) {
321 prid
= prid_from_string(argv
[optind
]);
324 fprintf(stderr
, _("%s - no such project in %s "
325 "or invalid project number\n"),
326 argv
[optind
], projects_file
);
328 project(argv
[optind
], type
);
338 project_cmd
.name
= "project";
339 project_cmd
.altname
= "tree";
340 project_cmd
.cfunc
= project_f
;
341 project_cmd
.args
= _("[-c|-s|-C|-d <depth>|-p <path>] project ...");
342 project_cmd
.argmin
= 1;
343 project_cmd
.argmax
= -1;
344 project_cmd
.oneline
= _("check, setup or clear project quota trees");
345 project_cmd
.help
= project_help
;
346 project_cmd
.flags
= CMD_FLAG_FOREIGN_OK
;
349 add_command(&project_cmd
);