Whamcloud - gitweb
LU-11872 quota: add get/set project support for non-dir/file
[fs/lustre-release.git] / lustre / utils / lfs_project.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2017, DataDirect Networks Storage.
24  * Copyright (c) 2017, Intel Corporation.
25  */
26 /*
27  * This file is part of Lustre, http://www.lustre.org/
28  *
29  * lustre/utils/lfs_project.c
30  *
31  * Author: Wang Shilong <wshilong@ddn.com>
32  * Author: Fan Yong <fan.yong@intel.com>
33  */
34 #include <errno.h>
35 #include <getopt.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <dirent.h>
42 #include <stddef.h>
43 #include <libintl.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <string.h>
47 #include <libcfs/util/list.h>
48 #include <libcfs/util/ioctl.h>
49 #include <sys/ioctl.h>
50 #include <libgen.h>
51
52 #include "lfs_project.h"
53 #include <lustre/lustreapi.h>
54
55 const char      *progname;
56
57 struct lfs_project_item {
58         struct list_head lpi_list;
59         char *lpi_pathname;
60 };
61
62 static int
63 lfs_project_item_alloc(struct list_head *head, const char *pathname)
64 {
65         struct lfs_project_item *lpi;
66
67         lpi = malloc(sizeof(struct lfs_project_item));
68         if (lpi == NULL) {
69                 fprintf(stderr,
70                         "%s: cannot allocate project item for '%s': %s\n",
71                         progname, pathname, strerror(ENOMEM));
72                 return -ENOMEM;
73         }
74
75         lpi->lpi_pathname = strdup(pathname);
76         if (!lpi->lpi_pathname) {
77                 free(lpi);
78                 return -ENOMEM;
79         } else
80                 list_add_tail(&lpi->lpi_list, head);
81
82         return 0;
83 }
84
85 static int project_get_fsxattr(const char *pathname, struct fsxattr *fsx,
86                              struct stat *st, char *ret_bname)
87 {
88         int ret = 0, fd = -1;
89         char dname_path[PATH_MAX + 1] = { 0 };
90         char bname_path[PATH_MAX + 1] = { 0 };
91         char *dname, *bname;
92         struct lu_project lu_project = { 0 };
93
94         ret = lstat(pathname, st);
95         if (ret) {
96                 fprintf(stderr, "%s: failed to stat '%s': %s\n",
97                         progname, pathname, strerror(errno));
98                 ret = -errno;
99                 goto out;
100         }
101
102         /* currently, only file and dir supported */
103         if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode))
104                 goto new_api;
105
106         fd = open(pathname, O_RDONLY | O_NOCTTY | O_NDELAY);
107         if (fd < 0) {
108                 fprintf(stderr, "%s: failed to open '%s': %s\n",
109                         progname, pathname, strerror(errno));
110                 ret = -errno;
111                 goto out;
112         }
113
114         ret = ioctl(fd, FS_IOC_FSGETXATTR, fsx);
115         if (ret) {
116                 fprintf(stderr, "%s: failed to get xattr for '%s': %s\n",
117                         progname, pathname, strerror(errno));
118                 ret = -errno;
119         }
120         goto out;
121 new_api:
122         strncpy(dname_path, pathname, PATH_MAX);
123         strncpy(bname_path, pathname, PATH_MAX);
124         dname = dirname(dname_path);
125         bname = basename(bname_path);
126         fd = open(dname, O_RDONLY | O_NOCTTY | O_NDELAY);
127         if (fd < 0) {
128                 ret = -errno;
129                 goto out;
130         }
131         lu_project.project_type = LU_PROJECT_GET;
132         if (bname) {
133                 strncpy(lu_project.project_name, bname, NAME_MAX);
134                 if (ret_bname)
135                         strncpy(ret_bname, bname, NAME_MAX);
136         }
137         ret = ioctl(fd, LL_IOC_PROJECT, &lu_project);
138         if (ret) {
139                 fprintf(stderr, "%s: failed to get xattr for '%s': %s\n",
140                         progname, pathname, strerror(errno));
141                 ret = -errno;
142         } else {
143                 fsx->fsx_xflags = lu_project.project_xflags;
144                 fsx->fsx_projid = lu_project.project_id;
145         }
146 out:
147         if (ret && fd >= 0)
148                 close(fd);
149         return ret ? ret : fd;
150 }
151
152 static int
153 project_check_one(const char *pathname, struct project_handle_control *phc)
154 {
155         struct fsxattr fsx;
156         int ret;
157         struct stat st;
158
159         ret = project_get_fsxattr(pathname, &fsx, &st, NULL);
160         if (ret < 0)
161                 return ret;
162
163         /* use top directory's project ID if not specified */
164         if (!phc->assign_projid) {
165                 phc->assign_projid = true;
166                 phc->projid = fsx.fsx_projid;
167         }
168
169         if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) &&
170             S_ISDIR(st.st_mode)) {
171                 if (!phc->newline) {
172                         printf("%s%c", pathname, '\0');
173                         goto out;
174                 }
175                  printf("%s - project inheritance flag is not set\n",
176                         pathname);
177         }
178
179         if (fsx.fsx_projid != phc->projid) {
180                 if (!phc->newline) {
181                         printf("%s%c", pathname, '\0');
182                         goto out;
183                 }
184                 printf("%s - project identifier is not set (inode=%u, tree=%u)\n",
185                        pathname, fsx.fsx_projid, phc->projid);
186         }
187 out:
188         close(ret);
189         return 0;
190 }
191
192 static int
193 project_list_one(const char *pathname, struct project_handle_control *phc)
194 {
195         struct fsxattr fsx;
196         struct stat st;
197         int ret;
198
199         ret = project_get_fsxattr(pathname, &fsx, &st, NULL);
200         if (ret < 0)
201                 return ret;
202
203         printf("%5u %c %s\n", fsx.fsx_projid,
204                (fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) ?
205                 'P' : '-', pathname);
206
207         close(ret);
208         return 0;
209 }
210
211 static int
212 project_set_one(const char *pathname, struct project_handle_control *phc)
213 {
214         struct fsxattr fsx;
215         struct stat st;
216         int fd, ret = 0;
217         char bname[NAME_MAX + 1] = { 0 };
218         struct lu_project lp = { 0 };
219
220         fd = project_get_fsxattr(pathname, &fsx, &st, bname);
221         if (fd < 0)
222                 return fd;
223
224         if ((!phc->set_projid || fsx.fsx_projid == phc->projid) &&
225             (!phc->set_inherit || (fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)))
226                 goto out;
227
228         if (phc->set_inherit)
229                 fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;
230         if (phc->set_projid)
231                 fsx.fsx_projid = phc->projid;
232
233         if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
234                 ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
235         } else {
236                 lp.project_xflags = fsx.fsx_xflags;
237                 lp.project_id = fsx.fsx_projid;
238                 lp.project_type = LU_PROJECT_SET;
239                 strncpy(lp.project_name, bname, NAME_MAX);
240                 lp.project_name[NAME_MAX] = '\0';
241                 ret = ioctl(fd, LL_IOC_PROJECT, &lp);
242         }
243 out:
244         if (ret)
245                 fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
246                         progname, pathname, strerror(errno));
247         close(fd);
248         return ret;
249 }
250
251 static int
252 project_clear_one(const char *pathname, struct project_handle_control *phc)
253 {
254         struct fsxattr fsx;
255         struct stat st;
256         int ret = 0, fd;
257         char bname[NAME_MAX + 1] = { 0 };
258         struct lu_project lp = { 0 };
259
260         fd = project_get_fsxattr(pathname, &fsx, &st, bname);
261         if (fd < 0)
262                 return fd;
263
264         if ((!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)) &&
265              (fsx.fsx_projid == 0 || phc->keep_projid))
266                 goto out;
267
268         fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;
269         if (!phc->keep_projid)
270                 fsx.fsx_projid = 0;
271
272         if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
273                 ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
274         } else {
275                 lp.project_xflags = fsx.fsx_xflags;
276                 lp.project_id = fsx.fsx_projid;
277                 lp.project_type = LU_PROJECT_SET;
278                 strncpy(lp.project_name, bname, NAME_MAX);
279                 lp.project_name[NAME_MAX] = '\0';
280                 ret = ioctl(fd, LL_IOC_PROJECT, &lp);
281         }
282         if (ret)
283                 fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
284                         progname, pathname, strerror(errno));
285 out:
286         close(fd);
287         return ret;
288 }
289
290 static int
291 lfs_project_handle_dir(struct list_head *head, const char *pathname,
292                        struct project_handle_control *phc,
293                        int (*func)(const char *,
294                                    struct project_handle_control *))
295 {
296         char fullname[PATH_MAX];
297         struct dirent *ent;
298         DIR *dir;
299         int ret = 0;
300         int rc;
301
302         dir = opendir(pathname);
303         if (dir == NULL) {
304                 ret = -errno;
305                 fprintf(stderr, "%s: failed to opendir '%s': %s\n",
306                         progname, pathname, strerror(-ret));
307                 return ret;
308         }
309
310         while ((ent = readdir(dir)) != NULL) {
311                 /* skip "." and ".." */
312                 if (strcmp(ent->d_name, ".") == 0 ||
313                     strcmp(ent->d_name, "..") == 0)
314                         continue;
315
316                 if (strlen(ent->d_name) + strlen(pathname) + 1 >=
317                     sizeof(fullname)) {
318                         ret = -ENAMETOOLONG;
319                         errno = ENAMETOOLONG;
320                         fprintf(stderr, "%s: ignored too long path: %s/%s\n",
321                                         progname, pathname, ent->d_name);
322                         continue;
323                 }
324                 snprintf(fullname, PATH_MAX, "%s/%s", pathname,
325                          ent->d_name);
326
327                 rc = func(fullname, phc);
328                 if (rc && !ret)
329                         ret = rc;
330                 if (phc->recursive && ent->d_type == DT_DIR) {
331                         rc = lfs_project_item_alloc(head, fullname);
332                         if (rc && !ret)
333                                 ret = rc;
334                 }
335         }
336
337         closedir(dir);
338         return ret;
339 }
340
341 static int lfs_project_iterate(const char *pathname,
342                                struct project_handle_control *phc,
343                                int (*func)(const char *,
344                                            struct project_handle_control *))
345 {
346         struct lfs_project_item *lpi;
347         struct list_head head;
348         struct stat st;
349         int ret = 0;
350         int rc = 0;
351
352         ret = stat(pathname, &st);
353         if (ret) {
354                 fprintf(stderr, "%s: failed to stat '%s': %s\n",
355                         progname, pathname, strerror(errno));
356                 return ret;
357         }
358
359         /* list opeation will skip top directory in default */
360         if (!S_ISDIR(st.st_mode) || phc->dironly ||
361             project_list_one != func)
362                 ret = func(pathname, phc);
363
364         /* dironly first, recursive will be ignored */
365         if (!S_ISDIR(st.st_mode) || phc->dironly || ret)
366                 return ret;
367
368         INIT_LIST_HEAD(&head);
369         ret = lfs_project_item_alloc(&head, pathname);
370         if (ret)
371                 return ret;
372
373         while (!list_empty(&head)) {
374                 lpi = list_entry(head.next, struct lfs_project_item, lpi_list);
375                 list_del(&lpi->lpi_list);
376                 rc = lfs_project_handle_dir(&head, lpi->lpi_pathname,
377                                              phc, func);
378                 if (!ret && rc)
379                         ret = rc;
380                 free(lpi->lpi_pathname);
381                 free(lpi);
382         }
383
384         return ret;
385 }
386
387
388 int lfs_project_check(const char *pathname,
389                       struct project_handle_control *phc)
390 {
391         return lfs_project_iterate(pathname, phc, project_check_one);
392 }
393
394 int lfs_project_clear(const char *pathname,
395                       struct project_handle_control *phc)
396 {
397         return lfs_project_iterate(pathname, phc, project_clear_one);
398 }
399
400 int lfs_project_set(const char *pathname,
401                     struct project_handle_control *phc)
402 {
403         return lfs_project_iterate(pathname, phc, project_set_one);
404 }
405
406 int lfs_project_list(const char *pathname,
407                      struct project_handle_control *phc)
408 {
409         return lfs_project_iterate(pathname, phc, project_list_one);
410 }