xref: /haiku/src/kits/storage/Entry.cpp (revision 25a7b01d15612846f332751841da3579db313082)
1 /*
2  * Copyright 2002-2012, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  *		Ingo Weinhold, bonefish@users.sf.net
8  */
9 
10 
11 #include <Entry.h>
12 
13 #include <fcntl.h>
14 #include <new>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #include <compat/sys/stat.h>
21 
22 #include <Directory.h>
23 #include <Path.h>
24 #include <SymLink.h>
25 
26 #include <syscalls.h>
27 
28 #include "storage_support.h"
29 
30 
31 using namespace std;
32 
33 
34 //	#pragma mark - struct entry_ref
35 
36 
37 entry_ref::entry_ref()
38 	:
39 	device((dev_t)-1),
40 	directory((ino_t)-1),
41 	name(NULL)
42 {
43 }
44 
45 
46 entry_ref::entry_ref(dev_t dev, ino_t dir, const char* name)
47 	:
48 	device(dev),
49 	directory(dir),
50 	name(NULL)
51 {
52 	set_name(name);
53 }
54 
55 
56 entry_ref::entry_ref(const entry_ref& ref)
57 	:
58 	device(ref.device),
59 	directory(ref.directory),
60 	name(NULL)
61 {
62 	set_name(ref.name);
63 }
64 
65 
66 entry_ref::~entry_ref()
67 {
68 	free(name);
69 }
70 
71 
72 status_t
73 entry_ref::set_name(const char* name)
74 {
75 	free(this->name);
76 
77 	if (name == NULL) {
78 		this->name = NULL;
79 	} else {
80 		this->name = strdup(name);
81 		if (!this->name)
82 			return B_NO_MEMORY;
83 	}
84 
85 	return B_OK;
86 }
87 
88 
89 bool
90 entry_ref::operator==(const entry_ref& ref) const
91 {
92 	return (device == ref.device
93 		&& directory == ref.directory
94 		&& (name == ref.name
95 			|| (name != NULL && ref.name != NULL
96 				&& strcmp(name, ref.name) == 0)));
97 }
98 
99 
100 bool
101 entry_ref::operator!=(const entry_ref& ref) const
102 {
103 	return !(*this == ref);
104 }
105 
106 
107 entry_ref&
108 entry_ref::operator=(const entry_ref& ref)
109 {
110 	if (this == &ref)
111 		return *this;
112 
113 	device = ref.device;
114 	directory = ref.directory;
115 	set_name(ref.name);
116 	return *this;
117 }
118 
119 
120 //	#pragma mark - BEntry
121 
122 
123 BEntry::BEntry()
124 	:
125 	fDirFd(-1),
126 	fName(NULL),
127 	fCStatus(B_NO_INIT)
128 {
129 }
130 
131 
132 BEntry::BEntry(const BDirectory* dir, const char* path, bool traverse)
133 	:
134 	fDirFd(-1),
135 	fName(NULL),
136 	fCStatus(B_NO_INIT)
137 {
138 	SetTo(dir, path, traverse);
139 }
140 
141 
142 BEntry::BEntry(const entry_ref* ref, bool traverse)
143 	:
144 	fDirFd(-1),
145 	fName(NULL),
146 	fCStatus(B_NO_INIT)
147 {
148 	SetTo(ref, traverse);
149 }
150 
151 
152 BEntry::BEntry(const char* path, bool traverse)
153 	:
154 	fDirFd(-1),
155 	fName(NULL),
156 	fCStatus(B_NO_INIT)
157 {
158 	SetTo(path, traverse);
159 }
160 
161 
162 BEntry::BEntry(const BEntry& entry)
163 	:
164 	fDirFd(-1),
165 	fName(NULL),
166 	fCStatus(B_NO_INIT)
167 {
168 	*this = entry;
169 }
170 
171 
172 BEntry::~BEntry()
173 {
174 	Unset();
175 }
176 
177 
178 status_t
179 BEntry::InitCheck() const
180 {
181 	return fCStatus;
182 }
183 
184 
185 bool
186 BEntry::Exists() const
187 {
188 	// just stat the beast
189 	struct stat st;
190 	return GetStat(&st) == B_OK;
191 }
192 
193 
194 const char*
195 BEntry::Name() const
196 {
197 	if (fCStatus != B_OK)
198 		return NULL;
199 
200 	return fName;
201 }
202 
203 
204 status_t
205 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse)
206 {
207 	// check params
208 	if (!dir)
209 		return (fCStatus = B_BAD_VALUE);
210 	if (path && path[0] == '\0')	// R5 behaviour
211 		path = NULL;
212 
213 	// if path is absolute, let the path-only SetTo() do the job
214 	if (BPrivate::Storage::is_absolute_path(path))
215 		return SetTo(path, traverse);
216 
217 	Unset();
218 
219 	if (dir->InitCheck() != B_OK)
220 		fCStatus = B_BAD_VALUE;
221 
222 	// dup() the dir's FD and let set() do the rest
223 	int dirFD = _kern_dup(dir->get_fd());
224 	if (dirFD < 0)
225 		return (fCStatus = dirFD);
226 	return (fCStatus = _SetTo(dirFD, path, traverse));
227 }
228 
229 
230 status_t
231 BEntry::SetTo(const entry_ref* ref, bool traverse)
232 {
233 	Unset();
234 	if (ref == NULL)
235 		return (fCStatus = B_BAD_VALUE);
236 
237 	// if ref-name is absolute, let the path-only SetTo() do the job
238 	if (BPrivate::Storage::is_absolute_path(ref->name))
239 		return SetTo(ref->name, traverse);
240 
241 	// open the directory and let set() do the rest
242 	int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL);
243 	if (dirFD < 0)
244 		return (fCStatus = dirFD);
245 	return (fCStatus = _SetTo(dirFD, ref->name, traverse));
246 }
247 
248 
249 status_t
250 BEntry::SetTo(const char* path, bool traverse)
251 {
252 	Unset();
253 	// check the argument
254 	if (!path)
255 		return (fCStatus = B_BAD_VALUE);
256 	return (fCStatus = _SetTo(-1, path, traverse));
257 }
258 
259 
260 void
261 BEntry::Unset()
262 {
263 	// Close the directory
264 	if (fDirFd >= 0)
265 		_kern_close(fDirFd);
266 
267 	// Free our leaf name
268 	free(fName);
269 
270 	fDirFd = -1;
271 	fName = NULL;
272 	fCStatus = B_NO_INIT;
273 }
274 
275 
276 status_t
277 BEntry::GetRef(entry_ref* ref) const
278 {
279 	if (fCStatus != B_OK)
280 		return B_NO_INIT;
281 
282 	if (ref == NULL)
283 		return B_BAD_VALUE;
284 
285 	struct stat st;
286 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
287 		sizeof(struct stat));
288 	if (error == B_OK) {
289 		ref->device = st.st_dev;
290 		ref->directory = st.st_ino;
291 		error = ref->set_name(fName);
292 	}
293 	return error;
294 }
295 
296 
297 status_t
298 BEntry::GetPath(BPath* path) const
299 {
300 	if (fCStatus != B_OK)
301 		return B_NO_INIT;
302 
303 	if (path == NULL)
304 		return B_BAD_VALUE;
305 
306 	return path->SetTo(this);
307 }
308 
309 
310 status_t BEntry::GetParent(BEntry* entry) const
311 {
312 	// check parameter and initialization
313 	if (fCStatus != B_OK)
314 		return B_NO_INIT;
315 	if (entry == NULL)
316 		return B_BAD_VALUE;
317 
318 	// check whether we are the root directory
319 	// It is sufficient to check whether our leaf name is ".".
320 	if (strcmp(fName, ".") == 0)
321 		return B_ENTRY_NOT_FOUND;
322 
323 	// open the parent directory
324 	char leafName[B_FILE_NAME_LENGTH];
325 	int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH);
326 	if (parentFD < 0)
327 		return parentFD;
328 
329 	// set close on exec flag on dir FD
330 	fcntl(parentFD, F_SETFD, FD_CLOEXEC);
331 
332 	// init the entry
333 	entry->Unset();
334 	entry->fDirFd = parentFD;
335 	entry->fCStatus = entry->_SetName(leafName);
336 	if (entry->fCStatus != B_OK)
337 		entry->Unset();
338 	return entry->fCStatus;
339 }
340 
341 
342 status_t
343 BEntry::GetParent(BDirectory* dir) const
344 {
345 	// check initialization and parameter
346 	if (fCStatus != B_OK)
347 		return B_NO_INIT;
348 	if (dir == NULL)
349 		return B_BAD_VALUE;
350 	// check whether we are the root directory
351 	// It is sufficient to check whether our leaf name is ".".
352 	if (strcmp(fName, ".") == 0)
353 		return B_ENTRY_NOT_FOUND;
354 	// get a node ref for the directory and init it
355 	struct stat st;
356 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
357 		sizeof(struct stat));
358 	if (error != B_OK)
359 		return error;
360 	node_ref ref;
361 	ref.device = st.st_dev;
362 	ref.node = st.st_ino;
363 	return dir->SetTo(&ref);
364 	// TODO: This can be optimized: We already have a FD for the directory,
365 	// so we could dup() it and set it on the directory. We just need a private
366 	// API for being able to do that.
367 }
368 
369 
370 status_t
371 BEntry::GetName(char* buffer) const
372 {
373 	if (fCStatus != B_OK)
374 		return B_NO_INIT;
375 	if (buffer == NULL)
376 		return B_BAD_VALUE;
377 
378 	strcpy(buffer, fName);
379 	return B_OK;
380 }
381 
382 
383 status_t
384 BEntry::Rename(const char* path, bool clobber)
385 {
386 	// check parameter and initialization
387 	if (path == NULL)
388 		return B_BAD_VALUE;
389 	if (fCStatus != B_OK)
390 		return B_NO_INIT;
391 	// get an entry representing the target location
392 	BEntry target;
393 	status_t error;
394 	if (BPrivate::Storage::is_absolute_path(path)) {
395 		error = target.SetTo(path);
396 	} else {
397 		int dirFD = _kern_dup(fDirFd);
398 		if (dirFD < 0)
399 			return dirFD;
400 		// init the entry
401 		error = target.fCStatus = target._SetTo(dirFD, path, false);
402 	}
403 	if (error != B_OK)
404 		return error;
405 	return _Rename(target, clobber);
406 }
407 
408 
409 status_t
410 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber)
411 {
412 	// check parameters and initialization
413 	if (fCStatus != B_OK)
414 		return B_NO_INIT;
415 	if (dir == NULL)
416 		return B_BAD_VALUE;
417 	if (dir->InitCheck() != B_OK)
418 		return B_BAD_VALUE;
419 	// NULL path simply means move without renaming
420 	if (path == NULL)
421 		path = fName;
422 	// get an entry representing the target location
423 	BEntry target;
424 	status_t error = target.SetTo(dir, path);
425 	if (error != B_OK)
426 		return error;
427 	return _Rename(target, clobber);
428 }
429 
430 
431 status_t
432 BEntry::Remove()
433 {
434 	if (fCStatus != B_OK)
435 		return B_NO_INIT;
436 
437 	if (IsDirectory())
438 		return _kern_remove_dir(fDirFd, fName);
439 
440 	return _kern_unlink(fDirFd, fName);
441 }
442 
443 
444 bool
445 BEntry::operator==(const BEntry& item) const
446 {
447 	// First check statuses
448 	if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) {
449 		return true;
450 	} else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) {
451 
452 		// Directories don't compare well directly, so we'll
453 		// compare entry_refs instead
454 		entry_ref ref1, ref2;
455 		if (this->GetRef(&ref1) != B_OK)
456 			return false;
457 		if (item.GetRef(&ref2) != B_OK)
458 			return false;
459 		return (ref1 == ref2);
460 
461 	} else {
462 		return false;
463 	}
464 
465 }
466 
467 
468 bool
469 BEntry::operator!=(const BEntry& item) const
470 {
471 	return !(*this == item);
472 }
473 
474 
475 BEntry&
476 BEntry::operator=(const BEntry& item)
477 {
478 	if (this == &item)
479 		return *this;
480 
481 	Unset();
482 	if (item.fCStatus == B_OK) {
483 		fDirFd = _kern_dup(item.fDirFd);
484 		if (fDirFd >= 0)
485 			fCStatus = _SetName(item.fName);
486 		else
487 			fCStatus = fDirFd;
488 
489 		if (fCStatus != B_OK)
490 			Unset();
491 	}
492 
493 	return *this;
494 }
495 
496 
497 void BEntry::_PennyEntry1(){}
498 void BEntry::_PennyEntry2(){}
499 void BEntry::_PennyEntry3(){}
500 void BEntry::_PennyEntry4(){}
501 void BEntry::_PennyEntry5(){}
502 void BEntry::_PennyEntry6(){}
503 
504 
505 status_t
506 BEntry::set_stat(struct stat& st, uint32 what)
507 {
508 	if (fCStatus != B_OK)
509 		return B_FILE_ERROR;
510 
511 	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
512 		what);
513 }
514 
515 
516 status_t
517 BEntry::_SetTo(int dirFD, const char* path, bool traverse)
518 {
519 	bool requireConcrete = false;
520 	FDCloser fdCloser(dirFD);
521 	char tmpPath[B_PATH_NAME_LENGTH];
522 	char leafName[B_FILE_NAME_LENGTH];
523 	int32 linkLimit = B_MAX_SYMLINKS;
524 	while (true) {
525 		if (!path || strcmp(path, ".") == 0) {
526 			// "."
527 			// if no dir FD is supplied, we need to open the current directory
528 			// first
529 			if (dirFD < 0) {
530 				dirFD = _kern_open_dir(-1, ".");
531 				if (dirFD < 0)
532 					return dirFD;
533 				fdCloser.SetTo(dirFD);
534 			}
535 			// get the parent directory
536 			int parentFD = _kern_open_parent_dir(dirFD, leafName,
537 				B_FILE_NAME_LENGTH);
538 			if (parentFD < 0)
539 				return parentFD;
540 			dirFD = parentFD;
541 			fdCloser.SetTo(dirFD);
542 			break;
543 		} else if (strcmp(path, "..") == 0) {
544 			// ".."
545 			// open the parent directory
546 			int parentFD = _kern_open_dir(dirFD, "..");
547 			if (parentFD < 0)
548 				return parentFD;
549 			dirFD = parentFD;
550 			fdCloser.SetTo(dirFD);
551 			// get the parent's parent directory
552 			parentFD = _kern_open_parent_dir(dirFD, leafName,
553 				B_FILE_NAME_LENGTH);
554 			if (parentFD < 0)
555 				return parentFD;
556 			dirFD = parentFD;
557 			fdCloser.SetTo(dirFD);
558 			break;
559 		} else {
560 			// an ordinary path; analyze it
561 			char dirPath[B_PATH_NAME_LENGTH];
562 			status_t error = BPrivate::Storage::parse_path(path, dirPath,
563 				leafName);
564 			if (error != B_OK)
565 				return error;
566 			// special case: root directory ("/")
567 			if (leafName[0] == '\0' && dirPath[0] == '/')
568 				strcpy(leafName, ".");
569 			if (leafName[0] == '\0') {
570 				// the supplied path is already a leaf
571 				error = BPrivate::Storage::check_entry_name(dirPath);
572 				if (error != B_OK)
573 					return error;
574 				strcpy(leafName, dirPath);
575 				// if no directory was given, we need to open the current dir
576 				// now
577 				if (dirFD < 0) {
578 					char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
579 					if (!cwd)
580 						return B_ERROR;
581 					dirFD = _kern_open_dir(-1, cwd);
582 					if (dirFD < 0)
583 						return dirFD;
584 					fdCloser.SetTo(dirFD);
585 				}
586 			} else if (strcmp(leafName, ".") == 0
587 					|| strcmp(leafName, "..") == 0) {
588 				// We have to resolve this to get the entry name. Just open
589 				// the dir and let the next iteration deal with it.
590 				dirFD = _kern_open_dir(-1, path);
591 				if (dirFD < 0)
592 					return dirFD;
593 				fdCloser.SetTo(dirFD);
594 				path = NULL;
595 				continue;
596 			} else {
597 				int parentFD = _kern_open_dir(dirFD, dirPath);
598 				if (parentFD < 0)
599 					return parentFD;
600 				dirFD = parentFD;
601 				fdCloser.SetTo(dirFD);
602 			}
603 			// traverse symlinks, if desired
604 			if (!traverse)
605 				break;
606 			struct stat st;
607 			error = _kern_read_stat(dirFD, leafName, false, &st,
608 				sizeof(struct stat));
609 			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
610 				// that's fine -- the entry is abstract and was not target of
611 				// a symlink we resolved
612 				break;
613 			}
614 			if (error != B_OK)
615 				return error;
616 			// the entry is concrete
617 			if (!S_ISLNK(st.st_mode))
618 				break;
619 			requireConcrete = true;
620 			// we need to traverse the symlink
621 			if (--linkLimit < 0)
622 				return B_LINK_LIMIT;
623 			size_t bufferSize = B_PATH_NAME_LENGTH - 1;
624 			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
625 			if (error < 0)
626 				return error;
627 			tmpPath[bufferSize] = '\0';
628 			path = tmpPath;
629 			// next round...
630 		}
631 	}
632 
633 	// set close on exec flag on dir FD
634 	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
635 
636 	// set the result
637 	status_t error = _SetName(leafName);
638 	if (error != B_OK)
639 		return error;
640 	fdCloser.Detach();
641 	fDirFd = dirFD;
642 	return B_OK;
643 }
644 
645 
646 status_t
647 BEntry::_SetName(const char* name)
648 {
649 	if (name == NULL)
650 		return B_BAD_VALUE;
651 
652 	free(fName);
653 
654 	fName = strdup(name);
655 	if (fName == NULL)
656 		return B_NO_MEMORY;
657 
658 	return B_OK;
659 }
660 
661 
662 status_t
663 BEntry::_Rename(BEntry& target, bool clobber)
664 {
665 	// check, if there's an entry in the way
666 	if (!clobber && target.Exists())
667 		return B_FILE_EXISTS;
668 	// rename
669 	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
670 	if (error == B_OK) {
671 		Unset();
672 		fCStatus = target.fCStatus;
673 		fDirFd = target.fDirFd;
674 		fName = target.fName;
675 		target.fCStatus = B_NO_INIT;
676 		target.fDirFd = -1;
677 		target.fName = NULL;
678 	}
679 	return error;
680 }
681 
682 
683 void
684 BEntry::_Dump(const char* name)
685 {
686 	if (name != NULL) {
687 		printf("------------------------------------------------------------\n");
688 		printf("%s\n", name);
689 		printf("------------------------------------------------------------\n");
690 	}
691 
692 	printf("fCStatus == %" B_PRId32 "\n", fCStatus);
693 
694 	struct stat st;
695 	if (fDirFd != -1
696 		&& _kern_read_stat(fDirFd, NULL, false, &st,
697 				sizeof(struct stat)) == B_OK) {
698 		printf("dir.device == %" B_PRIdDEV "\n", st.st_dev);
699 		printf("dir.inode  == %" B_PRIdINO "\n", st.st_ino);
700 	} else {
701 		printf("dir == NullFd\n");
702 	}
703 
704 	printf("leaf == '%s'\n", fName);
705 	printf("\n");
706 
707 }
708 
709 
710 status_t
711 BEntry::_GetStat(struct stat* st) const
712 {
713 	if (fCStatus != B_OK)
714 		return B_NO_INIT;
715 
716 	return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat));
717 }
718 
719 
720 status_t
721 BEntry::_GetStat(struct stat_beos* st) const
722 {
723 	struct stat newStat;
724 	status_t error = _GetStat(&newStat);
725 	if (error != B_OK)
726 		return error;
727 
728 	convert_to_stat_beos(&newStat, st);
729 	return B_OK;
730 }
731 
732 
733 // #pragma mark -
734 
735 
736 status_t
737 get_ref_for_path(const char* path, entry_ref* ref)
738 {
739 	status_t error = path && ref ? B_OK : B_BAD_VALUE;
740 	if (error == B_OK) {
741 		BEntry entry(path);
742 		error = entry.InitCheck();
743 		if (error == B_OK)
744 			error = entry.GetRef(ref);
745 	}
746 	return error;
747 }
748 
749 
750 bool
751 operator<(const entry_ref& a, const entry_ref& b)
752 {
753 	return (a.device < b.device
754 		|| (a.device == b.device
755 			&& (a.directory < b.directory
756 			|| (a.directory == b.directory
757 				&& ((a.name == NULL && b.name != NULL)
758 				|| (a.name != NULL && b.name != NULL
759 					&& strcmp(a.name, b.name) < 0))))));
760 }
761 
762 
763 // #pragma mark - symbol versions
764 
765 
766 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
767 #	if __GNUC__ == 2	// gcc 2
768 
769 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
770 		"GetStat__C6BEntryP4stat@@LIBBE_TEST");
771 
772 #	else	// gcc 4
773 
774 	// Haiku GetStat()
775 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
776 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST");
777 
778 #	endif	// gcc 4
779 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
780 #	if __GNUC__ == 2	// gcc 2
781 
782 	// BeOS compatible GetStat()
783 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos",
784 		"GetStat__C6BEntryP4stat@LIBBE_BASE");
785 
786 	// Haiku GetStat()
787 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
788 		"GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1");
789 
790 #	else	// gcc 4
791 
792 	// BeOS compatible GetStat()
793 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos",
794 		"_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE");
795 
796 	// Haiku GetStat()
797 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
798 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1");
799 
800 #	endif	// gcc 4
801 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
802