From 4a91298cbb8cc43b80d82c5126237235a287e1fc Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 5 Mar 2005 20:58:58 +0000 Subject: [PATCH] * Use our open mode flags and seek modes instead of the build platform ones. * Added listattr command. * Added cd command. It can change the CWD of the emulated as well as of the host OS (prefix `:'). * Changed do_dir() to print symlinks with their target. * Rewrote the rm command. It now support the options -r and -f and multiple files. * Rewrote the symlink command (and renamed it to link, alias ln). It now supports creating hardlinks and understands the options -s and -f. * Added command xcp, with support for options -r, -f, -d, and pretty close approximation of the shell command cp, save for the fact, that it copies attributes and can freely copy in the emulated and host OS (and between them, of course; prefix `:'). Now it should be possible to change makehdimage to use a scripted bfs_shell instead of UserlandFS to build a HD image. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11587 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/fs_shell/fsh.cpp | 385 ++++- .../kernel/file_systems/fs_shell/xcp.cpp | 1245 +++++++++++++++++ .../kernel/file_systems/fs_shell/xcp.h | 19 + 3 files changed, 1603 insertions(+), 46 deletions(-) create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/xcp.h diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.cpp b/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.cpp index 25ba5b914c..e794e9700d 100644 --- a/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.cpp +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.cpp @@ -27,7 +27,9 @@ #include "argv.h" #include "external_commands.h" #include "kprotos.h" +#include "path_util.h" #include "tracker.h" +#include "xcp.h" #include #include @@ -37,6 +39,7 @@ static bool sInteractiveMode = true; static int do_lat_fs(int argc, char **argv); static void do_fsh(void); +static int remove_entry(int dir, const char *entry, bool recursive, bool force); static void print_usage(const char *program) @@ -95,6 +98,7 @@ main(int argc, char **argv) do_fsh(); + sys_chdir(1, -1, "/"); if (sys_unmount(1, -1, "/myfs") != 0) { printf("could not un-mount /myfs\n"); return 5; @@ -163,7 +167,7 @@ do_open(int argc, char **argv) else sprintf(name, "/myfs/%s", &argv[1][0]); - cur_fd = sys_open(1, -1, name, O_RDWR, MY_S_IFREG, 0); + cur_fd = sys_open(1, -1, name, MY_O_RDWR, MY_S_IFREG, 0); if (cur_fd < 0) { printf("error opening %s : %s (%d)\n", name, fs_strerror(cur_fd), cur_fd); return cur_fd; @@ -188,7 +192,7 @@ do_make(int argc, char **argv) else strcpy(name, &argv[1][0]); - cur_fd = sys_open(1, -1, name, O_RDWR|O_CREAT, 0666, 0); + cur_fd = sys_open(1, -1, name, MY_O_RDWR | MY_O_CREAT, 0666, 0); if (cur_fd < 0) { printf("error creating: %s: %s\n", name, fs_strerror(cur_fd)); return cur_fd; @@ -437,6 +441,56 @@ do_remove_attr(int argc, char **argv) } +static int +do_list_attr(int argc, char **argv) +{ + if (argc < 2 && cur_fd < 0) { + fprintf(stderr, "Must specify a file!\n"); + return FS_EINVAL; + } + + // open attr dir + int attrDir = -1; + if (argc >= 2) { + attrDir = sys_open_attr_dir(true, -1, argv[1]); + } else if (cur_fd >= 0) { + attrDir = sys_open_attr_dir(true, cur_fd, NULL); + } else { + fprintf(stderr, "Must specify a file!\n"); + return FS_EINVAL; + } + if (attrDir < 0) { + fprintf(stderr, "Failed to open attribute directory: %s\n", + fs_strerror(attrDir)); + return attrDir; + } + + printf(" Type Size Name\n"); + printf("---------- -------- --------------------------------\n"); + + // iterate through the attributes + char entryBuffer[sizeof(my_dirent) + B_ATTR_NAME_LENGTH]; + my_dirent *entry = (my_dirent *)entryBuffer; + while (sys_readdir(true, attrDir, entry, sizeof(entryBuffer), 1) > 0) { + // stat the attribute + my_attr_info info; + int error = sys_stat_attr(true, attrDir, NULL, entry->d_name, &info); + if (error == FS_OK) { + printf("0x%08lx %8lld %32s\n", info.type, (long long)info.size, + entry->d_name); + } else { + fprintf(stderr, "Failed to stat attribute `%s': %s\n", + entry->d_name, fs_strerror(error)); + } + } + + // close the attr dir + sys_closedir(true, attrDir); + + return FS_OK; +} + + static void mode_bits_to_str(int mode, char *str) { @@ -464,6 +518,35 @@ mode_bits_to_str(int mode, char *str) } +static int +do_chdir(int argc, char **argv) +{ + if (argc <= 1) { + fprintf(stderr, "No directory specified!\n"); + return FS_EINVAL; + } + + // change dir + const char *dir = argv[1]; + int error = 0; + if (dir[0] == ':') { + // host environment + if (chdir(dir + 1) < 0) + error = from_platform_error(errno); + } else { + // emulated environment + error = sys_chdir(1, -1, dir); + } + + if (error != 0) { + fprintf(stderr, "Failed to cd into `%s': %s\n", argv[1], + fs_strerror(error)); + } + + return error; +} + + static int do_dir(int argc, char **argv) { @@ -476,9 +559,10 @@ do_dir(int argc, char **argv) char mode_str[16]; dent = (struct my_dirent *)buff; - strcpy(dirname, "/myfs/"); if (argc > 1) - strcat(dirname, &argv[1][0]); + strcpy(dirname, &argv[1][0]); + else + strcpy(dirname, "."); if ((dirfd = sys_opendir(1, -1, dirname, 0)) < 0) { printf("dir: error opening: %s\n", dirname); @@ -503,7 +587,7 @@ do_dir(int argc, char **argv) if (err == 0) break; - err = sys_rstat(1, dirfd, dent->d_name, &st, 1); + err = sys_rstat(1, dirfd, dent->d_name, &st, false); if (err != 0) { // may happen for unresolvable links printf("stat failed for: %s (%Ld)\n", dent->d_name, dent->d_ino); @@ -515,8 +599,24 @@ do_dir(int argc, char **argv) mode_bits_to_str(st.mode, mode_str); - printf("%12Ld %s %6d %6d %12Ld %s %s\n", st.ino, mode_str, + printf("%12Ld %s %6d %6d %12Ld %s %s", st.ino, mode_str, st.uid, st.gid, st.size, time_buf, dent->d_name); + + // if the entry is a symlink, print its target + if (MY_S_ISLNK(st.mode)) { + char linkTo[B_PATH_NAME_LENGTH]; + ssize_t bytesRead = sys_readlink(true, dirfd, dent->d_name, linkTo, + sizeof(linkTo) - 1); + if (bytesRead >= 0) { + linkTo[bytesRead] = '\0'; + printf(" -> %s", linkTo); + } else { + printf(" -> (%s)", fs_strerror(bytesRead)); + } + } + + printf("\n"); + count++; } @@ -544,7 +644,7 @@ do_ioctl(int argc, char **argv) return FS_EINVAL; } - if ((fd = sys_open(1, -1, "/myfs/.", O_RDONLY, S_IFREG, 0)) < 0) { + if ((fd = sys_open(1, -1, "/myfs/.", MY_O_RDONLY, S_IFREG, 0)) < 0) { printf("ioctl: error opening '.'\n"); return fd; } @@ -682,7 +782,7 @@ do_seek(int argc, char **argv) } pos = strtoul(&argv[1][0], NULL, 0); - err = sys_lseek(1, cur_fd, pos, SEEK_SET); + err = sys_lseek(1, cur_fd, pos, MY_SEEK_SET); if (err != pos) { printf("seek to %Ld failed (%Ld)\n", pos, err); } @@ -691,28 +791,144 @@ do_seek(int argc, char **argv) } +int +remove_dir_contents(int parentDir, const char *name, bool force) +{ + // open the dir + int dir = sys_opendir(true, parentDir, name, true); + if (dir < 0) { + fprintf(stderr, "Failed to open dir `%s': %s\n", name, + fs_strerror(dir)); + return dir; + } + + status_t error = FS_OK; + + // iterate through the entries + ssize_t numRead; + char buffer[sizeof(my_dirent) + B_FILE_NAME_LENGTH]; + my_dirent *entry = (my_dirent*)buffer; + while ((numRead = sys_readdir(true, dir, entry, sizeof(buffer), 1)) > 0) { + // skip "." and ".." + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + error = remove_entry(dir, entry->d_name, true, force); + if (error != FS_OK) + break; + } + + if (numRead < 0) { + fprintf(stderr, "Error while reading directory `%s': %s\n", name, + fs_strerror(numRead)); + return numRead; + } + + // close + sys_closedir(true, dir); + + return error; +} + + +static int +remove_entry(int dir, const char *entry, bool recursive, bool force) +{ + // stat the file + struct my_stat st; + status_t error = sys_rstat(true, dir, entry, &st, false); + if (error != FS_OK) { + if (force && error == FS_ENTRY_NOT_FOUND) + return FS_OK; + + fprintf(stderr, "Failed to remove `%s': %s\n", entry, + fs_strerror(error)); + return error; + } + + if (MY_S_ISDIR(st.mode)) { + if (!recursive) { + fprintf(stderr, "`%s' is a directory.\n", entry); + // TODO: get the full path + return FS_EISDIR; + } + + // remove the contents + error = remove_dir_contents(dir, entry, force); + if (error != FS_OK) + return error; + + // remove the directory + error = sys_rmdir(true, dir, entry); + if (error != FS_OK) { + fprintf(stderr, "Failed to remove directory `%s': %s\n", entry, + fs_strerror(error)); + return error; + } + } else { + // remove the entry + error = sys_unlink(true, dir, entry); + if (error != FS_OK) { + fprintf(stderr, "Failed to remove entry `%s': %s\n", entry, + fs_strerror(error)); + return error; + } + } + + return FS_OK; +} + + static int do_rm(int argc, char **argv) { - int err; - char name[256]; - if (cur_fd >= 0) do_close(0, NULL); - if (argc < 2) { - printf("rm: need a file name to remove\n"); - return FS_EINVAL; - } + bool recursive = false; + bool force = false; - sprintf(name, "/myfs/%s", &argv[1][0]); - - err = sys_unlink(1, -1, name); - if (err != 0) { - printf("error removing: %s: %s\n", name, fs_strerror(err)); - } + // parse parameters + int argi = 1; + for (argi = 1; argi < argc; argi++) { + const char *arg = argv[argi]; + if (arg[0] != '-') + break; - return err; + if (arg[1] == '\0') { + fprintf(stderr, "Invalid option `-'\n"); + return FS_EINVAL; + } + + for (int i = 1; arg[i]; i++) { + switch (arg[i]) { + case 'f': + force = true; + break; + case 'r': + recursive = true; + break; + default: + fprintf(stderr, "Unknown option `-%c'\n", arg[i]); + return FS_EINVAL; + } + } + } + + // check params + if (argi >= argc) { + fprintf(stderr, "Usage: %s [ -r ] ...\n", argv[0]); + return FS_EINVAL; + } + + // remove loop + for (; argi < argc; argi++) { + status_t error = remove_entry(-1, argv[argi], recursive, force); + if (error != FS_OK) + return error; + } + + return FS_OK; } @@ -758,7 +974,7 @@ do_copy_to_myfs(char *host_file, char *bfile) return from_platform_error(errno); } - if ((bfd = sys_open(1, -1, myfs_name, O_RDWR|O_CREAT, + if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDWR|MY_O_CREAT, MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { fclose(fp); printf("error opening: %s\n", myfs_name); @@ -804,7 +1020,7 @@ do_copy_from_myfs(char *bfile, char *host_file) return from_platform_error(errno); } - if ((bfd = sys_open(1, -1, myfs_name, O_RDONLY, MY_S_IFREG, 0)) < 0) { + if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDONLY, MY_S_IFREG, 0)) < 0) { fclose(fp); printf("error opening: %s\n", myfs_name); return bfd; @@ -917,7 +1133,7 @@ copydir(char *fromPath,char *toPath) } sprintf(myfs_name, "%s/%s", toPath, dirent->d_name); - if ((bfd = sys_open(1, -1, myfs_name, O_RDWR|O_CREAT, + if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDWR|MY_O_CREAT, MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { close(fd); printf("error opening: %s\n", myfs_name); @@ -1110,8 +1326,10 @@ create_remove_thread(void *data) while (sRunStatTest) { int fd; - if ((fd = sys_open(1, -1, sStatTestFile, O_RDWR | O_CREAT, S_IFREG | S_IRWXU, 0)) >= 0) + if ((fd = sys_open(1, -1, sStatTestFile, MY_O_RDWR | MY_O_CREAT, + S_IFREG | S_IRWXU, 0)) >= 0) { sys_close(1, fd); + } snooze(10000); sys_unlink(1, -1, sStatTestFile); @@ -1226,27 +1444,101 @@ do_rename(int argc, char **argv) static int -do_symlink(int argc, char **argv) +do_link(int argc, char **argv) { - char target[B_PATH_NAME_LENGTH]; - char *source; - status_t status; + bool force = false; + bool symbolic = false; - if (argc < 3) { - printf("usage: symlink \n"); + // parse parameters + int argi = 1; + for (argi = 1; argi < argc; argi++) { + const char *arg = argv[argi]; + if (arg[0] != '-') + break; + + if (arg[1] == '\0') { + fprintf(stderr, "Invalid option `-'\n"); + return FS_EINVAL; + } + + for (int i = 1; arg[i]; i++) { + switch (arg[i]) { + case 'f': + force = true; + break; + case 's': + symbolic = true; + break; + default: + fprintf(stderr, "Unknown option `-%c'\n", arg[i]); + return FS_EINVAL; + } + } + } + + if (argc - argi != 2) { + printf("usage: %s [Options] \n", argv[0]); return FS_EINVAL; } - source = argv[1]; + const char *source = argv[argi]; + const char *target = argv[argi + 1]; - strcpy(target, "/myfs/"); - strcpy(target + 6, argv[2]); + // check, if the the target is an existing directory + struct my_stat st; + char targetBuffer[B_PATH_NAME_LENGTH]; + status_t error = sys_rstat(true, -1, target, &st, true); + if (error == FS_OK) { + if (MY_S_ISDIR(st.mode)) { + // get source leaf + char leaf[B_FILE_NAME_LENGTH]; + error = get_last_path_component(source, leaf, sizeof(leaf)); + if (error != B_OK) { + fprintf(stderr, "Failed to get leaf name of source path: %s\n", + fs_strerror(error)); + return error; + } - status = sys_symlink(1, source, -1, target); - if (status != FS_OK) - printf("symlink failed with error: %s\n", fs_strerror(status)); + // compose a new path + int len = strlen(target) + 1 + strlen(leaf); + if (len > (int)sizeof(targetBuffer)) { + fprintf(stderr, "Resulting target path is too long.\n"); + return FS_EINVAL; + } + + strcpy(targetBuffer, target); + strcat(targetBuffer, "/"); + strcat(targetBuffer, leaf); + target = targetBuffer; + } + } - return status; + // check, if the target exists + error = sys_rstat(true, -1, target, &st, true); + if (error == FS_OK) { + if (!force) { + fprintf(stderr, "Can't create link. `%s' is in the way.", target); + return FS_FILE_EXISTS; + } + + // unlink the entry + error = sys_unlink(true, -1, target); + if (error != FS_OK) { + fprintf(stderr, "Failed to remove `%s' to make way for link: " + "%s\n", target, fs_strerror(error)); + return error; + } + } + + // finally create the link + if (symbolic) + error = sys_symlink(1, source, -1, target); + else + error = sys_link(1, -1, source, -1, target); + if (error != FS_OK) + printf("Failed to create link: %s\n", fs_strerror(error)); + + return error; } @@ -1434,7 +1726,7 @@ do_cio(int argc, char **argv) else strcat(fname, &argv[1][0]); - fd = sys_open(1, -1, fname, O_RDONLY, MY_S_IFREG, 0); + fd = sys_open(1, -1, fname, MY_O_RDONLY, MY_S_IFREG, 0); if (fd < 0) { printf("can't open %s\n", fname); return fd; @@ -1452,7 +1744,7 @@ do_cio(int argc, char **argv) } pos = 0; - if (sys_lseek(1, fd, pos, SEEK_SET) != pos) { + if (sys_lseek(1, fd, pos, MY_SEEK_SET) != pos) { perror("cio lseek"); break; } @@ -1480,7 +1772,7 @@ mkfile(char *s, int sz) int err = 0; char *buffer; - if ((fd = sys_open(1, -1, s, O_RDWR|O_CREAT, + if ((fd = sys_open(1, -1, s, MY_O_RDWR|MY_O_CREAT, MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { printf("error creating: %s\n", s); return fd; @@ -1593,6 +1885,7 @@ static int do_help(int argc, char **argv); static cmd_entry builtin_commands[] = { + { "cd", do_chdir, "change current directory" }, { "ls", do_dir, "print a directory listing" }, { "dir", do_dir, "print a directory listing (same as ls)" }, { "open", do_open, "open an existing file for read/write access" }, @@ -1614,6 +1907,7 @@ static cmd_entry builtin_commands[] = { "wrattr", do_write_attr, "write attribute \"name\" to the current file (N bytes [256])." }, { "rdattr", do_read_attr, "read attribute \"name\" from the current file (N bytes [256])." }, { "rmattr", do_remove_attr, "remove attribute \"name\" from the current file." }, + { "listattr", do_list_attr, "lists all attributes of the given or current file." }, { "attrs", do_attrtest, "writes random attributes [a-z] up to 1023 bytes, N [10240] iterations." }, { "lat_fs", do_lat_fs, "simulate what the lmbench test lat_fs does" }, { "create", do_create, "create N files. default is 100" }, @@ -1630,7 +1924,9 @@ static cmd_entry builtin_commands[] = { "tracker", do_tracker, "starts a Tracker like background thread that will listen to fs updates" }, { "cio", do_cio, "does a I/O speed test" }, { "stattest", do_stattest, "does an \"early\"/\"late\" stat test for files that are created or deleted" }, - { "symlink", do_symlink, "creates the specified symlink on the device" }, + { "link", do_link, "creates the specified [sym]link on the device" }, + { "ln", do_link, "creates the specified [sym]link on the device" }, + { "xcp", do_xcp, "similar to shell cp, can copy in, out, within, and outside the FS, supports attributes." }, { NULL, NULL } }; @@ -1746,6 +2042,3 @@ do_fsh(void) do_close(0, NULL); } - - - diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp b/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp new file mode 100644 index 0000000000..3f78f76435 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp @@ -0,0 +1,1245 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ + +#include "compat.h" + +#include +#include +#include + +#include +#include +#include + +#include "kprotos.h" +#include "path_util.h" +#include "stat_util.h" +#include "xcp.h" + +static void *sCopyBuffer = NULL; +static const int sCopyBufferSize = 64 * 1024; // 64 KB + +struct Options { + Options() + : dereference(true), + force(false), + recursive(false) + { + } + + bool dereference; + bool force; + bool recursive; +}; + +class Directory; +class File; +class SymLink; + +// Node +class Node { +public: + Node() {} + virtual ~Node() {} + + const my_stat &Stat() const { return fStat; } + bool IsFile() const { return MY_S_ISREG(fStat.mode); } + bool IsDirectory() const { return MY_S_ISDIR(fStat.mode); } + bool IsSymLink() const { return MY_S_ISLNK(fStat.mode); } + + virtual File *ToFile() { return NULL; } + virtual Directory *ToDirectory() { return NULL; } + virtual SymLink *ToSymLink() { return NULL; } + + virtual ssize_t GetNextAttr(char *name, int size) = 0; + virtual status_t GetAttrInfo(const char *name, my_attr_info &info) = 0; + virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, + void *buffer, int size) = 0; + virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, + const void *buffer, int size) = 0; + +protected: + struct my_stat fStat; // To be initialized by implementing classes. +}; + +// Directory +class Directory : public virtual Node { +public: + virtual Directory *ToDirectory() { return this; } + + virtual ssize_t GetNextEntry(struct my_dirent *entry, int size) = 0; +}; + +// File +class File : public virtual Node { +public: + virtual File *ToFile() { return this; } + + virtual ssize_t Read(void *buffer, int size) = 0; + virtual ssize_t Write(const void *buffer, int size) = 0; +}; + +// SymLink +class SymLink : public virtual Node { +public: + virtual SymLink *ToSymLink() { return this; } + + virtual ssize_t ReadLink(char *buffer, int bufferSize) = 0; +}; + +// FSDomain +class FSDomain { +public: + virtual status_t Open(const char *path, bool followLink, Node *&node) = 0; + + virtual status_t CreateFile(const char *path, const struct my_stat &st, + File *&file) = 0; + virtual status_t CreateDirectory(const char *path, const struct my_stat &st, + Directory *&dir) = 0; + virtual status_t CreateSymLink(const char *path, const char *linkTo, + const struct my_stat &st, SymLink *&link) = 0; + + virtual status_t Unlink(const char *path) = 0; +}; + + +// #pragma mark - + +// HostNode +class HostNode : public virtual Node { +public: + HostNode() + : Node(), + fFD(-1), + fAttrDir(NULL) + { + } + + virtual ~HostNode() + { + if (fFD >= 0) + close(fFD); + if (fAttrDir) + fs_close_attr_dir(fAttrDir); + } + + virtual status_t Init(const char *path, int fd, const my_stat &st) + { + fFD = fd; + fStat = st; + + // open the attribute directory + fAttrDir = fs_fopen_attr_dir(fd); + if (!fAttrDir) + return from_platform_error(errno); + + return FS_OK; + } + + virtual ssize_t GetNextAttr(char *name, int size) + { + if (!fAttrDir) + return 0; + + errno = 0; + struct dirent *entry = fs_read_attr_dir(fAttrDir); + if (!entry) + return from_platform_error(errno); + + int len = strlen(entry->d_name); + if (len >= size) + return FS_NAME_TOO_LONG; + + strcpy(name, entry->d_name); + return 1; + } + + virtual status_t GetAttrInfo(const char *name, my_attr_info &info) + { + attr_info hostInfo; + if (fs_stat_attr(fFD, name, &hostInfo) < 0) + return from_platform_error(errno); + + info.type = hostInfo.type; + info.size = hostInfo.size; + return FS_OK; + } + + virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, + void *buffer, int size) + { + ssize_t bytesRead = fs_read_attr(fFD, name, type, pos, buffer, size); + return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); + } + + virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, + const void *buffer, int size) + { + ssize_t bytesWritten = fs_write_attr(fFD, name, type, pos, buffer, + size); + return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno)); + } + +protected: + int fFD; + DIR *fAttrDir; +}; + +// HostDirectory +class HostDirectory : public Directory, public HostNode { +public: + HostDirectory() + : Directory(), + HostNode(), + fDir(NULL) + { + } + + virtual ~HostDirectory() + { + if (fDir) + closedir(fDir); + } + + virtual status_t Init(const char *path, int fd, const my_stat &st) + { + status_t error = HostNode::Init(path, fd, st); + if (error != FS_OK) + return error; + + fDir = opendir(path); + if (!fDir) + return from_platform_error(errno); + + return FS_OK; + } + + virtual ssize_t GetNextEntry(struct my_dirent *entry, int size) + { + errno = 0; + struct dirent *hostEntry = readdir(fDir); + if (!hostEntry) + return from_platform_error(errno); + + int nameLen = strlen(hostEntry->d_name); + int recLen = entry->d_name + nameLen + 1 - (char*)entry; + if (recLen > size) + return FS_NAME_TOO_LONG; + + entry->d_dev = hostEntry->d_dev; + entry->d_ino = hostEntry->d_ino; + strcpy(entry->d_name, hostEntry->d_name); + entry->d_reclen = recLen; + + return 1; + } + +private: + DIR *fDir; +}; + +// HostFile +class HostFile : public File, public HostNode { +public: + HostFile() + : File(), + HostNode() + { + } + + virtual ~HostFile() + { + } + + virtual ssize_t Read(void *buffer, int size) + { + ssize_t bytesRead = read(fFD, buffer, size); + return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); + } + + virtual ssize_t Write(const void *buffer, int size) + { + ssize_t bytesWritten = write(fFD, buffer, size); + return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno)); + } +}; + +// HostSymLink +class HostSymLink : public SymLink, public HostNode { +public: + HostSymLink() + : SymLink(), + HostNode() + { + } + + virtual ~HostSymLink() + { + if (fPath) + free(fPath); + } + + virtual status_t Init(const char *path, int fd, const my_stat &st) + { + status_t error = HostNode::Init(path, fd, st); + if (error != FS_OK) + return error; + + fPath = strdup(path); + if (!fPath) + return FS_NO_MEMORY; + + return FS_OK; + } + + virtual ssize_t ReadLink(char *buffer, int bufferSize) + { + ssize_t bytesRead = readlink(fPath, buffer, bufferSize); + return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); + } + +private: + char *fPath; +}; + +// HostFSDomain +class HostFSDomain : public FSDomain { +public: + HostFSDomain() {} + virtual ~HostFSDomain() {} + + virtual status_t Open(const char *path, bool followLink, Node *&_node) + { + // open the node + int fd = open(path, O_RDONLY + | (followLink ? 0 : O_NOTRAVERSE)); + if (fd < 0) + return from_platform_error(errno); + + // stat the node + struct stat st; + if (fstat(fd, &st) < 0) { + close(fd); + return from_platform_error(errno); + } + + // check the node type and create the node + HostNode *node = NULL; + switch (st.st_mode & S_IFMT) { + case S_IFLNK: + node = new HostSymLink; + break; + case S_IFREG: + node = new HostFile; + break; + case S_IFDIR: + node = new HostDirectory; + break; + default: + close(fd); + return FS_EINVAL; + } + + // convert the stat + struct my_stat myst; + from_platform_stat(&st, &myst); + + // init the node + status_t error = node->Init(path, fd, myst); + // the node receives ownership of the FD + if (error != FS_OK) { + delete node; + return error; + } + + _node = node; + return FS_OK; + } + + virtual status_t CreateFile(const char *path, const struct my_stat &myst, + File *&_file) + { + struct stat st; + to_platform_stat(&myst, &st); + + // create the file + int fd = creat(path, st.st_mode & S_IUMSK); + if (fd < 0) + return from_platform_error(errno); + + // apply the other stat fields + status_t error = _ApplyStat(fd, st); + if (error != FS_OK) { + close(fd); + return error; + } + + // create the object + HostFile *file = new HostFile; + error = file->Init(path, fd, myst); + if (error != FS_OK) { + delete file; + return error; + } + + _file = file; + return FS_OK; + } + + virtual status_t CreateDirectory(const char *path, + const struct my_stat &myst, Directory *&_dir) + { + struct stat st; + to_platform_stat(&myst, &st); + + // create the dir + if (mkdir(path, st.st_mode & S_IUMSK) < 0) + return from_platform_error(errno); + + // open the dir node + int fd = open(path, O_RDONLY | O_NOTRAVERSE); + if (fd < 0) + return from_platform_error(errno); + + // apply the other stat fields + status_t error = _ApplyStat(fd, st); + if (error != FS_OK) { + close(fd); + return error; + } + + // create the object + HostDirectory *dir = new HostDirectory; + error = dir->Init(path, fd, myst); + if (error != FS_OK) { + delete dir; + return error; + } + + _dir = dir; + return FS_OK; + } + + virtual status_t CreateSymLink(const char *path, const char *linkTo, + const struct my_stat &myst, SymLink *&_link) + { + struct stat st; + to_platform_stat(&myst, &st); + + // create the link + if (symlink(linkTo, path) < 0) + return from_platform_error(errno); + + // open the symlink node + int fd = open(path, O_RDONLY | O_NOTRAVERSE); + if (fd < 0) + return from_platform_error(errno); + + // apply the other stat fields + status_t error = _ApplyStat(fd, st); + if (error != FS_OK) { + close(fd); + return error; + } + + // create the object + HostSymLink *link = new HostSymLink; + error = link->Init(path, fd, myst); + if (error != FS_OK) { + delete link; + return error; + } + + _link = link; + return FS_OK; + } + + + virtual status_t Unlink(const char *path) + { + if (unlink(path) < 0) + return from_platform_error(errno); + return FS_OK; + } + +private: + status_t _ApplyStat(int fd, const struct stat &st) + { + // TODO: Set times... + return FS_OK; + } +}; + + +// #pragma mark - + +// GuestNode +class GuestNode : public virtual Node { +public: + GuestNode() + : Node(), + fFD(-1), + fAttrDir(-1) + { + } + + virtual ~GuestNode() + { + if (fFD >= 0) + sys_close(true, fFD); + if (fAttrDir) + sys_closedir(true, fAttrDir); + } + + virtual status_t Init(const char *path, int fd, const my_stat &st) + { + fFD = fd; + fStat = st; + + // open the attribute directory + fAttrDir = sys_open_attr_dir(true, fd, NULL); + if (fAttrDir < 0) + return fAttrDir; + + return FS_OK; + } + + virtual status_t GetNextAttr(char *name, int size) + { + if (fAttrDir < 0) + return 0; + + char buffer[sizeof(my_dirent) + B_ATTR_NAME_LENGTH]; + struct my_dirent *entry = (my_dirent *)buffer; + int numRead = sys_readdir(true, fAttrDir, entry, sizeof(buffer), 1); + if (numRead < 0) + return numRead; + if (numRead == 0) + return 0; + + int len = strlen(entry->d_name); + if (len >= size) + return FS_NAME_TOO_LONG; + + strcpy(name, entry->d_name); + return 1; + } + + virtual status_t GetAttrInfo(const char *name, my_attr_info &info) + { + return sys_stat_attr(true, fFD, NULL, name, &info); + } + + virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, + void *buffer, int size) + { + return sys_read_attr(true, fFD, name, type, buffer, size, pos); + } + + virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, + const void *buffer, int size) + { + return sys_write_attr(true, fFD, name, type, buffer, size, pos); + } + +protected: + int fFD; + int fAttrDir; +}; + +// GuestDirectory +class GuestDirectory : public Directory, public GuestNode { +public: + GuestDirectory() + : Directory(), + GuestNode(), + fDir(-1) + { + } + + virtual ~GuestDirectory() + { + if (fDir) + sys_closedir(true, fDir); + } + + virtual status_t Init(const char *path, int fd, const my_stat &st) + { + status_t error = GuestNode::Init(path, fd, st); + if (error != FS_OK) + return error; + + fDir = sys_opendir(true, fd, NULL, true); + if (fDir < 0) + return fDir; + + return FS_OK; + } + + virtual status_t GetNextEntry(struct my_dirent *entry, int size) + { + return sys_readdir(true, fDir, entry, size, 1); + } + +private: + int fDir; +}; + +// GuestFile +class GuestFile : public File, public GuestNode { +public: + GuestFile() + : File(), + GuestNode() + { + } + + virtual ~GuestFile() + { + } + + virtual ssize_t Read(void *buffer, int size) + { + return sys_read(true, fFD, buffer, size); + } + + virtual ssize_t Write(const void *buffer, int size) + { + return sys_write(true, fFD, buffer, size); + } +}; + +// GuestSymLink +class GuestSymLink : public SymLink, public GuestNode { +public: + GuestSymLink() + : SymLink(), + GuestNode() + { + } + + virtual ~GuestSymLink() + { + } + + virtual ssize_t ReadLink(char *buffer, int bufferSize) + { + return sys_readlink(true, fFD, NULL, buffer, bufferSize); + } +}; + +// GuestFSDomain +class GuestFSDomain : public FSDomain { +public: + GuestFSDomain() {} + virtual ~GuestFSDomain() {} + + virtual status_t Open(const char *path, bool followLink, Node *&_node) + { + // open the node + int fd = sys_open(true, -1, path, + MY_O_RDONLY | (followLink ? 0 : MY_O_NOTRAVERSE), + 0, true); + if (fd < 0) + return fd; + + // stat the node + struct my_stat st; + status_t error = sys_rstat(true, fd, NULL, &st, false); + if (error < 0) { + sys_close(true, fd); + return error; + } + + // check the node type and create the node + GuestNode *node = NULL; + switch (st.mode & MY_S_IFMT) { + case MY_S_IFLNK: + node = new GuestSymLink; + break; + case MY_S_IFREG: + node = new GuestFile; + break; + case MY_S_IFDIR: + node = new GuestDirectory; + break; + default: + sys_close(true, fd); + return FS_EINVAL; + } + + // init the node + error = node->Init(path, fd, st); + // the node receives ownership of the FD + if (error != FS_OK) { + delete node; + return error; + } + + _node = node; + return FS_OK; + } + + virtual status_t CreateFile(const char *path, const struct my_stat &st, + File *&_file) + { + // create the file + int fd = sys_open(true, -1, path, MY_O_RDWR | MY_O_EXCL | MY_O_CREAT, + st.mode & MY_S_IUMSK, true); + if (fd < 0) + return fd; + + // apply the other stat fields + status_t error = _ApplyStat(fd, st); + if (error != FS_OK) { + sys_close(true, fd); + return error; + } + + // create the object + GuestFile *file = new GuestFile; + error = file->Init(path, fd, st); + if (error != FS_OK) { + delete file; + return error; + } + + _file = file; + return FS_OK; + } + + virtual status_t CreateDirectory(const char *path, const struct my_stat &st, + Directory *&_dir) + { + // create the dir + status_t error = sys_mkdir(true, -1, path, st.mode & MY_S_IUMSK); + if (error < 0) + return error; + + // open the dir node + int fd = sys_open(true, -1, path, MY_O_RDONLY | MY_O_NOTRAVERSE, + 0, true); + if (fd < 0) + return fd; + + // apply the other stat fields + error = _ApplyStat(fd, st); + if (error != FS_OK) { + sys_close(true, fd); + return error; + } + + // create the object + GuestDirectory *dir = new GuestDirectory; + error = dir->Init(path, fd, st); + if (error != FS_OK) { + delete dir; + return error; + } + + _dir = dir; + return FS_OK; + } + + virtual status_t CreateSymLink(const char *path, const char *linkTo, + const struct my_stat &st, SymLink *&_link) + { + // create the link + status_t error = sys_symlink(true, linkTo, -1, path); + if (error < 0) + return error; + + // open the symlink node + int fd = sys_open(true, -1, path, MY_O_RDONLY | MY_O_NOTRAVERSE, + 0, true); + if (fd < 0) + return fd; + + // apply the other stat fields + error = _ApplyStat(fd, st); + if (error != FS_OK) { + sys_close(true, fd); + return error; + } + + // create the object + GuestSymLink *link = new GuestSymLink; + error = link->Init(path, fd, st); + if (error != FS_OK) { + delete link; + return error; + } + + _link = link; + return FS_OK; + } + + virtual status_t Unlink(const char *path) + { + return sys_unlink(true, -1, path); + } + +private: + status_t _ApplyStat(int fd, const struct my_stat &st) + { + // TODO: Set times... + return FS_OK; + } +}; + + +// #pragma mark - + +static status_t copy_entry(FSDomain *sourceDomain, const char *source, + FSDomain *targetDomain, const char *target, const Options &options); + +static FSDomain * +get_file_domain(const char *target, const char *&fsTarget) +{ + if (target[0] == ':') { + fsTarget = target + 1; + return new HostFSDomain; + } else { + fsTarget = target; + return new GuestFSDomain; + } +} + +typedef ObjectDeleter NodeDeleter; +typedef ObjectDeleter DomainDeleter; +typedef MemoryDeleter PathDeleter; + + +static status_t +copy_file_contents(const char *source, File *sourceFile, const char *target, + File *targetFile) +{ + ssize_t bytesRead; + while ((bytesRead = sourceFile->Read(sCopyBuffer, sCopyBufferSize)) > 0) { + ssize_t bytesWritten = targetFile->Write(sCopyBuffer, bytesRead); + if (bytesWritten < 0) { + fprintf(stderr, "Error while writing to file `%s': %s\n", + target, fs_strerror(bytesWritten)); + return bytesWritten; + } + } + + if (bytesRead < 0) { + fprintf(stderr, "Error while reading from file `%s': %s\n", + source, fs_strerror(bytesRead)); + return bytesRead; + } + + return FS_OK; +} + + +static status_t +copy_dir_contents(FSDomain *sourceDomain, const char *source, + Directory *sourceDir, FSDomain *targetDomain, const char *target, + const Options &options) +{ + char buffer[sizeof(my_dirent) + B_FILE_NAME_LENGTH]; + struct my_dirent *entry = (struct my_dirent *)buffer; + ssize_t numRead; + while ((numRead = sourceDir->GetNextEntry(entry, sizeof(buffer))) > 0) { + // skip "." and ".." + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // compose a new source path name + char *sourceEntry = make_path(source, entry->d_name); + if (!sourceEntry) { + fprintf(stderr, "Failed to allocate source path!\n"); + return FS_ENOMEM; + } + PathDeleter sourceDeleter(sourceEntry); + + // compose a new target path name + char *targetEntry = make_path(target, entry->d_name); + if (!targetEntry) { + fprintf(stderr, "Failed to allocate target path!\n"); + return FS_ENOMEM; + } + PathDeleter targetDeleter(targetEntry); + + status_t error = copy_entry(sourceDomain, sourceEntry, targetDomain, + targetEntry, options); + if (error != FS_OK) + return error; + } + + if (numRead < 0) { + fprintf(stderr, "Error reading directory `%s': %s\n", source, + fs_strerror(numRead)); + return numRead; + } + + return FS_OK; +} + + +static status_t +copy_attribute(const char *source, Node *sourceNode, const char *target, + Node *targetNode, const char *name, const my_attr_info &info) +{ + // special case: empty attribute + if (info.size <= 0) { + ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, 0, + sCopyBuffer, 0); + if (bytesWritten) { + fprintf(stderr, "Error while writing to attribute `%s' of file " + "`%s': %s\n", name, target, fs_strerror(bytesWritten)); + return bytesWritten; + } + + return FS_OK; + } + + // non-empty attribute + fs_off_t pos = 0; + int toCopy = info.size; + while (toCopy > 0) { + // read data from source + int toRead = (toCopy < sCopyBufferSize ? toCopy : sCopyBufferSize); + ssize_t bytesRead = sourceNode->ReadAttr(name, info.type, pos, + sCopyBuffer, toRead); + if (bytesRead < 0) { + fprintf(stderr, "Error while reading from attribute `%s' of file " + "`%s': %s\n", name, source, fs_strerror(bytesRead)); + return bytesRead; + } + + // write data to target + ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, pos, + sCopyBuffer, bytesRead); + if (bytesWritten < 0) { + fprintf(stderr, "Error while writing to attribute `%s' of file " + "`%s': %s\n", name, target, fs_strerror(bytesWritten)); + return bytesWritten; + } + + pos += bytesRead; + toCopy -= bytesRead; + } + + return FS_OK; +} + + +static status_t +copy_attributes(const char *source, Node *sourceNode, const char *target, + Node *targetNode) +{ + char name[B_ATTR_NAME_LENGTH]; + ssize_t numRead; + while ((numRead = sourceNode->GetNextAttr(name, sizeof(name))) > 0) { + my_attr_info info; + // get attribute info + status_t error = sourceNode->GetAttrInfo(name, info); + if (error != FS_OK) { + fprintf(stderr, "Error getting info for attribute `%s' of file " + "`%s': %s\n", name, source, fs_strerror(error)); + return error; + } + + // copy the attribute + error = copy_attribute(source, sourceNode, target, targetNode, name, + info); + if (error != FS_OK) + return error; + } + + if (numRead < 0) { + fprintf(stderr, "Error reading attribute directory of `%s': %s\n", + source, fs_strerror(numRead)); + return numRead; + } + + return FS_OK; +} + + +static status_t +copy_entry(FSDomain *sourceDomain, const char *source, + FSDomain *targetDomain, const char *target, const Options &options) +{ + // open the source node + Node *sourceNode; + status_t error = sourceDomain->Open(source, options.dereference, + sourceNode); + if (error != FS_OK) { + fprintf(stderr, "Failed to open source path `%s': %s\n", source, + fs_strerror(error)); + return error; + } + NodeDeleter sourceDeleter(sourceNode); + + // check, if target exists + Node *targetNode = NULL; + error = targetDomain->Open(target, false, targetNode); + NodeDeleter targetDeleter; + if (error == FS_OK) { + // 1. target exists: + // check, if it is a dir and, if so, whether source is a dir too + targetDeleter.SetTo(targetNode); + + if (sourceNode->IsDirectory() && targetNode->IsDirectory()) { + // 1.1. target and source are dirs: + // -> just copy their contents + // ... + } else { + // 1.2. source and/or target are no dirs + // -> if /force/, remove the target and continue with 2., + // otherwise fail + if (!options.force) { + fprintf(stderr, "File `%s' does exist.\n", target); + return FS_FILE_EXISTS; + } + + targetDeleter.SetTo(NULL); + targetNode = NULL; + error = targetDomain->Unlink(target); + if (error != FS_OK) { + fprintf(stderr, "Failed to remove `%s'\n", target); + return error; + } + } + } // else: 2. target doesn't exist: -> just create it + + // create the target node + error = FS_OK; + if (sourceNode->IsFile()) { + File *file = NULL; + error = targetDomain->CreateFile(target, sourceNode->Stat(), file); + if (error == 0) + targetNode = file; + } else if (sourceNode->IsDirectory()) { + // check /recursive/ + if (!options.recursive) { + fprintf(stderr, "Entry `%s' is a directory.\n", source); + return FS_EISDIR; + } + + // create the target only, if it doesn't already exist + if (!targetNode) { + Directory *dir = NULL; + error = targetDomain->CreateDirectory(target, sourceNode->Stat(), + dir); + if (error == 0) + targetNode = dir; + } + } else if (sourceNode->IsSymLink()) { + // read the source link + SymLink *sourceLink = sourceNode->ToSymLink(); + char linkTo[B_PATH_NAME_LENGTH]; + ssize_t bytesRead = sourceLink->ReadLink(linkTo, sizeof(linkTo) - 1); + if (bytesRead < 0) { + fprintf(stderr, "Failed to read symlink `%s': %s\n", source, + fs_strerror(bytesRead)); + } + linkTo[bytesRead] = '\0'; // always NULL-terminate + + // create the target link + SymLink *link; + error = targetDomain->CreateSymLink(target, linkTo, + sourceNode->Stat(), link); + if (error == 0) + targetNode = link; + } else { + fprintf(stderr, "Unknown node type. We shouldn't be here!\n"); + return FS_EINVAL; + } + + if (error != FS_OK) { + fprintf(stderr, "Failed to create `%s': %s\n", target, + fs_strerror(error)); + return error; + } + targetDeleter.Detach(); + targetDeleter.SetTo(targetNode); + + // copy attributes + error = copy_attributes(source, sourceNode, target, targetNode); + if (error != FS_OK) + return error; + + // copy contents + if (sourceNode->IsFile()) { + error = copy_file_contents(source, sourceNode->ToFile(), target, + targetNode->ToFile()); + } else if (sourceNode->IsDirectory()) { + error = copy_dir_contents(sourceDomain, source, + sourceNode->ToDirectory(), targetDomain, target, options); + } + + return error; +} + + +int +do_xcp(int argc, char **argv) +{ + enum { MAX_SOURCE_FILES = 128 }; + const char *sources[MAX_SOURCE_FILES]; + int sourceCount = 0; + Options options; + + if (argc > MAX_SOURCE_FILES) { + fprintf(stderr, "Too many args!\n"); + return FS_EINVAL; + } + + // parse parameters + for (int argi = 1; argi < argc; argi++) { + const char *arg = argv[argi]; + if (arg[0] == '-') { + if (arg[1] == '\0') { + fprintf(stderr, "Invalid option `-'\n"); + return FS_EINVAL; + } + + for (int i = 1; arg[i]; i++) { + switch (arg[i]) { + case 'd': + options.dereference = false; + break; + case 'f': + options.force = true; + break; + case 'r': + options.recursive = true; + break; + default: + fprintf(stderr, "Unknown option `-%c'\n", arg[i]); + return FS_EINVAL; + } + } + } else { + sources[sourceCount++] = arg; + } + } + + // check params + if (sourceCount < 2) { + fprintf(stderr, "Must specify at least 2 files!\n"); + return FS_EINVAL; + } + + // check the target + const char *target = sources[--sourceCount]; + bool targetIsDir = false; + bool targetExists = false; + FSDomain *targetDomain = get_file_domain(target, target); + DomainDeleter targetDomainDeleter(targetDomain); + + Node *targetNode; + status_t error = targetDomain->Open(target, true, targetNode); + if (error == 0) { + NodeDeleter targetDeleter(targetNode); + targetExists = true; + + if (targetNode->IsDirectory()) { + targetIsDir = true; + } else { + if (sourceCount > 1) { + fprintf(stderr, "Destination `%s' is not a directory!", + target); + return FS_NOT_A_DIRECTORY; + } + } + } else { + if (sourceCount > 1) { + fprintf(stderr, "Failed to open destination directory `%s': `%s'\n", + target, fs_strerror(error)); + return error; + } + } + + // allocate a copy buffer + sCopyBuffer = malloc(sCopyBufferSize); + if (!sCopyBuffer) { + fprintf(stderr, "Failed to allocate copy buffer.\n"); + return FS_ENOMEM; + } + MemoryDeleter copyBufferDeleter(sCopyBuffer); + + // the copy loop + for (int i = 0; i < sourceCount; i++) { + const char *source = sources[i]; + FSDomain *sourceDomain = get_file_domain(source, source); + DomainDeleter sourceDomainDeleter(sourceDomain); + if (targetExists && targetIsDir) { + // 1. target exists: + // 1.1. target is a dir: + // get the source leaf name + char leafName[B_FILE_NAME_LENGTH]; + error = get_last_path_component(source, leafName, sizeof(leafName)); + if (error != FS_OK) { + fprintf(stderr, "Failed to get last path component of `%s': " + "%s\n", source, fs_strerror(error)); + return error; + } + + if (strcmp(leafName, ".") == 0 || strcmp(leafName, "..") == 0) { + // 1.1.1. source name is `.' or `..' + // -> copy the contents only + // (copy_dir_contents()) + // open the source dir + Node *sourceNode; + error = sourceDomain->Open(source, options.dereference, + sourceNode); + if (error != FS_OK) { + fprintf(stderr, "Failed to open `%s': %s.\n", source, + fs_strerror(error)); + return error; + } + NodeDeleter sourceDeleter(sourceNode); + + // check, if it is a dir + Directory *sourceDir = sourceNode->ToDirectory(); + if (!sourceDir) { + fprintf(stderr, "Source `%s' is not a directory although" + "it's last path component is `%s'\n", source, leafName); + return FS_EINVAL; + } + + error = copy_dir_contents(sourceDomain, source, sourceDir, + targetDomain, target, options); + } else { + // 1.1.2. source has normal name + // -> we copy into the dir + // (copy_entry(, /)) + // compose a new target path name + char *targetEntry = make_path(target, leafName); + if (!targetEntry) { + fprintf(stderr, "Failed to allocate target path!\n"); + return FS_ENOMEM; + } + PathDeleter targetDeleter(targetEntry); + + error = copy_entry(sourceDomain, source, targetDomain, + targetEntry, options); + } + } else { + // 1.2. target is no dir: + // -> if /force/ is given, we replace the target, otherwise + // we fail + // (copy_entry(, )) + // or + // 2. target doesn't exist: + // -> we create the target as a clone of the source + // (copy_entry(, )) + error = copy_entry(sourceDomain, source, targetDomain, target, + options); + } + + if (error != 0) + return error; + } + + return 0; +} + diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.h b/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.h new file mode 100644 index 0000000000..c189d79bb7 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.h @@ -0,0 +1,19 @@ +/* + * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Distributed under the terms of the MIT License. + */ +#ifndef FS_SHELL_XCP_H +#define FS_SHELL_XCP_H + +#ifdef __cplusplus +extern "C" { +#endif + +int do_xcp(int argc, char **argv); + +#ifdef __cplusplus +} +#endif + + +#endif FS_SHELL_XCP_H