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