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
31using namespace std;
32
33
34//	#pragma mark - struct entry_ref
35
36
37entry_ref::entry_ref()
38	:
39	device((dev_t)-1),
40	directory((ino_t)-1),
41	name(NULL)
42{
43}
44
45
46entry_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
56entry_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
66entry_ref::~entry_ref()
67{
68	free(name);
69}
70
71
72status_t
73entry_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
89bool
90entry_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
100bool
101entry_ref::operator!=(const entry_ref& ref) const
102{
103	return !(*this == ref);
104}
105
106
107entry_ref&
108entry_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
123BEntry::BEntry()
124	:
125	fDirFd(-1),
126	fName(NULL),
127	fCStatus(B_NO_INIT)
128{
129}
130
131
132BEntry::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
142BEntry::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
152BEntry::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
162BEntry::BEntry(const BEntry& entry)
163	:
164	fDirFd(-1),
165	fName(NULL),
166	fCStatus(B_NO_INIT)
167{
168	*this = entry;
169}
170
171
172BEntry::~BEntry()
173{
174	Unset();
175}
176
177
178status_t
179BEntry::InitCheck() const
180{
181	return fCStatus;
182}
183
184
185bool
186BEntry::Exists() const
187{
188	// just stat the beast
189	struct stat st;
190	return GetStat(&st) == B_OK;
191}
192
193
194const char*
195BEntry::Name() const
196{
197	if (fCStatus != B_OK)
198		return NULL;
199
200	return fName;
201}
202
203
204status_t
205BEntry::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
230status_t
231BEntry::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
249status_t
250BEntry::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
260void
261BEntry::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
276status_t
277BEntry::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
297status_t
298BEntry::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
310status_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
342status_t
343BEntry::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
370status_t
371BEntry::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
383status_t
384BEntry::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
409status_t
410BEntry::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
431status_t
432BEntry::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
444bool
445BEntry::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
468bool
469BEntry::operator!=(const BEntry& item) const
470{
471	return !(*this == item);
472}
473
474
475BEntry&
476BEntry::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
497void BEntry::_PennyEntry1(){}
498void BEntry::_PennyEntry2(){}
499void BEntry::_PennyEntry3(){}
500void BEntry::_PennyEntry4(){}
501void BEntry::_PennyEntry5(){}
502void BEntry::_PennyEntry6(){}
503
504
505/*!	Updates the BEntry with the data from the stat structure according
506	to the \a what mask.
507
508	\param st The stat structure to set.
509	\param what A mask
510
511	\returns A status code.
512	\retval B_OK Everything went fine.
513	\retval B_FILE_ERROR There was an error writing to the BEntry object.
514*/
515status_t
516BEntry::set_stat(struct stat& st, uint32 what)
517{
518	if (fCStatus != B_OK)
519		return B_FILE_ERROR;
520
521	return _kern_write_stat(fDirFd, fName, false, &st, sizeof(struct stat),
522		what);
523}
524
525
526/*!	Sets the entry to point to the entry specified by the path \a path
527	relative to the given directory.
528
529	If \a traverse is \c true and the given entry is a symbolic link, the
530	object is recursively set to point to the entry pointed to by the symlink.
531
532	If \a path is an absolute path, \a dirFD is ignored.
533
534	If \a dirFD is -1, \a path is considered relative to the current directory
535	(unless it is an absolute path).
536
537	The ownership of the file descriptor \a dirFD is transferred to the
538	method, regardless of whether it succeeds or fails. The caller must not
539	close the FD afterwards.
540
541	\param dirFD File descriptor of a directory relative to which path is to
542		be considered. May be -1 if the current directory shall be considered.
543	\param path Pointer to a path relative to the given directory.
544	\param traverse If \c true and the given entry is a symbolic link, the
545		object is recursively set to point to the entry linked to by the
546		symbolic link.
547
548	\returns \c B_OK on success, or an error code on failure.
549*/
550status_t
551BEntry::_SetTo(int dirFD, const char* path, bool traverse)
552{
553	bool requireConcrete = false;
554	FDCloser fdCloser(dirFD);
555	char tmpPath[B_PATH_NAME_LENGTH];
556	char leafName[B_FILE_NAME_LENGTH];
557	int32 linkLimit = B_MAX_SYMLINKS;
558	while (true) {
559		if (!path || strcmp(path, ".") == 0) {
560			// "."
561			// if no dir FD is supplied, we need to open the current directory
562			// first
563			if (dirFD < 0) {
564				dirFD = _kern_open_dir(-1, ".");
565				if (dirFD < 0)
566					return dirFD;
567				fdCloser.SetTo(dirFD);
568			}
569			// get the parent directory
570			int parentFD = _kern_open_parent_dir(dirFD, leafName,
571				B_FILE_NAME_LENGTH);
572			if (parentFD < 0)
573				return parentFD;
574			dirFD = parentFD;
575			fdCloser.SetTo(dirFD);
576			break;
577		} else if (strcmp(path, "..") == 0) {
578			// ".."
579			// open the parent directory
580			int parentFD = _kern_open_dir(dirFD, "..");
581			if (parentFD < 0)
582				return parentFD;
583			dirFD = parentFD;
584			fdCloser.SetTo(dirFD);
585			// get the parent's parent directory
586			parentFD = _kern_open_parent_dir(dirFD, leafName,
587				B_FILE_NAME_LENGTH);
588			if (parentFD < 0)
589				return parentFD;
590			dirFD = parentFD;
591			fdCloser.SetTo(dirFD);
592			break;
593		} else {
594			// an ordinary path; analyze it
595			char dirPath[B_PATH_NAME_LENGTH];
596			status_t error = BPrivate::Storage::parse_path(path, dirPath,
597				leafName);
598			if (error != B_OK)
599				return error;
600			// special case: root directory ("/")
601			if (leafName[0] == '\0' && dirPath[0] == '/')
602				strcpy(leafName, ".");
603			if (leafName[0] == '\0') {
604				// the supplied path is already a leaf
605				error = BPrivate::Storage::check_entry_name(dirPath);
606				if (error != B_OK)
607					return error;
608				strcpy(leafName, dirPath);
609				// if no directory was given, we need to open the current dir
610				// now
611				if (dirFD < 0) {
612					char* cwd = getcwd(tmpPath, B_PATH_NAME_LENGTH);
613					if (!cwd)
614						return B_ERROR;
615					dirFD = _kern_open_dir(-1, cwd);
616					if (dirFD < 0)
617						return dirFD;
618					fdCloser.SetTo(dirFD);
619				}
620			} else if (strcmp(leafName, ".") == 0
621					|| strcmp(leafName, "..") == 0) {
622				// We have to resolve this to get the entry name. Just open
623				// the dir and let the next iteration deal with it.
624				dirFD = _kern_open_dir(-1, path);
625				if (dirFD < 0)
626					return dirFD;
627				fdCloser.SetTo(dirFD);
628				path = NULL;
629				continue;
630			} else {
631				int parentFD = _kern_open_dir(dirFD, dirPath);
632				if (parentFD < 0)
633					return parentFD;
634				dirFD = parentFD;
635				fdCloser.SetTo(dirFD);
636			}
637			// traverse symlinks, if desired
638			if (!traverse)
639				break;
640			struct stat st;
641			error = _kern_read_stat(dirFD, leafName, false, &st,
642				sizeof(struct stat));
643			if (error == B_ENTRY_NOT_FOUND && !requireConcrete) {
644				// that's fine -- the entry is abstract and was not target of
645				// a symlink we resolved
646				break;
647			}
648			if (error != B_OK)
649				return error;
650			// the entry is concrete
651			if (!S_ISLNK(st.st_mode))
652				break;
653			requireConcrete = true;
654			// we need to traverse the symlink
655			if (--linkLimit < 0)
656				return B_LINK_LIMIT;
657			size_t bufferSize = B_PATH_NAME_LENGTH - 1;
658			error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize);
659			if (error < 0)
660				return error;
661			tmpPath[bufferSize] = '\0';
662			path = tmpPath;
663			// next round...
664		}
665	}
666
667	// set close on exec flag on dir FD
668	fcntl(dirFD, F_SETFD, FD_CLOEXEC);
669
670	// set the result
671	status_t error = _SetName(leafName);
672	if (error != B_OK)
673		return error;
674	fdCloser.Detach();
675	fDirFd = dirFD;
676	return B_OK;
677}
678
679
680/*!	Handles string allocation, deallocation, and copying for the
681	leaf name of the entry.
682
683	\param name The leaf \a name of the entry.
684
685	\returns A status code.
686	\retval B_OK Everything went fine.
687	\retval B_BAD_VALUE \a name is \c NULL.
688	\retval B_NO_MEMORY Ran out of memory trying to allocate \a name.
689*/
690status_t
691BEntry::_SetName(const char* name)
692{
693	if (name == NULL)
694		return B_BAD_VALUE;
695
696	free(fName);
697
698	fName = strdup(name);
699	if (fName == NULL)
700		return B_NO_MEMORY;
701
702	return B_OK;
703}
704
705
706/*!	Renames the entry referred to by this object to the location
707	specified by \a target.
708
709	If an entry exists at the target location, the method fails, unless
710	\a clobber is \c true, in which case that entry is overwritten (doesn't
711	work for non-empty directories, though).
712
713	If the operation was successful, this entry is made a clone of the
714	supplied one and the supplied one is uninitialized.
715
716	\param target The entry specifying the target location.
717	\param clobber If \c true, the an entry existing at the target location
718		   will be overwritten.
719
720	\return \c B_OK, if everything went fine, another error code otherwise.
721*/
722status_t
723BEntry::_Rename(BEntry& target, bool clobber)
724{
725	// check, if there's an entry in the way
726	if (!clobber && target.Exists())
727		return B_FILE_EXISTS;
728	// rename
729	status_t error = _kern_rename(fDirFd, fName, target.fDirFd, target.fName);
730	if (error == B_OK) {
731		Unset();
732		fCStatus = target.fCStatus;
733		fDirFd = target.fDirFd;
734		fName = target.fName;
735		target.fCStatus = B_NO_INIT;
736		target.fDirFd = -1;
737		target.fName = NULL;
738	}
739	return error;
740}
741
742
743/*!	Debugging function, dumps the given entry to stdout.
744
745	\param name A pointer to a string to be printed along with the dump for
746		   identification purposes.
747*/
748void
749BEntry::_Dump(const char* name)
750{
751	if (name != NULL) {
752		printf("------------------------------------------------------------\n");
753		printf("%s\n", name);
754		printf("------------------------------------------------------------\n");
755	}
756
757	printf("fCStatus == %" B_PRId32 "\n", fCStatus);
758
759	struct stat st;
760	if (fDirFd != -1
761		&& _kern_read_stat(fDirFd, NULL, false, &st,
762				sizeof(struct stat)) == B_OK) {
763		printf("dir.device == %" B_PRIdDEV "\n", st.st_dev);
764		printf("dir.inode  == %" B_PRIdINO "\n", st.st_ino);
765	} else {
766		printf("dir == NullFd\n");
767	}
768
769	printf("leaf == '%s'\n", fName);
770	printf("\n");
771
772}
773
774
775status_t
776BEntry::_GetStat(struct stat* st) const
777{
778	if (fCStatus != B_OK)
779		return B_NO_INIT;
780
781	return _kern_read_stat(fDirFd, fName, false, st, sizeof(struct stat));
782}
783
784
785status_t
786BEntry::_GetStat(struct stat_beos* st) const
787{
788	struct stat newStat;
789	status_t error = _GetStat(&newStat);
790	if (error != B_OK)
791		return error;
792
793	convert_to_stat_beos(&newStat, st);
794	return B_OK;
795}
796
797
798// #pragma mark -
799
800
801status_t
802get_ref_for_path(const char* path, entry_ref* ref)
803{
804	status_t error = path && ref ? B_OK : B_BAD_VALUE;
805	if (error == B_OK) {
806		BEntry entry(path);
807		error = entry.InitCheck();
808		if (error == B_OK)
809			error = entry.GetRef(ref);
810	}
811	return error;
812}
813
814
815bool
816operator<(const entry_ref& a, const entry_ref& b)
817{
818	return (a.device < b.device
819		|| (a.device == b.device
820			&& (a.directory < b.directory
821			|| (a.directory == b.directory
822				&& ((a.name == NULL && b.name != NULL)
823				|| (a.name != NULL && b.name != NULL
824					&& strcmp(a.name, b.name) < 0))))));
825}
826
827
828// #pragma mark - symbol versions
829
830
831#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
832#	if __GNUC__ == 2	// gcc 2
833
834	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
835		"GetStat__C6BEntryP4stat@@LIBBE_TEST");
836
837#	else	// gcc 4
838
839	// Haiku GetStat()
840	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
841		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_TEST");
842
843#	endif	// gcc 4
844#else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
845#	if __GNUC__ == 2	// gcc 2
846
847	// BeOS compatible GetStat()
848	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP9stat_beos",
849		"GetStat__C6BEntryP4stat@LIBBE_BASE");
850
851	// Haiku GetStat()
852	B_DEFINE_SYMBOL_VERSION("_GetStat__C6BEntryP4stat",
853		"GetStat__C6BEntryP4stat@@LIBBE_1_ALPHA1");
854
855#	else	// gcc 4
856
857	// BeOS compatible GetStat()
858	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP9stat_beos",
859		"_ZNK6BEntry7GetStatEP4stat@LIBBE_BASE");
860
861	// Haiku GetStat()
862	B_DEFINE_SYMBOL_VERSION("_ZNK6BEntry8_GetStatEP4stat",
863		"_ZNK6BEntry7GetStatEP4stat@@LIBBE_1_ALPHA1");
864
865#	endif	// gcc 4
866#endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
867