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