]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/project.c
xfsprogs: Validate string -> number conversion
[thirdparty/xfsprogs-dev.git] / quota / project.c
1 /*
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
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
7 * published by the Free Software Foundation.
8 *
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.
13 *
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
17 */
18
19 #include <xfs/command.h>
20 #include <xfs/input.h>
21 #include "init.h"
22 #include "quota.h"
23
24 static cmdinfo_t project_cmd;
25 static prid_t prid;
26 static int recurse_depth = -1;
27
28 enum {
29 CHECK_PROJECT = 0x1,
30 SETUP_PROJECT = 0x2,
31 CLEAR_PROJECT = 0x4,
32 };
33
34 #define EXCLUDED_FILE_TYPES(x) \
35 (S_ISCHR((x)) \
36 || S_ISBLK((x)) \
37 || S_ISFIFO((x)) \
38 || S_ISLNK((x)) \
39 || S_ISSOCK((x)))
40
41 static void
42 project_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 identifier 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"
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"
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"
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
93 static int
94 check_project(
95 const char *path,
96 const struct stat *stat,
97 int flag,
98 struct FTW *data)
99 {
100 struct fsxattr fsx;
101 int fd;
102
103 if (recurse_depth >= 0 && data->level > recurse_depth)
104 return -1;
105
106 if (flag == FTW_NS ){
107 exitcode = 1;
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
116 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
117 exitcode = 1;
118 fprintf(stderr, _("%s: cannot open %s: %s\n"),
119 progname, path, strerror(errno));
120 } else if ((xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
121 exitcode = 1;
122 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
123 progname, path, strerror(errno));
124 } else {
125 if (fsx.fsx_projid != prid)
126 printf(_("%s - project identifier is not set"
127 " (inode=%u, tree=%u)\n"),
128 path, fsx.fsx_projid, (unsigned int)prid);
129 if (!(fsx.fsx_xflags & XFS_XFLAG_PROJINHERIT))
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
138 static int
139 clear_project(
140 const char *path,
141 const struct stat *stat,
142 int flag,
143 struct FTW *data)
144 {
145 struct fsxattr fsx;
146 int fd;
147
148 if (recurse_depth >= 0 && data->level > recurse_depth)
149 return -1;
150
151 if (flag == FTW_NS ){
152 exitcode = 1;
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
161 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
162 exitcode = 1;
163 fprintf(stderr, _("%s: cannot open %s: %s\n"),
164 progname, path, strerror(errno));
165 return 0;
166 } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) {
167 exitcode = 1;
168 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
169 progname, path, strerror(errno));
170 close(fd);
171 return 0;
172 }
173
174 fsx.fsx_projid = 0;
175 fsx.fsx_xflags &= ~XFS_XFLAG_PROJINHERIT;
176 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
177 exitcode = 1;
178 fprintf(stderr, _("%s: cannot clear project on %s: %s\n"),
179 progname, path, strerror(errno));
180 }
181 close(fd);
182 return 0;
183 }
184
185 static int
186 setup_project(
187 const char *path,
188 const struct stat *stat,
189 int flag,
190 struct FTW *data)
191 {
192 struct fsxattr fsx;
193 int fd;
194
195 if (recurse_depth >= 0 && data->level > recurse_depth)
196 return -1;
197
198 if (flag == FTW_NS ){
199 exitcode = 1;
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
208 if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) {
209 exitcode = 1;
210 fprintf(stderr, _("%s: cannot open %s: %s\n"),
211 progname, path, strerror(errno));
212 return 0;
213 } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) {
214 exitcode = 1;
215 fprintf(stderr, _("%s: cannot get flags on %s: %s\n"),
216 progname, path, strerror(errno));
217 close(fd);
218 return 0;
219 }
220
221 fsx.fsx_projid = prid;
222 fsx.fsx_xflags |= XFS_XFLAG_PROJINHERIT;
223 if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) {
224 exitcode = 1;
225 fprintf(stderr, _("%s: cannot set project on %s: %s\n"),
226 progname, path, strerror(errno));
227 }
228 close(fd);
229 return 0;
230 }
231
232 static void
233 project_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);
241 nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT);
242 break;
243 case SETUP_PROJECT:
244 printf(_("Setting up project %s (path %s)...\n"), project, dir);
245 nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT);
246 break;
247 case CLEAR_PROJECT:
248 printf(_("Clearing project %s (path %s)...\n"), project, dir);
249 nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT);
250 break;
251 }
252 }
253
254 static void
255 project(
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))) {
265 if (prid != path->fs_prid && path->fs_prid != -1)
266 continue;
267 project_operations(project, path->fs_dir, type);
268 count++;
269 }
270
271 printf(_("Processed %d (%s and cmdline) paths for project %s with "
272 "recursion depth %s (%d).\n"),
273 count, projects_file, project,
274 recurse_depth < 0 ? _("infinite") : _("limited"), recurse_depth);
275 }
276
277 static int
278 project_f(
279 int argc,
280 char **argv)
281 {
282 int c, type = 0, ispath = 0;
283
284 while ((c = getopt(argc, argv, "cd:p:sC")) != EOF) {
285 switch (c) {
286 case 'c':
287 type = CHECK_PROJECT;
288 break;
289 case 'd':
290 recurse_depth = atoi(optarg);
291 if (recurse_depth < 0)
292 recurse_depth = -1;
293 break;
294 case 'p':
295 ispath = 1;
296 fs_table_insert_project_path(optarg, -1);
297 break;
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();
317 if (!ispath && access(projects_file, F_OK) != 0) {
318 exitcode = 1;
319 fprintf(stderr, _("projects file \"%s\" doesn't exist\n"),
320 projects_file);
321 return 0;
322 }
323
324 if (ispath && argc - optind > 1) {
325 exitcode = 1;
326 fprintf(stderr, _("%s: only one projid/name can be specified "
327 "when using -p <path>, %d found.\n"),
328 progname, argc - optind);
329 return 0;
330 }
331
332 while (argc > optind) {
333 prid = prid_from_string(argv[optind]);
334 if (prid == -1) {
335 exitcode = 1;
336 fprintf(stderr, _("%s - no such project in %s "
337 "or invalid project number\n"),
338 argv[optind], projects_file);
339 } else
340 project(argv[optind], type);
341 optind++;
342 }
343
344 return 0;
345 }
346
347 void
348 project_init(void)
349 {
350 project_cmd.name = _("project");
351 project_cmd.altname = _("tree");
352 project_cmd.cfunc = project_f;
353 project_cmd.args = _("[-c|-s|-C|-d <depth>|-p <path>] project ...");
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;
358
359 if (expert)
360 add_command(&project_cmd);
361 }