[PATCH 2/2] xfsprogs: xfs_shrinkfs(8)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



xfs_shrinkfs(8) for xfsprogs.

This version does not really works for data moving, it only would be used for reflect
my current thoughts for implementing this utility.

Signed-off-by: Jie Liu <jeff.liu@xxxxxxxxxx>

---
 Makefile                 |    3 +-
 include/xfs_dfrag.h      |   20 +
 include/xfs_fs.h         |    6 +
 shrinkfs/Makefile        |   30 ++
 shrinkfs/xfs_shrink.c    | 1184 ++++++++++++++++++++++++++++++++++++++++++++++
 shrinkfs/xfs_shrinkfs.sh |   99 ++++
 6 files changed, 1341 insertions(+), 1 deletion(-)
 create mode 100644 shrinkfs/Makefile
 create mode 100644 shrinkfs/xfs_shrink.c
 create mode 100755 shrinkfs/xfs_shrinkfs.sh

diff --git a/Makefile b/Makefile
index 0bdc5e8..c30ea21 100644
--- a/Makefile
+++ b/Makefile
@@ -41,7 +41,7 @@ endif
 
 LIB_SUBDIRS = libxfs libxlog libxcmd libhandle libdisk
 TOOL_SUBDIRS = copy db estimate fsck fsr growfs io logprint mkfs quota \
-		mdrestore repair rtcp m4 man doc po debian
+		mdrestore repair rtcp m4 man doc po debian shrinkfs
 
 SUBDIRS = include $(LIB_SUBDIRS) $(TOOL_SUBDIRS)
 
@@ -62,6 +62,7 @@ io: libxcmd libhandle
 mkfs: libxfs
 quota: libxcmd
 repair: libxfs libxlog
+shrinkfs: libxfs libxcmd
 
 ifneq ($(ENABLE_BLKID), yes)
 mkfs: libdisk
diff --git a/include/xfs_dfrag.h b/include/xfs_dfrag.h
index 20bdd93..17a076b 100644
--- a/include/xfs_dfrag.h
+++ b/include/xfs_dfrag.h
@@ -38,6 +38,21 @@ typedef struct xfs_swapext
  */
 #define XFS_SX_VERSION		0
 
+/*
+ * Structure passed to xfs_swapino.
+ */
+typedef struct xfs_swapino {
+	__int64_t	si_version;	/* version */
+	__int64_t	si_fdtarget;	/* fd of target file */
+	__int64_t	si_fdtmp;	/* fd of temp file */
+	char		si_pad[16];	/* pad space, unused */
+} xfs_swapino_t;
+
+/*
+ * Version flag.
+ */
+#define XFS_SI_VERSION          0
+
 #ifdef __KERNEL__
 /*
  * Prototypes for visible xfs_dfrag.c routines.
@@ -48,6 +63,11 @@ typedef struct xfs_swapext
  */
 int	xfs_swapext(struct xfs_swapext *sx);
 
+/*
+ * Syscall interface for xfs_swapino
+ */
+int	xfs_swapino(struct xfs_swapino *si);
+
 #endif	/* __KERNEL__ */
 
 #endif	/* __XFS_DFRAG_H__ */
diff --git a/include/xfs_fs.h b/include/xfs_fs.h
index c749474..5b9c1b4 100644
--- a/include/xfs_fs.h
+++ b/include/xfs_fs.h
@@ -267,6 +267,10 @@ typedef struct xfs_growfs_rt {
 	__u32		extsize;	/* new realtime extent size, fsblocks */
 } xfs_growfs_rt_t;
 
+typedef struct xfs_shrinkfs_data {
+	__u64		newblocks;
+	__u32		imaxpct;
+} xfs_shrinkfs_data_t;
 
 /*
  * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE
@@ -485,6 +489,8 @@ typedef struct xfs_handle {
 #define XFS_IOC_GOINGDOWN	     _IOR ('X', 125, __uint32_t)
 #define XFS_IOC_SET_AGSTATE	     _IOW ('X', 126, struct xfs_ioc_agstate)
 #define XFS_IOC_GET_AGSTATE	     _IOR ('X', 127, struct xfs_ioc_agstate)
+#define XFS_IOC_SWAPINO		     _IOWR ('X', 128, struct xfs_swapino)
+#define XFS_IOC_FSSHRINKFS_DATA	     _IOW ('X', 129, struct xfs_shrinkfs_data)
 /*	XFS_IOC_GETFSUUID ---------- deprecated 140	 */
 
 
