]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/vfs.c
Merge tag 'scrub-fix-legalese-6.6_2024-01-11' of https://git.kernel.org/pub/scm/linux...
[thirdparty/xfsprogs-dev.git] / scrub / vfs.c
CommitLineData
8d318d62 1// SPDX-License-Identifier: GPL-2.0-or-later
b364a9c0 2/*
52520522 3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
8d318d62 4 * Author: Darrick J. Wong <djwong@kernel.org>
b364a9c0 5 */
a440f877 6#include "xfs.h"
b364a9c0 7#include <stdint.h>
b364a9c0
DW
8#include <dirent.h>
9#include <sys/types.h>
10#include <sys/statvfs.h>
b364a9c0 11#include "handle.h"
42b4c8e8 12#include "libfrog/paths.h"
56598728 13#include "libfrog/workqueue.h"
b364a9c0
DW
14#include "xfs_scrub.h"
15#include "common.h"
16#include "vfs.h"
17
18#ifndef AT_NO_AUTOMOUNT
19# define AT_NO_AUTOMOUNT 0x800
20#endif
21
22/*
23 * Helper functions to assist in traversing a directory tree using regular
24 * VFS calls.
25 */
26
27/* Scan a filesystem tree. */
28struct scan_fs_tree {
29 unsigned int nr_dirs;
30 pthread_mutex_t lock;
31 pthread_cond_t wakeup;
32 struct stat root_sb;
f544ec31 33 bool aborted;
b364a9c0
DW
34 scan_fs_tree_dir_fn dir_fn;
35 scan_fs_tree_dirent_fn dirent_fn;
36 void *arg;
37};
38
39/* Per-work-item scan context. */
40struct scan_fs_tree_dir {
41 char *path;
42 struct scan_fs_tree *sft;
43 bool rootdir;
44};
45
83630b7f
DW
46static void scan_fs_dir(struct workqueue *wq, xfs_agnumber_t agno, void *arg);
47
4953e709
DW
48/* Increment the number of directories that are queued for processing. */
49static void
50inc_nr_dirs(
51 struct scan_fs_tree *sft)
52{
53 pthread_mutex_lock(&sft->lock);
54 sft->nr_dirs++;
55 pthread_mutex_unlock(&sft->lock);
56}
57
58/*
59 * Decrement the number of directories that are queued for processing and if
60 * we ran out of dirs to process, wake up anyone who was waiting for processing
61 * to finish.
62 */
63static void
64dec_nr_dirs(
65 struct scan_fs_tree *sft)
66{
67 pthread_mutex_lock(&sft->lock);
68 sft->nr_dirs--;
69 if (sft->nr_dirs == 0)
70 pthread_cond_signal(&sft->wakeup);
71 pthread_mutex_unlock(&sft->lock);
72}
73
83630b7f 74/* Queue a directory for scanning. */
f0bbbd72 75static int
83630b7f
DW
76queue_subdir(
77 struct scrub_ctx *ctx,
78 struct scan_fs_tree *sft,
79 struct workqueue *wq,
80 const char *path,
81 bool is_rootdir)
82{
83 struct scan_fs_tree_dir *new_sftd;
84 int error;
85
86 new_sftd = malloc(sizeof(struct scan_fs_tree_dir));
f0bbbd72
DW
87 if (!new_sftd)
88 return errno;
83630b7f
DW
89
90 new_sftd->path = strdup(path);
91 if (!new_sftd->path) {
f0bbbd72 92 error = errno;
83630b7f
DW
93 goto out_sftd;
94 }
95
96 new_sftd->sft = sft;
97 new_sftd->rootdir = is_rootdir;
98
4953e709 99 inc_nr_dirs(sft);
baed134d 100 error = -workqueue_add(wq, scan_fs_dir, 0, new_sftd);
83630b7f 101 if (error) {
4953e709 102 dec_nr_dirs(sft);
9d57cbfc 103 str_liberror(ctx, error, _("queueing directory scan work"));
83630b7f
DW
104 goto out_path;
105 }
106
f0bbbd72 107 return 0;
83630b7f
DW
108out_path:
109 free(new_sftd->path);
110out_sftd:
111 free(new_sftd);
f0bbbd72 112 return error;
83630b7f
DW
113}
114
b364a9c0
DW
115/* Scan a directory sub tree. */
116static void
117scan_fs_dir(
118 struct workqueue *wq,
119 xfs_agnumber_t agno,
120 void *arg)
121{
122 struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
123 struct scan_fs_tree_dir *sftd = arg;
124 struct scan_fs_tree *sft = sftd->sft;
125 DIR *dir;
126 struct dirent *dirent;
127 char newpath[PATH_MAX];
b364a9c0
DW
128 struct stat sb;
129 int dir_fd;
130 int error;
131
132 /* Open the directory. */
133 dir_fd = open(sftd->path, O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
134 if (dir_fd < 0) {
135 if (errno != ENOENT)
136 str_errno(ctx, sftd->path);
137 goto out;
138 }
139
140 /* Caller-specific directory checks. */
f544ec31
DW
141 error = sft->dir_fn(ctx, sftd->path, dir_fd, sft->arg);
142 if (error) {
143 sft->aborted = true;
6c05cc5d
DW
144 error = close(dir_fd);
145 if (error)
146 str_errno(ctx, sftd->path);
b364a9c0
DW
147 goto out;
148 }
149
150 /* Iterate the directory entries. */
151 dir = fdopendir(dir_fd);
152 if (!dir) {
153 str_errno(ctx, sftd->path);
f544ec31 154 sft->aborted = true;
e91c285f 155 close(dir_fd);
b364a9c0
DW
156 goto out;
157 }
158 rewinddir(dir);
f544ec31
DW
159 for (dirent = readdir(dir);
160 !sft->aborted && dirent != NULL;
161 dirent = readdir(dir)) {
b364a9c0
DW
162 snprintf(newpath, PATH_MAX, "%s/%s", sftd->path,
163 dirent->d_name);
164
165 /* Get the stat info for this directory entry. */
166 error = fstatat(dir_fd, dirent->d_name, &sb,
167 AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
168 if (error) {
169 str_errno(ctx, newpath);
170 continue;
171 }
172
173 /* Ignore files on other filesystems. */
174 if (sb.st_dev != sft->root_sb.st_dev)
175 continue;
176
177 /* Caller-specific directory entry function. */
f544ec31
DW
178 error = sft->dirent_fn(ctx, newpath, dir_fd, dirent, &sb,
179 sft->arg);
180 if (error) {
181 sft->aborted = true;
b364a9c0
DW
182 break;
183 }
184
273165cc 185 if (scrub_excessive_errors(ctx)) {
f544ec31 186 sft->aborted = true;
b364a9c0
DW
187 break;
188 }
189
190 /* If directory, call ourselves recursively. */
191 if (S_ISDIR(sb.st_mode) && strcmp(".", dirent->d_name) &&
192 strcmp("..", dirent->d_name)) {
f0bbbd72
DW
193 error = queue_subdir(ctx, sft, wq, newpath, false);
194 if (error) {
195 str_liberror(ctx, error,
196_("queueing subdirectory scan"));
f544ec31 197 sft->aborted = true;
b364a9c0 198 break;
f0bbbd72 199 }
b364a9c0
DW
200 }
201 }
202
203 /* Close dir, go away. */
204 error = closedir(dir);
205 if (error)
206 str_errno(ctx, sftd->path);
207
208out:
4953e709 209 dec_nr_dirs(sft);
b364a9c0
DW
210 free(sftd->path);
211 free(sftd);
212}
213
f544ec31
DW
214/*
215 * Scan the entire filesystem. This function returns 0 on success; if there
216 * are errors, this function will log them and returns nonzero.
217 */
218int
b364a9c0
DW
219scan_fs_tree(
220 struct scrub_ctx *ctx,
221 scan_fs_tree_dir_fn dir_fn,
222 scan_fs_tree_dirent_fn dirent_fn,
223 void *arg)
224{
225 struct workqueue wq;
f544ec31
DW
226 struct scan_fs_tree sft = {
227 .root_sb = ctx->mnt_sb,
228 .dir_fn = dir_fn,
229 .dirent_fn = dirent_fn,
230 .arg = arg,
231 };
b364a9c0
DW
232 int ret;
233
499c104f
DW
234 ret = pthread_mutex_init(&sft.lock, NULL);
235 if (ret) {
236 str_liberror(ctx, ret, _("creating directory scan lock"));
f544ec31 237 return ret;
499c104f
DW
238 }
239 ret = pthread_cond_init(&sft.wakeup, NULL);
240 if (ret) {
241 str_liberror(ctx, ret, _("creating directory scan signal"));
242 goto out_mutex;
243 }
b364a9c0 244
baed134d 245 ret = -workqueue_create(&wq, (struct xfs_mount *)ctx,
b364a9c0
DW
246 scrub_nproc_workqueue(ctx));
247 if (ret) {
499c104f
DW
248 str_liberror(ctx, ret, _("creating directory scan workqueue"));
249 goto out_cond;
b364a9c0 250 }
83630b7f 251
f0bbbd72
DW
252 ret = queue_subdir(ctx, &sft, &wq, ctx->mntpoint, true);
253 if (ret) {
254 str_liberror(ctx, ret, _("queueing directory scan"));
224df902 255 goto out_wq;
f0bbbd72 256 }
b364a9c0 257
83630b7f
DW
258 /*
259 * Wait for the wakeup to trigger, which should only happen when the
260 * last worker thread decrements nr_dirs to zero. Once the worker
261 * triggers the wakeup and unlocks the sft lock, it's no longer safe
262 * for any worker thread to access sft, as we now own the lock and are
263 * about to tear everything down.
264 */
b364a9c0 265 pthread_mutex_lock(&sft.lock);
817d1b67 266 while (sft.nr_dirs > 0)
44012ab0 267 pthread_cond_wait(&sft.wakeup, &sft.lock);
b364a9c0
DW
268 assert(sft.nr_dirs == 0);
269 pthread_mutex_unlock(&sft.lock);
b364a9c0 270
baed134d 271 ret = -workqueue_terminate(&wq);
71296cf8 272 if (ret) {
71296cf8 273 str_liberror(ctx, ret, _("finishing directory scan work"));
499c104f 274 goto out_wq;
71296cf8 275 }
499c104f 276
f544ec31
DW
277 if (!ret && sft.aborted)
278 ret = -1;
279
224df902
DW
280out_wq:
281 workqueue_destroy(&wq);
499c104f
DW
282out_cond:
283 pthread_cond_destroy(&sft.wakeup);
284out_mutex:
285 pthread_mutex_destroy(&sft.lock);
f544ec31 286 return ret;
b364a9c0 287}
7e36bc0f
DW
288
289#ifndef FITRIM
290struct fstrim_range {
291 __u64 start;
292 __u64 len;
293 __u64 minlen;
294};
295#define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */
296#endif
297
298/* Call FITRIM to trim all the unused space in a filesystem. */
299void
300fstrim(
301 struct scrub_ctx *ctx)
302{
303 struct fstrim_range range = {0};
304 int error;
305
306 range.len = ULLONG_MAX;
3f9efb2e 307 error = ioctl(ctx->mnt.fd, FITRIM, &range);
7e36bc0f
DW
308 if (error && errno != EOPNOTSUPP && errno != ENOTTY)
309 perror(_("fstrim"));
310}