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:
@@ -59,6 +59,7 @@ public:
|
|||||||
void *buffer, int size) = 0;
|
void *buffer, int size) = 0;
|
||||||
virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos,
|
virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos,
|
||||||
const void *buffer, int size) = 0;
|
const void *buffer, int size) = 0;
|
||||||
|
virtual status_t RemoveAttr(const char *name) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct my_stat fStat; // To be initialized by implementing classes.
|
struct my_stat fStat; // To be initialized by implementing classes.
|
||||||
@@ -92,7 +93,7 @@ public:
|
|||||||
// FSDomain
|
// FSDomain
|
||||||
class FSDomain {
|
class FSDomain {
|
||||||
public:
|
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,
|
virtual status_t CreateFile(const char *path, const struct my_stat &st,
|
||||||
File *&file) = 0;
|
File *&file) = 0;
|
||||||
@@ -182,6 +183,12 @@ public:
|
|||||||
return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno));
|
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:
|
protected:
|
||||||
int fFD;
|
int fFD;
|
||||||
DIR *fAttrDir;
|
DIR *fAttrDir;
|
||||||
@@ -312,11 +319,10 @@ public:
|
|||||||
HostFSDomain() {}
|
HostFSDomain() {}
|
||||||
virtual ~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
|
// open the node
|
||||||
int fd = open(path, O_RDONLY
|
int fd = open(path, to_platform_open_mode(openMode));
|
||||||
| (followLink ? 0 : O_NOTRAVERSE));
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return from_platform_error(errno);
|
return from_platform_error(errno);
|
||||||
|
|
||||||
@@ -546,6 +552,11 @@ public:
|
|||||||
return sys_write_attr(true, fFD, name, type, buffer, size, pos);
|
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:
|
protected:
|
||||||
int fFD;
|
int fFD;
|
||||||
int fAttrDir;
|
int fAttrDir;
|
||||||
@@ -638,12 +649,10 @@ public:
|
|||||||
GuestFSDomain() {}
|
GuestFSDomain() {}
|
||||||
virtual ~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
|
// open the node
|
||||||
int fd = sys_open(true, -1, path,
|
int fd = sys_open(true, -1, path, openMode, 0, true);
|
||||||
MY_O_RDONLY | (followLink ? 0 : MY_O_NOTRAVERSE),
|
|
||||||
0, true);
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
@@ -887,6 +896,9 @@ static status_t
|
|||||||
copy_attribute(const char *source, Node *sourceNode, const char *target,
|
copy_attribute(const char *source, Node *sourceNode, const char *target,
|
||||||
Node *targetNode, const char *name, const my_attr_info &info)
|
Node *targetNode, const char *name, const my_attr_info &info)
|
||||||
{
|
{
|
||||||
|
// remove the attribute first
|
||||||
|
targetNode->RemoveAttr(name);
|
||||||
|
|
||||||
// special case: empty attribute
|
// special case: empty attribute
|
||||||
if (info.size <= 0) {
|
if (info.size <= 0) {
|
||||||
ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, 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
|
// open the source node
|
||||||
Node *sourceNode;
|
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);
|
sourceNode);
|
||||||
if (error != FS_OK) {
|
if (error != FS_OK) {
|
||||||
fprintf(stderr, "Failed to open source path `%s': %s\n", source,
|
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
|
// check, if target exists
|
||||||
Node *targetNode = NULL;
|
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;
|
NodeDeleter targetDeleter;
|
||||||
if (error == FS_OK) {
|
if (error == FS_OK) {
|
||||||
// 1. target exists:
|
// 1. target exists:
|
||||||
// check, if it is a dir and, if so, whether source is a dir too
|
// check, if it is a dir and, if so, whether source is a dir too
|
||||||
targetDeleter.SetTo(targetNode);
|
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()) {
|
if (sourceNode->IsDirectory() && targetNode->IsDirectory()) {
|
||||||
// 1.1. target and source are dirs:
|
// 1.1. target and source are dirs:
|
||||||
// -> just copy their contents
|
// -> just copy their contents
|
||||||
// ...
|
// ...
|
||||||
} else {
|
} else {
|
||||||
// 1.2. source and/or target are no dirs
|
// 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);
|
fprintf(stderr, "File `%s' does exist.\n", target);
|
||||||
return FS_FILE_EXISTS;
|
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
|
} // else: 2. target doesn't exist: -> just create it
|
||||||
|
|
||||||
// create the target node
|
// create the target node
|
||||||
error = FS_OK;
|
error = FS_OK;
|
||||||
if (sourceNode->IsFile()) {
|
if (sourceNode->IsFile()) {
|
||||||
File *file = NULL;
|
if (!targetNode) {
|
||||||
error = targetDomain->CreateFile(target, sourceNode->Stat(), file);
|
File *file = NULL;
|
||||||
if (error == 0)
|
error = targetDomain->CreateFile(target, sourceNode->Stat(), file);
|
||||||
targetNode = file;
|
if (error == 0)
|
||||||
|
targetNode = file;
|
||||||
|
}
|
||||||
} else if (sourceNode->IsDirectory()) {
|
} else if (sourceNode->IsDirectory()) {
|
||||||
// check /recursive/
|
// check /recursive/
|
||||||
if (!options.recursive) {
|
if (!options.recursive) {
|
||||||
@@ -1060,7 +1102,6 @@ copy_entry(FSDomain *sourceDomain, const char *source,
|
|||||||
fs_strerror(error));
|
fs_strerror(error));
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
targetDeleter.Detach();
|
|
||||||
targetDeleter.SetTo(targetNode);
|
targetDeleter.SetTo(targetNode);
|
||||||
|
|
||||||
// copy attributes
|
// copy attributes
|
||||||
@@ -1138,7 +1179,7 @@ do_xcp(int argc, char **argv)
|
|||||||
DomainDeleter targetDomainDeleter(targetDomain);
|
DomainDeleter targetDomainDeleter(targetDomain);
|
||||||
|
|
||||||
Node *targetNode;
|
Node *targetNode;
|
||||||
status_t error = targetDomain->Open(target, true, targetNode);
|
status_t error = targetDomain->Open(target, MY_O_RDONLY, targetNode);
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
NodeDeleter targetDeleter(targetNode);
|
NodeDeleter targetDeleter(targetNode);
|
||||||
targetExists = true;
|
targetExists = true;
|
||||||
@@ -1191,7 +1232,8 @@ do_xcp(int argc, char **argv)
|
|||||||
// (copy_dir_contents())
|
// (copy_dir_contents())
|
||||||
// open the source dir
|
// open the source dir
|
||||||
Node *sourceNode;
|
Node *sourceNode;
|
||||||
error = sourceDomain->Open(source, options.dereference,
|
error = sourceDomain->Open(source,
|
||||||
|
MY_O_RDONLY | (options.dereference ? 0 : MY_O_NOTRAVERSE),
|
||||||
sourceNode);
|
sourceNode);
|
||||||
if (error != FS_OK) {
|
if (error != FS_OK) {
|
||||||
fprintf(stderr, "Failed to open `%s': %s.\n", source,
|
fprintf(stderr, "Failed to open `%s': %s.\n", source,
|
||||||
|
|||||||
Reference in New Issue
Block a user