"Fossies" - the Fresh Open Source Software Archive

Member "xfsprogs-5.10.0/fsr/xfs_fsr.c" (24 Nov 2020, 41880 Bytes) of package /linux/misc/xfsprogs-5.10.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "xfs_fsr.c" see the Fossies "Dox" file reference documentation.

    1 // SPDX-License-Identifier: GPL-2.0
    2 /*
    3  * Copyright (c) 2000-2002 Silicon Graphics, Inc.
    4  * All Rights Reserved.
    5  */
    6 
    7 #include "libxfs.h"
    8 #include "xfs.h"
    9 #include "xfs_types.h"
   10 #include "jdm.h"
   11 #include "xfs_bmap_btree.h"
   12 #include "xfs_attr_sf.h"
   13 #include "libfrog/paths.h"
   14 #include "libfrog/fsgeom.h"
   15 #include "libfrog/bulkstat.h"
   16 
   17 #include <fcntl.h>
   18 #include <errno.h>
   19 #include <syslog.h>
   20 #include <signal.h>
   21 #include <sys/ioctl.h>
   22 #include <sys/wait.h>
   23 #include <sys/statvfs.h>
   24 #include <sys/xattr.h>
   25 #include <paths.h>
   26 
   27 #define _PATH_FSRLAST       "/var/tmp/.fsrlast_xfs"
   28 #define _PATH_PROC_MOUNTS   "/proc/mounts"
   29 
   30 
   31 char *progname;
   32 
   33 static int vflag;
   34 static int gflag;
   35 static int Mflag;
   36 /* static int nflag; */
   37 static int dflag = 0;
   38 /* static int sflag; */
   39 static int argv_blksz_dio;
   40 extern int max_ext_size;
   41 static int npasses = 10;
   42 static int startpass = 0;
   43 
   44 static struct getbmap  *outmap = NULL;
   45 static int      outmap_size = 0;
   46 static int      RealUid;
   47 static int      tmp_agi;
   48 static int64_t      minimumfree = 2048;
   49 
   50 #define MNTTYPE_XFS             "xfs"
   51 
   52 #define SMBUFSZ     1024
   53 #define ROOT        0
   54 #define NULLFD      -1
   55 #define GRABSZ      64
   56 #define TARGETRANGE 10
   57 #define BUFFER_MAX  (1<<24)
   58 
   59 static time_t howlong = 7200;       /* default seconds of reorganizing */
   60 static char *leftofffile = _PATH_FSRLAST; /* where we left off last */
   61 static time_t endtime;
   62 static time_t starttime;
   63 static xfs_ino_t    leftoffino = 0;
   64 static int  pagesize;
   65 
   66 void usage(int ret);
   67 static int  fsrfile(char *fname, xfs_ino_t ino);
   68 static int  fsrfile_common( char *fname, char *tname, char *mnt,
   69                             int fd, struct xfs_bstat *statp);
   70 static int  packfile(char *fname, char *tname, int fd,
   71                      struct xfs_bstat *statp, struct fsxattr *fsxp);
   72 static void fsrdir(char *dirname);
   73 static int  fsrfs(char *mntdir, xfs_ino_t ino, int targetrange);
   74 static void initallfs(char *mtab);
   75 static void fsrallfs(char *mtab, int howlong, char *leftofffile);
   76 static void fsrall_cleanup(int timeout);
   77 static int  getnextents(int);
   78 int xfsrtextsize(int fd);
   79 int xfs_getrt(int fd, struct statvfs *sfbp);
   80 char * gettmpname(char *fname);
   81 char * getparent(char *fname);
   82 int fsrprintf(const char *fmt, ...);
   83 int read_fd_bmap(int, struct xfs_bstat *, int *);
   84 int cmp(const void *, const void *);
   85 static void tmp_init(char *mnt);
   86 static char * tmp_next(char *mnt);
   87 static void tmp_close(char *mnt);
   88 
   89 static struct xfs_fsop_geom fsgeom; /* geometry of active mounted system */
   90 
   91 #define NMOUNT 64
   92 static int numfs;
   93 
   94 typedef struct fsdesc {
   95     char *dev;
   96     char *mnt;
   97     int  npass;
   98 } fsdesc_t;
   99 
  100 static fsdesc_t *fs, *fsbase, *fsend;
  101 static int  fsbufsize = 10; /* A starting value */
  102 static int  nfrags = 0; /* Debug option: Coerse into specific number
  103                  * of extents */
  104 static int  openopts = O_CREAT|O_EXCL|O_RDWR|O_DIRECT;
  105 
  106 static int
  107 xfs_swapext(int fd, xfs_swapext_t *sx)
  108 {
  109     return ioctl(fd, XFS_IOC_SWAPEXT, sx);
  110 }
  111 
  112 static int
  113 xfs_fscounts(int fd, xfs_fsop_counts_t *counts)
  114 {
  115     return ioctl(fd, XFS_IOC_FSCOUNTS, counts);
  116 }
  117 
  118 static void
  119 aborter(int unused)
  120 {
  121     fsrall_cleanup(1);
  122     exit(1);
  123 }
  124 
  125 int
  126 main(int argc, char **argv)
  127 {
  128     struct stat sb;
  129     char *argname;
  130     int c;
  131     struct fs_path  *fsp;
  132     char *mtab = NULL;
  133 
  134     setlinebuf(stdout);
  135     progname = basename(argv[0]);
  136 
  137     setlocale(LC_ALL, "");
  138     bindtextdomain(PACKAGE, LOCALEDIR);
  139     textdomain(PACKAGE);
  140 
  141     gflag = ! isatty(0);
  142 
  143     while ((c = getopt(argc, argv, "C:p:e:MgsdnvTt:f:m:b:N:FV")) != -1) {
  144         switch (c) {
  145         case 'M':
  146             Mflag = 1;
  147             break;
  148         case 'g':
  149             gflag = 1;
  150             break;
  151         case 'n':
  152             /* nflag = 1; */
  153             break;
  154         case 'v':
  155             ++vflag;
  156             break;
  157         case 'd':
  158             dflag = 1;
  159             break;
  160         case 's':       /* frag stats only */
  161             /* sflag = 1; */
  162             fprintf(stderr,
  163                 _("%s: Stats not yet supported for XFS\n"),
  164                 progname);
  165             usage(1);
  166             break;
  167         case 't':
  168             howlong = atoi(optarg);
  169             break;
  170         case 'f':
  171             leftofffile = optarg;
  172             break;
  173         case 'm':
  174             mtab = optarg;
  175             break;
  176         case 'b':
  177             argv_blksz_dio = atoi(optarg);
  178             break;
  179         case 'p':
  180             npasses = atoi(optarg);
  181             break;
  182         case 'C':
  183             /* Testing opt: coerses frag count in result */
  184             if (getenv("FSRXFSTEST") != NULL) {
  185                 nfrags = atoi(optarg);
  186                 openopts |= O_SYNC;
  187             }
  188             break;
  189         case 'V':
  190             printf(_("%s version %s\n"), progname, VERSION);
  191             exit(0);
  192         default:
  193             usage(1);
  194         }
  195     }
  196 
  197     /*
  198      * If the user did not specify an explicit mount table, try to use
  199      * /proc/mounts if it is available, else /etc/mtab.  We prefer
  200      * /proc/mounts because it is kernel controlled, while /etc/mtab
  201      * may contain garbage that userspace tools like pam_mounts wrote
  202      * into it.
  203      */
  204     if (!mtab) {
  205         if (access(_PATH_PROC_MOUNTS, R_OK) == 0)
  206             mtab = _PATH_PROC_MOUNTS;
  207         else
  208             mtab = _PATH_MOUNTED;
  209     }
  210 
  211     if (vflag)
  212         setbuf(stdout, NULL);
  213 
  214     starttime = time(NULL);
  215 
  216     /* Save the caller's real uid */
  217     RealUid = getuid();
  218 
  219     pagesize = getpagesize();
  220     fs_table_initialise(0, NULL, 0, NULL);
  221     if (optind < argc) {
  222         for (; optind < argc; optind++) {
  223             argname = argv[optind];
  224 
  225             if (lstat(argname, &sb) < 0) {
  226                 fprintf(stderr,
  227                     _("%s: could not stat: %s: %s\n"),
  228                     progname, argname, strerror(errno));
  229                 continue;
  230             }
  231 
  232             if (S_ISLNK(sb.st_mode)) {
  233                 struct stat sb2;
  234 
  235                 if (stat(argname, &sb2) == 0 &&
  236                     (S_ISBLK(sb2.st_mode) ||
  237                      S_ISCHR(sb2.st_mode)))
  238                 sb = sb2;
  239             }
  240 
  241             fsp = fs_table_lookup_mount(argname);
  242             if (!fsp)
  243                 fsp = fs_table_lookup_blkdev(argname);
  244             if (fsp != NULL) {
  245                 fsrfs(fsp->fs_dir, 0, 100);
  246             } else if (S_ISCHR(sb.st_mode)) {
  247                 fprintf(stderr, _(
  248                     "%s: char special not supported: %s\n"),
  249                         progname, argname);
  250                 exit(1);
  251             } else if (S_ISDIR(sb.st_mode) || S_ISREG(sb.st_mode)) {
  252                 if (!platform_test_xfs_path(argname)) {
  253                     fprintf(stderr, _(
  254                         "%s: cannot defragment: %s: Not XFS\n"),
  255                         progname, argname);
  256                     continue;
  257                 }
  258                 if (S_ISDIR(sb.st_mode))
  259                     fsrdir(argname);
  260                 else
  261                     fsrfile(argname, sb.st_ino);
  262             } else {
  263                 printf(
  264             _("%s: not fsys dev, dir, or reg file, ignoring\n"),
  265                     argname);
  266             }
  267         }
  268     } else {
  269         initallfs(mtab);
  270         fsrallfs(mtab, howlong, leftofffile);
  271     }
  272     return 0;
  273 }
  274 
  275 void
  276 usage(int ret)
  277 {
  278     fprintf(stderr, _(
  279 "Usage: %s [-d] [-v] [-g] [-t time] [-p passes] [-f leftf] [-m mtab]\n"
  280 "       %s [-d] [-v] [-g] xfsdev | dir | file ...\n"
  281 "       %s -V\n\n"
  282 "Options:\n"
  283 "       -g              Print to syslog (default if stdout not a tty).\n"
  284 "       -t time         How long to run in seconds.\n"
  285 "       -p passes       Number of passes before terminating global re-org.\n"
  286 "       -f leftoff      Use this instead of %s.\n"
  287 "       -m mtab         Use something other than /etc/mtab.\n"
  288 "       -d              Debug, print even more.\n"
  289 "       -v              Verbose, more -v's more verbose.\n"
  290 "       -V              Print version number and exit.\n"
  291         ), progname, progname, progname, _PATH_FSRLAST);
  292     exit(ret);
  293 }
  294 
  295 /*
  296  * initallfs -- read the mount table and set up an internal form
  297  */
  298 static void
  299 initallfs(char *mtab)
  300 {
  301     struct mntent_cursor cursor;
  302     struct mntent *mnt= NULL;
  303     int mi;
  304     char *cp;
  305     struct stat sb;
  306 
  307     /* malloc a number of descriptors, increased later if needed */
  308     if (!(fsbase = (fsdesc_t *)malloc(fsbufsize * sizeof(fsdesc_t)))) {
  309         fsrprintf(_("out of memory: %s\n"), strerror(errno));
  310         exit(1);
  311     }
  312     fsend = (fsbase + fsbufsize - 1);
  313 
  314     /* find all rw xfs file systems */
  315     mi = 0;
  316     fs = fsbase;
  317 
  318     if (platform_mntent_open(&cursor, mtab) != 0){
  319         fprintf(stderr, "Error: can't get mntent entries.\n");
  320         exit(1);
  321     }
  322 
  323     while ((mnt = platform_mntent_next(&cursor)) != NULL) {
  324         int rw = 0;
  325 
  326         if (strcmp(mnt->mnt_type, MNTTYPE_XFS ) != 0 ||
  327             stat(mnt->mnt_fsname, &sb) == -1 ||
  328             !S_ISBLK(sb.st_mode))
  329             continue;
  330 
  331         cp = strtok(mnt->mnt_opts,",");
  332         do {
  333             if (strcmp("rw", cp) == 0)
  334                 rw++;
  335         } while ((cp = strtok(NULL, ",")) != NULL);
  336         if (rw == 0) {
  337             if (dflag)
  338                 fsrprintf(_("Skipping %s: not mounted rw\n"),
  339                     mnt->mnt_fsname);
  340             continue;
  341         }
  342 
  343         if (mi == fsbufsize) {
  344             fsbufsize += NMOUNT;
  345             if ((fsbase = (fsdesc_t *)realloc((char *)fsbase,
  346                           fsbufsize * sizeof(fsdesc_t))) == NULL) {
  347                 fsrprintf(_("out of memory: %s\n"),
  348                     strerror(errno));
  349                 exit(1);
  350             }
  351             if (!fsbase) {
  352                 fsrprintf(_("out of memory on realloc: %s\n"),
  353                           strerror(errno));
  354                 exit(1);
  355             }
  356             fs = (fsbase + mi);  /* Needed ? */
  357         }
  358 
  359         fs->dev = strdup(mnt->mnt_fsname);
  360         fs->mnt = strdup(mnt->mnt_dir);
  361 
  362         if (fs->dev == NULL) {
  363             fsrprintf(_("strdup(%s) failed\n"), mnt->mnt_fsname);
  364             exit(1);
  365         }
  366         if (fs->mnt == NULL) {
  367             fsrprintf(_("strdup(%s) failed\n"), mnt->mnt_dir);
  368             exit(1);
  369         }
  370         mi++;
  371         fs++;
  372     }
  373     platform_mntent_close(&cursor);
  374 
  375     numfs = mi;
  376     fsend = (fsbase + numfs);
  377     if (numfs == 0) {
  378         fsrprintf(_("no rw xfs file systems in mtab: %s\n"), mtab);
  379         exit(0);
  380     }
  381     if (vflag || dflag) {
  382         fsrprintf(_("Found %d mounted, writable, XFS filesystems\n"),
  383                    numfs);
  384         if (dflag)
  385             for (fs = fsbase; fs < fsend; fs++)
  386                 fsrprintf("\t%-30.30s%-30.30s\n", fs->dev, fs->mnt);
  387     }
  388 }
  389 
  390 static void
  391 fsrallfs(char *mtab, int howlong, char *leftofffile)
  392 {
  393     int fd;
  394     int error;
  395     int found = 0;
  396     char *fsname;
  397     char buf[SMBUFSZ];
  398     int mdonly = Mflag;
  399     char *ptr;
  400     xfs_ino_t startino = 0;
  401     fsdesc_t *fsp;
  402     struct stat sb, sb2;
  403 
  404     fsrprintf("xfs_fsr -m %s -t %d -f %s ...\n", mtab, howlong, leftofffile);
  405 
  406     endtime = starttime + howlong;
  407     fs = fsbase;
  408 
  409     /* where'd we leave off last time? */
  410     if (lstat(leftofffile, &sb) == 0) {
  411         if ( (fd = open(leftofffile, O_RDONLY)) == -1 ) {
  412             fsrprintf(_("%s: open failed\n"), leftofffile);
  413         }
  414         else if ( fstat(fd, &sb2) == 0) {
  415             /*
  416              * Verify that lstat & fstat point to the
  417              * same regular file (no links/no quick spoofs)
  418              */
  419             if ( (sb.st_dev  != sb2.st_dev) ||
  420                  (sb.st_ino  != sb2.st_ino) ||
  421                  ((sb.st_mode & S_IFMT) != S_IFREG) ||
  422                  ((sb2.st_mode & S_IFMT) != S_IFREG) ||
  423                  (sb2.st_uid  != ROOT) ||
  424                  (sb2.st_nlink != 1)
  425                )
  426             {
  427                 fsrprintf(_("Can't use %s: mode=0%o own=%d"
  428                     " nlink=%d\n"),
  429                     leftofffile, sb.st_mode,
  430                     sb.st_uid, sb.st_nlink);
  431                 close(fd);
  432                 fd = NULLFD;
  433             }
  434         }
  435         else {
  436             close(fd);
  437             fd = NULLFD;
  438         }
  439     }
  440     else {
  441         fd = NULLFD;
  442     }
  443 
  444     if (fd != NULLFD) {
  445         if (read(fd, buf, SMBUFSZ) == -1) {
  446             fs = fsbase;
  447             fsrprintf(_("could not read %s, starting with %s\n"),
  448                 leftofffile, *fs->dev);
  449         } else {
  450             /* Ensure the buffer we read is null terminated */
  451             buf[SMBUFSZ-1] = '\0';
  452             for (fs = fsbase; fs < fsend; fs++) {
  453                 fsname = fs->dev;
  454                 if ((strncmp(buf,fsname,strlen(fsname)) == 0)
  455                     && buf[strlen(fsname)] == ' ') {
  456                     found = 1;
  457                     break;
  458                 }
  459             }
  460             if (! found)
  461                 fs = fsbase;
  462 
  463             ptr = strchr(buf, ' ');
  464             if (ptr) {
  465                 startpass = atoi(++ptr);
  466                 ptr = strchr(ptr, ' ');
  467                 if (ptr) {
  468                     startino = strtoull(++ptr, NULL, 10);
  469                     /*
  470                      * NOTE: The inode number read in from
  471                      * the leftoff file is the last inode
  472                      * to have been fsr'd.  Since the v5
  473                      * xfrog_bulkstat function wants to be
  474                      * passed the first inode that we want
  475                      * to examine, increment the value that
  476                      * we read in.  The debug message below
  477                      * prints the lastoff value.
  478                      */
  479                     startino++;
  480                 }
  481             }
  482             if (startpass < 0)
  483                 startpass = 0;
  484 
  485             /* Init pass counts */
  486             for (fsp = fsbase; fsp < fs; fsp++) {
  487                 fsp->npass = startpass + 1;
  488             }
  489             for (fsp = fs; fsp <= fsend; fsp++) {
  490                 fsp->npass = startpass;
  491             }
  492         }
  493         close(fd);
  494     }
  495 
  496     if (vflag) {
  497         fsrprintf(_("START: pass=%d ino=%llu %s %s\n"),
  498               fs->npass, (unsigned long long)startino - 1,
  499               fs->dev, fs->mnt);
  500     }
  501 
  502     signal(SIGABRT, aborter);
  503     signal(SIGHUP, aborter);
  504     signal(SIGINT, aborter);
  505     signal(SIGQUIT, aborter);
  506     signal(SIGTERM, aborter);
  507 
  508     /* reorg for 'howlong' -- checked in 'fsrfs' */
  509     while (endtime > time(NULL)) {
  510         pid_t pid;
  511 
  512         if (npasses > 1 && !fs->npass)
  513             Mflag = 1;
  514         else
  515             Mflag = mdonly;
  516         pid = fork();
  517         switch(pid) {
  518         case -1:
  519             fsrprintf(_("couldn't fork sub process:"));
  520             exit(1);
  521             break;
  522         case 0:
  523             error = fsrfs(fs->mnt, startino, TARGETRANGE);
  524             exit (error);
  525             break;
  526         default:
  527             wait(&error);
  528             if (WIFEXITED(error) && WEXITSTATUS(error) == 1) {
  529                 /* child timed out & did fsrall_cleanup */
  530                 exit(0);
  531             }
  532             break;
  533         }
  534         startino = 0;  /* reset after the first time through */
  535         fs->npass++;
  536         fs++;
  537         if (fs == fsend)
  538             fs = fsbase;
  539         if (fs->npass == npasses) {
  540             fsrprintf(_("Completed all %d passes\n"), npasses);
  541             break;
  542         }
  543     }
  544     fsrall_cleanup(endtime <= time(NULL));
  545 }
  546 
  547 /*
  548  * fsrall_cleanup -- close files, print next starting location, etc.
  549  */
  550 static void
  551 fsrall_cleanup(int timeout)
  552 {
  553     int fd;
  554     int ret;
  555     char buf[SMBUFSZ];
  556 
  557     unlink(leftofffile);
  558 
  559     if (timeout) {
  560         fsrprintf(_("%s startpass %d, endpass %d, time %d seconds\n"),
  561             progname, startpass, fs->npass,
  562             time(NULL) - endtime + howlong);
  563 
  564         /* record where we left off */
  565         fd = open(leftofffile, O_WRONLY|O_CREAT|O_EXCL, 0644);
  566         if (fd == -1) {
  567             fsrprintf(_("open(%s) failed: %s\n"),
  568                       leftofffile, strerror(errno));
  569         } else {
  570             ret = sprintf(buf, "%s %d %llu\n", fs->dev,
  571                     fs->npass, (unsigned long long)leftoffino);
  572             if (write(fd, buf, ret) < strlen(buf))
  573                 fsrprintf(_("write(%s) failed: %s\n"),
  574                     leftofffile, strerror(errno));
  575             close(fd);
  576         }
  577     }
  578 }
  579 
  580 /*
  581  * fsrfs -- reorganize a file system
  582  */
  583 static int
  584 fsrfs(char *mntdir, xfs_ino_t startino, int targetrange)
  585 {
  586     struct xfs_fd   fsxfd = XFS_FD_INIT_EMPTY;
  587     int fd;
  588     int count = 0;
  589     int ret;
  590     char    fname[64];
  591     char    *tname;
  592     jdm_fshandle_t  *fshandlep;
  593     struct xfs_bulkstat_req *breq;
  594 
  595     fsrprintf(_("%s start inode=%llu\n"), mntdir,
  596         (unsigned long long)startino);
  597 
  598     fshandlep = jdm_getfshandle( mntdir );
  599     if ( ! fshandlep ) {
  600         fsrprintf(_("unable to get handle: %s: %s\n"),
  601                   mntdir, strerror( errno ));
  602         return -1;
  603     }
  604 
  605     ret = -xfd_open(&fsxfd, mntdir, O_RDONLY);
  606     if (ret) {
  607         fsrprintf(_("unable to open XFS file: %s: %s\n"),
  608                   mntdir, strerror(ret));
  609         free(fshandlep);
  610         return -1;
  611     }
  612     memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
  613 
  614     tmp_init(mntdir);
  615 
  616     ret = -xfrog_bulkstat_alloc_req(GRABSZ, startino, &breq);
  617     if (ret) {
  618         fsrprintf(_("Skipping %s: %s\n"), mntdir, strerror(ret));
  619         xfd_close(&fsxfd);
  620         free(fshandlep);
  621         return -1;
  622     }
  623 
  624     while ((ret = -xfrog_bulkstat(&fsxfd, breq) == 0)) {
  625         struct xfs_bstat    bs1;
  626         struct xfs_bulkstat *buf = breq->bulkstat;
  627         struct xfs_bulkstat *p;
  628         struct xfs_bulkstat *endp;
  629         uint32_t        buflenout = breq->hdr.ocount;
  630 
  631         if (buflenout == 0)
  632             goto out0;
  633 
  634         /* Each loop through, defrag targetrange percent of the files */
  635         count = (buflenout * targetrange) / 100;
  636 
  637         qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
  638 
  639         for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
  640             /* Do some obvious checks now */
  641             if (((p->bs_mode & S_IFMT) != S_IFREG) ||
  642                  (p->bs_extents < 2))
  643                 continue;
  644 
  645             ret = -xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
  646             if (ret) {
  647                 fsrprintf(_("bstat conversion error: %s\n"),
  648                         strerror(ret));
  649                 continue;
  650             }
  651 
  652             fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
  653             if (fd < 0) {
  654                 /* This probably means the file was
  655                  * removed while in progress of handling
  656                  * it.  Just quietly ignore this file.
  657                  */
  658                 if (dflag)
  659                     fsrprintf(_("could not open: "
  660                         "inode %llu\n"), p->bs_ino);
  661                 continue;
  662             }
  663 
  664             /* Don't know the pathname, so make up something */
  665             sprintf(fname, "ino=%lld", (long long)p->bs_ino);
  666 
  667             /* Get a tmp file name */
  668             tname = tmp_next(mntdir);
  669 
  670             ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
  671 
  672             leftoffino = p->bs_ino;
  673 
  674             close(fd);
  675 
  676             if (ret == 0) {
  677                 if (--count <= 0)
  678                     break;
  679             }
  680         }
  681         if (endtime && endtime < time(NULL)) {
  682             free(breq);
  683             tmp_close(mntdir);
  684             xfd_close(&fsxfd);
  685             fsrall_cleanup(1);
  686             exit(1);
  687         }
  688     }
  689     if (ret)
  690         fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
  691 out0:
  692     free(breq);
  693     tmp_close(mntdir);
  694     xfd_close(&fsxfd);
  695     free(fshandlep);
  696     return 0;
  697 }
  698 
  699 /*
  700  * To compare bstat structs for qsort.
  701  */
  702 int
  703 cmp(const void *s1, const void *s2)
  704 {
  705     return( ((struct xfs_bstat *)s2)->bs_extents -
  706             ((struct xfs_bstat *)s1)->bs_extents);
  707 
  708 }
  709 
  710 /*
  711  * reorganize by directory hierarchy.
  712  * Stay in dev (a restriction based on structure of this program -- either
  713  * call efs_{n,u}mount() around each file, something smarter or this)
  714  */
  715 static void
  716 fsrdir(char *dirname)
  717 {
  718     fsrprintf(_("%s: Directory defragmentation not supported\n"), dirname);
  719 }
  720 
  721 /*
  722  * Sets up the defragmentation of a file based on the
  723  * filepath.  It collects the bstat information, does
  724  * an open on the file and passes this all to fsrfile_common.
  725  */
  726 static int
  727 fsrfile(
  728     char            *fname,
  729     xfs_ino_t       ino)
  730 {
  731     struct xfs_fd       fsxfd = XFS_FD_INIT_EMPTY;
  732     struct xfs_bulkstat bulkstat;
  733     struct xfs_bstat    statbuf;
  734     jdm_fshandle_t      *fshandlep;
  735     int         fd = -1;
  736     int         error = -1;
  737     char            *tname;
  738 
  739     fshandlep = jdm_getfshandle(getparent (fname) );
  740     if (!fshandlep) {
  741         fsrprintf(_("unable to construct sys handle for %s: %s\n"),
  742             fname, strerror(errno));
  743         goto out;
  744     }
  745 
  746     /*
  747      * Need to open something on the same filesystem as the
  748      * file.  Open the parent.
  749      */
  750     error = -xfd_open(&fsxfd, getparent(fname), O_RDONLY);
  751     if (error) {
  752         fsrprintf(_("unable to open sys handle for XFS file %s: %s\n"),
  753             fname, strerror(error));
  754         goto out;
  755     }
  756 
  757     error = -xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
  758     if (error) {
  759         fsrprintf(_("unable to get bstat on %s: %s\n"),
  760             fname, strerror(error));
  761         goto out;
  762     }
  763     error = -xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
  764     if (error) {
  765         fsrprintf(_("bstat conversion error on %s: %s\n"),
  766             fname, strerror(error));
  767         goto out;
  768     }
  769 
  770     fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
  771     if (fd < 0) {
  772         fsrprintf(_("unable to open handle %s: %s\n"),
  773             fname, strerror(errno));
  774         goto out;
  775     }
  776 
  777     /* Stash the fs geometry for general use. */
  778     memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
  779 
  780     tname = gettmpname(fname);
  781 
  782     if (tname)
  783         error = fsrfile_common(fname, tname, NULL, fd, &statbuf);
  784 
  785 out:
  786     xfd_close(&fsxfd);
  787     if (fd >= 0)
  788         close(fd);
  789     free(fshandlep);
  790 
  791     return error;
  792 }
  793 
  794 
  795 /*
  796  * This is the common defrag code for either a full fs
  797  * defragmentation or a single file.  Check as much as
  798  * possible with the file, fork a process to setuid to the
  799  * target file owner's uid and defragment the file.
  800  * This is done so the new extents created in a tmp file are
  801  * reflected in the owners' quota without having to do any
  802  * special code in the kernel.  When the existing extents
  803  * are removed, the quotas will be correct.  It's ugly but
  804  * it saves us from doing some quota  re-construction in
  805  * the extent swap.  The price is that the defragmentation
  806  * will fail if the owner of the target file is already at
  807  * their quota limit.
  808  */
  809 static int
  810 fsrfile_common(
  811     char        *fname,
  812     char        *tname,
  813     char        *fsname,
  814     int     fd,
  815     struct xfs_bstat *statp)
  816 {
  817     int     error;
  818     struct statvfs  vfss;
  819     struct fsxattr  fsx;
  820     unsigned long   bsize;
  821 
  822     if (vflag)
  823         fsrprintf("%s\n", fname);
  824 
  825     if (fsync(fd) < 0) {
  826         fsrprintf(_("sync failed: %s: %s\n"), fname, strerror(errno));
  827         return -1;
  828     }
  829 
  830     if (statp->bs_size == 0) {
  831         if (vflag)
  832             fsrprintf(_("%s: zero size, ignoring\n"), fname);
  833         return(0);
  834     }
  835 
  836     /* Check if a mandatory lock is set on the file to try and
  837      * avoid blocking indefinitely on the reads later. Note that
  838      * someone could still set a mandatory lock after this check
  839      * but before all reads have completed to block fsr reads.
  840      * This change just closes the window a bit.
  841      */
  842     if ( (statp->bs_mode & S_ISGID) && ( ! (statp->bs_mode&S_IXGRP) ) ) {
  843         struct flock fl;
  844 
  845         fl.l_type = F_RDLCK;
  846         fl.l_whence = SEEK_SET;
  847         fl.l_start = (off_t)0;
  848         fl.l_len = 0;
  849         if ((fcntl(fd, F_GETLK, &fl)) < 0 ) {
  850             if (vflag)
  851                 fsrprintf(_("locking check failed: %s\n"),
  852                     fname);
  853             return(-1);
  854         }
  855         if (fl.l_type != F_UNLCK) {
  856             /* Mandatory lock is set */
  857             if (vflag)
  858                 fsrprintf(_("mandatory lock: %s: ignoring\n"),
  859                     fname);
  860             return(-1);
  861         }
  862     }
  863 
  864     /*
  865      * Check if there is room to copy the file.
  866      *
  867      * Note that xfs_bstat.bs_blksize returns the filesystem blocksize,
  868      * not the optimal I/O size as struct stat.
  869      */
  870     if (statvfs(fsname ? fsname : fname, &vfss) < 0) {
  871         fsrprintf(_("unable to get fs stat on %s: %s\n"),
  872             fname, strerror(errno));
  873         return -1;
  874     }
  875     bsize = vfss.f_frsize ? vfss.f_frsize : vfss.f_bsize;
  876     if (statp->bs_blksize * statp->bs_blocks >
  877         vfss.f_bfree * bsize - minimumfree) {
  878         fsrprintf(_("insufficient freespace for: %s: "
  879                 "size=%lld: ignoring\n"), fname,
  880                 statp->bs_blksize * statp->bs_blocks);
  881         return 1;
  882     }
  883 
  884     if ((ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
  885         fsrprintf(_("failed to get inode attrs: %s\n"), fname);
  886         return(-1);
  887     }
  888     if (fsx.fsx_xflags & (FS_XFLAG_IMMUTABLE|FS_XFLAG_APPEND)) {
  889         if (vflag)
  890             fsrprintf(_("%s: immutable/append, ignoring\n"), fname);
  891         return(0);
  892     }
  893     if (fsx.fsx_xflags & FS_XFLAG_NODEFRAG) {
  894         if (vflag)
  895             fsrprintf(_("%s: marked as don't defrag, ignoring\n"),
  896                 fname);
  897         return(0);
  898     }
  899     if (fsx.fsx_xflags & FS_XFLAG_REALTIME) {
  900         if (xfs_getrt(fd, &vfss) < 0) {
  901             fsrprintf(_("cannot get realtime geometry for: %s\n"),
  902                 fname);
  903             return(-1);
  904         }
  905         if (statp->bs_size > ((vfss.f_bfree * bsize) - minimumfree)) {
  906             fsrprintf(_("low on realtime free space: %s: "
  907                 "ignoring file\n"), fname);
  908             return(-1);
  909         }
  910     }
  911 
  912     if ((RealUid != ROOT) && (RealUid != statp->bs_uid)) {
  913         fsrprintf(_("cannot open: %s: Permission denied\n"), fname);
  914         return -1;
  915     }
  916 
  917     /*
  918      * Previously the code forked here, & the child changed it's uid to
  919      * that of the file's owner and then called packfile(), to keep
  920      * quota counts correct.  (defragged files could use fewer blocks).
  921      *
  922      * Instead, just fchown() the temp file to the uid,gid of the
  923      * file we're defragging, in packfile().
  924      */
  925 
  926     if ((error = packfile(fname, tname, fd, statp, &fsx)))
  927         return error;
  928     return -1; /* no error */
  929 }
  930 
  931 /*
  932  * Attempt to set the attr fork up correctly. This is simple for attr1
  933  * filesystems as they have a fixed inode fork offset. In that case
  934  * just create an attribute and that's all we need to do.
  935  *
  936  * For attr2 filesystems, see if we have the actual fork offset in
  937  * the bstat structure. If so, just create additional attributes on
  938  * the temporary inode until the offset matches.
  939  *
  940  * If it doesn't exist, we can only do best effort. Add an attribute at a time
  941  * to move the inode fork around, but take into account that the attribute
  942  * might be too small to move the fork every time we add one.  This should
  943  * hopefully put the fork offset in the right place. It's not a big deal if we
  944  * don't get it right - the kernel will reject it when we try to swap extents.
  945  */
  946 static int
  947 fsr_setup_attr_fork(
  948     int     fd,
  949     int     tfd,
  950     struct xfs_bstat *bstatp)
  951 {
  952 #ifdef HAVE_FSETXATTR
  953     struct xfs_fd   txfd = XFS_FD_INIT(tfd);
  954     struct stat tstatbuf;
  955     int     i;
  956     int     diff = 0;
  957     int     last_forkoff = 0;
  958     int     no_change_cnt = 0;
  959     int     ret;
  960 
  961     if (!(bstatp->bs_xflags & FS_XFLAG_HASATTR))
  962         return 0;
  963 
  964     /*
  965      * use the old method if we have attr1 or the kernel does not yet
  966      * support passing the fork offset in the bulkstat data.
  967      */
  968     if (!(fsgeom.flags & XFS_FSOP_GEOM_FLAGS_ATTR2) ||
  969         bstatp->bs_forkoff == 0) {
  970         /* attr1 */
  971         ret = fsetxattr(txfd.fd, "user.X", "X", 1, XATTR_CREATE);
  972         if (ret) {
  973             fsrprintf(_("could not set ATTR\n"));
  974             return -1;
  975         }
  976         goto out;
  977     }
  978 
  979     /* attr2 w/ fork offsets */
  980 
  981     if (fstat(txfd.fd, &tstatbuf) < 0) {
  982         fsrprintf(_("unable to stat temp file: %s\n"),
  983                     strerror(errno));
  984         return -1;
  985     }
  986 
  987     i = 0;
  988     do {
  989         struct xfs_bulkstat tbstat;
  990         char        name[64];
  991         int     ret;
  992 
  993         /*
  994          * bulkstat the temp inode to see what the forkoff is.  Use
  995          * this to compare against the target and determine what we
  996          * need to do.
  997          */
  998         ret = -xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0,
  999                 &tbstat);
 1000         if (ret) {
 1001             fsrprintf(_("unable to get bstat on temp file: %s\n"),
 1002                         strerror(ret));
 1003             return -1;
 1004         }
 1005         if (dflag)
 1006             fsrprintf(_("orig forkoff %d, temp forkoff %d\n"),
 1007                     bstatp->bs_forkoff, tbstat.bs_forkoff);
 1008         diff = tbstat.bs_forkoff - bstatp->bs_forkoff;
 1009 
 1010         /* if they are equal, we are done */
 1011         if (!diff)
 1012             goto out;
 1013 
 1014         snprintf(name, sizeof(name), "user.%d", i);
 1015 
 1016         /*
 1017          * If there is no attribute, then we need to create one to get
 1018          * an attribute fork at the default location.
 1019          */
 1020         if (!tbstat.bs_forkoff) {
 1021             ASSERT(i == 0);
 1022             ret = fsetxattr(txfd.fd, name, "XX", 2, XATTR_CREATE);
 1023             if (ret) {
 1024                 fsrprintf(_("could not set ATTR\n"));
 1025                 return -1;
 1026             }
 1027             continue;
 1028         } else if (i == 0) {
 1029             /*
 1030              * First pass, and temp file already has an inline
 1031              * xattr, probably due to selinux.
 1032              *
 1033              * It's *possible* that the temp file attr area
 1034              * is larger than the target file's:
 1035              *
 1036              *  Target       Temp
 1037              * +-------+ 0      +-------+ 0
 1038              * |       |        |       |
 1039              * |       |        | Data  |
 1040              * | Data  |        |       |
 1041              * |       |        v-------v forkoff
 1042              * |       |        |       |
 1043              * v-------v forkoff    | Attr  | local
 1044              * | Attr  |        |       |
 1045              * +-------+        +-------+
 1046              */
 1047 
 1048             /*
 1049              * If target attr area is less than the temp's
 1050              * (diff < 0) write a big attr to the temp file to knock
 1051              * the attr out of local format.
 1052              * (This should actually *increase* the temp file's
 1053              * forkoffset when the attr moves out of the inode)
 1054              */
 1055             if (diff < 0) {
 1056                 char val[2048];
 1057                 memset(val, 'X', 2048);
 1058                 if (fsetxattr(txfd.fd, name, val, 2048, 0)) {
 1059                     fsrprintf(_("big ATTR set failed\n"));
 1060                     return -1;
 1061                 }
 1062                 /* Go back & see where we're at now */
 1063                 continue;
 1064             }
 1065         }
 1066 
 1067         /*
 1068          * make a progress check so we don't get stuck trying to extend
 1069          * a large btree form attribute fork.
 1070          */
 1071         if (last_forkoff == tbstat.bs_forkoff) {
 1072             if (no_change_cnt++ > 10)
 1073                 break;
 1074         } else /* progress! */
 1075             no_change_cnt = 0;
 1076         last_forkoff = tbstat.bs_forkoff;
 1077 
 1078         /* work out which way to grow the fork */
 1079         if (abs(diff) > fsgeom.inodesize - sizeof(struct xfs_dinode)) {
 1080             fsrprintf(_("forkoff diff %d too large!\n"), diff);
 1081             return -1;
 1082         }
 1083 
 1084         /*
 1085          * if the temp inode fork offset is still smaller then we have
 1086          * to grow the data fork
 1087          */
 1088         if (diff < 0) {
 1089             /*
 1090              * create some temporary extents in the inode to move
 1091              * the fork in the direction we need. This can be done
 1092              * by preallocating some single block extents at
 1093              * non-contiguous offsets.
 1094              */
 1095             /* XXX: unimplemented! */
 1096             if (dflag)
 1097                 printf(_("data fork growth unimplemented\n"));
 1098             goto out;
 1099         }
 1100 
 1101         /* we need to grow the attr fork, so create another attr */
 1102         ret = fsetxattr(txfd.fd, name, "XX", 2, XATTR_CREATE);
 1103         if (ret) {
 1104             fsrprintf(_("could not set ATTR\n"));
 1105             return -1;
 1106         }
 1107 
 1108     } while (++i < 100); /* don't go forever */
 1109 
 1110 out:
 1111     if (dflag)
 1112         fsrprintf(_("set temp attr\n"));
 1113     /* We failed to resolve the fork difference */
 1114     if (dflag && diff)
 1115         fsrprintf(_("failed to match fork offset\n"));;
 1116 
 1117 #endif /* HAVE_FSETXATTR */
 1118     return 0;
 1119 }
 1120 
 1121 /*
 1122  * Do the defragmentation of a single file.
 1123  * We already are pretty sure we can and want to
 1124  * defragment the file.  Create the tmp file, copy
 1125  * the data (maintaining holes) and call the kernel
 1126  * extent swap routine.
 1127  *
 1128  * Return values:
 1129  * -1: Some error was encountered
 1130  *  0: Successfully defragmented the file
 1131  *  1: No change / No Error
 1132  */
 1133 static int
 1134 packfile(char *fname, char *tname, int fd,
 1135      struct xfs_bstat *statp, struct fsxattr *fsxp)
 1136 {
 1137     int         tfd = -1;
 1138     int     srval;
 1139     int     retval = -1;    /* Failure is the default */
 1140     int     nextents, extent, cur_nextents, new_nextents;
 1141     unsigned    blksz_dio;
 1142     unsigned    dio_min;
 1143     struct dioattr  dio;
 1144     static xfs_swapext_t   sx;
 1145     struct xfs_flock64  space;
 1146     off64_t     cnt, pos;
 1147     void        *fbuf = NULL;
 1148     int         ct, wc, wc_b4;
 1149     char        ffname[SMBUFSZ];
 1150     int     ffd = -1;
 1151 
 1152     /*
 1153      * Work out the extent map - nextents will be set to the
 1154      * minimum number of extents needed for the file (taking
 1155      * into account holes), cur_nextents is the current number
 1156      * of extents.
 1157      */
 1158     nextents = read_fd_bmap(fd, statp, &cur_nextents);
 1159 
 1160     if (cur_nextents == 1 || cur_nextents <= nextents) {
 1161         if (vflag)
 1162             fsrprintf(_("%s already fully defragmented.\n"), fname);
 1163         retval = 1; /* indicates no change/no error */
 1164         goto out;
 1165     }
 1166 
 1167     if (dflag)
 1168         fsrprintf(_("%s extents=%d can_save=%d tmp=%s\n"),
 1169                   fname, cur_nextents, (cur_nextents - nextents),
 1170                   tname);
 1171 
 1172     if ((tfd = open(tname, openopts, 0666)) < 0) {
 1173         if (vflag)
 1174             fsrprintf(_("could not open tmp file: %s: %s\n"),
 1175                    tname, strerror(errno));
 1176         goto out;
 1177     }
 1178     unlink(tname);
 1179 
 1180     /* Setup extended attributes */
 1181     if (fsr_setup_attr_fork(fd, tfd, statp) != 0) {
 1182         fsrprintf(_("failed to set ATTR fork on tmp: %s:\n"), tname);
 1183         goto out;
 1184     }
 1185 
 1186     /* Setup extended inode flags, project identifier, etc */
 1187     if (fsxp->fsx_xflags || fsxp->fsx_projid) {
 1188         if (ioctl(tfd, FS_IOC_FSSETXATTR, fsxp) < 0) {
 1189             fsrprintf(_("could not set inode attrs on tmp: %s\n"),
 1190                 tname);
 1191             goto out;
 1192         }
 1193     }
 1194 
 1195     if ((ioctl(tfd, XFS_IOC_DIOINFO, &dio)) < 0 ) {
 1196         fsrprintf(_("could not get DirectIO info on tmp: %s\n"), tname);
 1197         goto out;
 1198     }
 1199 
 1200     dio_min = dio.d_miniosz;
 1201     if (statp->bs_size <= dio_min) {
 1202         blksz_dio = dio_min;
 1203     } else {
 1204         blksz_dio = min(dio.d_maxiosz, BUFFER_MAX - pagesize);
 1205         if (argv_blksz_dio != 0)
 1206             blksz_dio = min(argv_blksz_dio, blksz_dio);
 1207         blksz_dio = (min(statp->bs_size, blksz_dio) / dio_min) * dio_min;
 1208     }
 1209 
 1210     if (dflag) {
 1211         fsrprintf(_("DEBUG: "
 1212             "fsize=%lld blsz_dio=%d d_min=%d d_max=%d pgsz=%d\n"),
 1213             statp->bs_size, blksz_dio, dio.d_miniosz,
 1214             dio.d_maxiosz, pagesize);
 1215     }
 1216 
 1217     if (!(fbuf = (char *)memalign(dio.d_mem, blksz_dio))) {
 1218         fsrprintf(_("could not allocate buf: %s\n"), tname);
 1219         goto out;
 1220     }
 1221 
 1222     if (nfrags) {
 1223         /* Create new tmp file in same AG as first */
 1224         sprintf(ffname, "%s.frag", tname);
 1225 
 1226         /* Open the new file for sync writes */
 1227         if ((ffd = open(ffname, openopts, 0666)) < 0) {
 1228             fsrprintf(_("could not open fragfile: %s : %s\n"),
 1229                    ffname, strerror(errno));
 1230             goto out;
 1231         }
 1232         unlink(ffname);
 1233     }
 1234 
 1235     /* Loop through block map allocating new extents */
 1236     for (extent = 0; extent < nextents; extent++) {
 1237         pos = outmap[extent].bmv_offset;
 1238         if (outmap[extent].bmv_block == -1) {
 1239             space.l_whence = SEEK_SET;
 1240             space.l_start = pos;
 1241             space.l_len = outmap[extent].bmv_length;
 1242             if (ioctl(tfd, XFS_IOC_UNRESVSP64, &space) < 0) {
 1243                 fsrprintf(_("could not trunc tmp %s\n"),
 1244                        tname);
 1245             }
 1246             if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
 1247                 fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
 1248                    tname, strerror(errno));
 1249                 goto out;
 1250             }
 1251             continue;
 1252         } else if (outmap[extent].bmv_length == 0) {
 1253             /* to catch holes at the beginning of the file */
 1254             continue;
 1255         }
 1256         if (! nfrags) {
 1257             space.l_whence = SEEK_CUR;
 1258             space.l_start = 0;
 1259             space.l_len = outmap[extent].bmv_length;
 1260 
 1261             if (ioctl(tfd, XFS_IOC_RESVSP64, &space) < 0) {
 1262                 fsrprintf(_("could not pre-allocate tmp space:"
 1263                     " %s\n"), tname);
 1264                 goto out;
 1265             }
 1266             if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
 1267                 fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
 1268                    tname, strerror(errno));
 1269                 goto out;
 1270             }
 1271         }
 1272     } /* end of space allocation loop */
 1273 
 1274     if (lseek(tfd, 0, SEEK_SET)) {
 1275         fsrprintf(_("Couldn't rewind on temporary file\n"));
 1276         goto out;
 1277     }
 1278 
 1279     /* Check if the temporary file has fewer extents */
 1280     new_nextents = getnextents(tfd);
 1281     if (dflag)
 1282         fsrprintf(_("Temporary file has %d extents (%d in original)\n"), new_nextents, cur_nextents);
 1283     if (cur_nextents <= new_nextents) {
 1284         if (vflag)
 1285             fsrprintf(_("No improvement will be made (skipping): %s\n"), fname);
 1286         retval = 1; /* no change/no error */
 1287         goto out;
 1288     }
 1289 
 1290     /* Loop through block map copying the file. */
 1291     for (extent = 0; extent < nextents; extent++) {
 1292         pos = outmap[extent].bmv_offset;
 1293         if (outmap[extent].bmv_block == -1) {
 1294             if (lseek(tfd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
 1295                 fsrprintf(_("could not lseek in tmpfile: %s : %s\n"),
 1296                    tname, strerror(errno));
 1297                 goto out;
 1298             }
 1299             if (lseek(fd, outmap[extent].bmv_length, SEEK_CUR) < 0) {
 1300                 fsrprintf(_("could not lseek in file: %s : %s\n"),
 1301                    fname, strerror(errno));
 1302                 goto out;
 1303             }
 1304             continue;
 1305         } else if (outmap[extent].bmv_length == 0) {
 1306             /* to catch holes at the beginning of the file */
 1307             continue;
 1308         }
 1309         for (cnt = outmap[extent].bmv_length; cnt > 0;
 1310              cnt -= ct, pos += ct) {
 1311             if (nfrags && --nfrags) {
 1312                 ct = min(cnt, dio_min);
 1313             } else if (cnt % dio_min == 0) {
 1314                 ct = min(cnt, blksz_dio);
 1315             } else {
 1316                 ct = min(cnt + dio_min - (cnt % dio_min),
 1317                     blksz_dio);
 1318             }
 1319             ct = read(fd, fbuf, ct);
 1320             if (ct == 0) {
 1321                 /* EOF, stop trying to read */
 1322                 extent = nextents;
 1323                 break;
 1324             }
 1325             /* Ensure we do direct I/O to correct block
 1326              * boundaries.
 1327              */
 1328             if (ct % dio_min != 0) {
 1329                 wc = ct + dio_min - (ct % dio_min);
 1330             } else {
 1331                 wc = ct;
 1332             }
 1333             wc_b4 = wc;
 1334             if (ct < 0 || ((wc = write(tfd, fbuf, wc)) != wc_b4)) {
 1335                 if (ct < 0)
 1336                     fsrprintf(_("bad read of %d bytes "
 1337                         "from %s: %s\n"), wc_b4,
 1338                         fname, strerror(errno));
 1339                 else if (wc < 0)
 1340                     fsrprintf(_("bad write of %d bytes "
 1341                         "to %s: %s\n"), wc_b4,
 1342                         tname, strerror(errno));
 1343                 else {
 1344                     /*
 1345                      * Might be out of space
 1346                      *
 1347                      * Try to finish write
 1348                      */
 1349                     int resid = ct-wc;
 1350 
 1351                     if ((wc = write(tfd, ((char *)fbuf)+wc,
 1352                             resid)) == resid) {
 1353                         /* worked on second attempt? */
 1354                         continue;
 1355                     }
 1356                     else if (wc < 0) {
 1357                         fsrprintf(_("bad write2 of %d "
 1358                             "bytes to %s: %s\n"),
 1359                             resid, tname,
 1360                             strerror(errno));
 1361                     } else {
 1362                         fsrprintf(_("bad copy to %s\n"),
 1363                             tname);
 1364                     }
 1365                 }
 1366                 goto out;
 1367             }
 1368             if (nfrags) {
 1369                 /* Do a matching write to the tmp file */
 1370                 wc_b4 = wc;
 1371                 if (((wc = write(ffd, fbuf, wc)) != wc_b4)) {
 1372                     fsrprintf(_("bad write of %d bytes "
 1373                         "to %s: %s\n"),
 1374                         wc_b4, ffname, strerror(errno));
 1375                 }
 1376             }
 1377         }
 1378     }
 1379     if (ftruncate(tfd, statp->bs_size) < 0) {
 1380         fsrprintf(_("could not truncate tmpfile: %s : %s\n"),
 1381                 fname, strerror(errno));
 1382         goto out;
 1383     }
 1384     if (fsync(tfd) < 0) {
 1385         fsrprintf(_("could not fsync tmpfile: %s : %s\n"),
 1386                 fname, strerror(errno));
 1387         goto out;
 1388     }
 1389 
 1390     sx.sx_stat     = *statp; /* struct copy */
 1391     sx.sx_version  = XFS_SX_VERSION;
 1392     sx.sx_fdtarget = fd;
 1393     sx.sx_fdtmp    = tfd;
 1394     sx.sx_offset   = 0;
 1395     sx.sx_length   = statp->bs_size;
 1396 
 1397     /* switch to the owner's id, to keep quota in line */
 1398         if (fchown(tfd, statp->bs_uid, statp->bs_gid) < 0) {
 1399                 if (vflag)
 1400                         fsrprintf(_("failed to fchown tmpfile %s: %s\n"),
 1401                                    tname, strerror(errno));
 1402         goto out;
 1403         }
 1404 
 1405     /* Swap the extents */
 1406     srval = xfs_swapext(fd, &sx);
 1407     if (srval < 0) {
 1408         if (errno == ENOTSUP) {
 1409             if (vflag || dflag)
 1410                fsrprintf(_("%s: file type not supported\n"), fname);
 1411         } else if (errno == EFAULT) {
 1412             /* The file has changed since we started the copy */
 1413             if (vflag || dflag)
 1414                fsrprintf(_("%s: file modified defrag aborted\n"),
 1415                      fname);
 1416         } else if (errno == EBUSY) {
 1417             /* Timestamp has changed or mmap'ed file */
 1418             if (vflag || dflag)
 1419                fsrprintf(_("%s: file busy\n"), fname);
 1420         } else {
 1421             fsrprintf(_("XFS_IOC_SWAPEXT failed: %s: %s\n"),
 1422                   fname, strerror(errno));
 1423         }
 1424         goto out;
 1425     }
 1426 
 1427     /* Report progress */
 1428     if (vflag)
 1429         fsrprintf(_("extents before:%d after:%d %s %s\n"),
 1430               cur_nextents, new_nextents,
 1431               (new_nextents <= nextents ? "DONE" : "    " ),
 1432                   fname);
 1433     retval = 0;
 1434 
 1435 out:
 1436     free(fbuf);
 1437     if (tfd != -1)
 1438         close(tfd);
 1439     if (ffd != -1)
 1440         close(ffd);
 1441     return retval;
 1442 }
 1443 
 1444 char *
 1445 gettmpname(char *fname)
 1446 {
 1447     static char buf[PATH_MAX+1];
 1448     char        sbuf[SMBUFSZ];
 1449     char        *ptr;
 1450 
 1451     sprintf(sbuf, "/.fsr%d", getpid());
 1452 
 1453     strncpy(buf, fname, PATH_MAX);
 1454     buf[PATH_MAX] = '\0';
 1455     ptr = strrchr(buf, '/');
 1456     if (ptr) {
 1457         *ptr = '\0';
 1458     } else {
 1459         strcpy(buf, ".");
 1460     }
 1461 
 1462     if ((strlen(buf) + strlen (sbuf)) > PATH_MAX) {
 1463         fsrprintf(_("tmp file name too long: %s\n"), fname);
 1464         return(NULL);
 1465     }
 1466 
 1467     strcat(buf, sbuf);
 1468 
 1469     return(buf);
 1470 }
 1471 
 1472 char *
 1473 getparent(char *fname)
 1474 {
 1475     static char buf[PATH_MAX+1];
 1476     char        *ptr;
 1477 
 1478     strncpy(buf, fname, PATH_MAX);
 1479     buf[PATH_MAX] = '\0';
 1480     ptr = strrchr(buf, '/');
 1481     if (ptr) {
 1482         if (ptr == &buf[0])
 1483             ++ptr;
 1484         *ptr = '\0';
 1485     } else {
 1486         strcpy(buf, ".");
 1487     }
 1488 
 1489     return(buf);
 1490 }
 1491 
 1492 /*
 1493  * Read in block map of the input file, coalesce contiguous
 1494  * extents into a single range, keep all holes. Convert from 512 byte
 1495  * blocks to bytes.
 1496  *
 1497  * This code was borrowed from mv.c with some minor mods.
 1498  */
 1499 #define MAPSIZE 128
 1500 #define OUTMAP_SIZE_INCREMENT   MAPSIZE
 1501 
 1502 int read_fd_bmap(int fd, struct xfs_bstat *sin, int *cur_nextents)
 1503 {
 1504     int     i, cnt;
 1505     struct getbmap  map[MAPSIZE];
 1506 
 1507 #define BUMP_CNT    \
 1508     if (++cnt >= outmap_size) { \
 1509         outmap_size += OUTMAP_SIZE_INCREMENT; \
 1510         outmap = (struct getbmap *)realloc(outmap, \
 1511                                    outmap_size*sizeof(*outmap)); \
 1512         if (outmap == NULL) { \
 1513             fsrprintf(_("realloc failed: %s\n"), \
 1514                 strerror(errno)); \
 1515             exit(1); \
 1516         } \
 1517     }
 1518 
 1519     /*  Initialize the outmap array.  It always grows - never shrinks.
 1520      *  Left-over memory allocation is saved for the next files.
 1521      */
 1522     if (outmap_size == 0) {
 1523         outmap_size = OUTMAP_SIZE_INCREMENT; /* Initial size */
 1524         outmap = (struct getbmap *)malloc(outmap_size*sizeof(*outmap));
 1525         if (!outmap) {
 1526             fsrprintf(_("malloc failed: %s\n"),
 1527                 strerror(errno));
 1528             exit(1);
 1529         }
 1530     }
 1531 
 1532     outmap[0].bmv_block = 0;
 1533     outmap[0].bmv_offset = 0;
 1534     outmap[0].bmv_length = sin->bs_size;
 1535 
 1536     /*
 1537      * If a non regular file is involved then forget holes
 1538      */
 1539 
 1540     if (!S_ISREG(sin->bs_mode))
 1541         return(1);
 1542 
 1543     outmap[0].bmv_length = 0;
 1544 
 1545     map[0].bmv_offset = 0;
 1546     map[0].bmv_block = 0;
 1547     map[0].bmv_entries = 0;
 1548     map[0].bmv_count = MAPSIZE;
 1549     map[0].bmv_length = -1;
 1550 
 1551     cnt = 0;
 1552     *cur_nextents = 0;
 1553 
 1554     do {
 1555         if (ioctl(fd, XFS_IOC_GETBMAP, map) < 0) {
 1556             fsrprintf(_("failed reading extents: inode %llu"),
 1557                      (unsigned long long)sin->bs_ino);
 1558             exit(1);
 1559         }
 1560 
 1561         /* Concatenate extents together and replicate holes into
 1562          * the output map.
 1563          */
 1564         *cur_nextents += map[0].bmv_entries;
 1565         for (i = 0; i < map[0].bmv_entries; i++) {
 1566             if (map[i + 1].bmv_block == -1) {
 1567                 BUMP_CNT;
 1568                 outmap[cnt] = map[i+1];
 1569             } else if (outmap[cnt].bmv_block == -1) {
 1570                 BUMP_CNT;
 1571                 outmap[cnt] = map[i+1];
 1572             } else {
 1573                 outmap[cnt].bmv_length += map[i + 1].bmv_length;
 1574             }
 1575         }
 1576     } while (map[0].bmv_entries == (MAPSIZE-1));
 1577     for (i = 0; i <= cnt; i++) {
 1578         outmap[i].bmv_offset = BBTOB(outmap[i].bmv_offset);
 1579         outmap[i].bmv_length = BBTOB(outmap[i].bmv_length);
 1580     }
 1581 
 1582     outmap[cnt].bmv_length = sin->bs_size - outmap[cnt].bmv_offset;
 1583 
 1584     return(cnt+1);
 1585 }
 1586 
 1587 /*
 1588  * Read the block map and return the number of extents.
 1589  */
 1590 static int
 1591 getnextents(int fd)
 1592 {
 1593     int     nextents;
 1594     struct getbmap  map[MAPSIZE];
 1595 
 1596     map[0].bmv_offset = 0;
 1597     map[0].bmv_block = 0;
 1598     map[0].bmv_entries = 0;
 1599     map[0].bmv_count = MAPSIZE;
 1600     map[0].bmv_length = -1;
 1601 
 1602     nextents = 0;
 1603 
 1604     do {
 1605         if (ioctl(fd,XFS_IOC_GETBMAP, map) < 0) {
 1606             fsrprintf(_("failed reading extents"));
 1607             exit(1);
 1608         }
 1609 
 1610         nextents += map[0].bmv_entries;
 1611     } while (map[0].bmv_entries == (MAPSIZE-1));
 1612 
 1613     return(nextents);
 1614 }
 1615 
 1616 /*
 1617  * Get xfs realtime space information
 1618  */
 1619 int
 1620 xfs_getrt(int fd, struct statvfs *sfbp)
 1621 {
 1622     unsigned long   bsize;
 1623     unsigned long   factor;
 1624     xfs_fsop_counts_t cnt;
 1625 
 1626     if (!fsgeom.rtblocks)
 1627         return -1;
 1628 
 1629     if (xfs_fscounts(fd, &cnt) < 0) {
 1630         close(fd);
 1631         return -1;
 1632     }
 1633     bsize = (sfbp->f_frsize ? sfbp->f_frsize : sfbp->f_bsize);
 1634     factor = fsgeom.blocksize / bsize;         /* currently this is == 1 */
 1635     sfbp->f_bfree = (cnt.freertx * fsgeom.rtextsize) * factor;
 1636     return 0;
 1637 }
 1638 
 1639 int
 1640 fsrprintf(const char *fmt, ...)
 1641 {
 1642     va_list ap;
 1643 
 1644     va_start(ap, fmt);
 1645     if (gflag) {
 1646         static int didopenlog;
 1647         if (!didopenlog) {
 1648             openlog("fsr", LOG_PID, LOG_USER);
 1649             didopenlog = 1;
 1650         }
 1651         vsyslog(LOG_INFO, fmt, ap);
 1652     } else
 1653         vprintf(fmt, ap);
 1654     va_end(ap);
 1655     return 0;
 1656 }
 1657 
 1658 /*
 1659  * Initialize a directory for tmp file use.  This is used
 1660  * by the full filesystem defragmentation when we're walking
 1661  * the inodes and do not know the path for the individual
 1662  * files.  Multiple directories are used to spread out the
 1663  * tmp data around to different ag's (since file data is
 1664  * usually allocated to the same ag as the directory and
 1665  * directories allocated round robin from the same
 1666  * parent directory).
 1667  */
 1668 static void
 1669 tmp_init(char *mnt)
 1670 {
 1671     int     i;
 1672     static char buf[SMBUFSZ];
 1673     mode_t  mask;
 1674 
 1675     tmp_agi = 0;
 1676     sprintf(buf, "%s/.fsr", mnt);
 1677 
 1678     mask = umask(0);
 1679     if (mkdir(buf, 0700) < 0) {
 1680         if (errno == EEXIST) {
 1681             if (dflag)
 1682                 fsrprintf(_("tmpdir already exists: %s\n"),
 1683                         buf);
 1684         } else {
 1685             fsrprintf(_("could not create tmpdir: %s: %s\n"),
 1686                     buf, strerror(errno));
 1687             exit(-1);
 1688         }
 1689     }
 1690     for (i=0; i < fsgeom.agcount; i++) {
 1691         sprintf(buf, "%s/.fsr/ag%d", mnt, i);
 1692         if (mkdir(buf, 0700) < 0) {
 1693             if (errno == EEXIST) {
 1694                 if (dflag)
 1695                     fsrprintf(
 1696                     _("tmpdir already exists: %s\n"), buf);
 1697             } else {
 1698                 fsrprintf(_("cannot create tmpdir: %s: %s\n"),
 1699                        buf, strerror(errno));
 1700                 exit(-1);
 1701             }
 1702         }
 1703     }
 1704     (void)umask(mask);
 1705     return;
 1706 }
 1707 
 1708 static char *
 1709 tmp_next(char *mnt)
 1710 {
 1711     static char buf[SMBUFSZ];
 1712 
 1713     sprintf(buf, "%s/.fsr/ag%d/tmp%d",
 1714             ( (strcmp(mnt, "/") == 0) ? "" : mnt),
 1715             tmp_agi,
 1716             getpid());
 1717 
 1718     if (++tmp_agi == fsgeom.agcount)
 1719         tmp_agi = 0;
 1720 
 1721     return(buf);
 1722 }
 1723 
 1724 static void
 1725 tmp_close(char *mnt)
 1726 {
 1727     static char buf[SMBUFSZ];
 1728     int i;
 1729 
 1730     /* No data is ever actually written so we can just do rmdir's */
 1731     for (i=0; i < fsgeom.agcount; i++) {
 1732         sprintf(buf, "%s/.fsr/ag%d", mnt, i);
 1733         if (rmdir(buf) < 0) {
 1734             if (errno != ENOENT) {
 1735                 fsrprintf(
 1736                     _("could not remove tmpdir: %s: %s\n"),
 1737                     buf, strerror(errno));
 1738             }
 1739         }
 1740     }
 1741     sprintf(buf, "%s/.fsr", mnt);
 1742     if (rmdir(buf) < 0) {
 1743         if (errno != ENOENT) {
 1744             fsrprintf(_("could not remove tmpdir: %s: %s\n"),
 1745                       buf, strerror(errno));
 1746         }
 1747     }
 1748 }