xref: /haiku/src/kits/storage/Entry.cpp (revision 6e434fd80e4640c64031faf5e49720c5672fc470)
1 /*
2  * Copyright 2002-2009, 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 status_t
195 BEntry::SetTo(const BDirectory* dir, const char* path, bool traverse)
196 {
197 	// check params
198 	if (!dir)
199 		return (fCStatus = B_BAD_VALUE);
200 	if (path && path[0] == '\0')	// R5 behaviour
201 		path = NULL;
202 
203 	// if path is absolute, let the path-only SetTo() do the job
204 	if (BPrivate::Storage::is_absolute_path(path))
205 		return SetTo(path, traverse);
206 
207 	Unset();
208 
209 	if (dir->InitCheck() != B_OK)
210 		fCStatus = B_BAD_VALUE;
211 
212 	// dup() the dir's FD and let set() do the rest
213 	int dirFD = _kern_dup(dir->get_fd());
214 	if (dirFD < 0)
215 		return (fCStatus = dirFD);
216 	return (fCStatus = _SetTo(dirFD, path, traverse));
217 }
218 
219 
220 status_t
221 BEntry::SetTo(const entry_ref* ref, bool traverse)
222 {
223 	Unset();
224 	if (ref == NULL)
225 		return (fCStatus = B_BAD_VALUE);
226 
227 	// if ref-name is absolute, let the path-only SetTo() do the job
228 	if (BPrivate::Storage::is_absolute_path(ref->name))
229 		return SetTo(ref->name, traverse);
230 
231 	// open the directory and let set() do the rest
232 	int dirFD = _kern_open_dir_entry_ref(ref->device, ref->directory, NULL);
233 	if (dirFD < 0)
234 		return (fCStatus = dirFD);
235 	return (fCStatus = _SetTo(dirFD, ref->name, traverse));
236 }
237 
238 
239 status_t
240 BEntry::SetTo(const char* path, bool traverse)
241 {
242 	Unset();
243 	// check the argument
244 	if (!path)
245 		return (fCStatus = B_BAD_VALUE);
246 	return (fCStatus = _SetTo(-1, path, traverse));
247 }
248 
249 
250 void
251 BEntry::Unset()
252 {
253 	// Close the directory
254 	if (fDirFd >= 0)
255 		_kern_close(fDirFd);
256 
257 	// Free our leaf name
258 	free(fName);
259 
260 	fDirFd = -1;
261 	fName = NULL;
262 	fCStatus = B_NO_INIT;
263 }
264 
265 
266 status_t
267 BEntry::GetRef(entry_ref* ref) const
268 {
269 	if (fCStatus != B_OK)
270 		return B_NO_INIT;
271 
272 	if (ref == NULL)
273 		return B_BAD_VALUE;
274 
275 	struct stat st;
276 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
277 		sizeof(struct stat));
278 	if (error == B_OK) {
279 		ref->device = st.st_dev;
280 		ref->directory = st.st_ino;
281 		error = ref->set_name(fName);
282 	}
283 	return error;
284 }
285 
286 
287 status_t
288 BEntry::GetPath(BPath* path) const
289 {
290 	if (fCStatus != B_OK)
291 		return B_NO_INIT;
292 
293 	if (path == NULL)
294 		return B_BAD_VALUE;
295 
296 	return path->SetTo(this);
297 }
298 
299 
300 status_t BEntry::GetParent(BEntry* entry) const
301 {
302 	// check parameter and initialization
303 	if (fCStatus != B_OK)
304 		return B_NO_INIT;
305 	if (entry == NULL)
306 		return B_BAD_VALUE;
307 
308 	// check whether we are the root directory
309 	// It is sufficient to check whether our leaf name is ".".
310 	if (strcmp(fName, ".") == 0)
311 		return B_ENTRY_NOT_FOUND;
312 
313 	// open the parent directory
314 	char leafName[B_FILE_NAME_LENGTH];
315 	int parentFD = _kern_open_parent_dir(fDirFd, leafName, B_FILE_NAME_LENGTH);
316 	if (parentFD < 0)
317 		return parentFD;
318 
319 	// set close on exec flag on dir FD
320 	fcntl(parentFD, F_SETFD, FD_CLOEXEC);
321 
322 	// init the entry
323 	entry->Unset();
324 	entry->fDirFd = parentFD;
325 	entry->fCStatus = entry->_SetName(leafName);
326 	if (entry->fCStatus != B_OK)
327 		entry->Unset();
328 	return entry->fCStatus;
329 }
330 
331 
332 status_t
333 BEntry::GetParent(BDirectory* dir) const
334 {
335 	// check initialization and parameter
336 	if (fCStatus != B_OK)
337 		return B_NO_INIT;
338 	if (dir == NULL)
339 		return B_BAD_VALUE;
340 	// check whether we are the root directory
341 	// It is sufficient to check whether our leaf name is ".".
342 	if (strcmp(fName, ".") == 0)
343 		return B_ENTRY_NOT_FOUND;
344 	// get a node ref for the directory and init it
345 	struct stat st;
346 	status_t error = _kern_read_stat(fDirFd, NULL, false, &st,
347 		sizeof(struct stat));
348 	if (error != B_OK)
349 		return error;
350 	node_ref ref;
351 	ref.device = st.st_dev;
352 	ref.node = st.st_ino;
353 	return dir->SetTo(&ref);
354 	// TODO: This can be optimized: We already have a FD for the directory,
355 	// so we could dup() it and set it on the directory. We just need a private
356 	// API for being able to do that.
357 }
358 
359 
360 status_t
361 BEntry::GetName(char* buffer) const
362 {
363 	status_t result = B_ERROR;
364 
365 	if (fCStatus != B_OK) {
366 		result = B_NO_INIT;
367 	} else if (buffer == NULL) {
368 		result = B_BAD_VALUE;
369 	} else {
370 		strcpy(buffer, fName);
371 		result = B_OK;
372 	}
373 
374 	return result;
375 }
376 
377 
378 status_t
379 BEntry::Rename(const char* path, bool clobber)
380 {
381 	// check parameter and initialization
382 	if (path == NULL)
383 		return B_BAD_VALUE;
384 	if (fCStatus != B_OK)
385 		return B_NO_INIT;
386 	// get an entry representing the target location
387 	BEntry target;
388 	status_t error;
389 	if (BPrivate::Storage::is_absolute_path(path)) {
390 		error = target.SetTo(path);
391 	} else {
392 		int dirFD = _kern_dup(fDirFd);
393 		if (dirFD < 0)
394 			return dirFD;
395 		// init the entry
396 		error = target.fCStatus = target._SetTo(dirFD, path, false);
397 	}
398 	if (error != B_OK)
399 		return error;
400 	return _Rename(target, clobber);
401 }
402 
403 
404 status_t
405 BEntry::MoveTo(BDirectory* dir, const char* path, bool clobber)
406 {
407 	// check parameters and initialization
408 	if (fCStatus != B_OK)
409 		return B_NO_INIT;
410 	if (dir == NULL)
411 		return B_BAD_VALUE;
412 	if (dir->InitCheck() != B_OK)
413 		return B_BAD_VALUE;
414 	// NULL path simply means move without renaming
415 	if (path == NULL)
416 		path = fName;
417 	// get an entry representing the target location
418 	BEntry target;
419 	status_t error = target.SetTo(dir, path);
420 	if (error != B_OK)
421 		return error;
422 	return _Rename(target, clobber);
423 }
424 
425 
426 status_t
427 BEntry::Remove()
428 {
429 	if (fCStatus != B_OK)
430 		return B_NO_INIT;
431 
432 	if (IsDirectory())
433 		return _kern_remove_dir(fDirFd, fName);
434 
435 	return _kern_unlink(fDirFd, fName);
436 }
437 
438 
439 bool
440 BEntry::operator==(const BEntry& item) const
441 {
442 	// First check statuses
443 	if (this->InitCheck() != B_OK && item.InitCheck() != B_OK) {
444 		return true;
445 	} else if (this->InitCheck() == B_OK && item.InitCheck() == B_OK) {
446 
447 		// Directories don't compare well directly, so we'll
448 		// compare entry_refs instead
449 		entry_ref ref1, ref2;
450 		if (this->GetRef(&ref1) != B_OK)
451 			return false;
452 		if (item.GetRef(&ref2) != B_OK)
453 			return false;
454 		return (ref1 == ref2);
455 
456 	} else {
457 		return false;
458 	}
459 
460 }
461 
462 
463 bool
464 BEntry::operator!=(const BEntry& item) const
465 {
466 	return !(*this == item);
467 }
468 
469 
470 BEntry&
471 BEntry::operator=(const BEntry& item)
472 {
473 	if (this == &item)
474 		return *this;
475 
476 	Unset();
477 	if (item.fCStatus == B_OK) {
478 		fDirFd = _kern_dup(item.fDirFd);
479 		if (fDirFd >= 0)
480 			fCStatus = _SetName(item.fName);
481 		else
482 			fCStatus = fDirFd;
483 
484 		if (fCStatus != B_OK)
485 			Unset();
486 	}
487 
488 	return *this;
489 }
490 
491 
492 void BEntry::_PennyEntry1(){}
493 void BEntry::_PennyEntry2(){}
494 void BEntry::_PennyEntry3(){}
495 void BEntry::_PennyEntry4(){}
496 void BEntry::_PennyEntry5(){}
497 void BEntry::_PennyEntry6(){}
498 
499 
500 status_t
501 BEntry::set_stat(struct stat& st, uint32 what)
502 {
503 	if (fCStatus != B_OK)
504 		return B_FILE_ERROR;
505 
506 	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
507 		what);
508 }
509 
510 
511 status_t
512 BEntry::_SetTo(int dirFD, const char* path, bool traverse)
513 {
514 	bool requireConcrete = false;
515 	FDCloser fdCloser(dirFD);
516 	char tmpPath[B_PATH_NAME_LENGTH];
517 	char leafName[B_FILE_NAME_LENGTH];
518 	int32 linkLimit = B_MAX_SYMLINKS;
519 	while (true) {
520 		if (!path || strcmp(path, ".") == 0) {
521 			// "."
522 			// if no dir FD is supplied, we need to open the current directory
523 			// first
524 			if (dirFD < 0) {
525 				dirFD = _kern_open_dir(-1, ".");
526 				if (dirFD < 0)
527 					return dirFD;
528 				fdCloser.SetTo(dirFD);
529 			}
530 			// get the parent directory
531 			int parentFD = _kern_open_parent_dir(dirFD, leafName,
532 				B_FILE_NAME_LENGTH);
533 			if (parentFD < 0)
534 				return parentFD;
535 			dirFD = parentFD;
536 			fdCloser.SetTo(dirFD);
537 			break;
538 		} else if (strcmp(path, "..") == 0) {
539 			// ".."
540 			// open the parent directory
541 			int parentFD = _kern_open_dir(dirFD, "..");
542 			if (parentFD < 0)
543 				return parentFD;
544 			dirFD = parentFD;
545 			fdCloser.SetTo(dirFD);
546 			// get the parent's parent directory
547 			parentFD = _kern_open_parent_dir(dirFD, leafName,
548 				B_FILE_NAME_LENGTH);
549 			if (parentFD < 0)
550 				return parentFD;
551 			dirFD = parentFD;
552 			fdCloser.SetTo(dirFD);
553 			break;
554 		} else {
555 			// an ordinary path; analyze it
556 			char dirPath[B_PATH_NAME_LENGTH];
557 			status_t error = BPrivate::Storage::parse_path(path, dirPath,
558 				leafName);
559 			if (error != B_OK)
560 				return error;
561 			// special case: root directory ("/")
562 			if (leafName[0] == '\0' && dirPath[0] == '/')
563 				strcpy(leafName, ".");
564 			if (leafName[0] == '\0') {
565 				// the supplied path is already a leaf
566 				error = BPrivate::Storage::check_entry_name(dirPath);
567 				if (error != B_OK)
568 					return error;
569 				strcpy(leafName, dirPath);
570 				// if no directory was given, we need to open the current dir
571 				// now
572 				if (dirFD < 0) {
573 					char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
574 					if (!cwd)
575 						return B_ERROR;
576 					dirFD = _kern_open_dir(-1, cwd);
577 					if (dirFD < 0)
578 						return dirFD;
579 					fdCloser.SetTo(dirFD);
580 				}
581 			} else if (strcmp(leafName, ".") == 0
582 					|| strcmp(leafName, "..") == 0) {
583 				// We have to resolve this to get the entry name. Just open
584 				// the dir and let the next iteration deal with it.
585 				dirFD = _kern_open_dir(-1, path);
586 				if (dirFD < 0)
587 					return dirFD;
588 				fdCloser.SetTo(dirFD);
589 				path = NULL;
590 				continue;
591 			} else {
592 				int parentFD = _kern_open_dir(dirFD, dirPath);
593 				if (parentFD < 0)
594 					return parentFD;
595 				dirFD = parentFD;
596 				fdCloser.SetTo(dirFD);
597 			}
598 			// traverse symlinks, if desired
599 			if (!traverse)
600 				break;
601 			struct stat st;
602 			error = _kern_read_stat(dirFD, leafName, false, &st,
603 				sizeof(struct stat));
604 			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
605 				// that's fine -- the entry is abstract and was not target of
606 				// a symlink we resolved
607 				break;
608 			}
609 			if (error != B_OK)
610 				return error;
611 			// the entry is concrete
612 			if (!S_ISLNK(st.st_mode))
613 				break;
614 			requireConcrete = true;
615 			// we need to traverse the symlink
616 			if (--linkLimit < 0)
617 				return B_LINK_LIMIT;
618 			size_t bufferSize = B_PATH_NAME_LENGTH - 1;
619 			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
620 			if (error < 0)
621 				return error;
622 			tmpPath[bufferSize] = '\0';
623 			path = tmpPath;
624 			// next round...
625 		}
626 	}
627 
628 	// set close on exec flag on dir FD
629 	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
630 
631 	// set the result
632 	status_t error = _SetName(leafName);
633 	if (error != B_OK)
634 		return error;
635 	fdCloser.Detach();
636 	fDirFd = dirFD;
637 	return B_OK;
638 }
639 
640 
641 status_t
642 BEntry::_SetName(const char* name)
643 {
644 	if (name == NULL)
645 		return B_BAD_VALUE;
646 
647 	free(fName);
648 
649 	fName = strdup(name);
650 	if (fName == NULL)
651 		return B_NO_MEMORY;
652 
653 	return B_OK;
654 }
655 
656 
657 status_t
658 BEntry::_Rename(BEntry& target, bool clobber)
659 {
660 	// check, if there's an entry in the way
661 	if (!clobber && target.Exists())
662 		return B_FILE_EXISTS;
663 	// rename
664 	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
665 	if (error == B_OK) {
666 		Unset();
667 		fCStatus = target.fCStatus;
668 		fDirFd = target.fDirFd;
669 		fName = target.fName;
670 		target.fCStatus = B_NO_INIT;
671 		target.fDirFd = -1;
672 		target.fName = NULL;
673 	}
674 	return error;
675 }
676 
677 
678 void
679 BEntry::_Dump(const char* name)
680 {
681 	if (name != NULL) {
682 		printf("------------------------------------------------------------\n");
683 		printf("%s\n", name);
684 		printf("------------------------------------------------------------\n");
685 	}
686 
687 	printf("fCStatus == %ld\n", fCStatus);
688 
689 	struct stat st;
690 	if (fDirFd != -1
691 		&& _kern_read_stat(fDirFd, NULL, false, &st,
692 				sizeof(struct stat)) == B_OK) {
693 		printf("dir.device == %ld\n", st.st_dev);
694 		printf("dir.inode  == %lld\n", st.st_ino);
695 	} else {
696 		printf("dir == NullFd\n");
697 	}
698 
699 	printf("leaf == '%s'\n", fName);
700 	printf("\n");
701 
702 }
703 
704 
705 status_t
706 BEntry::_GetStat(struct stat* st) const
707 {
708 	if (fCStatus != B_OK)
709 		return B_NO_INIT;
710 
711 	return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat));
712 }
713 
714 
715 status_t
716 BEntry::_GetStat(struct stat_beos* st) const
717 {
718 	struct stat newStat;
719 	status_t error = _GetStat(&newStat);
720 	if (error != B_OK)
721 		return error;
722 
723 	convert_to_stat_beos(&newStat, st);
724 	return B_OK;
725 }
726 
727 
728 // #pragma mark -
729 
730 
731 status_t
732 get_ref_for_path(const char* path, entry_ref* ref)
733 {
734 	status_t error = path && ref ? B_OK : B_BAD_VALUE;
735 	if (error == B_OK) {
736 		BEntry entry(path);
737 		error = entry.InitCheck();
738 		if (error == B_OK)
739 			error = entry.GetRef(ref);
740 	}
741 	return error;
742 }
743 
744 
745 bool
746 operator<(const entry_ref& a, const entry_ref& b)
747 {
748 	return (a.device < b.device
749 		|| (a.device == b.device
750 			&& (a.directory < b.directory
751 			|| (a.directory == b.directory
752 				&& ((a.name == NULL && b.name != NULL)
753 				|| (a.name != NULL && b.name != NULL
754 					&& strcmp(a.name, b.name) < 0))))));
755 }
756 
757 
758 // #pragma mark - symbol versions
759 
760 
761 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
762 #	if __GNUC__ == 2	// gcc 2
763 
764 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
765 		"GetStat__C6BEntryP4stat@@LIBBE_TEST");
766 
767 #	else	// gcc 4
768 
769 	// Haiku GetStat()
770 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
771 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST");
772 
773 #	endif	// gcc 4
774 #else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
775 #	if __GNUC__ == 2	// gcc 2
776 
777 	// BeOS compatible GetStat()
778 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos",
779 		"GetStat__C6BEntryP4stat@LIBBE_BASE");
780 
781 	// Haiku GetStat()
782 	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
783 		"GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1");
784 
785 #	else	// gcc 4
786 
787 	// BeOS compatible GetStat()
788 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos",
789 		"_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE");
790 
791 	// Haiku GetStat()
792 	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
793 		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1");
794 
795 #	endif	// gcc 4
796 #endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
797