Whamcloud - gitweb
LU-10986 lfs: make lfs project tolerant errors
[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
51 #include "lfs_project.h"
52 #include <lustre/lustreapi.h>
53
54 struct lfs_project_item {
55         struct list_head lpi_list;
56         char lpi_pathname[PATH_MAX];
57 };
58
59 static int
60 lfs_project_item_alloc(struct list_head *head, const char *pathname)
61 {
62         struct lfs_project_item *lpi;
63
64         lpi = malloc(sizeof(struct lfs_project_item));
65         if (lpi == NULL) {
66                 fprintf(stderr,
67                         "%s: cannot allocate project item for '%s': %s\n",
68                         progname, pathname, strerror(ENOMEM));
69                 return -ENOMEM;
70         }
71
72         strncpy(lpi->lpi_pathname, pathname, sizeof(lpi->lpi_pathname));
73         list_add_tail(&lpi->lpi_list, head);
74
75         return 0;
76 }
77
78 static int project_get_xattr(const char *pathname, struct fsxattr *fsx)
79 {
80         int ret, fd;
81
82         fd = open(pathname, O_RDONLY | O_NOCTTY | O_NDELAY);
83         if (fd < 0) {
84                 fprintf(stderr, "%s: failed to open '%s': %s\n",
85                         progname, pathname, strerror(errno));
86                 return -errno;
87         }
88
89         ret = ioctl(fd, LL_IOC_FSGETXATTR, fsx);
90         if (ret) {
91                 fprintf(stderr, "%s: failed to get xattr for '%s': %s\n",
92                         progname, pathname, strerror(errno));
93                 return -errno;
94         }
95         return fd;
96 }
97
98 static int
99 project_check_one(const char *pathname, struct project_handle_control *phc)
100 {
101         struct fsxattr fsx;
102         struct stat st;
103         int ret;
104
105         ret = stat(pathname, &st);
106         if (ret) {
107                 fprintf(stderr, "%s: failed to stat '%s': %s\n",
108                         progname, pathname, strerror(errno));
109                 return -errno;
110         }
111
112         ret = project_get_xattr(pathname, &fsx);
113         if (ret < 0)
114                 return ret;
115
116         /* use top directory's project ID if not specified */
117         if (!phc->assign_projid) {
118                 phc->assign_projid = true;
119                 phc->projid = fsx.fsx_projid;
120         }
121
122         if (!(fsx.fsx_xflags & LL_PROJINHERIT_FL)) {
123                 if (!phc->newline) {
124                         printf("%s%c", pathname, '\0');
125                         goto out;
126                 }
127                  printf("%s - project inheritance flag is not set\n",
128                         pathname);
129         }
130
131         if (fsx.fsx_projid != phc->projid) {
132                 if (!phc->newline) {
133                         printf("%s%c", pathname, '\0');
134                         goto out;
135                 }
136                 printf("%s - project identifier is not set (inode=%u, tree=%u)\n",
137                        pathname, fsx.fsx_projid, phc->projid);
138         }
139 out:
140         close(ret);
141         return 0;
142 }
143
144 static int
145 project_list_one(const char *pathname, struct project_handle_control *phc)
146 {
147         struct fsxattr fsx;
148         int ret;
149
150         ret = project_get_xattr(pathname, &fsx);
151         if (ret < 0)
152                 return ret;
153
154         printf("%5u %c %s\n", fsx.fsx_projid,
155                (fsx.fsx_xflags & LL_PROJINHERIT_FL) ?
156                 'P' : '-', pathname);
157
158         close(ret);
159         return 0;
160 }
161
162 static int
163 project_set_one(const char *pathname, struct project_handle_control *phc)
164 {
165         struct fsxattr fsx;
166         int fd, ret = 0;
167
168         fd = project_get_xattr(pathname, &fsx);
169         if (fd < 0)
170                 return fd;
171
172         if ((!phc->set_projid || fsx.fsx_projid == phc->projid) &&
173             (!phc->set_inherit || (fsx.fsx_xflags & LL_PROJINHERIT_FL)))
174                 goto out;
175
176         if (phc->set_inherit)
177                 fsx.fsx_xflags |= LL_PROJINHERIT_FL;
178         if (phc->set_projid)
179                 fsx.fsx_projid = phc->projid;
180
181         ret = ioctl(fd, LL_IOC_FSSETXATTR, &fsx);
182         if (ret)
183                 fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
184                         progname, pathname, strerror(errno));
185 out:
186         close(fd);
187         return ret;
188 }
189
190 static int
191 project_clear_one(const char *pathname, struct project_handle_control *phc)
192 {
193         struct fsxattr fsx;
194         int ret = 0, fd;
195
196         fd = project_get_xattr(pathname, &fsx);
197         if (fd < 0)
198                 return fd;
199
200         if ((!(fsx.fsx_xflags & LL_PROJINHERIT_FL)) &&
201              (fsx.fsx_projid == 0 || phc->keep_projid))
202                 goto out;
203
204         fsx.fsx_xflags &= ~LL_PROJINHERIT_FL;
205         if (!phc->keep_projid)
206                 fsx.fsx_projid = 0;
207
208         ret = ioctl(fd, LL_IOC_FSSETXATTR, &fsx);
209         if (ret)
210                 fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
211                         progname, pathname, strerror(errno));
212 out:
213         close(fd);
214         return ret;
215 }
216
217 static int
218 lfs_project_handle_dir(struct list_head *head, const char *pathname,
219                        struct project_handle_control *phc,
220                        int (*func)(const char *,
221                                    struct project_handle_control *))
222 {
223         char fullname[PATH_MAX];
224         struct dirent *ent;
225         DIR *dir;
226         int ret = 0;
227         int rc;
228
229         dir = opendir(pathname);
230         if (dir == NULL) {
231                 ret = -errno;
232                 fprintf(stderr, "%s: failed to opendir '%s': %s\n",
233                         progname, pathname, strerror(-ret));
234                 return ret;
235         }
236
237         while ((ent = readdir(dir)) != NULL) {
238                 /* skip "." and ".." */
239                 if (strcmp(ent->d_name, ".") == 0 ||
240                     strcmp(ent->d_name, "..") == 0)
241                         continue;
242
243                 if (strlen(ent->d_name) + strlen(pathname) + 1 >=
244                     sizeof(fullname)) {
245                         ret = -ENAMETOOLONG;
246                         errno = ENAMETOOLONG;
247                         fprintf(stderr, "%s: ignored too long path: %s/%s\n",
248                                         progname, pathname, ent->d_name);
249                         continue;
250                 }
251                 snprintf(fullname, PATH_MAX, "%s/%s", pathname,
252                          ent->d_name);
253
254                 rc = func(fullname, phc);
255                 if (rc && !ret)
256                         ret = rc;
257                 if (phc->recursive && ent->d_type == DT_DIR) {
258                         rc = lfs_project_item_alloc(head, fullname);
259                         if (rc && !ret)
260                                 ret = rc;
261                 }
262         }
263
264         if (ret)
265                 fprintf(stderr, "%s: failed to handle dir '%s': %s\n",
266                         progname, pathname, strerror(errno));
267
268         closedir(dir);
269         return ret;
270 }
271
272 static int lfs_project_iterate(const char *pathname,
273                                struct project_handle_control *phc,
274                                int (*func)(const char *,
275                                            struct project_handle_control *))
276 {
277         struct lfs_project_item *lpi;
278         struct list_head head;
279         struct stat st;
280         int ret = 0;
281         int rc = 0;
282
283         ret = stat(pathname, &st);
284         if (ret) {
285                 fprintf(stderr, "%s: failed to stat '%s': %s\n",
286                         progname, pathname, strerror(errno));
287                 return ret;
288         }
289
290         /* list opeation will skip top directory in default */
291         if (!S_ISDIR(st.st_mode) || phc->dironly ||
292             project_list_one != func)
293                 ret = func(pathname, phc);
294
295         /* dironly first, recursive will be ignored */
296         if (!S_ISDIR(st.st_mode) || phc->dironly || ret)
297                 return ret;
298
299         INIT_LIST_HEAD(&head);
300         ret = lfs_project_item_alloc(&head, pathname);
301         if (ret)
302                 return ret;
303
304         while (!list_empty(&head)) {
305                 lpi = list_entry(head.next, struct lfs_project_item, lpi_list);
306                 list_del(&lpi->lpi_list);
307                 if (rc == 0) {
308                         rc = lfs_project_handle_dir(&head, lpi->lpi_pathname,
309                                                      phc, func);
310                         if (!ret && rc)
311                                 ret = rc;
312                 }
313                 free(lpi);
314         }
315
316         return ret;
317 }
318
319
320 inline int lfs_project_check(const char *pathname,
321                              struct project_handle_control *phc)
322 {
323         return lfs_project_iterate(pathname, phc, project_check_one);
324 }
325
326 inline int lfs_project_clear(const char *pathname,
327                              struct project_handle_control *phc)
328 {
329         return lfs_project_iterate(pathname, phc, project_clear_one);
330 }
331
332 inline int lfs_project_set(const char *pathname,
333                            struct project_handle_control *phc)
334 {
335         return lfs_project_iterate(pathname, phc, project_set_one);
336 }
337
338 inline int lfs_project_list(const char *pathname,
339                             struct project_handle_control *phc)
340 {
341         return lfs_project_iterate(pathname, phc, project_list_one);
342 }