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