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