2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include "compatibility.h"
8 #include "command_cp.h"
15 #include <AutoDeleter.h>
16 #include <EntryFilter.h>
18 #include <StorageDefs.h>
20 #include "fssh_dirent.h"
21 #include "fssh_errno.h"
22 #include "fssh_errors.h"
23 #include "fssh_fcntl.h"
24 #include "fssh_fs_attr.h"
25 #include "fssh_stat.h"
26 #include "fssh_string.h"
27 #include "fssh_unistd.h"
28 #include "path_util.h"
29 #include "stat_util.h"
33 using BPrivate::EntryFilter
;
39 static void *sCopyBuffer
= NULL
;
40 static const int sCopyBufferSize
= 64 * 1024; // 64 KB
45 attributesOnly(false),
46 ignoreAttributes(false),
48 alwaysDereference(false),
54 EntryFilter entryFilter
;
56 bool ignoreAttributes
;
58 bool alwaysDereference
;
73 const struct fssh_stat
&Stat() const { return fStat
; }
74 bool IsFile() const { return FSSH_S_ISREG(fStat
.fssh_st_mode
); }
75 bool IsDirectory() const { return FSSH_S_ISDIR(fStat
.fssh_st_mode
); }
76 bool IsSymLink() const { return FSSH_S_ISLNK(fStat
.fssh_st_mode
); }
78 virtual File
*ToFile() { return NULL
; }
79 virtual Directory
*ToDirectory() { return NULL
; }
80 virtual SymLink
*ToSymLink() { return NULL
; }
82 virtual fssh_ssize_t
GetNextAttr(char *name
, int size
) = 0;
83 virtual fssh_status_t
GetAttrInfo(const char *name
,
84 fssh_attr_info
&info
) = 0;
85 virtual fssh_ssize_t
ReadAttr(const char *name
, uint32_t type
,
86 fssh_off_t pos
, void *buffer
, int size
) = 0;
87 virtual fssh_ssize_t
WriteAttr(const char *name
, uint32_t type
,
88 fssh_off_t pos
, const void *buffer
, int size
) = 0;
89 virtual fssh_status_t
RemoveAttr(const char *name
) = 0;
92 struct fssh_stat fStat
; // To be initialized by implementing classes.
96 class Directory
: public virtual Node
{
98 virtual Directory
*ToDirectory() { return this; }
100 virtual fssh_ssize_t
GetNextEntry(struct fssh_dirent
*entry
, int size
) = 0;
104 class File
: public virtual Node
{
106 virtual File
*ToFile() { return this; }
108 virtual fssh_ssize_t
Read(void *buffer
, int size
) = 0;
109 virtual fssh_ssize_t
Write(const void *buffer
, int size
) = 0;
113 class SymLink
: public virtual Node
{
115 virtual SymLink
*ToSymLink() { return this; }
117 virtual fssh_ssize_t
ReadLink(char *buffer
, int bufferSize
) = 0;
123 virtual ~FSDomain() {}
125 virtual fssh_status_t
Open(const char *path
, int openMode
, Node
*&node
) = 0;
127 virtual fssh_status_t
CreateFile(const char *path
,
128 const struct fssh_stat
&st
, File
*&file
) = 0;
129 virtual fssh_status_t
CreateDirectory(const char *path
,
130 const struct fssh_stat
&st
, Directory
*&dir
) = 0;
131 virtual fssh_status_t
CreateSymLink(const char *path
, const char *linkTo
,
132 const struct fssh_stat
&st
, SymLink
*&link
) = 0;
134 virtual fssh_status_t
Unlink(const char *path
) = 0;
141 class HostNode
: public virtual Node
{
155 fs_close_attr_dir(fAttrDir
);
158 virtual fssh_status_t
Init(const char *path
, int fd
,
159 const struct fssh_stat
&st
)
164 // open the attribute directory
165 fAttrDir
= fs_fopen_attr_dir(fd
);
167 return fssh_get_errno();
172 virtual fssh_ssize_t
GetNextAttr(char *name
, int size
)
177 fssh_set_errno(FSSH_B_OK
);
178 struct dirent
*entry
= fs_read_attr_dir(fAttrDir
);
180 return fssh_get_errno();
182 int len
= strlen(entry
->d_name
);
184 return FSSH_B_NAME_TOO_LONG
;
186 strcpy(name
, entry
->d_name
);
190 virtual fssh_status_t
GetAttrInfo(const char *name
, fssh_attr_info
&info
)
193 if (fs_stat_attr(fFD
, name
, &hostInfo
) < 0)
194 return fssh_get_errno();
196 info
.type
= hostInfo
.type
;
197 info
.size
= hostInfo
.size
;
201 virtual fssh_ssize_t
ReadAttr(const char *name
, uint32_t type
,
202 fssh_off_t pos
, void *buffer
, int size
)
204 fssh_ssize_t bytesRead
= fs_read_attr(fFD
, name
, type
, pos
, buffer
,
206 return (bytesRead
>= 0 ? bytesRead
: fssh_get_errno());
209 virtual fssh_ssize_t
WriteAttr(const char *name
, uint32_t type
,
210 fssh_off_t pos
, const void *buffer
, int size
)
212 fssh_ssize_t bytesWritten
= fs_write_attr(fFD
, name
, type
, pos
, buffer
,
214 return (bytesWritten
>= 0 ? bytesWritten
: fssh_get_errno());
217 virtual fssh_status_t
RemoveAttr(const char *name
)
219 return (fs_remove_attr(fFD
, name
) == 0 ? 0 : fssh_get_errno());
228 class HostDirectory
: public Directory
, public HostNode
{
237 virtual ~HostDirectory()
243 virtual fssh_status_t
Init(const char *path
, int fd
,
244 const struct fssh_stat
&st
)
246 fssh_status_t error
= HostNode::Init(path
, fd
, st
);
247 if (error
!= FSSH_B_OK
)
250 fDir
= opendir(path
);
252 return fssh_get_errno();
257 virtual fssh_ssize_t
GetNextEntry(struct fssh_dirent
*entry
, int size
)
259 fssh_set_errno(FSSH_B_OK
);
260 struct dirent
*hostEntry
= readdir(fDir
);
262 return fssh_get_errno();
264 int nameLen
= strlen(hostEntry
->d_name
);
265 int recLen
= entry
->d_name
+ nameLen
+ 1 - (char*)entry
;
267 return FSSH_B_NAME_TOO_LONG
;
269 #if (defined(__BEOS__) || defined(__HAIKU__))
270 entry
->d_dev
= hostEntry
->d_dev
;
272 entry
->d_ino
= hostEntry
->d_ino
;
273 strcpy(entry
->d_name
, hostEntry
->d_name
);
274 entry
->d_reclen
= recLen
;
284 class HostFile
: public File
, public HostNode
{
296 virtual fssh_ssize_t
Read(void *buffer
, int size
)
298 fssh_ssize_t bytesRead
= read(fFD
, buffer
, size
);
299 return (bytesRead
>= 0 ? bytesRead
: fssh_get_errno());
302 virtual fssh_ssize_t
Write(const void *buffer
, int size
)
304 fssh_ssize_t bytesWritten
= write(fFD
, buffer
, size
);
305 return (bytesWritten
>= 0 ? bytesWritten
: fssh_get_errno());
310 class HostSymLink
: public SymLink
, public HostNode
{
319 virtual ~HostSymLink()
325 virtual fssh_status_t
Init(const char *path
, int fd
,
326 const struct fssh_stat
&st
)
328 fssh_status_t error
= HostNode::Init(path
, fd
, st
);
329 if (error
!= FSSH_B_OK
)
332 fPath
= strdup(path
);
334 return FSSH_B_NO_MEMORY
;
339 virtual fssh_ssize_t
ReadLink(char *buffer
, int bufferSize
)
341 fssh_ssize_t bytesRead
= readlink(fPath
, buffer
, bufferSize
);
342 return (bytesRead
>= 0 ? bytesRead
: fssh_get_errno());
350 class HostFSDomain
: public FSDomain
{
353 virtual ~HostFSDomain() {}
355 virtual fssh_status_t
Open(const char *path
, int openMode
, Node
*&_node
)
358 int fd
= fssh_open(path
, openMode
);
360 return fssh_get_errno();
364 if (fssh_fstat(fd
, &st
) < 0) {
366 return fssh_get_errno();
369 // check the node type and create the node
370 HostNode
*node
= NULL
;
371 switch (st
.fssh_st_mode
& FSSH_S_IFMT
) {
373 node
= new HostSymLink
;
379 node
= new HostDirectory
;
387 fssh_status_t error
= node
->Init(path
, fd
, st
);
388 // the node receives ownership of the FD
389 if (error
!= FSSH_B_OK
) {
398 virtual fssh_status_t
CreateFile(const char *path
,
399 const struct fssh_stat
&st
, File
*&_file
)
402 int fd
= fssh_creat(path
, st
.fssh_st_mode
& FSSH_S_IUMSK
);
404 return fssh_get_errno();
406 // apply the other stat fields
407 fssh_status_t error
= _ApplyStat(fd
, st
);
408 if (error
!= FSSH_B_OK
) {
414 HostFile
*file
= new HostFile
;
415 error
= file
->Init(path
, fd
, st
);
416 if (error
!= FSSH_B_OK
) {
425 virtual fssh_status_t
CreateDirectory(const char *path
,
426 const struct fssh_stat
&st
, Directory
*&_dir
)
429 if (fssh_mkdir(path
, st
.fssh_st_mode
& FSSH_S_IUMSK
) < 0)
430 return fssh_get_errno();
433 int fd
= fssh_open(path
, FSSH_O_RDONLY
| FSSH_O_NOTRAVERSE
);
435 return fssh_get_errno();
437 // apply the other stat fields
438 fssh_status_t error
= _ApplyStat(fd
, st
);
439 if (error
!= FSSH_B_OK
) {
445 HostDirectory
*dir
= new HostDirectory
;
446 error
= dir
->Init(path
, fd
, st
);
447 if (error
!= FSSH_B_OK
) {
456 virtual fssh_status_t
CreateSymLink(const char *path
, const char *linkTo
,
457 const struct fssh_stat
&st
, SymLink
*&_link
)
460 if (symlink(linkTo
, path
) < 0)
461 return fssh_get_errno();
463 // open the symlink node
464 int fd
= fssh_open(path
, FSSH_O_RDONLY
| FSSH_O_NOTRAVERSE
);
466 return fssh_get_errno();
468 // apply the other stat fields
469 fssh_status_t error
= _ApplyStat(fd
, st
);
470 if (error
!= FSSH_B_OK
) {
476 HostSymLink
*link
= new HostSymLink
;
477 error
= link
->Init(path
, fd
, st
);
478 if (error
!= FSSH_B_OK
) {
488 virtual fssh_status_t
Unlink(const char *path
)
490 if (fssh_unlink(path
) < 0)
491 return fssh_get_errno();
496 fssh_status_t
_ApplyStat(int fd
, const struct fssh_stat
&st
)
498 // TODO: Set times...
507 class GuestNode
: public virtual Node
{
521 _kern_close(fAttrDir
);
524 virtual fssh_status_t
Init(const char *path
, int fd
,
525 const struct fssh_stat
&st
)
530 // open the attribute directory
531 fAttrDir
= _kern_open_attr_dir(fd
, NULL
);
533 // TODO: check if the file system supports attributes, and fail
539 virtual fssh_ssize_t
GetNextAttr(char *name
, int size
)
544 char buffer
[sizeof(fssh_dirent
) + B_ATTR_NAME_LENGTH
];
545 struct fssh_dirent
*entry
= (fssh_dirent
*)buffer
;
546 int numRead
= _kern_read_dir(fAttrDir
, entry
, sizeof(buffer
), 1);
552 int len
= strlen(entry
->d_name
);
554 return FSSH_B_NAME_TOO_LONG
;
556 strcpy(name
, entry
->d_name
);
560 virtual fssh_status_t
GetAttrInfo(const char *name
, fssh_attr_info
&info
)
563 int attrFD
= _kern_open_attr(fFD
, name
, FSSH_O_RDONLY
);
569 fssh_status_t error
= _kern_read_stat(attrFD
, NULL
, false, &st
,
575 if (error
!= FSSH_B_OK
)
578 // convert stat to attr info
579 info
.type
= st
.fssh_st_type
;
580 info
.size
= st
.fssh_st_size
;
585 virtual fssh_ssize_t
ReadAttr(const char *name
, uint32_t type
,
586 fssh_off_t pos
, void *buffer
, int size
)
589 int attrFD
= _kern_open_attr(fFD
, name
, FSSH_O_RDONLY
);
594 fssh_ssize_t bytesRead
= _kern_read(attrFD
, pos
, buffer
, size
);
602 virtual fssh_ssize_t
WriteAttr(const char *name
, uint32_t type
,
603 fssh_off_t pos
, const void *buffer
, int size
)
606 int attrFD
= _kern_create_attr(fFD
, name
, type
, FSSH_O_WRONLY
);
611 fssh_ssize_t bytesWritten
= _kern_write(attrFD
, pos
, buffer
, size
);
619 virtual fssh_status_t
RemoveAttr(const char *name
)
621 return _kern_remove_attr(fFD
, name
);
630 class GuestDirectory
: public Directory
, public GuestNode
{
639 virtual ~GuestDirectory()
645 virtual fssh_status_t
Init(const char *path
, int fd
,
646 const struct fssh_stat
&st
)
648 fssh_status_t error
= GuestNode::Init(path
, fd
, st
);
649 if (error
!= FSSH_B_OK
)
652 fDir
= _kern_open_dir(fd
, NULL
);
659 virtual fssh_ssize_t
GetNextEntry(struct fssh_dirent
*entry
, int size
)
661 return _kern_read_dir(fDir
, entry
, size
, 1);
669 class GuestFile
: public File
, public GuestNode
{
681 virtual fssh_ssize_t
Read(void *buffer
, int size
)
683 return _kern_read(fFD
, -1, buffer
, size
);
686 virtual fssh_ssize_t
Write(const void *buffer
, int size
)
688 return _kern_write(fFD
, -1, buffer
, size
);
693 class GuestSymLink
: public SymLink
, public GuestNode
{
701 virtual ~GuestSymLink()
705 virtual fssh_ssize_t
ReadLink(char *buffer
, int _bufferSize
)
707 fssh_size_t bufferSize
= _bufferSize
;
708 fssh_status_t error
= _kern_read_link(fFD
, NULL
, buffer
, &bufferSize
);
709 return (error
== FSSH_B_OK
? bufferSize
: error
);
714 class GuestFSDomain
: public FSDomain
{
717 virtual ~GuestFSDomain() {}
719 virtual fssh_status_t
Open(const char *path
, int openMode
, Node
*&_node
)
722 int fd
= _kern_open(-1, path
, openMode
, 0);
728 fssh_status_t error
= _kern_read_stat(fd
, NULL
, false, &st
, sizeof(st
));
734 // check the node type and create the node
735 GuestNode
*node
= NULL
;
736 switch (st
.fssh_st_mode
& FSSH_S_IFMT
) {
738 node
= new GuestSymLink
;
741 node
= new GuestFile
;
744 node
= new GuestDirectory
;
752 error
= node
->Init(path
, fd
, st
);
753 // the node receives ownership of the FD
754 if (error
!= FSSH_B_OK
) {
763 virtual fssh_status_t
CreateFile(const char *path
,
764 const struct fssh_stat
&st
, File
*&_file
)
767 int fd
= _kern_open(-1, path
, FSSH_O_RDWR
| FSSH_O_EXCL
| FSSH_O_CREAT
,
768 st
.fssh_st_mode
& FSSH_S_IUMSK
);
772 // apply the other stat fields
773 fssh_status_t error
= _ApplyStat(fd
, st
);
774 if (error
!= FSSH_B_OK
) {
780 GuestFile
*file
= new GuestFile
;
781 error
= file
->Init(path
, fd
, st
);
782 if (error
!= FSSH_B_OK
) {
791 virtual fssh_status_t
CreateDirectory(const char *path
,
792 const struct fssh_stat
&st
, Directory
*&_dir
)
795 fssh_status_t error
= _kern_create_dir(-1, path
,
796 st
.fssh_st_mode
& FSSH_S_IUMSK
);
801 int fd
= _kern_open(-1, path
, FSSH_O_RDONLY
| FSSH_O_NOTRAVERSE
, 0);
805 // apply the other stat fields
806 error
= _ApplyStat(fd
, st
);
807 if (error
!= FSSH_B_OK
) {
813 GuestDirectory
*dir
= new GuestDirectory
;
814 error
= dir
->Init(path
, fd
, st
);
815 if (error
!= FSSH_B_OK
) {
824 virtual fssh_status_t
CreateSymLink(const char *path
, const char *linkTo
,
825 const struct fssh_stat
&st
, SymLink
*&_link
)
828 fssh_status_t error
= _kern_create_symlink(-1, path
, linkTo
,
829 st
.fssh_st_mode
& FSSH_S_IUMSK
);
833 // open the symlink node
834 int fd
= _kern_open(-1, path
, FSSH_O_RDONLY
| FSSH_O_NOTRAVERSE
, 0);
838 // apply the other stat fields
839 error
= _ApplyStat(fd
, st
);
840 if (error
!= FSSH_B_OK
) {
846 GuestSymLink
*link
= new GuestSymLink
;
847 error
= link
->Init(path
, fd
, st
);
848 if (error
!= FSSH_B_OK
) {
857 virtual fssh_status_t
Unlink(const char *path
)
859 return _kern_unlink(-1, path
);
863 fssh_status_t
_ApplyStat(int fd
, const struct fssh_stat
&st
)
865 // TODO: Set times...
873 static fssh_status_t
copy_entry(FSDomain
*sourceDomain
, const char *source
,
874 FSDomain
*targetDomain
, const char *target
, const Options
&options
,
878 get_file_domain(const char *target
, const char *&fsTarget
)
880 if (target
[0] == ':') {
881 fsTarget
= target
+ 1;
882 return new HostFSDomain
;
885 return new GuestFSDomain
;
889 typedef ObjectDeleter
<Node
> NodeDeleter
;
890 typedef ObjectDeleter
<FSDomain
> DomainDeleter
;
891 typedef MemoryDeleter PathDeleter
;
895 copy_file_contents(const char *source
, File
*sourceFile
, const char *target
,
898 fssh_off_t chunkSize
= (sourceFile
->Stat().fssh_st_size
/ 20) / sCopyBufferSize
* sCopyBufferSize
;
902 bool progress
= sourceFile
->Stat().fssh_st_size
> 1024 * 1024;
904 printf("%s ", strrchr(target
, '/') ? strrchr(target
, '/') + 1 : target
);
908 fssh_off_t total
= 0;
909 fssh_ssize_t bytesRead
;
910 while ((bytesRead
= sourceFile
->Read(sCopyBuffer
, sCopyBufferSize
)) > 0) {
911 fssh_ssize_t bytesWritten
= targetFile
->Write(sCopyBuffer
, bytesRead
);
912 if (progress
&& (total
% chunkSize
) == 0) {
916 if (bytesWritten
< 0) {
917 fprintf(stderr
, "Error while writing to file `%s': %s\n",
918 target
, fssh_strerror(bytesWritten
));
921 if (bytesWritten
!= bytesRead
) {
922 fprintf(stderr
, "Could not write all data to file \"%s\".\n",
924 return FSSH_B_IO_ERROR
;
926 total
+= bytesWritten
;
930 fprintf(stderr
, "Error while reading from file `%s': %s\n",
931 source
, fssh_strerror(bytesRead
));
943 copy_dir_contents(FSDomain
*sourceDomain
, const char *source
,
944 Directory
*sourceDir
, FSDomain
*targetDomain
, const char *target
,
945 const Options
&options
)
947 char buffer
[sizeof(fssh_dirent
) + FSSH_B_FILE_NAME_LENGTH
];
948 struct fssh_dirent
*entry
= (struct fssh_dirent
*)buffer
;
949 fssh_ssize_t numRead
;
950 while ((numRead
= sourceDir
->GetNextEntry(entry
, sizeof(buffer
))) > 0) {
952 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
955 // compose a new source path name
956 char *sourceEntry
= make_path(source
, entry
->d_name
);
958 fprintf(stderr
, "Error: Failed to allocate source path!\n");
961 PathDeleter
sourceDeleter(sourceEntry
);
963 // compose a new target path name
964 char *targetEntry
= make_path(target
, entry
->d_name
);
966 fprintf(stderr
, "Error: Failed to allocate target path!\n");
969 PathDeleter
targetDeleter(targetEntry
);
971 fssh_status_t error
= copy_entry(sourceDomain
, sourceEntry
,
972 targetDomain
, targetEntry
, options
, options
.alwaysDereference
);
973 if (error
!= FSSH_B_OK
)
978 fprintf(stderr
, "Error reading directory `%s': %s\n", source
,
979 fssh_strerror(numRead
));
988 copy_attribute(const char *source
, Node
*sourceNode
, const char *target
,
989 Node
*targetNode
, const char *name
, const fssh_attr_info
&info
)
991 // remove the attribute first
992 targetNode
->RemoveAttr(name
);
994 // special case: empty attribute
995 if (info
.size
<= 0) {
996 fssh_ssize_t bytesWritten
= targetNode
->WriteAttr(name
, info
.type
, 0,
999 fprintf(stderr
, "Error while writing to attribute `%s' of file "
1000 "`%s': %s\n", name
, target
, fssh_strerror(bytesWritten
));
1001 return bytesWritten
;
1007 // non-empty attribute
1009 int toCopy
= info
.size
;
1010 while (toCopy
> 0) {
1011 // read data from source
1012 int toRead
= (toCopy
< sCopyBufferSize
? toCopy
: sCopyBufferSize
);
1013 fssh_ssize_t bytesRead
= sourceNode
->ReadAttr(name
, info
.type
, pos
,
1014 sCopyBuffer
, toRead
);
1015 if (bytesRead
< 0) {
1016 fprintf(stderr
, "Error while reading from attribute `%s' of file "
1017 "`%s': %s\n", name
, source
, fssh_strerror(bytesRead
));
1021 // write data to target
1022 fssh_ssize_t bytesWritten
= targetNode
->WriteAttr(name
, info
.type
, pos
,
1023 sCopyBuffer
, bytesRead
);
1024 if (bytesWritten
< 0) {
1025 fprintf(stderr
, "Error while writing to attribute `%s' of file "
1026 "`%s': %s\n", name
, target
, fssh_strerror(bytesWritten
));
1027 return bytesWritten
;
1031 toCopy
-= bytesRead
;
1038 static fssh_status_t
1039 copy_attributes(const char *source
, Node
*sourceNode
, const char *target
,
1042 char name
[B_ATTR_NAME_LENGTH
];
1043 fssh_ssize_t numRead
;
1044 while ((numRead
= sourceNode
->GetNextAttr(name
, sizeof(name
))) > 0) {
1045 fssh_attr_info info
;
1046 // get attribute info
1047 fssh_status_t error
= sourceNode
->GetAttrInfo(name
, info
);
1048 if (error
!= FSSH_B_OK
) {
1049 fprintf(stderr
, "Error getting info for attribute `%s' of file "
1050 "`%s': %s\n", name
, source
, fssh_strerror(error
));
1054 // copy the attribute
1055 error
= copy_attribute(source
, sourceNode
, target
, targetNode
, name
,
1057 if (error
!= FSSH_B_OK
)
1062 fprintf(stderr
, "Error reading attribute directory of `%s': %s\n",
1063 source
, fssh_strerror(numRead
));
1071 static fssh_status_t
1072 copy_entry(FSDomain
*sourceDomain
, const char *source
,
1073 FSDomain
*targetDomain
, const char *target
, const Options
&options
,
1076 // apply entry filter
1077 if (!options
.entryFilter
.Filter(source
))
1080 // open the source node
1082 fssh_status_t error
= sourceDomain
->Open(source
,
1083 FSSH_O_RDONLY
| (dereference
? 0 : FSSH_O_NOTRAVERSE
),
1085 if (error
!= FSSH_B_OK
) {
1086 fprintf(stderr
, "Error: Failed to open source path `%s': %s\n", source
,
1087 fssh_strerror(error
));
1090 NodeDeleter
sourceDeleter(sourceNode
);
1092 // check, if target exists
1093 Node
*targetNode
= NULL
;
1094 // try opening with resolving symlinks first
1095 error
= targetDomain
->Open(target
, FSSH_O_RDONLY
| FSSH_O_NOTRAVERSE
,
1097 NodeDeleter targetDeleter
;
1098 if (error
== FSSH_B_OK
) {
1099 // 1. target exists:
1100 // check, if it is a dir and, if so, whether source is a dir too
1101 targetDeleter
.SetTo(targetNode
);
1103 // if the target is a symlink, try resolving it
1104 if (targetNode
->IsSymLink()) {
1105 Node
*resolvedTargetNode
;
1106 error
= targetDomain
->Open(target
, FSSH_O_RDONLY
,
1107 resolvedTargetNode
);
1108 if (error
== FSSH_B_OK
) {
1109 targetNode
= resolvedTargetNode
;
1110 targetDeleter
.SetTo(targetNode
);
1114 if (sourceNode
->IsDirectory() && targetNode
->IsDirectory()) {
1115 // 1.1. target and source are dirs:
1116 // -> just copy their contents
1119 // 1.2. source and/or target are no dirs
1121 if (options
.force
) {
1123 // -> remove the target and continue with 2.
1124 targetDeleter
.Delete();
1126 error
= targetDomain
->Unlink(target
);
1127 if (error
!= FSSH_B_OK
) {
1128 fprintf(stderr
, "Error: Failed to remove `%s'\n", target
);
1131 } else if (sourceNode
->IsFile() && targetNode
->IsFile()) {
1132 // 1.2.1.1. !/force/, but both source and target are files
1133 // -> truncate the target file and continue
1134 targetDeleter
.Delete();
1136 error
= targetDomain
->Open(target
, FSSH_O_RDWR
| FSSH_O_TRUNC
,
1138 if (error
!= FSSH_B_OK
) {
1139 fprintf(stderr
, "Error: Failed to open `%s' for writing\n",
1144 // 1.2.1.2. !/force/, source or target isn't a file
1146 fprintf(stderr
, "Error: File `%s' does exist.\n", target
);
1147 return FSSH_B_FILE_EXISTS
;
1150 } // else: 2. target doesn't exist: -> just create it
1152 // create the target node
1154 if (sourceNode
->IsFile()) {
1157 error
= targetDomain
->CreateFile(target
, sourceNode
->Stat(), file
);
1161 } else if (sourceNode
->IsDirectory()) {
1162 // check /recursive/
1163 if (!options
.recursive
) {
1164 fprintf(stderr
, "Error: Entry `%s' is a directory.\n", source
);
1168 // create the target only, if it doesn't already exist
1170 Directory
*dir
= NULL
;
1171 error
= targetDomain
->CreateDirectory(target
, sourceNode
->Stat(),
1176 } else if (sourceNode
->IsSymLink()) {
1177 // read the source link
1178 SymLink
*sourceLink
= sourceNode
->ToSymLink();
1179 char linkTo
[FSSH_B_PATH_NAME_LENGTH
];
1180 fssh_ssize_t bytesRead
= sourceLink
->ReadLink(linkTo
,
1181 sizeof(linkTo
) - 1);
1182 if (bytesRead
< 0) {
1183 fprintf(stderr
, "Error: Failed to read symlink `%s': %s\n", source
,
1184 fssh_strerror(bytesRead
));
1186 linkTo
[bytesRead
] = '\0'; // always NULL-terminate
1188 // create the target link
1190 error
= targetDomain
->CreateSymLink(target
, linkTo
,
1191 sourceNode
->Stat(), link
);
1195 fprintf(stderr
, "Error: Unknown node type. We shouldn't be here!\n");
1199 if (error
!= FSSH_B_OK
) {
1200 fprintf(stderr
, "Error: Failed to create `%s': %s\n", target
,
1201 fssh_strerror(error
));
1204 targetDeleter
.SetTo(targetNode
);
1207 if (!options
.ignoreAttributes
) {
1208 error
= copy_attributes(source
, sourceNode
, target
, targetNode
);
1209 if (error
!= FSSH_B_OK
)
1214 if (sourceNode
->IsFile()) {
1215 error
= copy_file_contents(source
, sourceNode
->ToFile(), target
,
1216 targetNode
->ToFile());
1217 } else if (sourceNode
->IsDirectory()) {
1218 error
= copy_dir_contents(sourceDomain
, source
,
1219 sourceNode
->ToDirectory(), targetDomain
, target
, options
);
1227 command_cp(int argc
, const char* const* argv
)
1229 int sourceCount
= 0;
1232 const char **sources
= new const char*[argc
];
1234 fprintf(stderr
, "Error: No memory!\n");
1237 ArrayDeleter
<const char*> _(sources
);
1240 for (int argi
= 1; argi
< argc
; argi
++) {
1241 const char *arg
= argv
[argi
];
1242 if (arg
[0] == '-') {
1243 if (arg
[1] == '\0') {
1244 fprintf(stderr
, "Error: Invalid option '-'\n");
1248 if (arg
[1] == '-') {
1249 if (strcmp(arg
, "--ignore-attributes") == 0) {
1250 options
.ignoreAttributes
= true;
1252 fprintf(stderr
, "Error: Unknown option '%s'\n", arg
);
1256 for (int i
= 1; arg
[i
]; i
++) {
1259 options
.attributesOnly
= true;
1262 options
.dereference
= false;
1265 options
.force
= true;
1268 options
.dereference
= true;
1269 options
.alwaysDereference
= true;
1272 options
.recursive
= true;
1277 const char* pattern
;
1278 if (arg
[i
+ 1] == '\0') {
1279 if (++argi
>= argc
) {
1280 fprintf(stderr
, "Error: Option '-%c' need "
1281 "a pattern as parameter\n", arg
[i
]);
1284 pattern
= argv
[argi
];
1286 pattern
= arg
+ i
+ 1;
1288 options
.entryFilter
.AddExcludeFilter(pattern
,
1293 fprintf(stderr
, "Error: Unknown option '-%c'\n",
1300 sources
[sourceCount
++] = arg
;
1305 if (sourceCount
< 2) {
1306 fprintf(stderr
, "Error: Must specify at least 2 files!\n");
1311 const char *target
= sources
[--sourceCount
];
1312 bool targetIsDir
= false;
1313 bool targetExists
= false;
1314 FSDomain
*targetDomain
= get_file_domain(target
, target
);
1315 DomainDeleter
targetDomainDeleter(targetDomain
);
1318 fssh_status_t error
= targetDomain
->Open(target
, FSSH_O_RDONLY
, targetNode
);
1320 NodeDeleter
targetDeleter(targetNode
);
1321 targetExists
= true;
1323 if (options
.attributesOnly
) {
1324 // That's how it should be; we don't care whether the target is
1325 // a directory or not. We append the attributes to that node in
1327 } else if (targetNode
->IsDirectory()) {
1330 if (sourceCount
> 1) {
1331 fprintf(stderr
, "Error: Destination `%s' is not a directory!",
1333 return FSSH_B_NOT_A_DIRECTORY
;
1337 if (options
.attributesOnly
) {
1338 fprintf(stderr
, "Error: Failed to open target `%s' (it must exist "
1339 "in attributes only mode): `%s'\n", target
,
1340 fssh_strerror(error
));
1342 } else if (sourceCount
> 1) {
1343 fprintf(stderr
, "Error: Failed to open destination directory `%s':"
1344 " `%s'\n", target
, fssh_strerror(error
));
1349 // allocate a copy buffer
1350 sCopyBuffer
= malloc(sCopyBufferSize
);
1352 fprintf(stderr
, "Error: Failed to allocate copy buffer.\n");
1355 MemoryDeleter
copyBufferDeleter(sCopyBuffer
);
1357 // open the target node for attributes only mode
1358 NodeDeleter targetDeleter
;
1359 if (options
.attributesOnly
) {
1360 error
= targetDomain
->Open(target
, FSSH_O_RDONLY
, targetNode
);
1361 if (error
!= FSSH_B_OK
) {
1362 fprintf(stderr
, "Error: Failed to open target `%s' for writing: "
1363 "`%s'\n", target
, fssh_strerror(error
));
1367 targetDeleter
.SetTo(targetNode
);
1371 for (int i
= 0; i
< sourceCount
; i
++) {
1372 const char *source
= sources
[i
];
1373 FSDomain
*sourceDomain
= get_file_domain(source
, source
);
1374 DomainDeleter
sourceDomainDeleter(sourceDomain
);
1375 if (options
.attributesOnly
) {
1376 // 0. copy attributes only
1377 // open the source node
1379 error
= sourceDomain
->Open(source
,
1380 FSSH_O_RDONLY
| (options
.dereference
? 0 : FSSH_O_NOTRAVERSE
),
1382 if (error
!= FSSH_B_OK
) {
1383 fprintf(stderr
, "Error: Failed to open `%s': %s.\n", source
,
1384 fssh_strerror(error
));
1387 NodeDeleter
sourceDeleter(sourceNode
);
1389 // copy the attributes
1390 error
= copy_attributes(source
, sourceNode
, target
, targetNode
);
1392 } else if (targetExists
&& targetIsDir
) {
1393 // 1. target exists:
1394 // 1.1. target is a dir:
1395 // get the source leaf name
1396 char leafName
[FSSH_B_FILE_NAME_LENGTH
];
1397 error
= get_last_path_component(source
, leafName
, sizeof(leafName
));
1398 if (error
!= FSSH_B_OK
) {
1399 fprintf(stderr
, "Error: Failed to get last path component of "
1400 "`%s': %s\n", source
, fssh_strerror(error
));
1404 if (strcmp(leafName
, ".") == 0 || strcmp(leafName
, "..") == 0) {
1405 // 1.1.1. source name is `.' or `..'
1406 // -> copy the contents only
1407 // (copy_dir_contents())
1408 // open the source dir
1410 error
= sourceDomain
->Open(source
,
1412 | (options
.dereference
? 0 : FSSH_O_NOTRAVERSE
),
1414 if (error
!= FSSH_B_OK
) {
1415 fprintf(stderr
, "Error: Failed to open `%s': %s.\n", source
,
1416 fssh_strerror(error
));
1419 NodeDeleter
sourceDeleter(sourceNode
);
1421 // check, if it is a dir
1422 Directory
*sourceDir
= sourceNode
->ToDirectory();
1424 fprintf(stderr
, "Error: Source `%s' is not a directory "
1425 "although it's last path component is `%s'\n", source
,
1430 error
= copy_dir_contents(sourceDomain
, source
, sourceDir
,
1431 targetDomain
, target
, options
);
1433 // 1.1.2. source has normal name
1434 // -> we copy into the dir
1435 // (copy_entry(<source>, <target>/<source leaf>))
1436 // compose a new target path name
1437 char *targetEntry
= make_path(target
, leafName
);
1439 fprintf(stderr
, "Error: Failed to allocate target path!\n");
1442 PathDeleter
targetDeleter(targetEntry
);
1444 error
= copy_entry(sourceDomain
, source
, targetDomain
,
1445 targetEntry
, options
, options
.dereference
);
1448 // 1.2. target is no dir:
1449 // -> if /force/ is given, we replace the target, otherwise
1451 // (copy_entry(<source>, <target>))
1453 // 2. target doesn't exist:
1454 // -> we create the target as a clone of the source
1455 // (copy_entry(<source>, <target>))
1456 error
= copy_entry(sourceDomain
, source
, targetDomain
, target
,
1457 options
, options
.dereference
);
1468 } // namespace FSShell