All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jeff Liu <jeff.liu@oracle.com>
To: xfs@oss.sgi.com
Subject: [PATCH 2/2] xfsprogs: xfs_shrinkfs(8)
Date: Fri, 16 Nov 2012 14:51:11 +0800	[thread overview]
Message-ID: <50A5E25F.8020900@oracle.com> (raw)

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@oracle.com>

---
 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@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

                 reply	other threads:[~2012-11-16  6:49 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=50A5E25F.8020900@oracle.com \
    --to=jeff.liu@oracle.com \
    --cc=xfs@oss.sgi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.