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