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
505status_t
506BEntry::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
516status_t
517BEntry::_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
646status_t
647BEntry::_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
662status_t
663BEntry::_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
683void
684BEntry::_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
710status_t
711BEntry::_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
720status_t
721BEntry::_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
736status_t
737get_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
750bool
751operator<(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