From 01203fdf42da6e096d983f02f11183a9b0332b67 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 11 Mar 2005 17:52:29 +0000 Subject: [PATCH] Made a bit more compliant to the shell `cp': * When the target is a symlink, it is tried to be resolved before making the decision what to do. * If the (resolved) target is a file and the source is a file, we don't fail anymore, if `-f' wasn't specified. We truncate the target file instead and copy the source data into it. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11683 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/fs_shell/xcp.cpp | 98 +++++++++++++------ 1 file changed, 70 insertions(+), 28 deletions(-) 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 index b00b0cf530..7908aa214a 100644 --- a/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/xcp.cpp @@ -59,6 +59,7 @@ public: void *buffer, int size) = 0; virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, const void *buffer, int size) = 0; + virtual status_t RemoveAttr(const char *name) = 0; protected: struct my_stat fStat; // To be initialized by implementing classes. @@ -92,7 +93,7 @@ public: // FSDomain class FSDomain { public: - virtual status_t Open(const char *path, bool followLink, Node *&node) = 0; + virtual status_t Open(const char *path, int openMode, Node *&node) = 0; virtual status_t CreateFile(const char *path, const struct my_stat &st, File *&file) = 0; @@ -182,6 +183,12 @@ public: return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno)); } + virtual status_t RemoveAttr(const char *name) + { + return (fs_remove_attr(fFD, name) == 0 + ? 0 : from_platform_error(errno)); + } + protected: int fFD; DIR *fAttrDir; @@ -312,11 +319,10 @@ public: HostFSDomain() {} virtual ~HostFSDomain() {} - virtual status_t Open(const char *path, bool followLink, Node *&_node) + virtual status_t Open(const char *path, int openMode, Node *&_node) { // open the node - int fd = open(path, O_RDONLY - | (followLink ? 0 : O_NOTRAVERSE)); + int fd = open(path, to_platform_open_mode(openMode)); if (fd < 0) return from_platform_error(errno); @@ -546,6 +552,11 @@ public: return sys_write_attr(true, fFD, name, type, buffer, size, pos); } + virtual status_t RemoveAttr(const char *name) + { + return sys_remove_attr(true, fFD, name); + } + protected: int fFD; int fAttrDir; @@ -638,12 +649,10 @@ public: GuestFSDomain() {} virtual ~GuestFSDomain() {} - virtual status_t Open(const char *path, bool followLink, Node *&_node) + virtual status_t Open(const char *path, int openMode, Node *&_node) { // open the node - int fd = sys_open(true, -1, path, - MY_O_RDONLY | (followLink ? 0 : MY_O_NOTRAVERSE), - 0, true); + int fd = sys_open(true, -1, path, openMode, 0, true); if (fd < 0) return fd; @@ -887,6 +896,9 @@ static status_t copy_attribute(const char *source, Node *sourceNode, const char *target, Node *targetNode, const char *name, const my_attr_info &info) { + // remove the attribute first + targetNode->RemoveAttr(name); + // special case: empty attribute if (info.size <= 0) { ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, 0, @@ -970,7 +982,8 @@ copy_entry(FSDomain *sourceDomain, const char *source, { // open the source node Node *sourceNode; - status_t error = sourceDomain->Open(source, options.dereference, + status_t error = sourceDomain->Open(source, + MY_O_RDONLY | (options.dereference ? 0 : MY_O_NOTRAVERSE), sourceNode); if (error != FS_OK) { fprintf(stderr, "Failed to open source path `%s': %s\n", source, @@ -981,43 +994,72 @@ copy_entry(FSDomain *sourceDomain, const char *source, // check, if target exists Node *targetNode = NULL; - error = targetDomain->Open(target, false, targetNode); + // try opening with resolving symlinks first + error = targetDomain->Open(target, MY_O_RDONLY | MY_O_NOTRAVERSE, + 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 the target is a symlink, try resolving it + if (targetNode->IsSymLink()) { + Node *resolvedTargetNode; + error = targetDomain->Open(target, MY_O_RDONLY, resolvedTargetNode); + if (error == FS_OK) { + targetNode = resolvedTargetNode; + 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) { + + if (options.force) { + // 1.2.1. /force/ + // -> remove the target and continue with 2. + targetDeleter.Delete(); + targetNode = NULL; + error = targetDomain->Unlink(target); + if (error != FS_OK) { + fprintf(stderr, "Failed to remove `%s'\n", target); + return error; + } + } else if (sourceNode->IsFile() && targetNode->IsFile()) { + // 1.2.1.1. !/force/, but both source and target are files + // -> truncate the target file and continue + targetDeleter.Delete(); + targetNode = NULL; + error = targetDomain->Open(target, MY_O_RDWR | MY_O_TRUNC, + targetNode); + if (error != FS_OK) { + fprintf(stderr, "Failed to open `%s' for writing\n", + target); + return error; + } + } else { + // 1.2.1.2. !/force/, source or target isn't a file + // -> fail 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; + if (!targetNode) { + File *file = NULL; + error = targetDomain->CreateFile(target, sourceNode->Stat(), file); + if (error == 0) + targetNode = file; + } } else if (sourceNode->IsDirectory()) { // check /recursive/ if (!options.recursive) { @@ -1060,7 +1102,6 @@ copy_entry(FSDomain *sourceDomain, const char *source, fs_strerror(error)); return error; } - targetDeleter.Detach(); targetDeleter.SetTo(targetNode); // copy attributes @@ -1138,7 +1179,7 @@ do_xcp(int argc, char **argv) DomainDeleter targetDomainDeleter(targetDomain); Node *targetNode; - status_t error = targetDomain->Open(target, true, targetNode); + status_t error = targetDomain->Open(target, MY_O_RDONLY, targetNode); if (error == 0) { NodeDeleter targetDeleter(targetNode); targetExists = true; @@ -1191,7 +1232,8 @@ do_xcp(int argc, char **argv) // (copy_dir_contents()) // open the source dir Node *sourceNode; - error = sourceDomain->Open(source, options.dereference, + error = sourceDomain->Open(source, + MY_O_RDONLY | (options.dereference ? 0 : MY_O_NOTRAVERSE), sourceNode); if (error != FS_OK) { fprintf(stderr, "Failed to open `%s': %s.\n", source,