]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - fsr/xfs_fsr.c
libfrog: convert fsgeom.c functions to negative error codes
[thirdparty/xfsprogs-dev.git] / fsr / xfs_fsr.c
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 breq = xfrog_bulkstat_alloc_req(GRABSZ, startino);
617 if (!breq) {
618 fsrprintf(_("Skipping %s: not enough memory\n"),
619 mntdir);
620 xfd_close(&fsxfd);
621 free(fshandlep);
622 return -1;
623 }
624
625 while ((ret = xfrog_bulkstat(&fsxfd, breq) == 0)) {
626 struct xfs_bstat bs1;
627 struct xfs_bulkstat *buf = breq->bulkstat;
628 struct xfs_bulkstat *p;
629 struct xfs_bulkstat *endp;
630 uint32_t buflenout = breq->hdr.ocount;
631
632 if (buflenout == 0)
633 goto out0;
634
635 /* Each loop through, defrag targetrange percent of the files */
636 count = (buflenout * targetrange) / 100;
637
638 qsort((char *)buf, buflenout, sizeof(struct xfs_bulkstat), cmp);
639
640 for (p = buf, endp = (buf + buflenout); p < endp ; p++) {
641 /* Do some obvious checks now */
642 if (((p->bs_mode & S_IFMT) != S_IFREG) ||
643 (p->bs_extents < 2))
644 continue;
645
646 ret = xfrog_bulkstat_v5_to_v1(&fsxfd, &bs1, p);
647 if (ret) {
648 fsrprintf(_("bstat conversion error: %s\n"),
649 strerror(ret));
650 continue;
651 }
652
653 fd = jdm_open(fshandlep, &bs1, O_RDWR | O_DIRECT);
654 if (fd < 0) {
655 /* This probably means the file was
656 * removed while in progress of handling
657 * it. Just quietly ignore this file.
658 */
659 if (dflag)
660 fsrprintf(_("could not open: "
661 "inode %llu\n"), p->bs_ino);
662 continue;
663 }
664
665 /* Don't know the pathname, so make up something */
666 sprintf(fname, "ino=%lld", (long long)p->bs_ino);
667
668 /* Get a tmp file name */
669 tname = tmp_next(mntdir);
670
671 ret = fsrfile_common(fname, tname, mntdir, fd, &bs1);
672
673 leftoffino = p->bs_ino;
674
675 close(fd);
676
677 if (ret == 0) {
678 if (--count <= 0)
679 break;
680 }
681 }
682 if (endtime && endtime < time(NULL)) {
683 free(breq);
684 tmp_close(mntdir);
685 xfd_close(&fsxfd);
686 fsrall_cleanup(1);
687 exit(1);
688 }
689 }
690 if (ret)
691 fsrprintf(_("%s: bulkstat: %s\n"), progname, strerror(ret));
692 out0:
693 free(breq);
694 tmp_close(mntdir);
695 xfd_close(&fsxfd);
696 free(fshandlep);
697 return 0;
698 }
699
700 /*
701 * To compare bstat structs for qsort.
702 */
703 int
704 cmp(const void *s1, const void *s2)
705 {
706 return( ((struct xfs_bstat *)s2)->bs_extents -
707 ((struct xfs_bstat *)s1)->bs_extents);
708
709 }
710
711 /*
712 * reorganize by directory hierarchy.
713 * Stay in dev (a restriction based on structure of this program -- either
714 * call efs_{n,u}mount() around each file, something smarter or this)
715 */
716 static void
717 fsrdir(char *dirname)
718 {
719 fsrprintf(_("%s: Directory defragmentation not supported\n"), dirname);
720 }
721
722 /*
723 * Sets up the defragmentation of a file based on the
724 * filepath. It collects the bstat information, does
725 * an open on the file and passes this all to fsrfile_common.
726 */
727 static int
728 fsrfile(
729 char *fname,
730 xfs_ino_t ino)
731 {
732 struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
733 struct xfs_bulkstat bulkstat;
734 struct xfs_bstat statbuf;
735 jdm_fshandle_t *fshandlep;
736 int fd = -1;
737 int error = -1;
738 char *tname;
739
740 fshandlep = jdm_getfshandle(getparent (fname) );
741 if (!fshandlep) {
742 fsrprintf(_("unable to construct sys handle for %s: %s\n"),
743 fname, strerror(errno));
744 goto out;
745 }
746
747 /*
748 * Need to open something on the same filesystem as the
749 * file. Open the parent.
750 */
751 error = -xfd_open(&fsxfd, getparent(fname), O_RDONLY);
752 if (error) {
753 fsrprintf(_("unable to open sys handle for XFS file %s: %s\n"),
754 fname, strerror(error));
755 goto out;
756 }
757
758 error = xfrog_bulkstat_single(&fsxfd, ino, 0, &bulkstat);
759 if (error) {
760 fsrprintf(_("unable to get bstat on %s: %s\n"),
761 fname, strerror(error));
762 goto out;
763 }
764 error = xfrog_bulkstat_v5_to_v1(&fsxfd, &statbuf, &bulkstat);
765 if (error) {
766 fsrprintf(_("bstat conversion error on %s: %s\n"),
767 fname, strerror(error));
768 goto out;
769 }
770
771 fd = jdm_open(fshandlep, &statbuf, O_RDWR|O_DIRECT);
772 if (fd < 0) {
773 fsrprintf(_("unable to open handle %s: %s\n"),
774 fname, strerror(errno));
775 goto out;
776 }
777
778 /* Stash the fs geometry for general use. */
779 memcpy(&fsgeom, &fsxfd.fsgeom, sizeof(fsgeom));
780
781 tname = gettmpname(fname);
782
783 if (tname)
784 error = fsrfile_common(fname, tname, NULL, fd, &statbuf);
785
786 out:
787 xfd_close(&fsxfd);
788 if (fd >= 0)
789 close(fd);
790 free(fshandlep);
791
792 return error;
793 }
794
795
796 /*
797 * This is the common defrag code for either a full fs
798 * defragmentation or a single file. Check as much as
799 * possible with the file, fork a process to setuid to the
800 * target file owner's uid and defragment the file.
801 * This is done so the new extents created in a tmp file are
802 * reflected in the owners' quota without having to do any
803 * special code in the kernel. When the existing extents
804 * are removed, the quotas will be correct. It's ugly but
805 * it saves us from doing some quota re-construction in
806 * the extent swap. The price is that the defragmentation
807 * will fail if the owner of the target file is already at
808 * their quota limit.
809 */
810 static int
811 fsrfile_common(
812 char *fname,
813 char *tname,
814 char *fsname,
815 int fd,
816 struct xfs_bstat *statp)
817 {
818 int error;
819 struct statvfs vfss;
820 struct fsxattr fsx;
821 unsigned long bsize;
822
823 if (vflag)
824 fsrprintf("%s\n", fname);
825
826 if (fsync(fd) < 0) {
827 fsrprintf(_("sync failed: %s: %s\n"), fname, strerror(errno));
828 return -1;
829 }
830
831 if (statp->bs_size == 0) {
832 if (vflag)
833 fsrprintf(_("%s: zero size, ignoring\n"), fname);
834 return(0);
835 }
836
837 /* Check if a mandatory lock is set on the file to try and
838 * avoid blocking indefinitely on the reads later. Note that
839 * someone could still set a mandatory lock after this check
840 * but before all reads have completed to block fsr reads.
841 * This change just closes the window a bit.
842 */
843 if ( (statp->bs_mode & S_ISGID) && ( ! (statp->bs_mode&S_IXGRP) ) ) {
844 struct flock fl;
845
846 fl.l_type = F_RDLCK;
847 fl.l_whence = SEEK_SET;
848 fl.l_start = (off_t)0;
849 fl.l_len = 0;
850 if ((fcntl(fd, F_GETLK, &fl)) < 0 ) {
851 if (vflag)
852 fsrprintf(_("locking check failed: %s\n"),
853 fname);
854 return(-1);
855 }
856 if (fl.l_type != F_UNLCK) {
857 /* Mandatory lock is set */
858 if (vflag)
859 fsrprintf(_("mandatory lock: %s: ignoring\n"),
860 fname);
861 return(-1);
862 }
863 }
864
865 /*
866 * Check if there is room to copy the file.
867 *
868 * Note that xfs_bstat.bs_blksize returns the filesystem blocksize,
869 * not the optimal I/O size as struct stat.
870 */
871 if (statvfs(fsname ? fsname : fname, &vfss) < 0) {
872 fsrprintf(_("unable to get fs stat on %s: %s\n"),
873 fname, strerror(errno));
874 return -1;
875 }
876 bsize = vfss.f_frsize ? vfss.f_frsize : vfss.f_bsize;
877 if (statp->bs_blksize * statp->bs_blocks >
878 vfss.f_bfree * bsize - minimumfree) {
879 fsrprintf(_("insufficient freespace for: %s: "
880 "size=%lld: ignoring\n"), fname,
881 statp->bs_blksize * statp->bs_blocks);
882 return 1;
883 }
884
885 if ((ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
886 fsrprintf(_("failed to get inode attrs: %s\n"), fname);
887 return(-1);
888 }
889 if (fsx.fsx_xflags & (FS_XFLAG_IMMUTABLE|FS_XFLAG_APPEND)) {
890 if (vflag)
891 fsrprintf(_("%s: immutable/append, ignoring\n"), fname);
892 return(0);
893 }
894 if (fsx.fsx_xflags & FS_XFLAG_NODEFRAG) {
895 if (vflag)
896 fsrprintf(_("%s: marked as don't defrag, ignoring\n"),
897 fname);
898 return(0);
899 }
900 if (fsx.fsx_xflags & FS_XFLAG_REALTIME) {
901 if (xfs_getrt(fd, &vfss) < 0) {
902 fsrprintf(_("cannot get realtime geometry for: %s\n"),
903 fname);
904 return(-1);
905 }
906 if (statp->bs_size > ((vfss.f_bfree * bsize) - minimumfree)) {
907 fsrprintf(_("low on realtime free space: %s: "
908 "ignoring file\n"), fname);
909 return(-1);
910 }
911 }
912
913 if ((RealUid != ROOT) && (RealUid != statp->bs_uid)) {
914 fsrprintf(_("cannot open: %s: Permission denied\n"), fname);
915 return -1;
916 }
917
918 /*
919 * Previously the code forked here, & the child changed it's uid to
920 * that of the file's owner and then called packfile(), to keep
921 * quota counts correct. (defragged files could use fewer blocks).
922 *
923 * Instead, just fchown() the temp file to the uid,gid of the
924 * file we're defragging, in packfile().
925 */
926
927 if ((error = packfile(fname, tname, fd, statp, &fsx)))
928 return error;
929 return -1; /* no error */
930 }
931
932 /*
933 * Attempt to set the attr fork up correctly. This is simple for attr1
934 * filesystems as they have a fixed inode fork offset. In that case
935 * just create an attribute and that's all we need to do.
936 *
937 * For attr2 filesystems, see if we have the actual fork offset in
938 * the bstat structure. If so, just create additional attributes on
939 * the temporary inode until the offset matches.
940 *
941 * If it doesn't exist, we can only do best effort. Add an attribute at a time
942 * to move the inode fork around, but take into account that the attribute
943 * might be too small to move the fork every time we add one. This should
944 * hopefully put the fork offset in the right place. It's not a big deal if we
945 * don't get it right - the kernel will reject it when we try to swap extents.
946 */
947 static int
948 fsr_setup_attr_fork(
949 int fd,
950 int tfd,
951 struct xfs_bstat *bstatp)
952 {
953 #ifdef HAVE_FSETXATTR
954 struct xfs_fd txfd = XFS_FD_INIT(tfd);
955 struct stat tstatbuf;
956 int i;
957 int diff = 0;
958 int last_forkoff = 0;
959 int no_change_cnt = 0;
960 int ret;
961
962 if (!(bstatp->bs_xflags & FS_XFLAG_HASATTR))
963 return 0;
964
965 /*
966 * use the old method if we have attr1 or the kernel does not yet
967 * support passing the fork offset in the bulkstat data.
968 */
969 if (!(fsgeom.flags & XFS_FSOP_GEOM_FLAGS_ATTR2) ||
970 bstatp->bs_forkoff == 0) {
971 /* attr1 */
972 ret = fsetxattr(txfd.fd, "user.X", "X", 1, XATTR_CREATE);
973 if (ret) {
974 fsrprintf(_("could not set ATTR\n"));
975 return -1;
976 }
977 goto out;
978 }
979
980 /* attr2 w/ fork offsets */
981
982 if (fstat(txfd.fd, &tstatbuf) < 0) {
983 fsrprintf(_("unable to stat temp file: %s\n"),
984 strerror(errno));
985 return -1;
986 }
987
988 i = 0;
989 do {
990 struct xfs_bulkstat tbstat;
991 char name[64];
992 int ret;
993
994 /*
995 * bulkstat the temp inode to see what the forkoff is. Use
996 * this to compare against the target and determine what we
997 * need to do.
998 */
999 ret = xfrog_bulkstat_single(&txfd, tstatbuf.st_ino, 0, &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 }