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