[PATCH v1 2/2] xfs_db: add a directory copy command

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

 



Add support for a command to recursively copy files from a block device to
the native filesystem.

Signed-off-by: Catherine Hoang <catherine.hoang@xxxxxxxxxx>
---
 db/Makefile       |   2 +-
 db/command.c      |   1 +
 db/command.h      |   1 +
 db/copyout.c      | 320 ++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/xfs_db.8 |   7 +
 5 files changed, 330 insertions(+), 1 deletion(-)
 create mode 100644 db/copyout.c

diff --git a/db/Makefile b/db/Makefile
index 07f0b41f..0009bedc 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -14,7 +14,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
 	io.h logformat.h malloc.h metadump.h namei.h output.h print.h quit.h sb.h \
 	sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \
 	fuzz.h
-CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c \
+CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c copyout.c info.c \
 	timelimit.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
diff --git a/db/command.c b/db/command.c
index 02f778b9..abe4db3f 100644
--- a/db/command.c
+++ b/db/command.c
@@ -116,6 +116,7 @@ init_commands(void)
 	btheight_init();
 	check_init();
 	convert_init();
+	copyout_init();
 	crc_init();
 	debug_init();
 	echo_init();
diff --git a/db/command.h b/db/command.h
index 498983ff..206f72ff 100644
--- a/db/command.h
+++ b/db/command.h
@@ -34,3 +34,4 @@ extern void		info_init(void);
 extern void		btheight_init(void);
 extern void		timelimit_init(void);
 extern void		namei_init(void);
