+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002 Silicon Graphics, Inc.
* All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <xfs/xfs.h>
-#include <xfs/jdm.h>
-#include <xfs/xfs_dfrag.h>
+#include "libxfs.h"
+#include "xfs.h"
+#include "xfs_types.h"
+#include "jdm.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "libfrog/paths.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/bulkstat.h"
#include <fcntl.h>
#include <errno.h>
-#include <malloc.h>
-#include <mntent.h>
#include <syslog.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
-#include <sys/vfs.h>
#include <sys/statvfs.h>
#include <sys/xattr.h>
+#include <paths.h>
+#define _PATH_FSRLAST "/var/tmp/.fsrlast_xfs"
+#define _PATH_PROC_MOUNTS "/proc/mounts"
-#ifndef XFS_XFLAG_NODEFRAG
-#define XFS_XFLAG_NODEFRAG 0x00002000 /* src dependancy, remove later */
-#endif
-
-#define _PATH_FSRLAST "/var/tmp/.fsrlast_xfs"
char *progname;
-int vflag;
-int gflag;
+static int vflag;
+static int gflag;
static int Mflag;
/* static int nflag; */
-int dflag = 0;
+static int dflag = 0;
/* static int sflag; */
-int argv_blksz_dio;
+static int argv_blksz_dio;
extern int max_ext_size;
static int npasses = 10;
static int startpass = 0;
-struct getbmap *outmap = NULL;
-int outmap_size = 0;
-int RealUid;
-int tmp_agi;
-static __int64_t minimumfree = 2048;
+static struct getbmap *outmap = NULL;
+static int outmap_size = 0;
+static int RealUid;
+static int tmp_agi;
+static int64_t minimumfree = 2048;
#define MNTTYPE_XFS "xfs"
#define NULLFD -1
#define GRABSZ 64
#define TARGETRANGE 10
-#define V_NONE 0
-#define V_OVERVIEW 1
-#define V_ALL 2
-#define BUFFER_SIZE (1<<16)
#define BUFFER_MAX (1<<24)
-#define min(x, y) ((x) < (y) ? (x) : (y))
static time_t howlong = 7200; /* default seconds of reorganizing */
static char *leftofffile = _PATH_FSRLAST; /* where we left off last */
-static char *mtab = MOUNTED;
static time_t endtime;
static time_t starttime;
static xfs_ino_t leftoffino = 0;
void usage(int ret);
static int fsrfile(char *fname, xfs_ino_t ino);
static int fsrfile_common( char *fname, char *tname, char *mnt,
- int fd, xfs_bstat_t *statp);
+ int fd, struct xfs_bstat *statp);
static int packfile(char *fname, char *tname, int fd,
- xfs_bstat_t *statp, struct fsxattr *fsxp);
+ struct xfs_bstat *statp, struct fsxattr *fsxp);
static void fsrdir(char *dirname);
static int fsrfs(char *mntdir, xfs_ino_t ino, int targetrange);
static void initallfs(char *mtab);
-static void fsrallfs(int howlong, char *leftofffile);
+static void fsrallfs(char *mtab, int howlong, char *leftofffile);
static void fsrall_cleanup(int timeout);
static int getnextents(int);
int xfsrtextsize(int fd);
-int xfs_getrt(int fd, struct statvfs64 *sfbp);
+int xfs_getrt(int fd, struct statvfs *sfbp);
char * gettmpname(char *fname);
char * getparent(char *fname);
int fsrprintf(const char *fmt, ...);
-int read_fd_bmap(int, xfs_bstat_t *, int *);
+int read_fd_bmap(int, struct xfs_bstat *, int *);
int cmp(const void *, const void *);
static void tmp_init(char *mnt);
static char * tmp_next(char *mnt);
static void tmp_close(char *mnt);
-int xfs_getgeom(int , xfs_fsop_geom_v1_t * );
-static int getmntany(FILE *, struct mntent *, struct mntent *, struct stat64 *);
-xfs_fsop_geom_v1_t fsgeom; /* geometry of active mounted system */
+static struct xfs_fsop_geom fsgeom; /* geometry of active mounted system */
#define NMOUNT 64
static int numfs;
int npass;
} fsdesc_t;
-fsdesc_t *fs, *fsbase, *fsend;
-int fsbufsize = 10; /* A starting value */
-int nfrags = 0; /* Debug option: Coerse into specific number
+static fsdesc_t *fs, *fsbase, *fsend;
+static int fsbufsize = 10; /* A starting value */
+static int nfrags = 0; /* Debug option: Coerse into specific number
* of extents */
-int openopts = O_CREAT|O_EXCL|O_RDWR|O_DIRECT;
-
-int
-xfs_fsgeometry(int fd, xfs_fsop_geom_v1_t *geom)
-{
- return ioctl(fd, XFS_IOC_FSGEOMETRY_V1, geom);
-}
-
-int
-xfs_bulkstat_single(int fd, xfs_ino_t *lastip, xfs_bstat_t *ubuffer)
-{
- xfs_fsop_bulkreq_t bulkreq;
-
- bulkreq.lastip = lastip;
- bulkreq.icount = 1;
- bulkreq.ubuffer = ubuffer;
- bulkreq.ocount = NULL;
- return ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
-}
-
-int
-xfs_bulkstat(int fd, xfs_ino_t *lastip, int icount,
- xfs_bstat_t *ubuffer, __s32 *ocount)
-{
- xfs_fsop_bulkreq_t bulkreq;
-
- bulkreq.lastip = lastip;
- bulkreq.icount = icount;
- bulkreq.ubuffer = ubuffer;
- bulkreq.ocount = ocount;
- return ioctl(fd, XFS_IOC_FSBULKSTAT, &bulkreq);
-}
+static int openopts = O_CREAT|O_EXCL|O_RDWR|O_DIRECT;
-int
+static int
xfs_swapext(int fd, xfs_swapext_t *sx)
{
return ioctl(fd, XFS_IOC_SWAPEXT, sx);
}
-int
+static int
xfs_fscounts(int fd, xfs_fsop_counts_t *counts)
{
return ioctl(fd, XFS_IOC_FSCOUNTS, counts);
}
-void
+static void
aborter(int unused)
{
fsrall_cleanup(1);
int
main(int argc, char **argv)
{
- struct stat64 sb, sb2;
+ struct stat sb;
char *argname;
- char *cp;
int c;
- struct mntent mntpref;
- register struct mntent *mntp;
- struct mntent ment;
- register FILE *mtabp;
+ struct fs_path *fsp;
+ char *mtab = NULL;
setlinebuf(stdout);
progname = basename(argv[0]);
gflag = ! isatty(0);
- while ((c = getopt(argc, argv, "C:p:e:MgsdnvTt:f:m:b:N:FV")) != -1 )
+ while ((c = getopt(argc, argv, "C:p:e:MgsdnvTt:f:m:b:N:FV")) != -1) {
switch (c) {
case 'M':
Mflag = 1;
default:
usage(1);
}
+ }
+
+ /*
+ * If the user did not specify an explicit mount table, try to use
+ * /proc/mounts if it is available, else /etc/mtab. We prefer
+ * /proc/mounts because it is kernel controlled, while /etc/mtab
+ * may contain garbage that userspace tools like pam_mounts wrote
+ * into it.
+ */
+ if (!mtab) {
+ if (access(_PATH_PROC_MOUNTS, R_OK) == 0)
+ mtab = _PATH_PROC_MOUNTS;
+ else
+ mtab = _PATH_MOUNTED;
+ }
+
if (vflag)
setbuf(stdout, NULL);
- starttime = time(0);
+ starttime = time(NULL);
/* Save the caller's real uid */
RealUid = getuid();
pagesize = getpagesize();
-
+ fs_table_initialise(0, NULL, 0, NULL);
if (optind < argc) {
for (; optind < argc; optind++) {
argname = argv[optind];
- mntp = NULL;
- if (lstat64(argname, &sb) < 0) {
+
+ if (lstat(argname, &sb) < 0) {
fprintf(stderr,
_("%s: could not stat: %s: %s\n"),
progname, argname, strerror(errno));
continue;
}
- if (S_ISLNK(sb.st_mode) && stat64(argname, &sb2) == 0 &&
- (S_ISBLK(sb2.st_mode) || S_ISCHR(sb2.st_mode)))
+
+ if (S_ISLNK(sb.st_mode)) {
+ struct stat sb2;
+
+ if (stat(argname, &sb2) == 0 &&
+ (S_ISBLK(sb2.st_mode) ||
+ S_ISCHR(sb2.st_mode)))
sb = sb2;
- if (S_ISBLK(sb.st_mode) || (S_ISDIR(sb.st_mode))) {
- if ((mtabp = setmntent(mtab, "r")) == NULL) {
- fprintf(stderr,
- _("%s: cannot read %s\n"),
- progname, mtab);
- exit(1);
- }
- bzero(&mntpref, sizeof(mntpref));
- if (S_ISDIR(sb.st_mode))
- mntpref.mnt_dir = argname;
- else
- mntpref.mnt_fsname = argname;
-
- if (getmntany(mtabp, &ment, &mntpref, &sb) &&
- strcmp(ment.mnt_type, MNTTYPE_XFS) == 0) {
- mntp = &ment;
- if (S_ISBLK(sb.st_mode)) {
- cp = mntp->mnt_dir;
- if (cp == NULL ||
- stat64(cp, &sb2) < 0) {
- fprintf(stderr, _(
- "%s: could not stat: %s: %s\n"),
- progname, argname,
- strerror(errno));
- continue;
- }
- sb = sb2;
- argname = cp;
- }
- }
}
- if (mntp != NULL) {
- fsrfs(mntp->mnt_dir, 0, 100);
+
+ fsp = fs_table_lookup_mount(argname);
+ if (!fsp)
+ fsp = fs_table_lookup_blkdev(argname);
+ if (fsp != NULL) {
+ fsrfs(fsp->fs_dir, 0, 100);
} else if (S_ISCHR(sb.st_mode)) {
fprintf(stderr, _(
"%s: char special not supported: %s\n"),
}
} else {
initallfs(mtab);
- fsrallfs(howlong, leftofffile);
+ fsrallfs(mtab, howlong, leftofffile);
}
return 0;
}
usage(int ret)
{
fprintf(stderr, _(
-"Usage: %s [-d] [-v] [-n] [-s] [-g] [-t time] [-p passes] [-f leftf] [-m mtab]\n"
-" %s [-d] [-v] [-n] [-s] [-g] xfsdev | dir | file ...\n\n"
+"Usage: %s [-d] [-v] [-g] [-t time] [-p passes] [-f leftf] [-m mtab]\n"
+" %s [-d] [-v] [-g] xfsdev | dir | file ...\n"
+" %s -V\n\n"
"Options:\n"
-" -n Do nothing, only interesting with -v. Not\n"
-" effective with in mtab mode.\n"
-" -s Print statistics only.\n"
" -g Print to syslog (default if stdout not a tty).\n"
" -t time How long to run in seconds.\n"
-" -p passes Number of passes before terminating global re-org.\n"
+" -p passes Number of passes before terminating global re-org.\n"
" -f leftoff Use this instead of %s.\n"
" -m mtab Use something other than /etc/mtab.\n"
" -d Debug, print even more.\n"
-" -v Verbose, more -v's more verbose.\n"
- ), progname, progname, _PATH_FSRLAST);
+" -v Verbose, more -v's more verbose.\n"
+" -V Print version number and exit.\n"
+ ), progname, progname, progname, _PATH_FSRLAST);
exit(ret);
}
static void
initallfs(char *mtab)
{
- FILE *fp;
- struct mntent *mp;
+ struct mntent_cursor cursor;
+ struct mntent *mnt= NULL;
int mi;
char *cp;
- struct stat64 sb;
-
- fp = setmntent(mtab, "r");
- if (fp == NULL) {
- fsrprintf(_("could not open mtab file: %s\n"), mtab);
- exit(1);
- }
+ struct stat sb;
/* malloc a number of descriptors, increased later if needed */
if (!(fsbase = (fsdesc_t *)malloc(fsbufsize * sizeof(fsdesc_t)))) {
/* find all rw xfs file systems */
mi = 0;
fs = fsbase;
- while ((mp = getmntent(fp))) {
+
+ if (platform_mntent_open(&cursor, mtab) != 0){
+ fprintf(stderr, "Error: can't get mntent entries.\n");
+ exit(1);
+ }
+
+ while ((mnt = platform_mntent_next(&cursor)) != NULL) {
int rw = 0;
- if (strcmp(mp->mnt_type, MNTTYPE_XFS ) != 0 ||
- stat64(mp->mnt_fsname, &sb) == -1 ||
+ if (strcmp(mnt->mnt_type, MNTTYPE_XFS ) != 0 ||
+ stat(mnt->mnt_fsname, &sb) == -1 ||
!S_ISBLK(sb.st_mode))
continue;
- cp = strtok(mp->mnt_opts,",");
+ cp = strtok(mnt->mnt_opts,",");
do {
if (strcmp("rw", cp) == 0)
rw++;
if (rw == 0) {
if (dflag)
fsrprintf(_("Skipping %s: not mounted rw\n"),
- mp->mnt_fsname);
+ mnt->mnt_fsname);
continue;
}
fs = (fsbase + mi); /* Needed ? */
}
- fs->dev = strdup(mp->mnt_fsname);
- fs->mnt = strdup(mp->mnt_dir);
+ fs->dev = strdup(mnt->mnt_fsname);
+ fs->mnt = strdup(mnt->mnt_dir);
- if (fs->mnt == NULL || fs->mnt == NULL) {
- fsrprintf(_("strdup(%s) failed\n"), mp->mnt_fsname);
+ if (fs->dev == NULL) {
+ fsrprintf(_("strdup(%s) failed\n"), mnt->mnt_fsname);
+ exit(1);
+ }
+ if (fs->mnt == NULL) {
+ fsrprintf(_("strdup(%s) failed\n"), mnt->mnt_dir);
exit(1);
}
mi++;
fs++;
}
+ platform_mntent_close(&cursor);
+
numfs = mi;
fsend = (fsbase + numfs);
- endmntent(fp);
if (numfs == 0) {
fsrprintf(_("no rw xfs file systems in mtab: %s\n"), mtab);
exit(0);
}
static void
-fsrallfs(int howlong, char *leftofffile)
+fsrallfs(char *mtab, int howlong, char *leftofffile)
{
int fd;
int error;
char *ptr;
xfs_ino_t startino = 0;
fsdesc_t *fsp;
- struct stat64 sb, sb2;
+ struct stat sb, sb2;
fsrprintf("xfs_fsr -m %s -t %d -f %s ...\n", mtab, howlong, leftofffile);
fs = fsbase;
/* where'd we leave off last time? */
- if (lstat64(leftofffile, &sb) == 0) {
+ if (lstat(leftofffile, &sb) == 0) {
if ( (fd = open(leftofffile, O_RDONLY)) == -1 ) {
fsrprintf(_("%s: open failed\n"), leftofffile);
}
- else if ( fstat64(fd, &sb2) == 0) {
+ else if ( fstat(fd, &sb2) == 0) {
/*
* Verify that lstat & fstat point to the
* same regular file (no links/no quick spoofs)
fsrprintf(_("could not read %s, starting with %s\n"),
leftofffile, *fs->dev);
} else {
+ /* Ensure the buffer we read is null terminated */
+ buf[SMBUFSZ-1] = '\0';
for (fs = fsbase; fs < fsend; fs++) {
fsname = fs->dev;
if ((strncmp(buf,fsname,strlen(fsname)) == 0)
ptr = strchr(ptr, ' ');
if (ptr) {
startino = strtoull(++ptr, NULL, 10);
+ /*
+ * NOTE: The inode number read in from
+ * the leftoff file is the last inode
+ * to have been fsr'd. Since the v5
+ * xfrog_bulkstat function wants to be
+ * passed the first inode that we want
+ * to examine, increment the value that
+ * we read in. The debug message below
+ * prints the lastoff value.
+ */
+ startino++;
}
}
if (startpass < 0)
if (vflag) {
fsrprintf(_("START: pass=%d ino=%llu %s %s\n"),
- fs->npass, (unsigned long long)startino,
+ fs->npass, (unsigned long long)startino - 1,
fs->dev, fs->mnt);
}
signal(SIGTERM, aborter);
/* reorg for 'howlong' -- checked in 'fsrfs' */
- while (endtime > time(0)) {
+ while (endtime > time(NULL)) {
pid_t pid;
- if (fs == fsend)
- fs = fsbase;
- if (fs->npass == npasses) {
- fsrprintf(_("Completed all %d passes\n"), npasses);
- break;
- }
+
if (npasses > 1 && !fs->npass)
Mflag = 1;
else
break;
default:
wait(&error);
- close(fd);
if (WIFEXITED(error) && WEXITSTATUS(error) == 1) {
/* child timed out & did fsrall_cleanup */
exit(0);
startino = 0; /* reset after the first time through */
fs->npass++;
fs++;
+ if (fs == fsend)
+ fs = fsbase;
+ if (fs->npass == npasses) {
+ fsrprintf(_("Completed all %d passes\n"), npasses);
+ break;
+ }
}
- fsrall_cleanup(endtime <= time(0));
+ fsrall_cleanup(endtime <= time(NULL));
}
/*
int ret;
char buf[SMBUFSZ];
- /* record where we left off */
unlink(leftofffile);
- fd = open(leftofffile, O_WRONLY|O_CREAT|O_EXCL, 0644);
- if (fd == -1)
- fsrprintf(_("open(%s) failed: %s\n"),
- leftofffile, strerror(errno));
- else {
- if (timeout) {
+
+ if (timeout) {
+ fsrprintf(_("%s startpass %d, endpass %d, time %d seconds\n"),
+ progname, startpass, fs->npass,
+ time(NULL) - endtime + howlong);
+
+ /* record where we left off */
+ fd = open(leftofffile, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd == -1) {
+ fsrprintf(_("open(%s) failed: %s\n"),
+ leftofffile, strerror(errno));
+ } else {
ret = sprintf(buf, "%s %d %llu\n", fs->dev,
fs->npass, (unsigned long long)leftoffino);
if (write(fd, buf, ret) < strlen(buf))
close(fd);
}
}
-
- if (timeout)
- fsrprintf(_("%s startpass %d, endpass %d, time %d seconds\n"),
- progname, startpass, fs->npass,
- time(0) - endtime + howlong);
}
/*
static int
fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
{
-
- int fsfd, fd;
+ struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
+ int fd;
int count = 0;
int ret;
- __s32 buflenout;
- xfs_bstat_t buf[GRABSZ];
char fname[64];
char *tname;
jdm_fshandle_t *fshandlep;
- xfs_ino_t lastino = startino;
+ struct xfs_bulkstat_req *breq;
fsrprintf(_("%s start inode=%llu\n"), mntdir,
(unsigned long long)startino);
return -1;
}
- if ((fsfd = open(mntdir, O_RDONLY)) < 0) {
- fsrprintf(_("unable to open: %s: %s\n"),
- mntdir, strerror( errno ));
+ ret = xfd_open(&fsxfd, mntdir, O_RDONLY);
+ if (ret) {
+ fsrprintf(_("unable to open XFS file: %s: %s\n"),
+ mntdir, strerror(ret));
+ free(fshandlep);
return -1;
}
+ memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
- if (xfs_getgeom(fsfd, &fsgeom) < 0 ) {
- fsrprintf(_("Skipping %s: could not get XFS geometry\n"),
+ tmp_init(mntdir);
+
+ breq = xfrog_bulkstat_alloc_req(GRABSZ, startino);
+ if (!breq) {
+ fsrprintf(_("Skipping %s: not enough memory\n"),
mntdir);
+ xfd_close(&fsxfd);
+ free(fshandlep);
return -1;
}
- tmp_init(mntdir);
-
- while ((ret = xfs_bulkstat(fsfd,
- &lastino, GRABSZ, &buf[0], &buflenout) == 0)) {
- xfs_bstat_t *p;
- xfs_bstat_t *endp;
+ while ((ret = xfrog_bulkstat(&fsxfd, breq) == 0)) {
+ struct xfs_bstat bs1;
+ struct xfs_bulkstat *buf = breq->bulkstat;
+ struct xfs_bulkstat *p;
+ struct xfs_bulkstat *endp;
+ uint32_t buflenout = breq->hdr.ocount;
if (buflenout == 0)
goto out0;
/* Each loop through, defrag targetrange percent of the files */
count = (buflenout * targetrange) / 100;
- qsort((char *)buf, buflenout, sizeof(struct xfs_bstat), cmp);
+ qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
/* Do some obvious checks now */
(p->bs_extents < 2))
continue;
- if ((fd = jdm_open(fshandlep, p, O_RDWR)) < 0) {
+ ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
+ if (ret) {
+ fsrprintf(_("bstat conversion error: %s\n"),
+ strerror(ret));
+ continue;
+ }
+
+ fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
+ if (fd < 0) {
/* This probably means the file was
* removed while in progress of handling
* it. Just quietly ignore this file.
/* Get a tmp file name */
tname = tmp_next(mntdir);
- ret = fsrfile_common(fname, tname, mntdir, fd, p);
+ ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
leftoffino = p->bs_ino;
break;
}
}
- if (endtime && endtime < time(0)) {
+ if (endtime && endtime < time(NULL)) {
+ free(breq);
tmp_close(mntdir);
- close(fsfd);
+ xfd_close(&fsxfd);
fsrall_cleanup(1);
exit(1);
}
}
- if (ret < 0)
- fsrprintf(_("%s: xfs_bulkstat: %s\n"), progname, strerror(errno));
+ if (ret)
+ fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
out0:
+ free(breq);
tmp_close(mntdir);
- close(fsfd);
+ xfd_close(&fsxfd);
+ free(fshandlep);
return 0;
}
int
cmp(const void *s1, const void *s2)
{
- return( ((xfs_bstat_t *)s2)->bs_extents -
- ((xfs_bstat_t *)s1)->bs_extents);
+ return( ((struct xfs_bstat *)s2)->bs_extents -
+ ((struct xfs_bstat *)s1)->bs_extents);
}
* an open on the file and passes this all to fsrfile_common.
*/
static int
-fsrfile(char *fname, xfs_ino_t ino)
+fsrfile(
+ char *fname,
+ xfs_ino_t ino)
{
- xfs_bstat_t statbuf;
- jdm_fshandle_t *fshandlep;
- int fd, fsfd;
- int error = 0;
- char *tname;
+ struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
+ struct xfs_bulkstat bulkstat;
+ struct xfs_bstat statbuf;
+ jdm_fshandle_t *fshandlep;
+ int fd = -1;
+ int error = -1;
+ char *tname;
fshandlep = jdm_getfshandle(getparent (fname) );
- if (! fshandlep) {
+ if (!fshandlep) {
fsrprintf(_("unable to construct sys handle for %s: %s\n"),
fname, strerror(errno));
- return -1;
+ goto out;
}
/*
* Need to open something on the same filesystem as the
* file. Open the parent.
*/
- fsfd = open(getparent(fname), O_RDONLY);
- if (fsfd < 0) {
- fsrprintf(_("unable to open sys handle for %s: %s\n"),
- fname, strerror(errno));
- return -1;
+ error = xfd_open(&fsxfd, getparent(fname), O_RDONLY);
+ if (error) {
+ fsrprintf(_("unable to open sys handle for XFS file %s: %s\n"),
+ fname, strerror(error));
+ goto out;
}
- if ((xfs_bulkstat_single(fsfd, &ino, &statbuf)) < 0) {
+ error = xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
+ if (error) {
fsrprintf(_("unable to get bstat on %s: %s\n"),
- fname, strerror(errno));
- close(fsfd);
- return -1;
+ fname, strerror(error));
+ goto out;
+ }
+ error = xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
+ if (error) {
+ fsrprintf(_("bstat conversion error on %s: %s\n"),
+ fname, strerror(error));
+ goto out;
}
- fd = jdm_open( fshandlep, &statbuf, O_RDWR);
+ fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
if (fd < 0) {
fsrprintf(_("unable to open handle %s: %s\n"),
fname, strerror(errno));
- close(fsfd);
- return -1;
+ goto out;
}
- /* Get the fs geometry */
- if (xfs_getgeom(fsfd, &fsgeom) < 0 ) {
- fsrprintf(_("Unable to get geom on fs for: %s\n"), fname);
- close(fsfd);
- return -1;
- }
-
- close(fsfd);
+ /* Stash the fs geometry for general use. */
+ memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
tname = gettmpname(fname);
if (tname)
error = fsrfile_common(fname, tname, NULL, fd, &statbuf);
- close(fd);
+out:
+ xfd_close(&fsxfd);
+ if (fd >= 0)
+ close(fd);
+ free(fshandlep);
return error;
}
char *tname,
char *fsname,
int fd,
- xfs_bstat_t *statp)
+ struct xfs_bstat *statp)
{
int error;
- struct statvfs64 vfss;
+ struct statvfs vfss;
struct fsxattr fsx;
unsigned long bsize;
* Note that xfs_bstat.bs_blksize returns the filesystem blocksize,
* not the optimal I/O size as struct stat.
*/
- if (statvfs64(fsname ? fsname : fname, &vfss) < 0) {
+ if (statvfs(fsname ? fsname : fname, &vfss) < 0) {
fsrprintf(_("unable to get fs stat on %s: %s\n"),
fname, strerror(errno));
return -1;
return 1;
}
- if ((ioctl(fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
+ if ((ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
fsrprintf(_("failed to get inode attrs: %s\n"), fname);
return(-1);
}
- if (fsx.fsx_xflags & (XFS_XFLAG_IMMUTABLE|XFS_XFLAG_APPEND)) {
+ if (fsx.fsx_xflags & (FS_XFLAG_IMMUTABLE|FS_XFLAG_APPEND)) {
if (vflag)
fsrprintf(_("%s: immutable/append, ignoring\n"), fname);
return(0);
}
- if (fsx.fsx_xflags & XFS_XFLAG_NODEFRAG) {
+ if (fsx.fsx_xflags & FS_XFLAG_NODEFRAG) {
if (vflag)
fsrprintf(_("%s: marked as don't defrag, ignoring\n"),
fname);
return(0);
}
- if (fsx.fsx_xflags & XFS_XFLAG_REALTIME) {
+ if (fsx.fsx_xflags & FS_XFLAG_REALTIME) {
if (xfs_getrt(fd, &vfss) < 0) {
fsrprintf(_("cannot get realtime geometry for: %s\n"),
fname);
return -1; /* no error */
}
+/*
+ * Attempt to set the attr fork up correctly. This is simple for attr1
+ * filesystems as they have a fixed inode fork offset. In that case
+ * just create an attribute and that's all we need to do.
+ *
+ * For attr2 filesystems, see if we have the actual fork offset in
+ * the bstat structure. If so, just create additional attributes on
+ * the temporary inode until the offset matches.
+ *
+ * If it doesn't exist, we can only do best effort. Add an attribute at a time
+ * to move the inode fork around, but take into account that the attribute
+ * might be too small to move the fork every time we add one. This should
+ * hopefully put the fork offset in the right place. It's not a big deal if we
+ * don't get it right - the kernel will reject it when we try to swap extents.
+ */
+static int
+fsr_setup_attr_fork(
+ int fd,
+ int tfd,
+ struct xfs_bstat *bstatp)
+{
+#ifdef HAVE_FSETXATTR
+ struct xfs_fd txfd = XFS_FD_INIT(tfd);
+ struct stat tstatbuf;
+ int i;
+ int diff = 0;
+ int last_forkoff = 0;
+ int no_change_cnt = 0;
+ int ret;
+
+ if (!(bstatp->bs_xflags & FS_XFLAG_HASATTR))
+ return 0;
+
+ /*
+ * use the old method if we have attr1 or the kernel does not yet
+ * support passing the fork offset in the bulkstat data.
+ */
+ if (!(fsgeom.flags & XFS_FSOP_GEOM_FLAGS_ATTR2) ||
+ bstatp->bs_forkoff == 0) {
+ /* attr1 */
+ ret = fsetxattr(txfd.fd, "user.X", "X", 1, XATTR_CREATE);
+ if (ret) {
+ fsrprintf(_("could not set ATTR\n"));
+ return -1;
+ }
+ goto out;
+ }
+
+ /* attr2 w/ fork offsets */
+
+ if (fstat(txfd.fd, &tstatbuf) < 0) {
+ fsrprintf(_("unable to stat temp file: %s\n"),
+ strerror(errno));
+ return -1;
+ }
+
+ i = 0;
+ do {
+ struct xfs_bulkstat tbstat;
+ char name[64];
+ int ret;
+
+ /*
+ * bulkstat the temp inode to see what the forkoff is. Use
+ * this to compare against the target and determine what we
+ * need to do.
+ */
+ ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0, &tbstat);
+ if (ret) {
+ fsrprintf(_("unable to get bstat on temp file: %s\n"),
+ strerror(ret));
+ return -1;
+ }
+ if (dflag)
+ fsrprintf(_("orig forkoff %d, temp forkoff %d\n"),
+ bstatp->bs_forkoff, tbstat.bs_forkoff);
+ diff = tbstat.bs_forkoff - bstatp->bs_forkoff;
+
+ /* if they are equal, we are done */
+ if (!diff)
+ goto out;
+
+ snprintf(name, sizeof(name), "user.%d", i);
+
+ /*
+ * If there is no attribute, then we need to create one to get
+ * an attribute fork at the default location.
+ */
+ if (!tbstat.bs_forkoff) {
+ ASSERT(i == 0);
+ ret = fsetxattr(txfd.fd, name, "XX", 2, XATTR_CREATE);
+ if (ret) {
+ fsrprintf(_("could not set ATTR\n"));
+ return -1;
+ }
+ continue;
+ } else if (i == 0) {
+ /*
+ * First pass, and temp file already has an inline
+ * xattr, probably due to selinux.
+ *
+ * It's *possible* that the temp file attr area
+ * is larger than the target file's:
+ *
+ * Target Temp
+ * +-------+ 0 +-------+ 0
+ * | | | |
+ * | | | Data |
+ * | Data | | |
+ * | | v-------v forkoff
+ * | | | |
+ * v-------v forkoff | Attr | local
+ * | Attr | | |
+ * +-------+ +-------+
+ */
+
+ /*
+ * If target attr area is less than the temp's
+ * (diff < 0) write a big attr to the temp file to knock
+ * the attr out of local format.
+ * (This should actually *increase* the temp file's
+ * forkoffset when the attr moves out of the inode)
+ */
+ if (diff < 0) {
+ char val[2048];
+ memset(val, 'X', 2048);
+ if (fsetxattr(txfd.fd, name, val, 2048, 0)) {
+ fsrprintf(_("big ATTR set failed\n"));
+ return -1;
+ }
+ /* Go back & see where we're at now */
+ continue;
+ }
+ }
+
+ /*
+ * make a progress check so we don't get stuck trying to extend
+ * a large btree form attribute fork.
+ */
+ if (last_forkoff == tbstat.bs_forkoff) {
+ if (no_change_cnt++ > 10)
+ break;
+ } else /* progress! */
+ no_change_cnt = 0;
+ last_forkoff = tbstat.bs_forkoff;
+
+ /* work out which way to grow the fork */
+ if (abs(diff) > fsgeom.inodesize - sizeof(struct xfs_dinode)) {
+ fsrprintf(_("forkoff diff %d too large!\n"), diff);
+ return -1;
+ }
+
+ /*
+ * if the temp inode fork offset is still smaller then we have
+ * to grow the data fork
+ */
+ if (diff < 0) {
+ /*
+ * create some temporary extents in the inode to move
+ * the fork in the direction we need. This can be done
+ * by preallocating some single block extents at
+ * non-contiguous offsets.
+ */
+ /* XXX: unimplemented! */
+ if (dflag)
+ printf(_("data fork growth unimplemented\n"));
+ goto out;
+ }
+
+ /* we need to grow the attr fork, so create another attr */
+ ret = fsetxattr(txfd.fd, name, "XX", 2, XATTR_CREATE);
+ if (ret) {
+ fsrprintf(_("could not set ATTR\n"));
+ return -1;
+ }
+
+ } while (++i < 100); /* don't go forever */
+
+out:
+ if (dflag)
+ fsrprintf(_("set temp attr\n"));
+ /* We failed to resolve the fork difference */
+ if (dflag && diff)
+ fsrprintf(_("failed to match fork offset\n"));;
+
+#endif /* HAVE_FSETXATTR */
+ return 0;
+}
/*
* Do the defragmentation of a single file.
* We already are pretty sure we can and want to
* defragment the file. Create the tmp file, copy
* the data (maintaining holes) and call the kernel
- * extent swap routinte.
+ * extent swap routine.
+ *
+ * Return values:
+ * -1: Some error was encountered
+ * 0: Successfully defragmented the file
+ * 1: No change / No Error
*/
static int
packfile(char *fname, char *tname, int fd,
- xfs_bstat_t *statp, struct fsxattr *fsxp)
+ struct xfs_bstat *statp, struct fsxattr *fsxp)
{
- int tfd;
+ int tfd = -1;
int srval;
+ int retval = -1; /* Failure is the default */
int nextents, extent, cur_nextents, new_nextents;
unsigned blksz_dio;
unsigned dio_min;
static xfs_swapext_t sx;
struct xfs_flock64 space;
off64_t cnt, pos;
- void *fbuf;
+ void *fbuf = NULL;
int ct, wc, wc_b4;
char ffname[SMBUFSZ];
int ffd = -1;
if (cur_nextents == 1 || cur_nextents <= nextents) {
if (vflag)
fsrprintf(_("%s already fully defragmented.\n"), fname);
- return 1; /* indicates no change/no error */
+ retval = 1; /* indicates no change/no error */
+ goto out;
}
if (dflag)
if (vflag)
fsrprintf(_("could not open tmp file: %s: %s\n"),
tname, strerror(errno));
- return -1;
+ goto out;
}
unlink(tname);
/* Setup extended attributes */
- if (statp->bs_xflags & XFS_XFLAG_HASATTR) {
- if (fsetxattr(tfd, "user.X", "X", 1, XATTR_CREATE) != 0) {
- fsrprintf(_("could not set ATTR on tmp: %s:\n"), tname);
- close(tfd);
- return -1;
- }
- if (dflag)
- fsrprintf(_("%s set temp attr\n"), tname);
+ if (fsr_setup_attr_fork(fd, tfd, statp) != 0) {
+ fsrprintf(_("failed to set ATTR fork on tmp: %s:\n"), tname);
+ goto out;
}
/* Setup extended inode flags, project identifier, etc */
if (fsxp->fsx_xflags || fsxp->fsx_projid) {
- if (ioctl(tfd, XFS_IOC_FSSETXATTR, fsxp) < 0) {
+ if (ioctl(tfd, FS_IOC_FSSETXATTR, fsxp) < 0) {
fsrprintf(_("could not set inode attrs on tmp: %s\n"),
tname);
- close(tfd);
- return -1;
+ goto out;
}
}
if ((ioctl(tfd, XFS_IOC_DIOINFO, &dio)) < 0 ) {
fsrprintf(_("could not get DirectIO info on tmp: %s\n"), tname);
- close(tfd);
- return -1;
+ goto out;
}
dio_min = dio.d_miniosz;
if (!(fbuf = (char *)memalign(dio.d_mem, blksz_dio))) {
fsrprintf(_("could not allocate buf: %s\n"), tname);
- close(tfd);
- return -1;
+ goto out;
}
if (nfrags) {
if ((ffd = open(ffname, openopts, 0666)) < 0) {
fsrprintf(_("could not open fragfile: %s : %s\n"),
ffname, strerror(errno));
- close(tfd);
- free(fbuf);
- return -1;
+ goto out;
}
unlink(ffname);
}
fsrprintf(_("could not trunc tmp %s\n"),
tname);
}
- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
+ if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
+ tname, strerror(errno));
+ goto out;
+ }
continue;
} else if (outmap[extent].bmv_length == 0) {
/* to catch holes at the beginning of the file */
if (ioctl(tfd, XFS_IOC_RESVSP64, &space) < 0) {
fsrprintf(_("could not pre-allocate tmp space:"
" %s\n"), tname);
- close(tfd);
- free(fbuf);
- return -1;
+ goto out;
+ }
+ if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
+ tname, strerror(errno));
+ goto out;
}
- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
}
} /* end of space allocation loop */
- if (lseek64(tfd, 0, SEEK_SET)) {
+ if (lseek(tfd, 0, SEEK_SET)) {
fsrprintf(_("Couldn't rewind on temporary file\n"));
- close(tfd);
- free(fbuf);
- return -1;
+ goto out;
}
/* Check if the temporary file has fewer extents */
if (cur_nextents <= new_nextents) {
if (vflag)
fsrprintf(_("No improvement will be made (skipping): %s\n"), fname);
- free(fbuf);
- close(tfd);
- return 1; /* no change/no error */
+ retval = 1; /* no change/no error */
+ goto out;
}
/* Loop through block map copying the file. */
for (extent = 0; extent < nextents; extent++) {
pos = outmap[extent].bmv_offset;
if (outmap[extent].bmv_block == -1) {
- lseek64(tfd, outmap[extent].bmv_length, SEEK_CUR);
- lseek64(fd, outmap[extent].bmv_length, SEEK_CUR);
+ if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
+ tname, strerror(errno));
+ goto out;
+ }
+ if (lseek(fd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
+ fsrprintf(_("could not lseek in file: %s : %s\n"),
+ fname, strerror(errno));
+ goto out;
+ }
continue;
} else if (outmap[extent].bmv_length == 0) {
/* to catch holes at the beginning of the file */
tname);
}
}
- free(fbuf);
- close(tfd);
- return -1;
+ goto out;
}
if (nfrags) {
/* Do a matching write to the tmp file */
}
}
}
- ftruncate64(tfd, statp->bs_size);
- if (ffd > 0) close(ffd);
- fsync(tfd);
-
- free(fbuf);
+ if (ftruncate(tfd, statp->bs_size) < 0) {
+ fsrprintf(_("could not truncate tmpfile: %s : %s\n"),
+ fname, strerror(errno));
+ goto out;
+ }
+ if (fsync(tfd) < 0) {
+ fsrprintf(_("could not fsync tmpfile: %s : %s\n"),
+ fname, strerror(errno));
+ goto out;
+ }
sx.sx_stat = *statp; /* struct copy */
sx.sx_version = XFS_SX_VERSION;
if (vflag)
fsrprintf(_("failed to fchown tmpfile %s: %s\n"),
tname, strerror(errno));
- close(tfd);
- return -1;
+ goto out;
}
/* Swap the extents */
fsrprintf(_("XFS_IOC_SWAPEXT failed: %s: %s\n"),
fname, strerror(errno));
}
- close(tfd);
- return -1;
+ goto out;
}
/* Report progress */
cur_nextents, new_nextents,
(new_nextents <= nextents ? "DONE" : " " ),
fname);
- close(tfd);
- return 0;
+ retval = 0;
+
+out:
+ free(fbuf);
+ if (tfd != -1)
+ close(tfd);
+ if (ffd != -1)
+ close(ffd);
+ return retval;
}
char *
sprintf(sbuf, "/.fsr%d", getpid());
- strcpy(buf, fname);
+ strncpy(buf, fname, PATH_MAX);
+ buf[PATH_MAX] = '\0';
ptr = strrchr(buf, '/');
if (ptr) {
*ptr = '\0';
static char buf[PATH_MAX+1];
char *ptr;
- strcpy(buf, fname);
+ strncpy(buf, fname, PATH_MAX);
+ buf[PATH_MAX] = '\0';
ptr = strrchr(buf, '/');
if (ptr) {
if (ptr == &buf[0])
#define MAPSIZE 128
#define OUTMAP_SIZE_INCREMENT MAPSIZE
-int read_fd_bmap(int fd, xfs_bstat_t *sin, int *cur_nextents)
+int read_fd_bmap(int fd, struct xfs_bstat *sin, int *cur_nextents)
{
int i, cnt;
struct getbmap map[MAPSIZE];
/*
* Read the block map and return the number of extents.
*/
-int
+static int
getnextents(int fd)
{
int nextents;
return(nextents);
}
-/*
- * Get the fs geometry
- */
-int
-xfs_getgeom(int fd, xfs_fsop_geom_v1_t * fsgeom)
-{
- if (xfs_fsgeometry(fd, fsgeom) < 0) {
- return -1;
- }
- return 0;
-}
-
/*
* Get xfs realtime space information
*/
int
-xfs_getrt(int fd, struct statvfs64 *sfbp)
+xfs_getrt(int fd, struct statvfs *sfbp)
{
unsigned long bsize;
unsigned long factor;
return 0;
}
-/*
- * emulate getmntany
- */
-static int
-getmntany(FILE *fp, struct mntent *mp, struct mntent *mpref, struct stat64 *s)
-{
- struct mntent *t;
- struct stat64 ms;
-
- while ((t = getmntent(fp))) {
- if (mpref->mnt_fsname) { /* device */
- if (stat64(t->mnt_fsname, &ms) < 0)
- continue;
- if (s->st_rdev != ms.st_rdev)
- continue;
- }
- if (mpref->mnt_dir) { /* mount point */
- if (stat64(t->mnt_dir, &ms) < 0)
- continue;
- if (s->st_ino != ms.st_ino || s->st_dev != ms.st_dev)
- continue;
- }
- *mp = *t;
- break;
- }
- return (t != NULL);
-}
-
-
/*
* Initialize a directory for tmp file use. This is used
* by the full filesystem defragmentation when we're walking
}
for (i=0; i < fsgeom.agcount; i++) {
sprintf(buf, "%s/.fsr/ag%d", mnt, i);
- if (mkdir(buf, 0777) < 0) {
+ if (mkdir(buf, 0700) < 0) {
if (errno == EEXIST) {
if (dflag)
fsrprintf(