2 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
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.
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.
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.
24 #include <sys/types.h>
25 #include <sys/statvfs.h>
29 #include "workqueue.h"
30 #include "xfs_scrub.h"
34 #ifndef AT_NO_AUTOMOUNT
35 # define AT_NO_AUTOMOUNT 0x800
39 * Helper functions to assist in traversing a directory tree using regular
43 /* Scan a filesystem tree. */
47 pthread_cond_t wakeup
;
50 scan_fs_tree_dir_fn dir_fn
;
51 scan_fs_tree_dirent_fn dirent_fn
;
55 /* Per-work-item scan context. */
56 struct scan_fs_tree_dir
{
58 struct scan_fs_tree
*sft
;
62 /* Scan a directory sub tree. */
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
;
73 struct dirent
*dirent
;
74 char newpath
[PATH_MAX
];
75 struct scan_fs_tree_dir
*new_sftd
;
80 /* Open the directory. */
81 dir_fd
= open(sftd
->path
, O_RDONLY
| O_NOATIME
| O_NOFOLLOW
| O_NOCTTY
);
84 str_errno(ctx
, sftd
->path
);
88 /* Caller-specific directory checks. */
89 if (!sft
->dir_fn(ctx
, sftd
->path
, dir_fd
, sft
->arg
)) {
95 /* Iterate the directory entries. */
96 dir
= fdopendir(dir_fd
);
98 str_errno(ctx
, sftd
->path
);
103 for (dirent
= readdir(dir
); dirent
!= NULL
; dirent
= readdir(dir
)) {
104 snprintf(newpath
, PATH_MAX
, "%s/%s", sftd
->path
,
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
);
111 str_errno(ctx
, newpath
);
115 /* Ignore files on other filesystems. */
116 if (sb
.st_dev
!= sft
->root_sb
.st_dev
)
119 /* Caller-specific directory entry function. */
120 if (!sft
->dirent_fn(ctx
, newpath
, dir_fd
, dirent
, &sb
,
126 if (xfs_scrub_excessive_errors(ctx
)) {
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
));
136 str_errno(ctx
, newpath
);
140 new_sftd
->path
= strdup(newpath
);
142 new_sftd
->rootdir
= false;
143 pthread_mutex_lock(&sft
->lock
);
145 pthread_mutex_unlock(&sft
->lock
);
146 error
= workqueue_add(wq
, scan_fs_dir
, 0, new_sftd
);
148 str_info(ctx
, ctx
->mntpoint
,
149 _("Could not queue subdirectory scan work."));
156 /* Close dir, go away. */
157 error
= closedir(dir
);
159 str_errno(ctx
, sftd
->path
);
162 pthread_mutex_lock(&sft
->lock
);
164 if (sft
->nr_dirs
== 0)
165 pthread_cond_signal(&sft
->wakeup
);
166 pthread_mutex_unlock(&sft
->lock
);
172 /* Scan the entire filesystem. */
175 struct scrub_ctx
*ctx
,
176 scan_fs_tree_dir_fn dir_fn
,
177 scan_fs_tree_dirent_fn dirent_fn
,
181 struct scan_fs_tree sft
;
182 struct scan_fs_tree_dir
*sftd
;
187 sft
.root_sb
= ctx
->mnt_sb
;
189 sft
.dirent_fn
= dirent_fn
;
191 pthread_mutex_init(&sft
.lock
, NULL
);
192 pthread_cond_init(&sft
.wakeup
, NULL
);
194 sftd
= malloc(sizeof(struct scan_fs_tree_dir
));
196 str_errno(ctx
, ctx
->mntpoint
);
199 sftd
->path
= strdup(ctx
->mntpoint
);
201 sftd
->rootdir
= true;
203 ret
= workqueue_create(&wq
, (struct xfs_mount
*)ctx
,
204 scrub_nproc_workqueue(ctx
));
206 str_info(ctx
, ctx
->mntpoint
, _("Could not create workqueue."));
209 ret
= workqueue_add(&wq
, scan_fs_dir
, 0, sftd
);
211 str_info(ctx
, ctx
->mntpoint
,
212 _("Could not queue directory scan work."));
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
);
230 struct fstrim_range
{
235 #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */
238 /* Call FITRIM to trim all the unused space in a filesystem. */
241 struct scrub_ctx
*ctx
)
243 struct fstrim_range range
= {0};
246 range
.len
= ULLONG_MAX
;
247 error
= ioctl(ctx
->mnt_fd
, FITRIM
, &range
);
248 if (error
&& errno
!= EOPNOTSUPP
&& errno
!= ENOTTY
)