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 *		Axel D��rfler, axeld@pinc-software.de
9 */
10
11
12#include "storage_support.h"
13
14#include <fcntl.h>
15#include <string.h>
16
17#include <compat/sys/stat.h>
18
19#include <Directory.h>
20#include <Entry.h>
21#include <File.h>
22#include <fs_info.h>
23#include <Path.h>
24#include <SymLink.h>
25
26#include <syscalls.h>
27#include <umask.h>
28
29
30BDirectory::BDirectory()
31	:
32	fDirFd(-1)
33{
34}
35
36
37BDirectory::BDirectory(const BDirectory& dir)
38	:
39	fDirFd(-1)
40{
41	*this = dir;
42}
43
44
45BDirectory::BDirectory(const entry_ref* ref)
46	:
47	fDirFd(-1)
48{
49	SetTo(ref);
50}
51
52
53BDirectory::BDirectory(const node_ref* nref)
54	:
55	fDirFd(-1)
56{
57	SetTo(nref);
58}
59
60
61BDirectory::BDirectory(const BEntry* entry)
62	:
63	fDirFd(-1)
64{
65	SetTo(entry);
66}
67
68
69BDirectory::BDirectory(const char* path)
70	:
71	fDirFd(-1)
72{
73	SetTo(path);
74}
75
76
77BDirectory::BDirectory(const BDirectory* dir, const char* path)
78	:
79	fDirFd(-1)
80{
81	SetTo(dir, path);
82}
83
84
85BDirectory::~BDirectory()
86{
87	// Also called by the BNode destructor, but we rather try to avoid
88	// problems with calling virtual functions in the base class destructor.
89	// Depending on the compiler implementation an object may be degraded to
90	// an object of the base class after the destructor of the derived class
91	// has been executed.
92	close_fd();
93}
94
95
96status_t
97BDirectory::SetTo(const entry_ref* ref)
98{
99	// open node
100	status_t error = _SetTo(ref, true);
101	if (error != B_OK)
102		return error;
103
104	// open dir
105	fDirFd = _kern_open_dir_entry_ref(ref->device, ref->directory, ref->name);
106	if (fDirFd < 0) {
107		status_t error = fDirFd;
108		Unset();
109		return (fCStatus = error);
110	}
111
112	// set close on exec flag on dir FD
113	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
114
115	return B_OK;
116}
117
118
119status_t
120BDirectory::SetTo(const node_ref* nref)
121{
122	Unset();
123	status_t error = (nref ? B_OK : B_BAD_VALUE);
124	if (error == B_OK) {
125		entry_ref ref(nref->device, nref->node, ".");
126		error = SetTo(&ref);
127	}
128	set_status(error);
129	return error;
130}
131
132
133status_t
134BDirectory::SetTo(const BEntry* entry)
135{
136	if (!entry) {
137		Unset();
138		return (fCStatus = B_BAD_VALUE);
139	}
140
141	// open node
142	status_t error = _SetTo(entry->fDirFd, entry->fName, true);
143	if (error != B_OK)
144		return error;
145
146	// open dir
147	fDirFd = _kern_open_dir(entry->fDirFd, entry->fName);
148	if (fDirFd < 0) {
149		status_t error = fDirFd;
150		Unset();
151		return (fCStatus = error);
152	}
153
154	// set close on exec flag on dir FD
155	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
156
157	return B_OK;
158}
159
160
161status_t
162BDirectory::SetTo(const char* path)
163{
164	// open node
165	status_t error = _SetTo(-1, path, true);
166	if (error != B_OK)
167		return error;
168
169	// open dir
170	fDirFd = _kern_open_dir(-1, path);
171	if (fDirFd < 0) {
172		status_t error = fDirFd;
173		Unset();
174		return (fCStatus = error);
175	}
176
177	// set close on exec flag on dir FD
178	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
179
180	return B_OK;
181}
182
183
184status_t
185BDirectory::SetTo(const BDirectory* dir, const char* path)
186{
187	if (!dir || !path || BPrivate::Storage::is_absolute_path(path)) {
188		Unset();
189		return (fCStatus = B_BAD_VALUE);
190	}
191
192	int dirFD = dir->fDirFd;
193	if (dir == this) {
194		// prevent that our file descriptor goes away in _SetTo()
195		fDirFd = -1;
196	}
197
198	// open node
199	status_t error = _SetTo(dirFD, path, true);
200	if (error != B_OK)
201		return error;
202
203	// open dir
204	fDirFd = _kern_open_dir(dirFD, path);
205	if (fDirFd < 0) {
206		status_t error = fDirFd;
207		Unset();
208		return (fCStatus = error);
209	}
210
211	if (dir == this) {
212		// cleanup after _SetTo()
213		_kern_close(dirFD);
214	}
215
216	// set close on exec flag on dir FD
217	fcntl(fDirFd, F_SETFD, FD_CLOEXEC);
218
219	return B_OK;
220}
221
222
223status_t
224BDirectory::GetEntry(BEntry* entry) const
225{
226	if (!entry)
227		return B_BAD_VALUE;
228	if (InitCheck() != B_OK)
229		return B_NO_INIT;
230	return entry->SetTo(this, ".", false);
231}
232
233
234bool
235BDirectory::IsRootDirectory() const
236{
237	// compare the directory's node ID with the ID of the root node of the FS
238	bool result = false;
239	node_ref ref;
240	fs_info info;
241	if (GetNodeRef(&ref) == B_OK && fs_stat_dev(ref.device, &info) == 0)
242		result = (ref.node == info.root);
243	return result;
244}
245
246
247status_t
248BDirectory::FindEntry(const char* path, BEntry* entry, bool traverse) const
249{
250	if (path == NULL || entry == NULL)
251		return B_BAD_VALUE;
252
253	entry->Unset();
254
255	// init a potentially abstract entry
256	status_t status;
257	if (InitCheck() == B_OK)
258		status = entry->SetTo(this, path, traverse);
259	else
260		status = entry->SetTo(path, traverse);
261
262	// fail, if entry is abstract
263	if (status == B_OK && !entry->Exists()) {
264		status = B_ENTRY_NOT_FOUND;
265		entry->Unset();
266	}
267
268	return status;
269}
270
271
272bool
273BDirectory::Contains(const char* path, int32 nodeFlags) const
274{
275	// check initialization and parameters
276	if (InitCheck() != B_OK)
277		return false;
278	if (!path)
279		return true;	// mimic R5 behavior
280
281	// turn the path into a BEntry and let the other version do the work
282	BEntry entry;
283	if (BPrivate::Storage::is_absolute_path(path))
284		entry.SetTo(path);
285	else
286		entry.SetTo(this, path);
287
288	return Contains(&entry, nodeFlags);
289}
290
291
292bool
293BDirectory::Contains(const BEntry* entry, int32 nodeFlags) const
294{
295	// check, if the entry exists at all
296	if (entry == NULL || !entry->Exists() || InitCheck() != B_OK)
297		return false;
298
299	if (nodeFlags != B_ANY_NODE) {
300		// test the node kind
301		bool result = false;
302		if ((nodeFlags & B_FILE_NODE) != 0)
303			result = entry->IsFile();
304		if (!result && (nodeFlags & B_DIRECTORY_NODE) != 0)
305			result = entry->IsDirectory();
306		if (!result && (nodeFlags & B_SYMLINK_NODE) != 0)
307			result = entry->IsSymLink();
308		if (!result)
309			return false;
310	}
311
312	// If the directory is initialized, get the canonical paths of the dir and
313	// the entry and check, if the latter is a prefix of the first one.
314	BPath dirPath(this, ".", true);
315	BPath entryPath(entry);
316	if (dirPath.InitCheck() != B_OK || entryPath.InitCheck() != B_OK)
317		return false;
318
319	uint32 dirLen = strlen(dirPath.Path());
320
321	if (!strncmp(dirPath.Path(), entryPath.Path(), dirLen)) {
322		// if the paths are identical, return a match to stay consistent with
323		// BeOS behavior.
324		if (entryPath.Path()[dirLen] == '\0' || entryPath.Path()[dirLen] == '/')
325			return true;
326	}
327	return false;
328}
329
330
331status_t
332BDirectory::GetNextEntry(BEntry* entry, bool traverse)
333{
334	if (entry == NULL)
335		return B_BAD_VALUE;
336
337	entry_ref ref;
338	status_t status = GetNextRef(&ref);
339	if (status != B_OK) {
340		entry->Unset();
341		return status;
342	}
343	return entry->SetTo(&ref, traverse);
344}
345
346
347status_t
348BDirectory::GetNextRef(entry_ref* ref)
349{
350	if (ref == NULL)
351		return B_BAD_VALUE;
352	if (InitCheck() != B_OK)
353		return B_FILE_ERROR;
354
355	BPrivate::Storage::LongDirEntry longEntry;
356	struct dirent* entry = longEntry.dirent();
357	bool next = true;
358	while (next) {
359		if (GetNextDirents(entry, sizeof(longEntry), 1) != 1)
360			return B_ENTRY_NOT_FOUND;
361
362		next = (!strcmp(entry->d_name, ".")
363			|| !strcmp(entry->d_name, ".."));
364	}
365
366	ref->device = entry->d_pdev;
367	ref->directory = entry->d_pino;
368	return ref->set_name(entry->d_name);
369}
370
371
372int32
373BDirectory::GetNextDirents(dirent* buf, size_t bufSize, int32 count)
374{
375	if (buf == NULL)
376		return B_BAD_VALUE;
377	if (InitCheck() != B_OK)
378		return B_FILE_ERROR;
379	return _kern_read_dir(fDirFd, buf, bufSize, count);
380}
381
382
383status_t
384BDirectory::Rewind()
385{
386	if (InitCheck() != B_OK)
387		return B_FILE_ERROR;
388	return _kern_rewind_dir(fDirFd);
389}
390
391
392int32
393BDirectory::CountEntries()
394{
395	status_t error = Rewind();
396	if (error != B_OK)
397		return error;
398	int32 count = 0;
399	BPrivate::Storage::LongDirEntry longEntry;
400	struct dirent* entry = longEntry.dirent();
401	while (error == B_OK) {
402		if (GetNextDirents(entry, sizeof(longEntry), 1) != 1)
403			break;
404		if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
405			count++;
406	}
407	Rewind();
408	return (error == B_OK ? count : error);
409}
410
411
412status_t
413BDirectory::CreateDirectory(const char* path, BDirectory* dir)
414{
415	if (!path)
416		return B_BAD_VALUE;
417
418	// create the dir
419	status_t error = _kern_create_dir(fDirFd, path,
420		(S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask);
421	if (error != B_OK)
422		return error;
423
424	if (dir == NULL)
425		return B_OK;
426
427	// init the supplied BDirectory
428	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
429		return dir->SetTo(path);
430
431	return dir->SetTo(this, path);
432}
433
434
435status_t
436BDirectory::CreateFile(const char* path, BFile* file, bool failIfExists)
437{
438	if (!path)
439		return B_BAD_VALUE;
440
441	// Let BFile do the dirty job.
442	uint32 openMode = B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE
443		| (failIfExists ? B_FAIL_IF_EXISTS : 0);
444	BFile tmpFile;
445	BFile* realFile = file ? file : &tmpFile;
446	status_t error = B_OK;
447	if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path))
448		error = realFile->SetTo(this, path, openMode);
449	else
450		error = realFile->SetTo(path, openMode);
451	if (error != B_OK && file) // mimic R5 behavior
452		file->Unset();
453	return error;
454}
455
456
457status_t
458BDirectory::CreateSymLink(const char* path, const char* linkToPath,
459	BSymLink* link)
460{
461	if (!path || !linkToPath)
462		return B_BAD_VALUE;
463
464	// create the symlink
465	status_t error = _kern_create_symlink(fDirFd, path, linkToPath,
466		(S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask);
467	if (error != B_OK)
468		return error;
469
470	if (link == NULL)
471		return B_OK;
472
473	// init the supplied BSymLink
474	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
475		return link->SetTo(path);
476
477	return link->SetTo(this, path);
478}
479
480
481BDirectory&
482BDirectory::operator=(const BDirectory& dir)
483{
484	if (&dir != this) {	// no need to assign us to ourselves
485		Unset();
486		if (dir.InitCheck() == B_OK)
487			SetTo(&dir, ".");
488	}
489	return *this;
490}
491
492
493status_t
494BDirectory::_GetStatFor(const char* path, struct stat* st) const
495{
496	if (!st)
497		return B_BAD_VALUE;
498	if (InitCheck() != B_OK)
499		return B_NO_INIT;
500
501	if (path != NULL) {
502		if (path[0] == '\0')
503			return B_ENTRY_NOT_FOUND;
504		return _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat));
505	}
506	return GetStat(st);
507}
508
509
510status_t
511BDirectory::_GetStatFor(const char* path, struct stat_beos* st) const
512{
513	struct stat newStat;
514	status_t error = _GetStatFor(path, &newStat);
515	if (error != B_OK)
516		return error;
517
518	convert_to_stat_beos(&newStat, st);
519	return B_OK;
520}
521
522
523// FBC
524void BDirectory::_ErectorDirectory1() {}
525void BDirectory::_ErectorDirectory2() {}
526void BDirectory::_ErectorDirectory3() {}
527void BDirectory::_ErectorDirectory4() {}
528void BDirectory::_ErectorDirectory5() {}
529void BDirectory::_ErectorDirectory6() {}
530
531
532//! Closes the BDirectory's file descriptor.
533void
534BDirectory::close_fd()
535{
536	if (fDirFd >= 0) {
537		_kern_close(fDirFd);
538		fDirFd = -1;
539	}
540	BNode::close_fd();
541}
542
543
544int
545BDirectory::get_fd() const
546{
547	return fDirFd;
548}
549
550
551//	#pragma mark - C functions
552
553
554// TODO: Check this method for efficiency.
555status_t
556create_directory(const char* path, mode_t mode)
557{
558	if (!path)
559		return B_BAD_VALUE;
560
561	// That's the strategy: We start with the first component of the supplied
562	// path, create a BPath object from it and successively add the following
563	// components. Each time we get a new path, we check, if the entry it
564	// refers to exists and is a directory. If it doesn't exist, we try
565	// to create it. This goes on, until we're done with the input path or
566	// an error occurs.
567	BPath dirPath;
568	char* component;
569	int32 nextComponent;
570	do {
571		// get the next path component
572		status_t error = BPrivate::Storage::parse_first_path_component(path,
573			component, nextComponent);
574		if (error != B_OK)
575			return error;
576
577		// append it to the BPath
578		if (dirPath.InitCheck() == B_NO_INIT)	// first component
579			error = dirPath.SetTo(component);
580		else
581			error = dirPath.Append(component);
582		delete[] component;
583		if (error != B_OK)
584			return error;
585		path += nextComponent;
586
587		// create a BEntry from the BPath
588		BEntry entry;
589		error = entry.SetTo(dirPath.Path(), true);
590		if (error != B_OK)
591			return error;
592
593		// check, if it exists
594		if (entry.Exists()) {
595			// yep, it exists
596			if (!entry.IsDirectory())	// but is no directory
597				return B_NOT_A_DIRECTORY;
598		} else {
599			// it doesn't exist -- create it
600			error = _kern_create_dir(-1, dirPath.Path(), mode & ~__gUmask);
601			if (error != B_OK)
602				return error;
603		}
604	} while (nextComponent != 0);
605	return B_OK;
606}
607
608
609// #pragma mark - symbol versions
610
611
612#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
613#	if __GNUC__ == 2	// gcc 2
614
615	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat",
616		"GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_TEST");
617
618#	else	// gcc 4
619
620	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat",
621		"_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_TEST");
622
623#	endif	// gcc 4
624#else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
625#	if __GNUC__ == 2	// gcc 2
626
627	// BeOS compatible GetStatFor()
628	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP9stat_beos",
629		"GetStatFor__C10BDirectoryPCcP4stat@LIBBE_BASE");
630
631	// Haiku GetStatFor()
632	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat",
633		"GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_1_ALPHA1");
634
635#	else	// gcc 4
636
637	// BeOS compatible GetStatFor()
638	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP9stat_beos",
639		"_ZNK10BDirectory10GetStatForEPKcP4stat@LIBBE_BASE");
640
641	// Haiku GetStatFor()
642	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat",
643		"_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_1_ALPHA1");
644
645#	endif	// gcc 4
646#endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
647