+extern void		copyout_init(void);
diff --git a/db/copyout.c b/db/copyout.c
new file mode 100644
index 00000000..288e40d5
--- /dev/null
+++ b/db/copyout.c
@@ -0,0 +1,320 @@
+#include "libxfs.h"
+#include "command.h"
+#include "output.h"
+#include "init.h"
+#include "io.h"
+#include "type.h"
+#include "faddr.h"
+#include "fprint.h"
+#include "field.h"
+#include "inode.h"
+#include "namei.h"
+
+static int do_copyout(char *name, char *fullpath);
+
+static void process_dirent(
+	struct xfs_mount	*mp,
+	xfs_dir2_dataptr_t	off,
+	char			*name,
+	ssize_t			namelen,
+	xfs_ino_t		ino,
+	uint8_t			dtype,
+	void			*priv)
+{
+	char			buf[namelen+1];
+
+	/* Ignore . and .. entries */
+	if (!strncmp(name, ".", 1) || !strncmp(name, "..", 2))
+		return;
+
+	/* Make sure name is null terminated */
+	strncpy(buf, name, namelen);
+	buf[namelen] = '\0';
+
+	set_cur_inode(ino);
+	do_copyout(buf, priv);
+}
+
+static int
+copyout_file(
+	struct xfs_inode	*ip,
+	char			*fullpath)
+{
+	int			error;
+	xfs_fileoff_t		off = 0;
+	struct xfs_bmbt_irec	map;
+	struct xfs_buf		*bp = NULL;
+	xfs_fileoff_t		filelen = XFS_B_TO_FSB(mp, ip->i_disk_size);
+	int			fd;
+	xfs_fsize_t		readct;
+	ssize_t			writect;
+	xfs_filblks_t		maplen;
+	int			nmap = 1;
+	xfs_fileoff_t		writeoff = 0;
+
+	fd = open(fullpath, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU);
+	if (fd == -1) {
+		dbprintf("can't open '%s': %s\n", fullpath, strerror(errno));
+		return 0;
+	}
+
+	while (off < filelen)
+	{
+		/* Read up to 1MB at a time. */
+		maplen = min(filelen - off, XFS_B_TO_FSBT(mp, 1048576));
+		error = -libxfs_bmapi_read(ip, off, maplen, &map, &nmap, 0);
+		if (error) {
+			dbprintf("unable to read %s mapping at file block 0x%llx: %s\n",
+					fullpath, off, strerror(error));
+			break;
+		}
+
+		if (map.br_startblock == HOLESTARTBLOCK) {
+			off += map.br_blockcount;
+			continue;
+		}
+
+		error = -libxfs_buf_read_uncached(mp->m_dev,
+				XFS_FSB_TO_DADDR(mp, map.br_startblock),
+				XFS_FSB_TO_BB(mp, map.br_blockcount),
+				0, &bp, NULL);
+		if (error) {
+			dbprintf("unable to read %s at dblock 0x%llx: %s\n",
+					fullpath, (unsigned long long)off, strerror(error));
+			break;
+		}
+
+		readct = min(XFS_FSB_TO_B(mp, map.br_blockcount),
+				ip->i_disk_size-XFS_FSB_TO_B(mp, off));
+		for (writeoff = 0; writeoff < readct; writeoff += writect) {
+			writect = pwrite(fd, bp->b_addr + writeoff, readct - writeoff,
+					XFS_FSB_TO_B(mp, map.br_startoff) + writeoff);
+			if (writect <= 0) {
+				dbprintf("pwrite to %s failed: %s", fullpath, strerror(error));
+				break;
+			}
+		}
+
+		off += map.br_blockcount;
+		libxfs_buf_relse(bp);
+		bp = NULL;
+	}
+
+	if (bp)
+		libxfs_buf_relse(bp);
+
+	fsync(fd);
+	error = close(fd);
+	if (error)
+		dbprintf("can't close '%s': %s", fullpath, strerror(error));
+
+	return 0;
+}
+
+static int
+copyout_link(
+	struct xfs_inode	*ip,
+	char			*fullpath)
+{
+	int			error;
+	xfs_fileoff_t		off = 0;
+	struct xfs_buf		*bp = NULL;
+	char			*target;
+	char			*ptr;
+	xfs_fsize_t		bytes = ip->i_disk_size;
+	xfs_bmbt_irec_t		mval[XFS_SYMLINK_MAPS];
+	int			nmap = XFS_SYMLINK_MAPS;
+	int			fs_blocks;
+	int			n;
+	int			writect;
+
+	target = malloc(bytes + 1);
+	target[bytes] = 0;
+	ptr = target;
+
+	if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
+		strcpy(target, ip->i_df.if_u1.if_data);
+	} else {
+		fs_blocks = libxfs_symlink_blocks(mp, XFS_SYMLINK_MAXLEN);
+		error = -libxfs_bmapi_read(ip, 0, fs_blocks, mval, &nmap, 0);
+		if (error) {
+			dbprintf("unable to read %s mapping at file block 0x%llx: %s\n",
+					fullpath, 0, strerror(error));
+			goto rele;
+		}
+
+		for (n = 0; n < nmap; n++) {
+			if (mval[n].br_startblock == HOLESTARTBLOCK)
+				continue;
+
+			error = -libxfs_buf_read_uncached(mp->m_dev,
+				XFS_FSB_TO_DADDR(mp, mval[n].br_startblock),
+				XFS_FSB_TO_BB(mp, mval[n].br_blockcount),
+				0, &bp, NULL);
+			if (error) {
+				dbprintf("unable to read %s at dblock 0x%llx: %s\n",
+						fullpath, (unsigned long long)off, strerror(error));
+				goto rele;
+			}
+
+			writect = min(bytes, XFS_SYMLINK_BUF_SPACE(mp,
+					 XFS_FSB_TO_B(mp, mval[n].br_blockcount)));
+
+			memcpy(ptr, bp->b_addr + sizeof(struct xfs_dsymlink_hdr), writect);
+			ptr += writect;
+
+			libxfs_buf_relse(bp);
+			bp = NULL;
+		}
+	}
+
+	if (symlink(target, fullpath) == -1) {
+		dbprintf("failed to create symlink %s -> %s: %s", target,
+				fullpath, strerror(errno));
+		goto rele;
+	}
+
+rele:
+	free(target);
+	if (bp)
+		libxfs_buf_relse(bp);
+
+	return 0;
+}
+
+static int
+do_copyout(
+	char			*name,
+	char			*destdir)
+{
+	struct xfs_inode	*ip;
+	int			error = 0;
+	char			*fullpath;
+	int			pathlen;
+
+	if (iocur_top->typ != &typtab[TYP_INODE])
+		return ENOENT;
+
+	error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
+	if (error)
+		return error;
+
+	pathlen = strlen(destdir) + strlen(name) + 2;
+	fullpath = malloc(pathlen);
+	if (snprintf(fullpath, pathlen, "%s/%s", destdir, name) != pathlen - 1) {
+		dbprintf("snprintf failed %s/%s", destdir, name);
+		goto rele;
+	}
+
+	switch (VFS_I(ip)->i_mode & S_IFMT) {
+		case S_IFDIR:
+			if (strcmp(name, "/") && mkdir(fullpath, S_IRWXU) == -1) {
+				dbprintf("failed to create dir %s: %s\n", fullpath, strerror(error));
+				goto rele;
+			}
+			error = listdir(ip, process_dirent, fullpath);
+			if (error)
+				goto rele;
+			break;
+		case S_IFLNK:
+			error = copyout_link(ip, fullpath);
+			if (error)
+				goto rele;
+			break;
+		case S_IFREG:
+			error = copyout_file(ip, fullpath);
+			if (error)
+				goto rele;
+			break;
+		case S_IFIFO:
+		case S_IFCHR:
+		case S_IFBLK:
+		case S_IFSOCK:
+			error = mknod(fullpath, VFS_I(ip)->i_mode,
+					IRIX_DEV_TO_KDEVT(VFS_I(ip)->i_rdev));
+			if (error)
+				goto rele;
+			break;
+		default:
+			break;
+	}
+
+rele:
+	free(fullpath);
+	libxfs_irele(ip);
+	return error;
+}
+
+static void
+copyout_help(void)
+{
+	dbprintf(_(
+"\n"
+" Copy files from the given directory paths to a specified location\n"
+" on the native filesystem.\n"
+	));
+}
+
+static int
+copyout_f(
+	int		argc,
+	char		**argv)
+{
+	int		c;
+	int		error = 0;
+	char		*destdir;
+	struct stat	st;
+
+	while ((c = getopt(argc, argv, "")) != -1) {
+		switch (c) {
+		default:
+			copyout_help();
+			return 0;
+		}
+	}
+
+	destdir = argv[argc-1];
+
+	if (stat(destdir, &st) == -1) {
+		dbprintf("can't stat %s: %s\n", destdir, strerror(error));
+		return 0;
+	}
+
+	if (!S_ISDIR(st.st_mode)) {
+		dbprintf("%s is not a directory\n", destdir);
+		return 0;
+	}
+
+	for (c = optind; c < argc - 1; c++) {
+		error = path_walk(argv[c]);
+		if (error)
+			goto err;
+
+		error = do_copyout(argv[c], destdir);
+		if (error)
+			goto err;
+	}
+
+err:
+	if (error)
+		exitcode = 1;
+
+	return 0;
+}
+
+static struct cmdinfo copyout_cmd = {
+	.name		= "copyout",
+	.cfunc		= copyout_f,
+	.argmin		= 2,
+	.argmax		= -1,
+	.canpush	= 0,
+	.args		= "[sources...] dest",
+	.help		= copyout_help,
+};
+
+void
+copyout_init(void)
+{
+	copyout_cmd.oneline = _("copy out files to the native file system");
+	add_command(&copyout_cmd);
+}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 1a2bb7e9..bca2b0bc 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -579,6 +579,13 @@ print the current debug option bits. These are for the use of the implementor.
 .BI "dquot [" \-g | \-p | \-u ] " id"
 Set current address to a group, project or user quota block for the given ID. Defaults to user quota.
 .TP
+.BI "copyout [" directories ] " destination"
+Recursively copy one or more
+.IR directories
+from an XFS filesystem to the named
+.IR destination ,
+which should be an existing directory on the native file system.
+.TP
 .BI "echo [" arg "] ..."
 Echo the arguments to the output.
 .TP
-- 
2.34.1




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux