]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/vfs.c
xfs_scrub: reclassify runtime errors
[thirdparty/xfsprogs-dev.git] / scrub / vfs.c
1 /*
2 * Copyright (C) 2018 Oracle. All Rights Reserved.
3 *
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <dirent.h>
24 #include <sys/types.h>
25 #include <sys/statvfs.h>
26 #include "xfs.h"
27 #include "handle.h"
28 #include "path.h"
29 #include "workqueue.h"
30 #include "xfs_scrub.h"
31 #include "common.h"
32 #include "vfs.h"
33
34 #ifndef AT_NO_AUTOMOUNT
35 # define AT_NO_AUTOMOUNT 0x800
36 #endif
37
38 /*
39 * Helper functions to assist in traversing a directory tree using regular
40 * VFS calls.
41 */
42
43 /* Scan a filesystem tree. */
44 struct scan_fs_tree {
45 unsigned int nr_dirs;
46 pthread_mutex_t lock;
47 pthread_cond_t wakeup;
48 struct stat root_sb;
49 bool moveon;
50 scan_fs_tree_dir_fn dir_fn;
51 scan_fs_tree_dirent_fn dirent_fn;
52 void *arg;
53 };
54
55 /* Per-work-item scan context. */
56 struct scan_fs_tree_dir {
57 char *path;
58 struct scan_fs_tree *sft;
59 bool rootdir;
60 };
61
62 /* Scan a directory sub tree. */
63 static void
64 scan_fs_dir(
65 struct workqueue *wq,
66 xfs_agnumber_t agno,
67 void *arg)
68 {
69 struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
70 struct scan_fs_tree_dir *sftd = arg;
71 struct scan_fs_tree *sft = sftd->sft;
72 DIR *dir;
73 struct dirent *dirent;
74 char newpath[PATH_MAX];
75 struct scan_fs_tree_dir *new_sftd;
76 struct stat sb;
77 int dir_fd;
78 int error;
79
80 /* Open the directory. */
81 dir_fd = open(sftd->path, O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
82 if (dir_fd < 0) {
83 if (errno != ENOENT)
84 str_errno(ctx, sftd->path);
85 goto out;
86 }
87
88 /* Caller-specific directory checks. */
89 if (!sft->dir_fn(ctx, sftd->path, dir_fd, sft->arg)) {
90 sft->moveon = false;
91 close(dir_fd);
92 goto out;
93 }
94
95 /* Iterate the directory entries. */
96 dir = fdopendir(dir_fd);
97 if (!dir) {
98 str_errno(ctx, sftd->path);
99 close(dir_fd);
100 goto out;
101 }
102 rewinddir(dir);
103 for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) {
104 snprintf(newpath, PATH_MAX, "%s/%s", sftd->path,
105 dirent->d_name);
106
107 /* Get the stat info for this directory entry. */
108 error = fstatat(dir_fd, dirent->d_name, &sb,
109 AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
110 if (error) {
111 str_errno(ctx, newpath);
112 continue;
113 }
114
115 /* Ignore files on other filesystems. */
116 if (sb.st_dev != sft->root_sb.st_dev)
117 continue;
118
119 /* Caller-specific directory entry function. */
120 if (!sft->dirent_fn(ctx, newpath, dir_fd, dirent, &sb,
121 sft->arg)) {
122 sft->moveon = false;
123 break;
124 }
125
126 if (xfs_scrub_excessive_errors(ctx)) {
127 sft->moveon = false;
128 break;
129 }
130
131 /* If directory, call ourselves recursively. */
132 if (S_ISDIR(sb.st_mode) && strcmp(".", dirent->d_name) &&
133 strcmp("..", dirent->d_name)) {
134 new_sftd = malloc(sizeof(struct scan_fs_tree_dir));
135 if (!new_sftd) {
136 str_errno(ctx, newpath);
137 sft->moveon = false;
138 break;
139 }
140 new_sftd->path = strdup(newpath);
141 new_sftd->sft = sft;
142 new_sftd->rootdir = false;
143 pthread_mutex_lock(&sft->lock);
144 sft->nr_dirs++;
145 pthread_mutex_unlock(&sft->lock);
146 error = workqueue_add(wq, scan_fs_dir, 0, new_sftd);
147 if (error) {
148 str_info(ctx, ctx->mntpoint,
149 _("Could not queue subdirectory scan work."));
150 sft->moveon = false;
151 break;
152 }
153 }
154 }
155
156 /* Close dir, go away. */
157 error = closedir(dir);
158 if (error)
159 str_errno(ctx, sftd->path);
160
161 out:
162 pthread_mutex_lock(&sft->lock);
163 sft->nr_dirs--;
164 if (sft->nr_dirs == 0)
165 pthread_cond_signal(&sft->wakeup);
166 pthread_mutex_unlock(&sft->lock);
167
168 free(sftd->path);
169 free(sftd);
170 }
171
172 /* Scan the entire filesystem. */
173 bool
174 scan_fs_tree(
175 struct scrub_ctx *ctx,
176 scan_fs_tree_dir_fn dir_fn,
177 scan_fs_tree_dirent_fn dirent_fn,
178 void *arg)
179 {
180 struct workqueue wq;
181 struct scan_fs_tree sft;
182 struct scan_fs_tree_dir *sftd;
183 int ret;
184
185 sft.moveon = true;
186 sft.nr_dirs = 1;
187 sft.root_sb = ctx->mnt_sb;
188 sft.dir_fn = dir_fn;
189 sft.dirent_fn = dirent_fn;
190 sft.arg = arg;
191 pthread_mutex_init(&sft.lock, NULL);
192 pthread_cond_init(&sft.wakeup, NULL);
193
194 sftd = malloc(sizeof(struct scan_fs_tree_dir));
195 if (!sftd) {
196 str_errno(ctx, ctx->mntpoint);
197 return false;
198 }
199 sftd->path = strdup(ctx->mntpoint);
200 sftd->sft = &sft;
201 sftd->rootdir = true;
202
203 ret = workqueue_create(&wq, (struct xfs_mount *)ctx,
204 scrub_nproc_workqueue(ctx));
205 if (ret) {
206 str_info(ctx, ctx->mntpoint, _("Could not create workqueue."));
207 goto out_free;
208 }
209 ret = workqueue_add(&wq, scan_fs_dir, 0, sftd);
210 if (ret) {
211 str_info(ctx, ctx->mntpoint,
212 _("Could not queue directory scan work."));
213 goto out_free;
214 }
215
216 pthread_mutex_lock(&sft.lock);
217 pthread_cond_wait(&sft.wakeup, &sft.lock);
218 assert(sft.nr_dirs == 0);
219 pthread_mutex_unlock(&sft.lock);
220 workqueue_destroy(&wq);
221
222 return sft.moveon;
223 out_free:
224 free(sftd->path);
225 free(sftd);
226 return false;
227 }
228
229 #ifndef FITRIM
230 struct fstrim_range {
231 __u64 start;
232 __u64 len;
233 __u64 minlen;
234 };
235 #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */
236 #endif
237
238 /* Call FITRIM to trim all the unused space in a filesystem. */
239 void
240 fstrim(
241 struct scrub_ctx *ctx)
242 {
243 struct fstrim_range range = {0};
244 int error;
245
246 range.len = ULLONG_MAX;
247 error = ioctl(ctx->mnt_fd, FITRIM, &range);
248 if (error && errno != EOPNOTSUPP && errno != ENOTTY)
249 perror(_("fstrim"));
250 }