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(©out_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