diff --git a/shrinkfs/Makefile b/shrinkfs/Makefile
new file mode 100644
index 0000000..48a623e
--- /dev/null
+++ b/shrinkfs/Makefile
@@ -0,0 +1,30 @@
+TOPDIR = ..
+include $(TOPDIR)/include/builddefs
+
+LTCOMMAND = xfs_shrink
+
+CFILES = xfs_shrink.c
+
+LLDLIBS = $(LIBXFS) $(LIBXCMD) $(LIBUUID) $(LIBRT) $(LIBPTHREAD)
+ifeq ($(ENABLE_READLINE),yes)
+LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
+endif
+
+ifeq ($(ENABLE_EDITLINE),yes)
+LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
+endif
+
+LTDEPENDENCIES = $(LIBXFS) $(LIBXCMD)
+LLDFLAGS = -static
+
+default: depend $(LTCOMMAND)
+
+include $(BUILDRULES)
+
+install: default
+	$(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
+	$(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
+	$(INSTALL) -m 755 xfs_shrinkfs.sh $(PKG_SBIN_DIR)/xfs_shrinkfs
+install-dev:
+
+-include .dep
diff --git a/shrinkfs/xfs_shrink.c b/shrinkfs/xfs_shrink.c
new file mode 100644
index 0000000..6f72063
--- /dev/null
+++ b/shrinkfs/xfs_shrink.c
@@ -0,0 +1,1184 @@
+#include <xfs/xfs.h>
+#include <xfs/libxfs.h>
+#include <xfs/xfs_types.h>
+#include <xfs/xfs_dfrag.h>
+#include <xfs/xfs_bmap_btree.h>
+#include <xfs/jdm.h>
+#include <libxfs.h>
+
+#include <path.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <malloc.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <errno.h>
+
+#define MNTTYPE_XFS	"xfs"
+#define ECBUFSZ		4096
+#define SMBUFSZ		1024
+
+const char		*program = "xfs_shrinkfs";
+int			vflag = 0; /* -v flag print verbose data moving info */
+int			mflag = 0;
+int			offline_agcount = 0;
+int			imaxpct;
+long int		end_ag_freesize = -1; /* a.g. free blocks in the last one */
+static __uint8_t	aginolog;
+xfs_agnumber_t		*offline_aglist = NULL;
+xfs_agnumber_t		first_offline_agno = 0; /* the 1st AG in offline state */
+xfs_fsop_geom_t		geom; /* old filesystem geometry */
+
+/*
+ * Which kind of volume to be shrink?
+ */
+enum {
+	DVOLUME = 0,
+	LVOLUME,
+	RVOLUME,
+};
+
+__uint8_t
+get_aginolog(void)
+{
+	int		blocklog;
+	int		inodelog;
+	int		inopblog;
+	int		agblklog;
+
+	blocklog = libxfs_highbit32(geom.blocksize);
+	inodelog = libxfs_highbit32(geom.inodesize);
+	inopblog = blocklog - inodelog;
+	agblklog = (__uint8_t)libxfs_log2_roundup((unsigned int)geom.agblocks);
+
+	return (__uint8_t)(inopblog + agblklog);
+}
+
+static int
+ino_to_agno(
+	ino_t		st_ino)
+{
+	xfs_ino_t	ino;
+
+	ino = (xfs_ino_t)st_ino;
+	return ino >> aginolog;
+}
+
+static uint32_t
+get_ag_state(
+	const char		*fname,
+	int			fd,
+	xfs_agnumber_t		agno)
+{
+	xfs_ioc_agstate_t	agstate;
+
+	agstate.agno = agno;
+	if (xfsctl(fname, fd, XFS_IOC_GET_AGSTATE, &agstate) < 0) {
+		fprintf(stderr, _("unable to get state of ag %u\n"), agno);
+		return -1;
+	}
+
+	return agstate.state;
+}
+
+static int
+change_ag_state(
+	const char		*fname,
+	int			fd,
+	xfs_agnumber_t		agno,
+	unsigned int		state)
+{
+	xfs_ioc_agstate_t	agstate;
+
+	agstate.agno = agno;
+	agstate.state = state;
+	if (xfsctl(fname, fd, XFS_IOC_SET_AGSTATE, &agstate) < 0) {
+		fprintf(stderr, _("unable to change state for ag %u\n"), agno);
+		return -errno;
+	}
+
+	return 0;
+}
+
+static int
+aglist_add(
+	xfs_agnumber_t	agno)
+{
+	offline_aglist = realloc(offline_aglist,
+				 (offline_agcount + 1) *
+				 sizeof(*offline_aglist));
+	if (!offline_aglist)
+		return -ENOMEM;
+
+	offline_aglist[offline_agcount] = agno;
+	offline_agcount++;
+
+	return 0;
+}
+
+static void
+aglist_free(void)
+{
+	int	agno;
+
+	for (agno = 0; agno < offline_agcount; agno++)
+		free(&offline_aglist[agno]);
+	free(offline_aglist);
+}
+
+void
+report_info(
+	char		*mntpoint,
+	int		isint,
+	char		*logname,
+	char		*rtname,
+	int		lazycount,
+	int		dirversion,
+	int		logversion,
+	int		attrversion,
+	int		cimode)
+{
+	printf(_(
+		"meta-data=%-22s isize=%-6u agcount=%u, agsize=%u blks\n"
+		"         =%-22s sectsz=%-5u attr=%u\n"
+		"data     =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
+		"         =%-22s sunit=%-6u swidth=%u blks\n"
+		"naming   =version %-14u bsize=%-6u ascii-ci=%d\n"
+		"log      =%-22s bsize=%-6u blocks=%u, version=%u\n"
+		"         =%-22s sectsz=%-5u sunit=%u blks, lazy-count=%u\n"
+		"realtime =%-22s extsz=%-6u blocks=%llu, rtextents=%llu\n"),
+		mntpoint, geom.inodesize, geom.agcount, geom.agblocks,
+		"", geom.sectsize, attrversion,
+		"", geom.blocksize, (unsigned long long)geom.datablocks,
+		    geom.imaxpct,
+		"", geom.sunit, geom.swidth,
+		dirversion, geom.dirblocksize, cimode,
+		isint ? _("internal") : logname ? logname : _("external"),
+			geom.blocksize, geom.logblocks, logversion,
+		"", geom.logsectsize, geom.logsunit / geom.blocksize,
+		    lazycount,
+		!geom.rtblocks ? _("none") : rtname ? rtname : _("external"),
+		geom.rtextsize * geom.blocksize,
+		(unsigned long long)geom.rtblocks,
+		(unsigned long long)geom.rtextents);
+}
+
+int
+xfs_bulkstat_single(
+	int		fd,
+	xfs_ino_t	*lastip,
+	xfs_bstat_t	*ubuffer)
+{
+    xfs_fsop_bulkreq_t  bulkreq;
+
+    bulkreq.lastip = (__u64 *)lastip;
+    bulkreq.icount = 1;
+    bulkreq.ubuffer = ubuffer;
+    bulkreq.ocount = NULL;
+
+    return ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq);
+}
+
+int
+xfs_bulkstat(
+	int		fd,
+	xfs_ino_t	*lastip,
+	int		icount,
+	xfs_bstat_t	*ubuffer,
+	__s32		*ocount)
+{
+    xfs_fsop_bulkreq_t  bulkreq;
+
+    bulkreq.lastip = (__u64 *)lastip;
+    bulkreq.icount = icount;
+    bulkreq.ubuffer = ubuffer;
+    bulkreq.ocount = ocount;
+    return ioctl(fd, XFS_IOC_FSBULKSTAT, &bulkreq);
+}
+
+static int
+xfs_swapino(
+	int		fd,
+	xfs_swapino_t	*si)
+{
+	return ioctl(fd, XFS_IOC_SWAPINO, si);
+}
+
+int
+xfs_fscounts(
+	int			fd,
+	xfs_fsop_counts_t	*counts)
+{
+    return ioctl(fd, XFS_IOC_FSCOUNTS, counts);
+}
+
+static int
+xfs_getxattr(int fd, struct fsxattr *attr)
+{
+	return ioctl(fd, XFS_IOC_FSGETXATTR, attr);
+}
+
+static int
+move_dir(
+	const char		*srcname,
+	const struct stat64	*st)
+{
+	xfs_swapino_t		si;
+	xfs_bstat_t		bstatbuf;
+	struct fsxattr		fsx;
+	char			target[PATH_MAX] = "";
+	char			*pname = NULL;
+	int			sfd = -1;
+	int			tfd = -1;
+	int			error = -1;
+
+	sfd = open(srcname, O_RDONLY);
+	if (sfd < 0) {
+		fprintf(stderr,
+			_("unable to open source directory %s: %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+
+	if (!platform_test_xfs_fd(sfd)) {
+		fprintf(stderr, _("%s is not an xfs file\n"), srcname);
+		goto out;
+	}
+
+	if (xfs_getxattr(sfd, &fsx) < 0) {
+		fprintf(stderr,
+			_("unable to get source directory %s attrs\n"),
+			srcname);
+		goto out;
+	}
+	if (fsx.fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND)) {
+		fprintf(stderr, _("%s: immutable/append, ignoring\n"),
+			srcname);
+		goto out;
+	}
+
+	/* mkdir parent/target */
+	pname = strdup(srcname);
+	if (!pname) {
+		fprintf(stderr,
+			_("unable to duplicate source directory %s: %s\n"),
+			pname, strerror(ENOMEM));
+		goto out;
+	}
+
+	dirname(pname);
+	sprintf(target, "%s/shrinkXXXXXX", pname);
+	if (!mkdtemp(target)) {
+		fprintf(stderr,
+			_("unable to create temp directory for target %s\n"),
+			target);
+		goto out;
+	}
+
+	tfd = open(target, O_RDONLY);
+	if (tfd < 0) {
+		fprintf(stderr,
+			_("Unable to create target directory %s\n"),
+			target);
+		goto out;
+	}
+
+	/* swap inodes src->target */
+	si.si_version	= XFS_SI_VERSION;
+	si.si_fdtarget	= tfd;
+	si.si_fdtmp	= sfd;
+	error = xfs_swapino(tfd, &si);
+	if (error < 0) {
+		fprintf(stderr,
+			_("unable to swap inodes from %s to target %s: %s\n"),
+			srcname, target, strerror(errno));
+		goto out_unlink;
+	}
+
+	/* verify swapino result */
+	if (xfs_bulkstat_single(sfd, (xfs_ino_t *)&st->st_ino, &bstatbuf) < 0) {
+		fprintf(stderr,
+			_("unable to bulkstat source file %s\n"),
+			srcname);
+		unlink(target);
+		goto out;
+	}
+	if (bstatbuf.bs_ino != st->st_ino) {
+		fprintf(stderr,
+			_("bulkstat of source directory %s returned wrong inode\n"),
+			srcname);
+		goto out;
+	}
+
+	ftruncate64(tfd, bstatbuf.bs_size);
+
+	/* remove source directory */
+	error = rmdir(srcname);
+	if (error < 0) {
+		fprintf(stderr,
+			_("unable to remove source directory %s\n"),
+			srcname);
+		goto out;
+	}
+
+	/* rename current target to source */
+	error = rename(target, srcname);
+	if (error < 0) {
+		/*
+		 * We can't abort since the source directory is gone now.
+		 * Let the admin clean this one up.
+		 */
+		fprintf(stderr,
+			_("unable to rename directory from %s to %s\n"),
+			target, srcname);
+	}
+	goto out;
+
+out_unlink:
+	error = rmdir(target);
+	if (error < 0) {
+		fprintf(stderr,
+			_("unable to remove target directory %s\n"),
+			target);
+	}
+
+out:
+	if (sfd >= 0)
+		close(sfd);
+	if (tfd >= 0)
+		close(tfd);
+
+	free(pname);
+	return error;
+}
+
+static int
+move_slink(
+	const char		*srcname,
+	const struct stat64	*st)
+{
+	xfs_swapino_t		si;
+	char			target[PATH_MAX] = "";
+	char			linkbuf[PATH_MAX];
+	char			*pname = NULL;
+	int			i = 0;
+	int			sfd = -1;
+	int			tfd = -1;
+	int			error = -1;
+
+	sfd = open(srcname, O_RDWR | O_DIRECT);
+	if (sfd < 0) {
+		fprintf(stderr,
+			_("unable to open source file %s as %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+
+	i = readlink(srcname, linkbuf, sizeof(linkbuf) - 1);
+	if (i < 0) {
+		fprintf(stderr,
+			_("unable to read link of source file %s as %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+	linkbuf[i] = '\0';
+
+	error = -1;
+	pname = strdup(srcname);
+	if (!pname) {
+		fprintf(stderr,
+			_("unable to duplicate source file %s as %s\n"),
+			srcname, strerror(ENOMEM));
+		goto out;
+	}
+
+	dirname(pname);
+	sprintf(target, "%s/shrinkXXXXXX", pname);
+	tfd = mkstemp(target);
+	if (tfd < 0) {
+		fprintf(stderr,
+			_("unable to create target file %s\n"),
+			target);
+		goto out;
+	}
+
+	if (symlink(linkbuf, target) != 0) {
+		fprintf(stderr,
+			_("unable to create source symbol link %s to %s\n"),
+			linkbuf, target);
+		goto out;
+	}
+
+	/* swap inode src->target */
+	si.si_version	= XFS_SI_VERSION;
+	si.si_fdtarget	= sfd;
+	si.si_fdtmp	= tfd;
+	error = xfs_swapino(sfd, &si);
+	if (error < 0) {
+		fprintf(stderr, _("unable to swap inodes from %s to %s: %s\n"),
+			srcname, target, strerror(errno));
+		goto out;
+	}
+
+	error = unlink(srcname);
+	if (error < 0) {
+		fprintf(stderr, _("unable to unlink source file %s: %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+
+	error = rename(target, srcname);
+	if (error < 0)
+		fprintf(stderr, _("unable to rename %s to %s\n"),
+			target, srcname);
+
+out:
+	if (sfd > 0)
+		close(sfd);
+	if (tfd > 0)
+		close(tfd);
+
+	free(pname);
+	return error;
+}
+
+static int
+move_file(
+	const char		*srcname,
+	const struct stat64	*st)
+{
+	xfs_swapino_t		si;
+	xfs_bstat_t		bstatbuf;
+	struct fsxattr		fsx;
+	char			target[SMBUFSZ];
+	char			*pname = NULL;
+	int			sfd = -1;
+	int			tfd = -1;
+	int			error = -1;
+
+	sfd = open(srcname, O_RDWR | O_DIRECT);
+	if (sfd < 0) {
+		fprintf(stderr,
+			_("count not open source source file: %s: %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+
+	if (!platform_test_xfs_fd(sfd)) {
+		fprintf(stderr,
+			_("specified file [\"%s\"] is not on an XFS filesystem\n"),
+			srcname);
+		goto out;
+	}
+
+	if (fsync(sfd) < 0) {
+		fprintf(stderr, _("sync failed: %s: %s\n"),
+			srcname, strerror(errno));
+		goto out;
+	}
+
+	/*
+	 * Check if a mandatory lock is set on the file to try and avoid
+	 * blocking indefinitely on the reads later.  Note that someone
+	 * could still set a mandatory lock after this check but before
+	 * all reads have completed to block xfs_shrinkfs reads.  This
+	 * change just closes the window a bit.
+	 */
+	if ((st->st_mode & S_ISGID) && !(st->st_mode & S_IXGRP)) {
+		struct flock fl;
+
+		fl.l_type = F_RDLCK;
+		fl.l_whence = SEEK_SET;
+		fl.l_start = (off_t)0;
+		fl.l_len = 0;
+		if (fcntl(sfd, F_GETLK, &fl) < 0) {
+			fprintf(stderr, _("locking check failed: %s\n"),
+				srcname);
+			goto out;
+		}
+		if (fl.l_type != F_UNLCK) {
+			fprintf(stderr, _("mandatory lock: %s: ignoring\n"),
+				srcname);
+			goto out;
+		}
+	}
+
+	if (xfs_getxattr(sfd, &fsx) < 0) {
+		fprintf(stderr, _("unable to get the attrs of source file %s\n"),
+			srcname);
+		goto out;
+	}
+
+	if (fsx.fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND)) {
+		fprintf(stderr, _("skip to move immutable file %s\n"),
+			srcname);
+		goto out;
+	}
+
+	/* create target */
+	pname = strdup(srcname);
+	if (!pname) {
+		fprintf(stderr, _("unable to duplate source file %s: %s\n"),
+			srcname, strerror(ENOMEM));
+		goto out;
+	}
+	dirname(pname);
+
+	sprintf(target, "%s/shrinkXXXXXX", pname);
+	tfd = mkstemp(target);
+	if (tfd < 0) {
+		fprintf(stderr, _("unable to create target file %s\n"),
+			target);
+		goto out;
+	}
+
+	/* swap inode source->target */
+	si.si_version = XFS_SI_VERSION;
+	si.si_fdtarget = sfd;
+	si.si_fdtmp = tfd;
+	error = xfs_swapino(sfd, &si);
+	if (error < 0) {
+		fprintf(stderr,
+			_("unable to swap inodes from %s to %s: %s\n"),
+			srcname, target, strerror(errno));
+		goto out_unlink;
+	}
+
+	if (xfs_bulkstat_single(sfd, (xfs_ino_t*)&st->st_ino, &bstatbuf) < 0) {
+		fprintf(stderr, _("unable to bulkstat source file: %s\n"),
+			srcname);
+		unlink(target);
+		goto out;
+	}
+
+	if (bstatbuf.bs_ino != st->st_ino) {
+		fprintf(stderr,
+			_("bulkstat of source file returned wrong inode: %s\n"),
+			srcname);
+		unlink(target);
+		goto out;
+	}
+
+	/* switch to the owner's id, to keep quota in line */
+	if (fchown(tfd, bstatbuf.bs_uid, bstatbuf.bs_gid) < 0) {
+		fprintf(stderr, _("failed to fchown tmpfile %s: %s\n"),
+			target, strerror(errno));
+		close(tfd);
+		goto out;
+	}
+
+	/* unlink src */
+	error = unlink(srcname);
+        if (error < 0) {
+                fprintf(stderr, _("unable to remove source file: %s"),
+			srcname);
+                goto out;
+        }
+
+	/* rename target source */
+	error = rename(target, srcname);
+	if (error < 0) {
+		/*
+		 * We can't abort since the source file is gone now.
+		 * Let the admin clean this one up.
+		 */
+		fprintf(stderr, _("unable to rename file %s to %s\n"),
+			target, srcname);
+	}
+
+out_unlink:
+	error = unlink(target);
+        if (error < 0)
+		fprintf(stderr, _("unable to remove file %s\n"), target);
+
+out:
+	if (sfd >= 0)
+		close(sfd);
+	if (tfd >= 0)
+		close(tfd);
+	if (pname)
+		free(pname);
+
+	return error;
+}
+
+static int
+move_item_common(
+	const char		*item_path,
+	const struct stat64	*st,
+	int			type,
+	struct FTW		*sntfw)
+{
+	xfs_agnumber_t		agno;
+	int			error = 0;
+
+	/* Skip item if is not located at an offline ag */
+	agno = ino_to_agno(st->st_ino);
+	if (agno < first_offline_agno)
+		return error;
+
+	if (vflag)
+		fprintf(stderr, _("start moving %s out of ag %d\n"),
+			item_path, agno);
+
+	switch (type) {
+	case FTW_D:
+		error = move_dir(item_path, st);
+		break;
+	case FTW_F:
+		error = move_file(item_path, st);
+		break;
+	case FTW_SL:
+		error = move_slink(item_path, st);
+		break;
+	default:
+		if (vflag) {
+			fprintf(stderr, _("skip file %s for data moving.\n"),
+				item_path);
+		}
+		/* FIXME: how to deal with such kind of file? */
+		break;
+	}
+
+	return error;
+}
+
+static int
+shrinkfs_init(
+	const char		*mntdir,	/* filesystem mount path */
+	int			ffd,		/* filesystem fd */
+	unsigned int		vtype,		/* which kind of volume to shrink(data/log/realtime) */
+	long long		fs_freesize,	/* filesystem free space in blocks */
+	long long		newsize)	/* volume size to be shrink up to */
+{
+	xfs_agnumber_t		start_agno;	/* start agno to truncate */
+	xfs_agnumber_t		agno;		/* agno index variable */
+	xfs_agnumber_t		temp;		/* temporary agno variable */
+	__uint32_t		agcount;	/* # of ag in filesystem */
+	__uint32_t		agblocks;	/* # of blocks per ag */
+	__uint64_t		totalsize;	/* volume size */
+	int			error = -1;
+
+	agcount = geom.agcount;
+	agblocks = geom.agblocks;
+
+	switch (vtype) {
+	case DVOLUME:
+		totalsize = geom.datablocks;
+		break;
+	case LVOLUME:
+		totalsize = geom.logblocks;
+		break;
+	case RVOLUME:
+		totalsize = geom.rtblocks;
+		break;
+	default:
+		fprintf(stderr, _("Invalid volume type\n"));
+		return -1;
+	}
+
+	if (newsize > totalsize) {
+		fprintf(stderr,
+	_("the new data section size %lld should less than old size %lld\n"),
+			(long long)newsize, (long long)geom.datablocks);
+		goto out;
+	} else if (newsize == totalsize) {
+		fprintf(stderr, _("data section size keep unchanged\n"));
+		goto out;
+	}
+
+	/*
+	 * There is no enough free space to truncate for shrinkfs, or we
+	 * can not save up the required space accrording to the new size.
+	 */
+	if (newsize < (totalsize - fs_freesize)) {
+		fprintf(stderr,
+	_("can't shrink filesystem %s: no enough free space\n"),
+			mntdir);
+		goto out;
+	}
+
+	start_agno = ((newsize / agblocks) + (newsize % agblocks != 0));
+	if (vflag) {
+		fprintf(stderr,
+	_("shrinkfs will make ag to be offline starting from: %d\n"),
+			start_agno);
+	}
+
+	first_offline_agno = --start_agno;
+	/*
+	 * Start to set those affected AGs to be offline.
+	 */
+	for (agno = first_offline_agno; agno < agcount; agno++) {
+		error = change_ag_state(mntdir, ffd, agno,
+					XFS_AG_STATE_ALLOC_DENY);
+		if (error) {
+			temp = agno;
+			goto out_cancel;
+		}
+
+		aglist_add(agno);
+		if (vflag) {
+			fprintf(stderr, _("ag %d is offline for moving data\n"),
+				agno);
+		}
+	}
+
+	/*
+	 * That's would be great if the truncated size is less that
+	 * the free blocks in the latest AG, so that we can avoid
+	 * going through the overall filesystem for moving data and
+	 * metadata to save up spaces.
+	 */
+	if (totalsize - newsize < end_ag_freesize)
+		goto out;
+
+	/*
+	 * Starting to move items from offline a.g. to others.
+	 */
+	error = nftw64(mntdir, move_item_common, 100, FTW_PHYS | FTW_MOUNT);
+	goto out;
+
+out_cancel:
+	for (agno = first_offline_agno; agno < temp; agno++) {
+		error = change_ag_state(mntdir, ffd, agno,
+					!XFS_AG_STATE_ALLOC_DENY);
+		if (error) {
+			fprintf(stderr, _("unable to set ag %d back to online\n"),
+				agno);
+			goto out_free;
+		}
+		offline_agcount--;
+		if (vflag)
+			fprintf(stderr, _("ag %d is back to be online\n"),
+				agno);
+	}
+
+out_free:
+	aglist_free();
+
+out:
+	return error;
+}
+
+static int
+shrinkfs(
+	const char		*mntdir,
+	int			ffd,
+	int			vtype,
+	__uint64_t		newsize)
+{
+	xfs_shrinkfs_data_t	in;
+	int			error;
+
+	if (vtype != DVOLUME) {
+		fprintf(stderr,
+			_("only support data volume shrink up for now\n"));
+		return -1;
+	}
+
+	in.newblocks = newsize;
+	in.imaxpct = mflag ? imaxpct : geom.imaxpct;
+	error = xfsctl(mntdir, ffd, XFS_IOC_FSSHRINKFS_DATA, &in);
+	if (error < 0) {
+		if (errno == EWOULDBLOCK) {
+			fprintf(stderr, _(
+		"%s: shrinkfs operation in progress already\n"),
+				progname);
+		} else {
+			fprintf(stderr, _(
+				"%s: XFS_IOC_FSSHRINKFS_DATA xfsctl failed: %s\n"),
+				progname, strerror(errno));
+		}
+	}
+
+	return error;
+}
+
+/*
+ * Show filesystem statistics after shrinking up.
+ */
+static int
+shrinkfs_done(
+	const char		*fname,
+	int			ffd)
+{
+	xfs_fsop_geom_v1_t	ngeom;	/* new fs geometry */
+	__uint32_t		state;
+	int			agno;
+	int			error;
+
+	/*
+	 * Free those affected AGs from the list and turn them back
+	 * to online.
+	 */
+	for (agno = first_offline_agno; agno < offline_agcount; agno++) {
+		error = change_ag_state(fname, ffd, agno,
+					!XFS_AG_STATE_ALLOC_DENY);
+		if (error)
+			goto out_free_aglist;
+	}
+
+	/*
+	 * Verify that if those AGs state are changed back to online.
+	 */
+	for (agno = first_offline_agno; agno < offline_agcount; agno++) {
+		state = get_ag_state(fname, ffd, agno);
+		if (state < 0)
+			goto out_free_aglist;
+		if (state != 0) {
+			fprintf(stderr, _("ag %d state: %d is wrong\n"),
+				agno, state);
+			goto out_free_aglist;
+		}
+
+		if (vflag) {
+			fprintf(stderr, _("ag %d is back to online\n"),
+				agno);
+		}
+	}
+
+	/*
+	 * Show current filesystem geomerty.
+	 */
+	if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY_V1, &ngeom) < 0) {
+		fprintf(stderr,
+			_("XFS_IOC_FSGEOMETRY xfsctl failed: %s\n"),
+			strerror(errno));
+		error = -1;
+		goto out;
+	}
+
+	if (geom.datablocks != ngeom.datablocks)
+		fprintf(stderr, _("data blocks changed from %lld to %lld\n"),
+			(long long)geom.datablocks, (long long)ngeom.datablocks);
+	if (geom.imaxpct != ngeom.imaxpct)
+		fprintf(stderr, _("inode max percent changed from %d to %d\n"),
+			geom.imaxpct, ngeom.imaxpct);
+	if (geom.logblocks != ngeom.logblocks)
+		fprintf(stderr, _("log blocks changed from %d to %d\n"),
+			geom.logblocks, ngeom.logblocks);
+	if ((geom.logstart == 0) != (ngeom.logstart == 0))
+		fprintf(stderr, _("log changed from %s to %s\n"),
+			geom.logstart ? _("internal") : _("external"),
+			ngeom.logstart ? _("internal") : _("external"));
+	if (geom.rtblocks != ngeom.rtblocks)
+		fprintf(stderr, _("realtime blocks changed from %lld to %lld\n"),
+			(long long)geom.rtblocks, (long long)ngeom.rtblocks);
+	if (geom.rtextsize != ngeom.rtextsize)
+		fprintf(stderr, _("realtime extent size changed from %d to %d\n"),
+			geom.rtextsize, ngeom.rtextsize);
+
+	goto out;
+
+out_free_aglist:
+	aglist_free();
+
+out:
+	return error;
+}
+
+int
+get_fsgeom(
+	const char	*mntdir,
+	int		ffd)
+{
+	/* Get the current filesystem size & geometry */
+	if (xfsctl(mntdir, ffd, XFS_IOC_FSGEOMETRY, &geom) < 0) {
+		/*
+		 * OK, new xfsctl barfed - back off and try earlier version
+		 * as we're probably running an older kernel version.
+		 * Only field added in the v2 geometry xfsctl is "logsunit"
+		 * so we'll zero that out for later display (as zero).
+		 */
+		geom.logsunit = 0;
+		if (xfsctl(mntdir, ffd, XFS_IOC_FSGEOMETRY_V1, &geom) < 0) {
+			fprintf(stderr, _(
+		"%s: cannot determine geometry of filesystem mounted at %s: %s\n"),
+				progname, mntdir, strerror(errno));
+			close(ffd);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+void
+usage(void)
+{
+	fprintf(stderr, _(
+"Usage: %s [options] device mountpoint\n\n"
+"Options:\n\
+	-d          shrink data/metadata section\n\
+	-l          shrink log section\n\
+	-r          shrink realtime section\n\
+	-n          don't change anything, just show geometry\n\
+	-D size     shrink data/metadata section to size blks\n\
+	-L size     shrink log section to size blks\n\
+	-R size     shrink realtime section to size blks\n\
+	-m imaxpct  set inode max percent to imaxpct\n\
+	-v	    print verbose shrinking info\n\
+	-V          print version information\n"),
+		program);
+	exit(2);
+}
+
+int
+main(
+	int			argc,
+	char			**argv)
+{
+	libxfs_init_t		xi;		/* libxfs structure */
+	fs_path_t		*fs;		/* mount point information */
+	long long		dsize = 0;	/* new data size in fs blocks */
+	long long		lsize = 0;	/* new log size in fs blocks */
+	long long		rsize = 0;	/* new realtime size in fs blocks */
+	long long		fs_freesize = 0;/* device size in 512-byte blocks */
+	char			*device;	/* data device name */
+	char			*mntdir;	/* mount point name */
+	char			*fsname;	/* filesystem name*/
+	char			*datadev;	/* data device name */
+	char			*logdev;	/* log device name */
+	char			*rtdev;		/* realtime device name */
+	int			attrversion;	/* attribute version number */
+	int			dirversion;	/* directory version number */
+	int			logversion;	/* log version number */
+	int			ffd = 0;	/* mount point file descriptor */
+	int			dflag = 0;	/* -d flag, shrink data area */
+	int			lflag = 0;	/* -l flag, shrink log area */
+	int			rflag = 0;	/* -r flag, shrink realtime area */
+	int			nflag = 0;	/* -n flag */
+	int			done = 0;	/* shrinkfs performed? */
+	int			isint;		/* log is currently internal */
+	int			ci;		/* ASCII case-insensitive fs */
+	int			c;		/* current option character */
+	int			lazycount;	/* lazy superblock counters */
+	int			error = 0;	/* we have hit an error */
+
+	while ((c = getopt(argc, argv, "dlnrvVF:f:m:D:L:R:")) != EOF) {
+		switch (c) {
+		case 'F':
+			fs_freesize = strtoll(optarg, NULL, 10);
+			break;
+		case 'f':
+			end_ag_freesize = strtol(optarg, NULL, 10);
+			break;
+		case 'D':
+			dsize = strtoll(optarg, NULL, 10);
+			break;
+		case 'L':
+			lsize = strtoll(optarg, NULL, 10);
+			break;
+		case 'R':
+			rsize = strtoll(optarg, NULL, 10);
+			break;
+		case 'm':
+			mflag = 1;
+			imaxpct = atoi(optarg);
+			break;
+		case 'd':
+			dflag = 1;
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'r':
+			rflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		case 'V':
+			printf(_("%s version %s\n"), program, VERSION);
+			exit(0);
+		case '?':
+		default:
+			usage();
+		}
+	}
+
+	if (argc - optind != 2)
+		usage();
+
+	if (dflag + lflag + rflag > 1) {
+		fprintf(stderr,
+			_("cannot shrink multiple filesystems for one time\n"));
+		exit(1);
+	}
+
+	if (!fs_freesize || end_ag_freesize < 0) {
+		fprintf(stderr, _("Invalid options\n"));
+		exit(1);
+	}
+
+	device = argv[optind++];
+	mntdir = argv[optind];
+	if (!device || !mntdir) {
+		fprintf(stderr, _("Invalid options\n"));
+		exit(1);
+	}
+
+	fs_table_initialise(0, NULL, 0, NULL);
+	fs = fs_table_lookup(mntdir, FS_MOUNT_POINT);
+	if (!fs) {
+		fprintf(stderr,
+		_("%s: %s is not a mounted XFS filesystem\n"),
+			progname, argv[optind]);
+		goto out;
+	}
+
+	fsname = fs->fs_dir;
+	datadev = fs->fs_name;
+	logdev = fs->fs_log;
+	rtdev = fs->fs_rt;
+
+	if (strcmp(fsname, mntdir)) {
+		fprintf(stderr, _("mount point %s is invalid\n"), mntdir);
+		goto out;
+	}
+	if (dflag && strcmp(datadev, device)) {
+		fprintf(stderr, _("device %s is invalid\n"), device);
+		goto out;
+	}
+	if (lflag && logdev && strcmp(logdev, device)) {
+		fprintf(stderr, _("device %s is invalid\n"), device);
+		goto out;
+	}
+	if (rflag && rtdev && strcmp(rtdev, device)) {
+		fprintf(stderr, _("device %s is invalid\n"), device);
+		goto out;
+	}
+
+	ffd = open(mntdir, O_RDONLY);
+	if (ffd < 0) {
+		fprintf(stderr, _("cannot open %s: %s\n"),
+			mntdir, strerror(errno));
+		goto out;
+	}
+
+	if (!platform_test_xfs_fd(ffd)) {
+		fprintf(stderr,
+	_("%s: specified file [\"%s\"] is not on an XFS filesystem\n"),
+			progname, mntdir);
+		goto out;
+	}
+
+	if (get_fsgeom(mntdir, ffd) < 0)
+		goto out;
+
+	isint = geom.logstart > 0;
+	lazycount = geom.flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0;
+	dirversion = geom.flags & XFS_FSOP_GEOM_FLAGS_DIRV2 ? 2 : 1;
+	logversion = geom.flags & XFS_FSOP_GEOM_FLAGS_LOGV2 ? 2 : 1;
+	attrversion = geom.flags & XFS_FSOP_GEOM_FLAGS_ATTR2 ? 2 : \
+			(geom.flags & XFS_FSOP_GEOM_FLAGS_ATTR ? 1 : 0);
+	ci = geom.flags & XFS_FSOP_GEOM_FLAGS_DIRV2CI ? 1 : 0;
+
+	/*
+	 * Specifies that no change to the filesystem is to be made.
+	 * The filesystem geometry is printed, but no shrink ocurrs.
+	 */
+	if (nflag) {
+		report_info(datadev, isint, logdev, rtdev, lazycount,
+			    dirversion, logversion, attrversion, ci);
+		goto out;
+	}
+
+	if (getuid() != 0) {
+		fprintf(stderr,
+	_("cannot shrink filesystem %s: Permission denied\n"),
+			mntdir);
+		goto out;
+	}
+
+	/*
+	 * Need root access from here on (using raw devices)...
+	 */
+	memset(&xi, 0, sizeof(xi));
+	xi.dname = datadev;
+	xi.logname = logdev;
+	xi.rtname = rtdev;
+	xi.isreadonly = LIBXFS_ISREADONLY;
+	if (!libxfs_init(&xi))
+		goto out;
+
+	/*
+	 * Check we got the info for all the sections we are trying to modify.
+	 */
+	if (dflag && !xi.ddev) {
+		fprintf(stderr,
+			_("%s: failed to access data device for %s\n"),
+			progname, fsname);
+		goto out;
+	} else if (lflag && !xi.logdev) {
+		fprintf(stderr,
+			_("%s: failed to access log device for %s\n"),
+			progname, fsname);
+		goto out;
+	} else if (rflag && !xi.rtdev) {
+		fprintf(stderr,
+			_("%s: failed to access realtime device for %s\n"),
+			progname, fsname);
+		goto out;
+	}
+
+	report_info(datadev, isint, logdev, rtdev, lazycount,
+		    dirversion, logversion, attrversion, ci);
+
+	/* Get agino_log which would be used at ino_to_agno() */
+	get_aginolog();
+
+	if (dflag && dsize > 0) {
+		error = shrinkfs_init(fsname, ffd, DVOLUME,
+				      fs_freesize, dsize);
+		if (error)
+			goto out;
+
+		error = shrinkfs(fsname, ffd, DVOLUME, dsize);
+		if (error)
+			goto out;
+		done = 1;
+	} else if (lflag && lsize > 0) {
+		error = shrinkfs_init(fsname, ffd, LVOLUME,
+				      fs_freesize, lsize);
+		if (error)
+			goto out;
+
+		error = shrinkfs(fsname, ffd, LVOLUME, lsize);
+		if (error)
+			goto out;
+		done = 1;
+	} else if (rflag && rsize > 0) {
+		error = shrinkfs_init(fsname, ffd, RVOLUME,
+				      fs_freesize, rsize);
+		if (error)
+			goto out;
+
+		error = shrinkfs(fsname, ffd, RVOLUME, rsize);
+		if (error)
+			goto out;
+		done = 1;
+	}
+
+	/* options are invalid */
+	if (!done) {
+		fprintf(stderr, _("%s: Invalid arguments %s\n"),
+			program, fsname);
+		goto out;
+	}
+
+	error = shrinkfs_done(fsname, ffd);
+
+out:
+	if (ffd > 0)
+		close(ffd);
+
+	return error;
+}
diff --git a/shrinkfs/xfs_shrinkfs.sh b/shrinkfs/xfs_shrinkfs.sh
new file mode 100755
index 0000000..319f09b
--- /dev/null
+++ b/shrinkfs/xfs_shrinkfs.sh
@@ -0,0 +1,99 @@
+#!/bin/sh -f
+
+DEVICE=""
+declare -A FSINFO
+FS_FREEBLKS=0;
+END_AG_FREEBLKS=0;
+
+_check_permission()
+{
+	[ `id -u ` -ne 0 ] && {
+		echo "Abort shrinking: permission denied" && exit 1
+	}
+}
+
+_get_ag_freeblks()
+{
+	local __agno=$1
+	local -a __temp
+
+	__temp=(`xfs_db -r -c "agf $__agno" -c 'p freeblks' -c 'p btreeblks' \
+		$DEVICE|cut -d' ' -f 3,6`)
+	echo $((${__temp[0]}-${__temp[1]}))
+}
+
+#
+# Fetch the free blocks of lastest A.G.
+#
+_get_lastest_agfreeblks()
+{
+	local __agcount=0
+	local __agno=0
+
+	__agcount=`xfs_db -r -c 'sb' -c 'p agcount' $DEVICE|cut -d' ' -f 3`
+	__agno=$(($__agcount - 1))
+
+	END_AG_FREEBLKS=$(_get_ag_freeblks $__agno)
+}
+
+_get_fsfreeblks()
+{
+	local __fs_freebllks=0
+	local __ag_freeblks=0
+	local __agcount=0
+	local __i=0
+
+	__agcount=`xfs_db -r -c 'sb' -c 'p agcount' $DEVICE|cut -d' ' -f 3`
+	while [ $__i -lt ${__agcount} ]
+	do
+		__ag_freeblks=$(_get_ag_freeblks $__i)
+		let "__fs_freeblks += __ag_freeblks"
+		let "__i += 1"
+	done
+
+	FS_FREEBLKS=${__fs_freeblks}
+}
+
+OPTS=" "
+USAGE="Usage: xfs_shrinkfs [-V] [options] device mountpoint"
+
+while getopts "dlrnvD:L:R:V" c
+do
+	case $c in
+	d)	OPTS=$OPTS"-d ";;
+	l)	OPTS=$OPTS"-l ";;
+	r)	OPTS=$OPTS"-r ";;
+	m)	OPTS=$OPTS"-m ";;
+	n)	OPTS=$OPTS"-n ";;
+	v)	OPTS=$OPTS"-v ";;
+	D)	OPTS=$OPTS"-D $OPTARG";;
+	L)	OPTS=$OPTS"-L $OPTARG";;
+	R)	OPTS=$OPTS"-R $OPTARG";;
+	V)	xfs_shrink -V
+		status=$?
+		exit $status
+		;;
+	\?)	echo $USAGE 1>&2
+		exit 2
+		;;
+	esac
+done
+
+set -- extra $@
+shift $OPTIND
+case $# in
+	2)
+		DEVICE="$1"
+		MNTDIR="$2"
+		_check_permission
+		_get_fsfreeblks "${DEVICE}"
+		_get_lastest_agfreeblks
+		echo ${FS_FREEBLKS} ${END_AG_FREEBLKS} ${OPTS}
+		xfs_shrink -F "$FS_FREEBLKS" -f "$END_AG_FREEBLKS" $OPTS "${DEVICE}" "${MNTDIR}"
+		status=$?
+		;;
+	*)	echo $USAGE 1>&2
+		exit 2
+		;;
+esac
+exit $status
-- 
1.7.9.5

_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs


[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux