xref: /haiku/src/tools/fs_shell/command_cp.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "compatibility.h"
7 
8 #include "command_cp.h"
9 
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 
15 #include <AutoDeleter.h>
16 #include <EntryFilter.h>
17 #include <fs_attr.h>
18 #include <StorageDefs.h>
19 
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"
30 #include "syscalls.h"
31 
32 
33 using BPrivate::EntryFilter;
34 
35 
36 namespace FSShell {
37 
38 
39 static void *sCopyBuffer = NULL;
40 static const int sCopyBufferSize = 64 * 1024;	// 64 KB
41 
42 struct Options {
43 	Options()
44 		: entryFilter(),
45 		  attributesOnly(false),
46 		  ignoreAttributes(false),
47 		  dereference(true),
48 		  alwaysDereference(false),
49 		  force(false),
50 		  recursive(false)
51 	{
52 	}
53 
54 	EntryFilter	entryFilter;
55 	bool		attributesOnly;
56 	bool		ignoreAttributes;
57 	bool		dereference;
58 	bool		alwaysDereference;
59 	bool		force;
60 	bool		recursive;
61 };
62 
63 class Directory;
64 class File;
65 class SymLink;
66 
67 // Node
68 class Node {
69 public:
70 	Node() {}
71 	virtual ~Node() {}
72 
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); }
77 
78 	virtual File *ToFile()				{ return NULL; }
79 	virtual Directory *ToDirectory()	{ return NULL; }
80 	virtual SymLink *ToSymLink()		{ return NULL; }
81 
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;
90 
91 protected:
92 	struct fssh_stat	fStat;	// To be initialized by implementing classes.
93 };
94 
95 // Directory
96 class Directory : public virtual Node {
97 public:
98 	virtual Directory *ToDirectory()	{ return this; }
99 
100 	virtual	fssh_ssize_t GetNextEntry(struct fssh_dirent *entry, int size) = 0;
101 };
102 
103 // File
104 class File : public virtual Node {
105 public:
106 	virtual File *ToFile()				{ return this; }
107 
108 	virtual fssh_ssize_t Read(void *buffer, int size) = 0;
109 	virtual fssh_ssize_t Write(const void *buffer, int size) = 0;
110 };
111 
112 // SymLink
113 class SymLink : public virtual Node {
114 public:
115 	virtual SymLink *ToSymLink()		{ return this; }
116 
117 	virtual fssh_ssize_t ReadLink(char *buffer, int bufferSize) = 0;
118 };
119 
120 // FSDomain
121 class FSDomain {
122 public:
123 	virtual ~FSDomain()	{}
124 
125 	virtual fssh_status_t Open(const char *path, int openMode, Node *&node) = 0;
126 
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;
133 
134 	virtual fssh_status_t Unlink(const char *path) = 0;
135 };
136 
137 
138 // #pragma mark -
139 
140 // HostNode
141 class HostNode : public virtual Node {
142 public:
143 	HostNode()
144 		: Node(),
145 		  fFD(-1),
146 		  fAttrDir(NULL)
147 	{
148 	}
149 
150 	virtual ~HostNode()
151 	{
152 		if (fFD >= 0)
153 			fssh_close(fFD);
154 		if (fAttrDir)
155 			fs_close_attr_dir(fAttrDir);
156 	}
157 
158 	virtual fssh_status_t Init(const char *path, int fd,
159 		const struct fssh_stat &st)
160 	{
161 		fFD = fd;
162 		fStat = st;
163 
164 		// open the attribute directory
165 		fAttrDir = fs_fopen_attr_dir(fd);
166 		if (!fAttrDir)
167 			return fssh_get_errno();
168 
169 		return FSSH_B_OK;
170 	}
171 
172 	virtual	fssh_ssize_t GetNextAttr(char *name, int size)
173 	{
174 		if (!fAttrDir)
175 			return 0;
176 
177 		fssh_set_errno(FSSH_B_OK);
178 		struct dirent *entry = fs_read_attr_dir(fAttrDir);
179 		if (!entry)
180 			return fssh_get_errno();
181 
182 		int len = strlen(entry->d_name);
183 		if (len >= size)
184 			return FSSH_B_NAME_TOO_LONG;
185 
186 		strcpy(name, entry->d_name);
187 		return 1;
188 	}
189 
190 	virtual fssh_status_t GetAttrInfo(const char *name, fssh_attr_info &info)
191 	{
192 		attr_info hostInfo;
193 		if (fs_stat_attr(fFD, name, &hostInfo) < 0)
194 			return fssh_get_errno();
195 
196 		info.type = hostInfo.type;
197 		info.size = hostInfo.size;
198 		return FSSH_B_OK;
199 	}
200 
201 	virtual fssh_ssize_t ReadAttr(const char *name, uint32_t type,
202 		fssh_off_t pos, void *buffer, int size)
203 	{
204 		fssh_ssize_t bytesRead = fs_read_attr(fFD, name, type, pos, buffer,
205 			size);
206 		return (bytesRead >= 0 ? bytesRead : fssh_get_errno());
207 	}
208 
209 	virtual fssh_ssize_t WriteAttr(const char *name, uint32_t type,
210 		fssh_off_t pos, const void *buffer, int size)
211 	{
212 		fssh_ssize_t bytesWritten = fs_write_attr(fFD, name, type, pos, buffer,
213 			size);
214 		return (bytesWritten >= 0 ? bytesWritten : fssh_get_errno());
215 	}
216 
217 	virtual fssh_status_t RemoveAttr(const char *name)
218 	{
219 		return (fs_remove_attr(fFD, name) == 0 ? 0 : fssh_get_errno());
220 	}
221 
222 protected:
223 	int				fFD;
224 	DIR				*fAttrDir;
225 };
226 
227 // HostDirectory
228 class HostDirectory : public Directory, public HostNode {
229 public:
230 	HostDirectory()
231 		: Directory(),
232 		  HostNode(),
233 		  fDir(NULL)
234 	{
235 	}
236 
237 	virtual ~HostDirectory()
238 	{
239 		if (fDir)
240 			closedir(fDir);
241 	}
242 
243 	virtual fssh_status_t Init(const char *path, int fd,
244 		const struct fssh_stat &st)
245 	{
246 		fssh_status_t error = HostNode::Init(path, fd, st);
247 		if (error != FSSH_B_OK)
248 			return error;
249 
250 		fDir = opendir(path);
251 		if (!fDir)
252 			return fssh_get_errno();
253 
254 		return FSSH_B_OK;
255 	}
256 
257 	virtual	fssh_ssize_t GetNextEntry(struct fssh_dirent *entry, int size)
258 	{
259 		fssh_set_errno(FSSH_B_OK);
260 		struct dirent *hostEntry = readdir(fDir);
261 		if (!hostEntry)
262 			return fssh_get_errno();
263 
264 		int nameLen = strlen(hostEntry->d_name);
265 		int recLen = entry->d_name + nameLen + 1 - (char*)entry;
266 		if (recLen > size)
267 			return FSSH_B_NAME_TOO_LONG;
268 
269 		#if (defined(__BEOS__) || defined(__HAIKU__))
270 			entry->d_dev = hostEntry->d_dev;
271 		#endif
272 		entry->d_ino = hostEntry->d_ino;
273 		strcpy(entry->d_name, hostEntry->d_name);
274 		entry->d_reclen = recLen;
275 
276 		return 1;
277 	}
278 
279 private:
280 	DIR	*fDir;
281 };
282 
283 // HostFile
284 class HostFile : public File, public HostNode {
285 public:
286 	HostFile()
287 		: File(),
288 		  HostNode()
289 	{
290 	}
291 
292 	virtual ~HostFile()
293 	{
294 	}
295 
296 	virtual fssh_ssize_t Read(void *buffer, int size)
297 	{
298 		fssh_ssize_t bytesRead = read(fFD, buffer, size);
299 		return (bytesRead >= 0 ? bytesRead : fssh_get_errno());
300 	}
301 
302 	virtual fssh_ssize_t Write(const void *buffer, int size)
303 	{
304 		fssh_ssize_t bytesWritten = write(fFD, buffer, size);
305 		return (bytesWritten >= 0 ? bytesWritten : fssh_get_errno());
306 	}
307 };
308 
309 // HostSymLink
310 class HostSymLink : public SymLink, public HostNode {
311 public:
312 	HostSymLink()
313 		: SymLink(),
314 		  HostNode(),
315 		  fPath(NULL)
316 	{
317 	}
318 
319 	virtual ~HostSymLink()
320 	{
321 		if (fPath)
322 			free(fPath);
323 	}
324 
325 	virtual fssh_status_t Init(const char *path, int fd,
326 		const struct fssh_stat &st)
327 	{
328 		fssh_status_t error = HostNode::Init(path, fd, st);
329 		if (error != FSSH_B_OK)
330 			return error;
331 
332 		fPath = strdup(path);
333 		if (!fPath)
334 			return FSSH_B_NO_MEMORY;
335 
336 		return FSSH_B_OK;
337 	}
338 
339 	virtual fssh_ssize_t ReadLink(char *buffer, int bufferSize)
340 	{
341 		fssh_ssize_t bytesRead = readlink(fPath, buffer, bufferSize);
342 		return (bytesRead >= 0 ? bytesRead : fssh_get_errno());
343 	}
344 
345 private:
346 	char	*fPath;
347 };
348 
349 // HostFSDomain
350 class HostFSDomain : public FSDomain {
351 public:
352 	HostFSDomain() {}
353 	virtual ~HostFSDomain() {}
354 
355 	virtual fssh_status_t Open(const char *path, int openMode, Node *&_node)
356 	{
357 		// open the node
358 		int fd = fssh_open(path, openMode);
359 		if (fd < 0)
360 			return fssh_get_errno();
361 
362 		// stat the node
363 		struct fssh_stat st;
364 		if (fssh_fstat(fd, &st) < 0) {
365 			fssh_close(fd);
366 			return fssh_get_errno();
367 		}
368 
369 		// check the node type and create the node
370 		HostNode *node = NULL;
371 		switch (st.fssh_st_mode & FSSH_S_IFMT) {
372 			case FSSH_S_IFLNK:
373 				node = new HostSymLink;
374 				break;
375 			case FSSH_S_IFREG:
376 				node = new HostFile;
377 				break;
378 			case FSSH_S_IFDIR:
379 				node = new HostDirectory;
380 				break;
381 			default:
382 				fssh_close(fd);
383 				return FSSH_EINVAL;
384 		}
385 
386 		// init the node
387 		fssh_status_t error = node->Init(path, fd, st);
388 			// the node receives ownership of the FD
389 		if (error != FSSH_B_OK) {
390 			delete node;
391 			return error;
392 		}
393 
394 		_node = node;
395 		return FSSH_B_OK;
396 	}
397 
398 	virtual fssh_status_t CreateFile(const char *path,
399 		const struct fssh_stat &st, File *&_file)
400 	{
401 		// create the file
402 		int fd = fssh_creat(path, st.fssh_st_mode & FSSH_S_IUMSK);
403 		if (fd < 0)
404 			return fssh_get_errno();
405 
406 		// apply the other stat fields
407 		fssh_status_t error = _ApplyStat(fd, st);
408 		if (error != FSSH_B_OK) {
409 			fssh_close(fd);
410 			return error;
411 		}
412 
413 		// create the object
414 		HostFile *file = new HostFile;
415 		error = file->Init(path, fd, st);
416 		if (error != FSSH_B_OK) {
417 			delete file;
418 			return error;
419 		}
420 
421 		_file = file;
422 		return FSSH_B_OK;
423 	}
424 
425 	virtual fssh_status_t CreateDirectory(const char *path,
426 		const struct fssh_stat &st, Directory *&_dir)
427 	{
428 		// create the dir
429 		if (fssh_mkdir(path, st.fssh_st_mode & FSSH_S_IUMSK) < 0)
430 			return fssh_get_errno();
431 
432 		// open the dir node
433 		int fd = fssh_open(path, FSSH_O_RDONLY | FSSH_O_NOTRAVERSE);
434 		if (fd < 0)
435 			return fssh_get_errno();
436 
437 		// apply the other stat fields
438 		fssh_status_t error = _ApplyStat(fd, st);
439 		if (error != FSSH_B_OK) {
440 			fssh_close(fd);
441 			return error;
442 		}
443 
444 		// create the object
445 		HostDirectory *dir = new HostDirectory;
446 		error = dir->Init(path, fd, st);
447 		if (error != FSSH_B_OK) {
448 			delete dir;
449 			return error;
450 		}
451 
452 		_dir = dir;
453 		return FSSH_B_OK;
454 	}
455 
456 	virtual fssh_status_t CreateSymLink(const char *path, const char *linkTo,
457 		const struct fssh_stat &st, SymLink *&_link)
458 	{
459 		// create the link
460 		if (symlink(linkTo, path) < 0)
461 			return fssh_get_errno();
462 
463 		// open the symlink node
464 		int fd = fssh_open(path, FSSH_O_RDONLY | FSSH_O_NOTRAVERSE);
465 		if (fd < 0)
466 			return fssh_get_errno();
467 
468 		// apply the other stat fields
469 		fssh_status_t error = _ApplyStat(fd, st);
470 		if (error != FSSH_B_OK) {
471 			fssh_close(fd);
472 			return error;
473 		}
474 
475 		// create the object
476 		HostSymLink *link = new HostSymLink;
477 		error = link->Init(path, fd, st);
478 		if (error != FSSH_B_OK) {
479 			delete link;
480 			return error;
481 		}
482 
483 		_link = link;
484 		return FSSH_B_OK;
485 	}
486 
487 
488 	virtual fssh_status_t Unlink(const char *path)
489 	{
490 		if (fssh_unlink(path) < 0)
491 			return fssh_get_errno();
492 		return FSSH_B_OK;
493 	}
494 
495 private:
496 	fssh_status_t _ApplyStat(int fd, const struct fssh_stat &st)
497 	{
498 		// TODO: Set times...
499 		return FSSH_B_OK;
500 	}
501 };
502 
503 
504 // #pragma mark -
505 
506 // GuestNode
507 class GuestNode : public virtual Node {
508 public:
509 	GuestNode()
510 		: Node(),
511 		  fFD(-1),
512 		  fAttrDir(-1)
513 	{
514 	}
515 
516 	virtual ~GuestNode()
517 	{
518 		if (fFD >= 0)
519 			_kern_close(fFD);
520 		if (fAttrDir)
521 			_kern_close(fAttrDir);
522 	}
523 
524 	virtual fssh_status_t Init(const char *path, int fd,
525 		const struct fssh_stat &st)
526 	{
527 		fFD = fd;
528 		fStat = st;
529 
530 		// open the attribute directory
531 		fAttrDir = _kern_open_attr_dir(fd, NULL);
532 		if (fAttrDir < 0) {
533 			// TODO: check if the file system supports attributes, and fail
534 		}
535 
536 		return FSSH_B_OK;
537 	}
538 
539 	virtual	fssh_ssize_t GetNextAttr(char *name, int size)
540 	{
541 		if (fAttrDir < 0)
542 			return 0;
543 
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);
547 		if (numRead < 0)
548 			return numRead;
549 		if (numRead == 0)
550 			return 0;
551 
552 		int len = strlen(entry->d_name);
553 		if (len >= size)
554 			return FSSH_B_NAME_TOO_LONG;
555 
556 		strcpy(name, entry->d_name);
557 		return 1;
558 	}
559 
560 	virtual fssh_status_t GetAttrInfo(const char *name, fssh_attr_info &info)
561 	{
562 		// open attr
563 		int attrFD = _kern_open_attr(fFD, name, FSSH_O_RDONLY);
564 		if (attrFD < 0)
565 			return attrFD;
566 
567 		// stat attr
568 		struct fssh_stat st;
569 		fssh_status_t error = _kern_read_stat(attrFD, NULL, false, &st,
570 			sizeof(st));
571 
572 		// close attr
573 		_kern_close(attrFD);
574 
575 		if (error != FSSH_B_OK)
576 			return error;
577 
578 		// convert stat to attr info
579 		info.type = st.fssh_st_type;
580 		info.size = st.fssh_st_size;
581 
582 		return FSSH_B_OK;
583 	}
584 
585 	virtual fssh_ssize_t ReadAttr(const char *name, uint32_t type,
586 		fssh_off_t pos, void *buffer, int size)
587 	{
588 		// open attr
589 		int attrFD = _kern_open_attr(fFD, name, FSSH_O_RDONLY);
590 		if (attrFD < 0)
591 			return attrFD;
592 
593 		// stat attr
594 		fssh_ssize_t bytesRead = _kern_read(attrFD, pos, buffer, size);
595 
596 		// close attr
597 		_kern_close(attrFD);
598 
599 		return bytesRead;
600 	}
601 
602 	virtual fssh_ssize_t WriteAttr(const char *name, uint32_t type,
603 		fssh_off_t pos, const void *buffer, int size)
604 	{
605 		// open attr
606 		int attrFD = _kern_create_attr(fFD, name, type, FSSH_O_WRONLY);
607 		if (attrFD < 0)
608 			return attrFD;
609 
610 		// stat attr
611 		fssh_ssize_t bytesWritten = _kern_write(attrFD, pos, buffer, size);
612 
613 		// close attr
614 		_kern_close(attrFD);
615 
616 		return bytesWritten;
617 	}
618 
619 	virtual fssh_status_t RemoveAttr(const char *name)
620 	{
621 		return _kern_remove_attr(fFD, name);
622 	}
623 
624 protected:
625 	int				fFD;
626 	int				fAttrDir;
627 };
628 
629 // GuestDirectory
630 class GuestDirectory : public Directory, public GuestNode {
631 public:
632 	GuestDirectory()
633 		: Directory(),
634 		  GuestNode(),
635 		  fDir(-1)
636 	{
637 	}
638 
639 	virtual ~GuestDirectory()
640 	{
641 		if (fDir)
642 			_kern_close(fDir);
643 	}
644 
645 	virtual fssh_status_t Init(const char *path, int fd,
646 		const struct fssh_stat &st)
647 	{
648 		fssh_status_t error = GuestNode::Init(path, fd, st);
649 		if (error != FSSH_B_OK)
650 			return error;
651 
652 		fDir = _kern_open_dir(fd, NULL);
653 		if (fDir < 0)
654 			return fDir;
655 
656 		return FSSH_B_OK;
657 	}
658 
659 	virtual	fssh_ssize_t GetNextEntry(struct fssh_dirent *entry, int size)
660 	{
661 		return _kern_read_dir(fDir, entry, size, 1);
662 	}
663 
664 private:
665 	int	fDir;
666 };
667 
668 // GuestFile
669 class GuestFile : public File, public GuestNode {
670 public:
671 	GuestFile()
672 		: File(),
673 		  GuestNode()
674 	{
675 	}
676 
677 	virtual ~GuestFile()
678 	{
679 	}
680 
681 	virtual fssh_ssize_t Read(void *buffer, int size)
682 	{
683 		return _kern_read(fFD, -1, buffer, size);
684 	}
685 
686 	virtual fssh_ssize_t Write(const void *buffer, int size)
687 	{
688 		return _kern_write(fFD, -1, buffer, size);
689 	}
690 };
691 
692 // GuestSymLink
693 class GuestSymLink : public SymLink, public GuestNode {
694 public:
695 	GuestSymLink()
696 		: SymLink(),
697 		  GuestNode()
698 	{
699 	}
700 
701 	virtual ~GuestSymLink()
702 	{
703 	}
704 
705 	virtual fssh_ssize_t ReadLink(char *buffer, int _bufferSize)
706 	{
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);
710 	}
711 };
712 
713 // GuestFSDomain
714 class GuestFSDomain : public FSDomain {
715 public:
716 	GuestFSDomain() {}
717 	virtual ~GuestFSDomain() {}
718 
719 	virtual fssh_status_t Open(const char *path, int openMode, Node *&_node)
720 	{
721 		// open the node
722 		int fd = _kern_open(-1, path, openMode, 0);
723 		if (fd < 0)
724 			return fd;
725 
726 		// stat the node
727 		struct fssh_stat st;
728 		fssh_status_t error = _kern_read_stat(fd, NULL, false, &st, sizeof(st));
729 		if (error < 0) {
730 			_kern_close(fd);
731 			return error;
732 		}
733 
734 		// check the node type and create the node
735 		GuestNode *node = NULL;
736 		switch (st.fssh_st_mode & FSSH_S_IFMT) {
737 			case FSSH_S_IFLNK:
738 				node = new GuestSymLink;
739 				break;
740 			case FSSH_S_IFREG:
741 				node = new GuestFile;
742 				break;
743 			case FSSH_S_IFDIR:
744 				node = new GuestDirectory;
745 				break;
746 			default:
747 				_kern_close(fd);
748 				return FSSH_EINVAL;
749 		}
750 
751 		// init the node
752 		error = node->Init(path, fd, st);
753 			// the node receives ownership of the FD
754 		if (error != FSSH_B_OK) {
755 			delete node;
756 			return error;
757 		}
758 
759 		_node = node;
760 		return FSSH_B_OK;
761 	}
762 
763 	virtual fssh_status_t CreateFile(const char *path,
764 		const struct fssh_stat &st, File *&_file)
765 	{
766 		// create the 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);
769 		if (fd < 0)
770 			return fd;
771 
772 		// apply the other stat fields
773 		fssh_status_t error = _ApplyStat(fd, st);
774 		if (error != FSSH_B_OK) {
775 			_kern_close(fd);
776 			return error;
777 		}
778 
779 		// create the object
780 		GuestFile *file = new GuestFile;
781 		error = file->Init(path, fd, st);
782 		if (error != FSSH_B_OK) {
783 			delete file;
784 			return error;
785 		}
786 
787 		_file = file;
788 		return FSSH_B_OK;
789 	}
790 
791 	virtual fssh_status_t CreateDirectory(const char *path,
792 		const struct fssh_stat &st, Directory *&_dir)
793 	{
794 		// create the dir
795 		fssh_status_t error = _kern_create_dir(-1, path,
796 			st.fssh_st_mode & FSSH_S_IUMSK);
797 		if (error < 0)
798 			return error;
799 
800 		// open the dir node
801 		int fd = _kern_open(-1, path, FSSH_O_RDONLY | FSSH_O_NOTRAVERSE, 0);
802 		if (fd < 0)
803 			return fd;
804 
805 		// apply the other stat fields
806 		error = _ApplyStat(fd, st);
807 		if (error != FSSH_B_OK) {
808 			_kern_close(fd);
809 			return error;
810 		}
811 
812 		// create the object
813 		GuestDirectory *dir = new GuestDirectory;
814 		error = dir->Init(path, fd, st);
815 		if (error != FSSH_B_OK) {
816 			delete dir;
817 			return error;
818 		}
819 
820 		_dir = dir;
821 		return FSSH_B_OK;
822 	}
823 
824 	virtual fssh_status_t CreateSymLink(const char *path, const char *linkTo,
825 		const struct fssh_stat &st, SymLink *&_link)
826 	{
827 		// create the link
828 		fssh_status_t error = _kern_create_symlink(-1, path, linkTo,
829 			st.fssh_st_mode & FSSH_S_IUMSK);
830 		if (error < 0)
831 			return error;
832 
833 		// open the symlink node
834 		int fd = _kern_open(-1, path, FSSH_O_RDONLY | FSSH_O_NOTRAVERSE, 0);
835 		if (fd < 0)
836 			return fd;
837 
838 		// apply the other stat fields
839 		error = _ApplyStat(fd, st);
840 		if (error != FSSH_B_OK) {
841 			_kern_close(fd);
842 			return error;
843 		}
844 
845 		// create the object
846 		GuestSymLink *link = new GuestSymLink;
847 		error = link->Init(path, fd, st);
848 		if (error != FSSH_B_OK) {
849 			delete link;
850 			return error;
851 		}
852 
853 		_link = link;
854 		return FSSH_B_OK;
855 	}
856 
857 	virtual fssh_status_t Unlink(const char *path)
858 	{
859 		return _kern_unlink(-1, path);
860 	}
861 
862 private:
863 	fssh_status_t _ApplyStat(int fd, const struct fssh_stat &st)
864 	{
865 		// TODO: Set times...
866 		return FSSH_B_OK;
867 	}
868 };
869 
870 
871 // #pragma mark -
872 
873 static fssh_status_t copy_entry(FSDomain *sourceDomain, const char *source,
874 	FSDomain *targetDomain, const char *target, const Options &options,
875 	bool dereference);
876 
877 static FSDomain *
878 get_file_domain(const char *target, const char *&fsTarget)
879 {
880 	if (target[0] == ':') {
881 		fsTarget = target + 1;
882 		return new HostFSDomain;
883 	} else {
884 		fsTarget = target;
885 		return new GuestFSDomain;
886 	}
887 }
888 
889 typedef ObjectDeleter<Node> NodeDeleter;
890 typedef ObjectDeleter<FSDomain> DomainDeleter;
891 typedef MemoryDeleter PathDeleter;
892 
893 
894 static fssh_status_t
895 copy_file_contents(const char *source, File *sourceFile, const char *target,
896 	File *targetFile)
897 {
898 	fssh_off_t chunkSize = (sourceFile->Stat().fssh_st_size / 20) / sCopyBufferSize * sCopyBufferSize;
899 	if (chunkSize == 0)
900 		chunkSize = 1;
901 
902 	bool progress = sourceFile->Stat().fssh_st_size > 1024 * 1024;
903 	if (progress) {
904 		printf("%s ", strrchr(target, '/') ? strrchr(target, '/') + 1 : target);
905 		fflush(stdout);
906 	}
907 
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) {
913 			putchar('.');
914 			fflush(stdout);
915 		}
916 		if (bytesWritten < 0) {
917 			fprintf(stderr, "Error while writing to file `%s': %s\n",
918 				target, fssh_strerror(bytesWritten));
919 			return bytesWritten;
920 		}
921 		if (bytesWritten != bytesRead) {
922 			fprintf(stderr, "Could not write all data to file \"%s\".\n",
923 				target);
924 			return FSSH_B_IO_ERROR;
925 		}
926 		total += bytesWritten;
927 	}
928 
929 	if (bytesRead < 0) {
930 		fprintf(stderr, "Error while reading from file `%s': %s\n",
931 			source, fssh_strerror(bytesRead));
932 		return bytesRead;
933 	}
934 
935 	if (progress)
936 		putchar('\n');
937 
938 	return FSSH_B_OK;
939 }
940 
941 
942 static fssh_status_t
943 copy_dir_contents(FSDomain *sourceDomain, const char *source,
944 	Directory *sourceDir, FSDomain *targetDomain, const char *target,
945 	const Options &options)
946 {
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) {
951 		// skip "." and ".."
952 		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
953 			continue;
954 
955 		// compose a new source path name
956 		char *sourceEntry = make_path(source, entry->d_name);
957 		if (!sourceEntry) {
958 			fprintf(stderr, "Error: Failed to allocate source path!\n");
959 			return FSSH_ENOMEM;
960 		}
961 		PathDeleter sourceDeleter(sourceEntry);
962 
963 		// compose a new target path name
964 		char *targetEntry = make_path(target, entry->d_name);
965 		if (!targetEntry) {
966 			fprintf(stderr, "Error: Failed to allocate target path!\n");
967 			return FSSH_ENOMEM;
968 		}
969 		PathDeleter targetDeleter(targetEntry);
970 
971 		fssh_status_t error = copy_entry(sourceDomain, sourceEntry,
972 			targetDomain, targetEntry, options, options.alwaysDereference);
973 		if (error != FSSH_B_OK)
974 			return error;
975 	}
976 
977 	if (numRead < 0) {
978 		fprintf(stderr, "Error reading directory `%s': %s\n", source,
979 			fssh_strerror(numRead));
980 		return numRead;
981 	}
982 
983 	return FSSH_B_OK;
984 }
985 
986 
987 static fssh_status_t
988 copy_attribute(const char *source, Node *sourceNode, const char *target,
989 	Node *targetNode, const char *name, const fssh_attr_info &info)
990 {
991 	// remove the attribute first
992 	targetNode->RemoveAttr(name);
993 
994 	// special case: empty attribute
995 	if (info.size <= 0) {
996 		fssh_ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, 0,
997 			sCopyBuffer, 0);
998 		if (bytesWritten) {
999 			fprintf(stderr, "Error while writing to attribute `%s' of file "
1000 				"`%s': %s\n", name, target, fssh_strerror(bytesWritten));
1001 			return bytesWritten;
1002 		}
1003 
1004 		return FSSH_B_OK;
1005 	}
1006 
1007 	// non-empty attribute
1008 	fssh_off_t pos = 0;
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));
1018 			return bytesRead;
1019 		}
1020 
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;
1028 		}
1029 
1030 		pos += bytesRead;
1031 		toCopy -= bytesRead;
1032 	}
1033 
1034 	return FSSH_B_OK;
1035 }
1036 
1037 
1038 static fssh_status_t
1039 copy_attributes(const char *source, Node *sourceNode, const char *target,
1040 	Node *targetNode)
1041 {
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));
1051 			return error;
1052 		}
1053 
1054 		// copy the attribute
1055 		error = copy_attribute(source, sourceNode, target, targetNode, name,
1056 			info);
1057 		if (error != FSSH_B_OK)
1058 			return error;
1059 	}
1060 
1061 	if (numRead < 0) {
1062 		fprintf(stderr, "Error reading attribute directory of `%s': %s\n",
1063 			source, fssh_strerror(numRead));
1064 		return numRead;
1065 	}
1066 
1067 	return FSSH_B_OK;
1068 }
1069 
1070 
1071 static fssh_status_t
1072 copy_entry(FSDomain *sourceDomain, const char *source,
1073 	FSDomain *targetDomain, const char *target, const Options &options,
1074 	bool dereference)
1075 {
1076 	// apply entry filter
1077 	if (!options.entryFilter.Filter(source))
1078 		return FSSH_B_OK;
1079 
1080 	// open the source node
1081 	Node *sourceNode;
1082 	fssh_status_t error = sourceDomain->Open(source,
1083 		FSSH_O_RDONLY | (dereference ? 0 : FSSH_O_NOTRAVERSE),
1084 		sourceNode);
1085 	if (error != FSSH_B_OK) {
1086 		fprintf(stderr, "Error: Failed to open source path `%s': %s\n", source,
1087 			fssh_strerror(error));
1088 		return error;
1089 	}
1090 	NodeDeleter sourceDeleter(sourceNode);
1091 
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,
1096 		targetNode);
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);
1102 
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);
1111 			}
1112 		}
1113 
1114 		if (sourceNode->IsDirectory() && targetNode->IsDirectory()) {
1115 			// 1.1. target and source are dirs:
1116 			//      -> just copy their contents
1117 			// ...
1118 		} else {
1119 			// 1.2. source and/or target are no dirs
1120 
1121 			if (options.force) {
1122 				// 1.2.1. /force/
1123 				//        -> remove the target and continue with 2.
1124 				targetDeleter.Delete();
1125 				targetNode = NULL;
1126 				error = targetDomain->Unlink(target);
1127 				if (error != FSSH_B_OK) {
1128 					fprintf(stderr, "Error: Failed to remove `%s'\n", target);
1129 					return error;
1130 				}
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();
1135 				targetNode = NULL;
1136 				error = targetDomain->Open(target, FSSH_O_RDWR | FSSH_O_TRUNC,
1137 					targetNode);
1138 				if (error != FSSH_B_OK) {
1139 					fprintf(stderr, "Error: Failed to open `%s' for writing\n",
1140 						target);
1141 					return error;
1142 				}
1143 			} else {
1144 				// 1.2.1.2. !/force/, source or target isn't a file
1145 				//          -> fail
1146 				fprintf(stderr, "Error: File `%s' does exist.\n", target);
1147 				return FSSH_B_FILE_EXISTS;
1148 			}
1149 		}
1150 	} // else: 2. target doesn't exist: -> just create it
1151 
1152 	// create the target node
1153 	error = FSSH_B_OK;
1154 	if (sourceNode->IsFile()) {
1155 		if (!targetNode) {
1156 			File *file = NULL;
1157 			error = targetDomain->CreateFile(target, sourceNode->Stat(), file);
1158 			if (error == 0) {
1159 				targetNode = file;
1160 				targetDeleter.SetTo(targetNode);
1161 			}
1162 		}
1163 	} else if (sourceNode->IsDirectory()) {
1164 		// check /recursive/
1165 		if (!options.recursive) {
1166 			fprintf(stderr, "Error: Entry `%s' is a directory.\n", source);
1167 			return FSSH_EISDIR;
1168 		}
1169 
1170 		// create the target only, if it doesn't already exist
1171 		if (!targetNode) {
1172 			Directory *dir = NULL;
1173 			error = targetDomain->CreateDirectory(target, sourceNode->Stat(),
1174 				dir);
1175 			if (error == 0) {
1176 				targetNode = dir;
1177 				targetDeleter.SetTo(targetNode);
1178 			}
1179 		}
1180 	} else if (sourceNode->IsSymLink()) {
1181 		// read the source link
1182 		SymLink *sourceLink = sourceNode->ToSymLink();
1183 		char linkTo[FSSH_B_PATH_NAME_LENGTH];
1184 		fssh_ssize_t bytesRead = sourceLink->ReadLink(linkTo,
1185 			sizeof(linkTo) - 1);
1186 		if (bytesRead < 0) {
1187 			fprintf(stderr, "Error: Failed to read symlink `%s': %s\n", source,
1188 				fssh_strerror(bytesRead));
1189 		}
1190 		linkTo[bytesRead] = '\0';	// always NULL-terminate
1191 
1192 		// create the target link
1193 		SymLink *link;
1194 		error = targetDomain->CreateSymLink(target, linkTo,
1195 			sourceNode->Stat(),	link);
1196 		if (error == 0) {
1197 			targetNode = link;
1198 			targetDeleter.SetTo(targetNode);
1199 		}
1200 	} else {
1201 		fprintf(stderr, "Error: Unknown node type. We shouldn't be here!\n");
1202 		return FSSH_EINVAL;
1203 	}
1204 
1205 	if (error != FSSH_B_OK) {
1206 		fprintf(stderr, "Error: Failed to create `%s': %s\n", target,
1207 			fssh_strerror(error));
1208 		return error;
1209 	}
1210 
1211 	// copy attributes
1212 	if (!options.ignoreAttributes) {
1213 		error = copy_attributes(source, sourceNode, target, targetNode);
1214 		if (error != FSSH_B_OK)
1215 			return error;
1216 	}
1217 
1218 	// copy contents
1219 	if (sourceNode->IsFile()) {
1220 		error = copy_file_contents(source, sourceNode->ToFile(), target,
1221 			targetNode->ToFile());
1222 	} else if (sourceNode->IsDirectory()) {
1223 		error = copy_dir_contents(sourceDomain, source,
1224 			sourceNode->ToDirectory(), targetDomain, target, options);
1225 	}
1226 
1227 	return error;
1228 }
1229 
1230 
1231 fssh_status_t
1232 command_cp(int argc, const char* const* argv)
1233 {
1234 	int sourceCount = 0;
1235 	Options options;
1236 
1237 	const char **sources = new const char*[argc];
1238 	if (!sources) {
1239 		fprintf(stderr, "Error: No memory!\n");
1240 		return FSSH_EINVAL;
1241 	}
1242 	ArrayDeleter<const char*> _(sources);
1243 
1244 	// parse parameters
1245 	for (int argi = 1; argi < argc; argi++) {
1246 		const char *arg = argv[argi];
1247 		if (arg[0] == '-') {
1248 			if (arg[1] == '\0') {
1249 				fprintf(stderr, "Error: Invalid option '-'\n");
1250 				return FSSH_EINVAL;
1251 			}
1252 
1253 			if (arg[1] == '-') {
1254 				if (strcmp(arg, "--ignore-attributes") == 0) {
1255 					options.ignoreAttributes = true;
1256 				} else {
1257 					fprintf(stderr, "Error: Unknown option '%s'\n", arg);
1258 					return FSSH_EINVAL;
1259 				}
1260 			} else {
1261 				for (int i = 1; arg[i]; i++) {
1262 					switch (arg[i]) {
1263 						case 'a':
1264 							options.attributesOnly = true;
1265 							break;
1266 						case 'd':
1267 							options.dereference = false;
1268 							break;
1269 						case 'f':
1270 							options.force = true;
1271 							break;
1272 						case 'L':
1273 							options.dereference = true;
1274 							options.alwaysDereference = true;
1275 							break;
1276 						case 'r':
1277 							options.recursive = true;
1278 							break;
1279 						case 'x':
1280 						case 'X':
1281 						{
1282 							const char* pattern;
1283 							if (arg[i + 1] == '\0') {
1284 								if (++argi >= argc) {
1285 									fprintf(stderr, "Error: Option '-%c' need "
1286 										"a pattern as parameter\n", arg[i]);
1287 									return FSSH_EINVAL;
1288 								}
1289 								pattern = argv[argi];
1290 							} else
1291 								pattern = arg + i + 1;
1292 
1293 							options.entryFilter.AddExcludeFilter(pattern,
1294 								arg[i] == 'x');
1295 							break;
1296 						}
1297 						default:
1298 							fprintf(stderr, "Error: Unknown option '-%c'\n",
1299 								arg[i]);
1300 							return FSSH_EINVAL;
1301 					}
1302 				}
1303 			}
1304 		} else {
1305 			sources[sourceCount++] = arg;
1306 		}
1307 	}
1308 
1309 	// check params
1310 	if (sourceCount < 2) {
1311 		fprintf(stderr, "Error: Must specify at least 2 files!\n");
1312 		return FSSH_EINVAL;
1313 	}
1314 
1315 	// check the target
1316 	const char *target = sources[--sourceCount];
1317 	bool targetIsDir = false;
1318 	bool targetExists = false;
1319 	FSDomain *targetDomain = get_file_domain(target, target);
1320 	DomainDeleter targetDomainDeleter(targetDomain);
1321 
1322 	Node *targetNode;
1323 	fssh_status_t error = targetDomain->Open(target, FSSH_O_RDONLY, targetNode);
1324 	if (error == 0) {
1325 		NodeDeleter targetDeleter(targetNode);
1326 		targetExists = true;
1327 
1328 		if (options.attributesOnly) {
1329 			// That's how it should be; we don't care whether the target is
1330 			// a directory or not. We append the attributes to that node in
1331 			// either case.
1332 		} else if (targetNode->IsDirectory()) {
1333 			targetIsDir = true;
1334 		} else {
1335 			if (sourceCount > 1) {
1336 				fprintf(stderr, "Error: Destination `%s' is not a directory!",
1337 					target);
1338 				return FSSH_B_NOT_A_DIRECTORY;
1339 			}
1340 		}
1341 	} else {
1342 		if (options.attributesOnly) {
1343 			fprintf(stderr, "Error: Failed to open target `%s' (it must exist "
1344 				"in attributes only mode): `%s'\n", target,
1345 				fssh_strerror(error));
1346 			return error;
1347 		} else if (sourceCount > 1) {
1348 			fprintf(stderr, "Error: Failed to open destination directory `%s':"
1349 				" `%s'\n", target, fssh_strerror(error));
1350 			return error;
1351 		}
1352 	}
1353 
1354 	// allocate a copy buffer
1355 	sCopyBuffer = malloc(sCopyBufferSize);
1356 	if (!sCopyBuffer) {
1357 		fprintf(stderr, "Error: Failed to allocate copy buffer.\n");
1358 		return FSSH_ENOMEM;
1359 	}
1360 	MemoryDeleter copyBufferDeleter(sCopyBuffer);
1361 
1362 	// open the target node for attributes only mode
1363 	NodeDeleter targetDeleter;
1364 	if (options.attributesOnly) {
1365 		error = targetDomain->Open(target, FSSH_O_RDONLY, targetNode);
1366 		if (error != FSSH_B_OK) {
1367 			fprintf(stderr, "Error: Failed to open target `%s' for writing: "
1368 				"`%s'\n", target, fssh_strerror(error));
1369 			return error;
1370 		}
1371 
1372 		targetDeleter.SetTo(targetNode);
1373 	}
1374 
1375 	// the copy loop
1376 	for (int i = 0; i < sourceCount; i++) {
1377 		const char *source = sources[i];
1378 		FSDomain *sourceDomain = get_file_domain(source, source);
1379 		DomainDeleter sourceDomainDeleter(sourceDomain);
1380 		if (options.attributesOnly) {
1381 			// 0. copy attributes only
1382 			// open the source node
1383 			Node *sourceNode;
1384 			error = sourceDomain->Open(source,
1385 				FSSH_O_RDONLY | (options.dereference ? 0 : FSSH_O_NOTRAVERSE),
1386 				sourceNode);
1387 			if (error != FSSH_B_OK) {
1388 				fprintf(stderr, "Error: Failed to open `%s': %s.\n", source,
1389 					fssh_strerror(error));
1390 				return error;
1391 			}
1392 			NodeDeleter sourceDeleter(sourceNode);
1393 
1394 			// copy the attributes
1395 			error = copy_attributes(source, sourceNode, target, targetNode);
1396 
1397 		} else if (targetExists && targetIsDir) {
1398 			// 1. target exists:
1399 			// 1.1. target is a dir:
1400 			// get the source leaf name
1401 			char leafName[FSSH_B_FILE_NAME_LENGTH];
1402 			error = get_last_path_component(source, leafName, sizeof(leafName));
1403 			if (error != FSSH_B_OK) {
1404 				fprintf(stderr, "Error: Failed to get last path component of "
1405 					"`%s': %s\n", source, fssh_strerror(error));
1406 				return error;
1407 			}
1408 
1409 			if (strcmp(leafName, ".") == 0 || strcmp(leafName, "..") == 0) {
1410 				// 1.1.1. source name is `.' or `..'
1411 				//        -> copy the contents only
1412 				//           (copy_dir_contents())
1413 				// open the source dir
1414 				Node *sourceNode;
1415 				error = sourceDomain->Open(source,
1416 					FSSH_O_RDONLY
1417 						| (options.dereference ? 0 : FSSH_O_NOTRAVERSE),
1418 					sourceNode);
1419 				if (error != FSSH_B_OK) {
1420 					fprintf(stderr, "Error: Failed to open `%s': %s.\n", source,
1421 						fssh_strerror(error));
1422 					return error;
1423 				}
1424 				NodeDeleter sourceDeleter(sourceNode);
1425 
1426 				// check, if it is a dir
1427 				Directory *sourceDir = sourceNode->ToDirectory();
1428 				if (!sourceDir) {
1429 					fprintf(stderr, "Error: Source `%s' is not a directory "
1430 						"although it's last path component is `%s'\n", source,
1431 						leafName);
1432 					return FSSH_EINVAL;
1433 				}
1434 
1435 				error = copy_dir_contents(sourceDomain, source, sourceDir,
1436 					targetDomain, target, options);
1437 			} else {
1438 				// 1.1.2. source has normal name
1439 				//        -> we copy into the dir
1440 				//           (copy_entry(<source>, <target>/<source leaf>))
1441 				// compose a new target path name
1442 				char *targetEntry = make_path(target, leafName);
1443 				if (!targetEntry) {
1444 					fprintf(stderr, "Error: Failed to allocate target path!\n");
1445 					return FSSH_ENOMEM;
1446 				}
1447 				PathDeleter targetDeleter(targetEntry);
1448 
1449 				error = copy_entry(sourceDomain, source, targetDomain,
1450 					targetEntry, options, options.dereference);
1451 			}
1452 		} else {
1453 			// 1.2. target is no dir:
1454 			//      -> if /force/ is given, we replace the target, otherwise
1455 			//         we fail
1456 			//         (copy_entry(<source>, <target>))
1457 			// or
1458 			// 2. target doesn't exist:
1459 			//    -> we create the target as a clone of the source
1460 			//         (copy_entry(<source>, <target>))
1461 			error = copy_entry(sourceDomain, source, targetDomain, target,
1462 				options, options.dereference);
1463 		}
1464 
1465 		if (error != 0)
1466 			return error;
1467 	}
1468 
1469 	return FSSH_B_OK;
1470 }
1471 
1472 
1473 }	// namespace FSShell
1474