]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/project.c
libxcmd: don't check generic library commands
[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
6b803e5a
CH
19#include "command.h"
20#include "input.h"
5aead01d
NS
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"
ff1f79a7 66" identifier matches the project identifier for the tree. The xfs_io utility\n"
5aead01d
NS
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"
02b26a6d
AM
79" The -p <path> option can be used to manually specify project path without\n"
80" need to create /etc/projects file. This option can be used multiple times\n"
81" to specify multiple paths. When using this option only one projid/name can\n"
82" be specified at command line. Note that /etc/projects is also used if exists.\n"
83"\n"
544f6ba0
AM
84" The -d <depth> option allows to descend at most <depth> levels of directories\n"
85" below the command line arguments. -d 0 means only apply the actions\n"
86" to the top level of the projects. -d -1 means no recursion limit (default).\n"
87"\n"
5aead01d
NS
88" The /etc/projid and /etc/projects file formats are simple, and described\n"
89" on the xfs_quota man page.\n"
90"\n"));
91}
92
93static int
94check_project(
95 const char *path,
96 const struct stat *stat,
366127f7 97 int flag,
5aead01d
NS
98 struct FTW *data)
99{
100 struct fsxattr fsx;
5aead01d
NS
101 int fd;
102
544f6ba0
AM
103 if (recurse_depth >= 0 && data->level > recurse_depth)
104 return -1;
105
366127f7 106 if (flag == FTW_NS ){
e3210fd8 107 exitcode = 1;
366127f7
DD
108 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
109 return 0;
110 }
111 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
112 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
113 return 0;
114 }
115
e3210fd8
AM
116 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
117 exitcode = 1;
5aead01d
NS
118 fprintf(stderr, _("%s: cannot open %s: %s\n"),
119 progname, path, strerror(errno));
83f4b5ac 120 } else if ((xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
e3210fd8 121 exitcode = 1;
5aead01d
NS
122 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
123 progname, path, strerror(errno));
e3210fd8 124 } else {
764b1982 125 if (fsx.fsx_projid != prid)
5aead01d
NS
126 printf(_("%s - project identifier is not set"
127 " (inode=%u, tree=%u)\n"),
2a1888c5 128 path, fsx.fsx_projid, (unsigned int)prid);
83f4b5ac 129 if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT))
5aead01d
NS
130 printf(_("%s - project inheritance flag is not set\n"),
131 path);
132 }
133 if (fd != -1)
134 close(fd);
135 return 0;
136}
137
138static int
139clear_project(
140 const char *path,
141 const struct stat *stat,
366127f7 142 int flag,
5aead01d
NS
143 struct FTW *data)
144{
145 struct fsxattr fsx;
146 int fd;
147
544f6ba0
AM
148 if (recurse_depth >= 0 && data->level > recurse_depth)
149 return -1;
150
366127f7 151 if (flag == FTW_NS ){
e3210fd8 152 exitcode = 1;
366127f7
DD
153 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
154 return 0;
155 }
156 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
157 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
158 return 0;
159 }
160
5aead01d 161 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 162 exitcode = 1;
5aead01d
NS
163 fprintf(stderr, _("%s: cannot open %s: %s\n"),
164 progname, path, strerror(errno));
165 return 0;
83f4b5ac 166 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 167 exitcode = 1;
5aead01d
NS
168 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
169 progname, path, strerror(errno));
170 close(fd);
171 return 0;
172 }
173
764b1982 174 fsx.fsx_projid = 0;
83f4b5ac
DC
175 fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;
176 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
e3210fd8 177 exitcode = 1;
764b1982 178 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
5aead01d 179 progname, path, strerror(errno));
e3210fd8 180 }
5aead01d
NS
181 close(fd);
182 return 0;
183}
184
185static int
186setup_project(
187 const char *path,
188 const struct stat *stat,
366127f7 189 int flag,
5aead01d
NS
190 struct FTW *data)
191{
192 struct fsxattr fsx;
193 int fd;
194
544f6ba0
AM
195 if (recurse_depth >= 0 && data->level > recurse_depth)
196 return -1;
197
366127f7 198 if (flag == FTW_NS ){
e3210fd8 199 exitcode = 1;
366127f7
DD
200 fprintf(stderr, _("%s: cannot stat file %s\n"), progname, path);
201 return 0;
202 }
203 if (EXCLUDED_FILE_TYPES(stat->st_mode)) {
204 fprintf(stderr, _("%s: skipping special file %s\n"), progname, path);
205 return 0;
206 }
207
5aead01d 208 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
e3210fd8 209 exitcode = 1;
5aead01d
NS
210 fprintf(stderr, _("%s: cannot open %s: %s\n"),
211 progname, path, strerror(errno));
212 return 0;
83f4b5ac 213 } else if (xfsctl(path, fd, FS_IOC_FSGETXATTR, &fsx) < 0) {
e3210fd8 214 exitcode = 1;
5aead01d
NS
215 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
216 progname, path, strerror(errno));
217 close(fd);
218 return 0;
219 }
220
764b1982 221 fsx.fsx_projid = prid;
83f4b5ac
DC
222 fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;
223 if (xfsctl(path, fd, FS_IOC_FSSETXATTR, &fsx) < 0) {
e3210fd8 224 exitcode = 1;
764b1982 225 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
5aead01d 226 progname, path, strerror(errno));
e3210fd8 227 }
5aead01d
NS
228 close(fd);
229 return 0;
230}
231
232static void
233project_operations(
234 char *project,
235 char *dir,
236 int type)
237{
238 switch (type) {
239 case CHECK_PROJECT:
240 printf(_("Checking project %s (path %s)...\n"), project, dir);
544f6ba0 241 nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
242 break;
243 case SETUP_PROJECT:
244 printf(_("Setting up project %s (path %s)...\n"), project, dir);
544f6ba0 245 nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
246 break;
247 case CLEAR_PROJECT:
248 printf(_("Clearing project %s (path %s)...\n"), project, dir);
544f6ba0 249 nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT);
5aead01d
NS
250 break;
251 }
252}
253
254static void
255project(
256 char *project,
257 int type)
258{
259 fs_cursor_t cursor;
260 fs_path_t *path;
261 int count = 0;
262
263 fs_cursor_initialise(NULL, FS_PROJECT_PATH, &cursor);
264 while ((path = fs_cursor_next_entry(&cursor))) {
02b26a6d 265 if (prid != path->fs_prid && path->fs_prid != -1)
5aead01d
NS
266 continue;
267 project_operations(project, path->fs_dir, type);
268 count++;
269 }
270
272f4db5
AM
271 printf(_("Processed %d (%s and cmdline) paths for project %s with "
272 "recursion depth %s (%d).\n"),
544f6ba0
AM
273 count, projects_file, project,
274 recurse_depth < 0 ? _("infinite") : _("limited"), recurse_depth);
5aead01d
NS
275}
276
277static int
278project_f(
279 int argc,
280 char **argv)
281{
02b26a6d 282 int c, type = 0, ispath = 0;
5aead01d 283
02b26a6d 284 while ((c = getopt(argc, argv, "cd:p:sC")) != EOF) {
5aead01d
NS
285 switch (c) {
286 case 'c':
287 type = CHECK_PROJECT;
288 break;
544f6ba0
AM
289 case 'd':
290 recurse_depth = atoi(optarg);
291 if (recurse_depth < 0)
292 recurse_depth = -1;
293 break;
02b26a6d
AM
294 case 'p':
295 ispath = 1;
296 fs_table_insert_project_path(optarg, -1);
297 break;
5aead01d
NS
298 case 's':
299 type = SETUP_PROJECT;
300 break;
301 case 'C':
302 type = CLEAR_PROJECT;
303 break;
304 default:
305 return command_usage(&project_cmd);
306 }
307 }
308
309 if (argc == optind)
310 return command_usage(&project_cmd);
311
312 /* no options - just check the given projects */
313 if (!type)
314 type = CHECK_PROJECT;
315
316 setprfiles();
02b26a6d 317 if (!ispath && access(projects_file, F_OK) != 0) {
e3210fd8 318 exitcode = 1;
5aead01d
NS
319 fprintf(stderr, _("projects file \"%s\" doesn't exist\n"),
320 projects_file);
321 return 0;
322 }
323
02b26a6d
AM
324 if (ispath && argc - optind > 1) {
325 exitcode = 1;
272f4db5
AM
326 fprintf(stderr, _("%s: only one projid/name can be specified "
327 "when using -p <path>, %d found.\n"),
328 progname, argc - optind);
02b26a6d
AM
329 return 0;
330 }
331
5aead01d
NS
332 while (argc > optind) {
333 prid = prid_from_string(argv[optind]);
e3210fd8
AM
334 if (prid == -1) {
335 exitcode = 1;
272f4db5
AM
336 fprintf(stderr, _("%s - no such project in %s "
337 "or invalid project number\n"),
5aead01d 338 argv[optind], projects_file);
e3210fd8 339 } else
5aead01d
NS
340 project(argv[optind], type);
341 optind++;
342 }
343
344 return 0;
345}
346
347void
348project_init(void)
349{
ad765595
AM
350 project_cmd.name = "project";
351 project_cmd.altname = "tree";
5aead01d 352 project_cmd.cfunc = project_f;
02b26a6d 353 project_cmd.args = _("[-c|-s|-C|-d <depth>|-p <path>] project ...");
5aead01d
NS
354 project_cmd.argmin = 1;
355 project_cmd.argmax = -1;
356 project_cmd.oneline = _("check, setup or clear project quota trees");
357 project_cmd.help = project_help;
29647c8d 358 project_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d
NS
359
360 if (expert)
361 add_command(&project_cmd);
362}