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
This commit is contained in:
Ingo Weinhold
2005-03-11 17:52:29 +00:00
parent 2c05bc8a8d
commit 01203fdf42
@@ -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,