1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
10 #include <sys/statvfs.h>
12 #include "libfrog/paths.h"
13 #include "libfrog/workqueue.h"
14 #include "xfs_scrub.h"
18 #ifndef AT_NO_AUTOMOUNT
19 # define AT_NO_AUTOMOUNT 0x800
23 * Helper functions to assist in traversing a directory tree using regular
27 /* Scan a filesystem tree. */
31 pthread_cond_t wakeup
;
34 scan_fs_tree_dir_fn dir_fn
;
35 scan_fs_tree_dirent_fn dirent_fn
;
39 /* Per-work-item scan context. */
40 struct scan_fs_tree_dir
{
42 struct scan_fs_tree
*sft
;
46 static void scan_fs_dir(struct workqueue
*wq
, xfs_agnumber_t agno
, void *arg
);
48 /* Increment the number of directories that are queued for processing. */
51 struct scan_fs_tree
*sft
)
53 pthread_mutex_lock(&sft
->lock
);
55 pthread_mutex_unlock(&sft
->lock
);
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
65 struct scan_fs_tree
*sft
)
67 pthread_mutex_lock(&sft
->lock
);
69 if (sft
->nr_dirs
== 0)
70 pthread_cond_signal(&sft
->wakeup
);
71 pthread_mutex_unlock(&sft
->lock
);
74 /* Queue a directory for scanning. */
77 struct scrub_ctx
*ctx
,
78 struct scan_fs_tree
*sft
,
83 struct scan_fs_tree_dir
*new_sftd
;
86 new_sftd
= malloc(sizeof(struct scan_fs_tree_dir
));
90 new_sftd
->path
= strdup(path
);
91 if (!new_sftd
->path
) {
97 new_sftd
->rootdir
= is_rootdir
;
100 error
= -workqueue_add(wq
, scan_fs_dir
, 0, new_sftd
);
103 str_liberror(ctx
, error
, _("queueing directory scan work"));
109 free(new_sftd
->path
);
115 /* Scan a directory sub tree. */
118 struct workqueue
*wq
,
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
;
126 struct dirent
*dirent
;
127 char newpath
[PATH_MAX
];
132 /* Open the directory. */
133 dir_fd
= open(sftd
->path
, O_RDONLY
| O_NOATIME
| O_NOFOLLOW
| O_NOCTTY
);
136 str_errno(ctx
, sftd
->path
);
140 /* Caller-specific directory checks. */
141 error
= sft
->dir_fn(ctx
, sftd
->path
, dir_fd
, sft
->arg
);
144 error
= close(dir_fd
);
146 str_errno(ctx
, sftd
->path
);
150 /* Iterate the directory entries. */
151 dir
= fdopendir(dir_fd
);
153 str_errno(ctx
, sftd
->path
);
159 for (dirent
= readdir(dir
);
160 !sft
->aborted
&& dirent
!= NULL
;
161 dirent
= readdir(dir
)) {
162 snprintf(newpath
, PATH_MAX
, "%s/%s", sftd
->path
,
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
);
169 str_errno(ctx
, newpath
);
173 /* Ignore files on other filesystems. */
174 if (sb
.st_dev
!= sft
->root_sb
.st_dev
)
177 /* Caller-specific directory entry function. */
178 error
= sft
->dirent_fn(ctx
, newpath
, dir_fd
, dirent
, &sb
,
185 if (scrub_excessive_errors(ctx
)) {
190 /* If directory, call ourselves recursively. */
191 if (S_ISDIR(sb
.st_mode
) && strcmp(".", dirent
->d_name
) &&
192 strcmp("..", dirent
->d_name
)) {
193 error
= queue_subdir(ctx
, sft
, wq
, newpath
, false);
195 str_liberror(ctx
, error
,
196 _("queueing subdirectory scan"));
203 /* Close dir, go away. */
204 error
= closedir(dir
);
206 str_errno(ctx
, sftd
->path
);
215 * Scan the entire filesystem. This function returns 0 on success; if there
216 * are errors, this function will log them and returns nonzero.
220 struct scrub_ctx
*ctx
,
221 scan_fs_tree_dir_fn dir_fn
,
222 scan_fs_tree_dirent_fn dirent_fn
,
226 struct scan_fs_tree sft
= {
227 .root_sb
= ctx
->mnt_sb
,
229 .dirent_fn
= dirent_fn
,
234 ret
= pthread_mutex_init(&sft
.lock
, NULL
);
236 str_liberror(ctx
, ret
, _("creating directory scan lock"));
239 ret
= pthread_cond_init(&sft
.wakeup
, NULL
);
241 str_liberror(ctx
, ret
, _("creating directory scan signal"));
245 ret
= -workqueue_create(&wq
, (struct xfs_mount
*)ctx
,
246 scrub_nproc_workqueue(ctx
));
248 str_liberror(ctx
, ret
, _("creating directory scan workqueue"));
252 ret
= queue_subdir(ctx
, &sft
, &wq
, ctx
->mntpoint
, true);
254 str_liberror(ctx
, ret
, _("queueing directory scan"));
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.
265 pthread_mutex_lock(&sft
.lock
);
267 pthread_cond_wait(&sft
.wakeup
, &sft
.lock
);
268 assert(sft
.nr_dirs
== 0);
269 pthread_mutex_unlock(&sft
.lock
);
271 ret
= -workqueue_terminate(&wq
);
273 str_liberror(ctx
, ret
, _("finishing directory scan work"));
277 if (!ret
&& sft
.aborted
)
281 workqueue_destroy(&wq
);
283 pthread_cond_destroy(&sft
.wakeup
);
285 pthread_mutex_destroy(&sft
.lock
);
290 struct fstrim_range
{
295 #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */
298 /* Call FITRIM to trim all the unused space in a filesystem. */
301 struct scrub_ctx
*ctx
)
303 struct fstrim_range range
= {0};
306 range
.len
= ULLONG_MAX
;
307 error
= ioctl(ctx
->mnt
.fd
, FITRIM
, &range
);
308 if (error
&& errno
!= EOPNOTSUPP
&& errno
!= ENOTTY
)