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 entry;
356	bool next = true;
357	while (next) {
358		if (GetNextDirents(&entry, sizeof(entry), 1) != 1)
359			return B_ENTRY_NOT_FOUND;
360
361		next = (!strcmp(entry.d_name, ".")
362			|| !strcmp(entry.d_name, ".."));
363	}
364
365	ref->device = entry.d_pdev;
366	ref->directory = entry.d_pino;
367	return ref->set_name(entry.d_name);
368}
369
370
371int32
372BDirectory::GetNextDirents(dirent* buf, size_t bufSize, int32 count)
373{
374	if (buf == NULL)
375		return B_BAD_VALUE;
376	if (InitCheck() != B_OK)
377		return B_FILE_ERROR;
378	return _kern_read_dir(fDirFd, buf, bufSize, count);
379}
380
381
382status_t
383BDirectory::Rewind()
384{
385	if (InitCheck() != B_OK)
386		return B_FILE_ERROR;
387	return _kern_rewind_dir(fDirFd);
388}
389
390
391int32
392BDirectory::CountEntries()
393{
394	status_t error = Rewind();
395	if (error != B_OK)
396		return error;
397	int32 count = 0;
398	BPrivate::Storage::LongDirEntry entry;
399	while (error == B_OK) {
400		if (GetNextDirents(&entry, sizeof(entry), 1) != 1)
401			break;
402		if (strcmp(entry.d_name, ".") != 0 && strcmp(entry.d_name, "..") != 0)
403			count++;
404	}
405	Rewind();
406	return (error == B_OK ? count : error);
407}
408
409
410status_t
411BDirectory::CreateDirectory(const char* path, BDirectory* dir)
412{
413	if (!path)
414		return B_BAD_VALUE;
415
416	// create the dir
417	status_t error = _kern_create_dir(fDirFd, path,
418		(S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask);
419	if (error != B_OK)
420		return error;
421
422	if (dir == NULL)
423		return B_OK;
424
425	// init the supplied BDirectory
426	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
427		return dir->SetTo(path);
428
429	return dir->SetTo(this, path);
430}
431
432
433status_t
434BDirectory::CreateFile(const char* path, BFile* file, bool failIfExists)
435{
436	if (!path)
437		return B_BAD_VALUE;
438
439	// Let BFile do the dirty job.
440	uint32 openMode = B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE
441		| (failIfExists ? B_FAIL_IF_EXISTS : 0);
442	BFile tmpFile;
443	BFile* realFile = file ? file : &tmpFile;
444	status_t error = B_OK;
445	if (InitCheck() == B_OK && !BPrivate::Storage::is_absolute_path(path))
446		error = realFile->SetTo(this, path, openMode);
447	else
448		error = realFile->SetTo(path, openMode);
449	if (error != B_OK && file) // mimic R5 behavior
450		file->Unset();
451	return error;
452}
453
454
455status_t
456BDirectory::CreateSymLink(const char* path, const char* linkToPath,
457	BSymLink* link)
458{
459	if (!path || !linkToPath)
460		return B_BAD_VALUE;
461
462	// create the symlink
463	status_t error = _kern_create_symlink(fDirFd, path, linkToPath,
464		(S_IRWXU | S_IRWXG | S_IRWXO) & ~__gUmask);
465	if (error != B_OK)
466		return error;
467
468	if (link == NULL)
469		return B_OK;
470
471	// init the supplied BSymLink
472	if (InitCheck() != B_OK || BPrivate::Storage::is_absolute_path(path))
473		return link->SetTo(path);
474
475	return link->SetTo(this, path);
476}
477
478
479BDirectory&
480BDirectory::operator=(const BDirectory& dir)
481{
482	if (&dir != this) {	// no need to assign us to ourselves
483		Unset();
484		if (dir.InitCheck() == B_OK)
485			SetTo(&dir, ".");
486	}
487	return *this;
488}
489
490
491status_t
492BDirectory::_GetStatFor(const char* path, struct stat* st) const
493{
494	if (!st)
495		return B_BAD_VALUE;
496	if (InitCheck() != B_OK)
497		return B_NO_INIT;
498
499	if (path != NULL) {
500		if (path[0] == '\0')
501			return B_ENTRY_NOT_FOUND;
502		return _kern_read_stat(fDirFd, path, false, st, sizeof(struct stat));
503	}
504	return GetStat(st);
505}
506
507
508status_t
509BDirectory::_GetStatFor(const char* path, struct stat_beos* st) const
510{
511	struct stat newStat;
512	status_t error = _GetStatFor(path, &newStat);
513	if (error != B_OK)
514		return error;
515
516	convert_to_stat_beos(&newStat, st);
517	return B_OK;
518}
519
520
521// FBC
522void BDirectory::_ErectorDirectory1() {}
523void BDirectory::_ErectorDirectory2() {}
524void BDirectory::_ErectorDirectory3() {}
525void BDirectory::_ErectorDirectory4() {}
526void BDirectory::_ErectorDirectory5() {}
527void BDirectory::_ErectorDirectory6() {}
528
529
530//! Closes the BDirectory's file descriptor.
531void
532BDirectory::close_fd()
533{
534	if (fDirFd >= 0) {
535		_kern_close(fDirFd);
536		fDirFd = -1;
537	}
538	BNode::close_fd();
539}
540
541
542int
543BDirectory::get_fd() const
544{
545	return fDirFd;
546}
547
548
549//	#pragma mark - C functions
550
551
552// TODO: Check this method for efficiency.
553status_t
554create_directory(const char* path, mode_t mode)
555{
556	if (!path)
557		return B_BAD_VALUE;
558
559	// That's the strategy: We start with the first component of the supplied
560	// path, create a BPath object from it and successively add the following
561	// components. Each time we get a new path, we check, if the entry it
562	// refers to exists and is a directory. If it doesn't exist, we try
563	// to create it. This goes on, until we're done with the input path or
564	// an error occurs.
565	BPath dirPath;
566	char* component;
567	int32 nextComponent;
568	do {
569		// get the next path component
570		status_t error = BPrivate::Storage::parse_first_path_component(path,
571			component, nextComponent);
572		if (error != B_OK)
573			return error;
574
575		// append it to the BPath
576		if (dirPath.InitCheck() == B_NO_INIT)	// first component
577			error = dirPath.SetTo(component);
578		else
579			error = dirPath.Append(component);
580		delete[] component;
581		if (error != B_OK)
582			return error;
583		path += nextComponent;
584
585		// create a BEntry from the BPath
586		BEntry entry;
587		error = entry.SetTo(dirPath.Path(), true);
588		if (error != B_OK)
589			return error;
590
591		// check, if it exists
592		if (entry.Exists()) {
593			// yep, it exists
594			if (!entry.IsDirectory())	// but is no directory
595				return B_NOT_A_DIRECTORY;
596		} else {
597			// it doesn't exist -- create it
598			error = _kern_create_dir(-1, dirPath.Path(), mode & ~__gUmask);
599			if (error != B_OK)
600				return error;
601		}
602	} while (nextComponent != 0);
603	return B_OK;
604}
605
606
607// #pragma mark - symbol versions
608
609
610#ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
611#	if __GNUC__ == 2	// gcc 2
612
613	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat",
614		"GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_TEST");
615
616#	else	// gcc 4
617
618	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat",
619		"_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_TEST");
620
621#	endif	// gcc 4
622#else	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
623#	if __GNUC__ == 2	// gcc 2
624
625	// BeOS compatible GetStatFor()
626	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP9stat_beos",
627		"GetStatFor__C10BDirectoryPCcP4stat@LIBBE_BASE");
628
629	// Haiku GetStatFor()
630	B_DEFINE_SYMBOL_VERSION("_GetStatFor__C10BDirectoryPCcP4stat",
631		"GetStatFor__C10BDirectoryPCcP4stat@@LIBBE_1_ALPHA1");
632
633#	else	// gcc 4
634
635	// BeOS compatible GetStatFor()
636	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP9stat_beos",
637		"_ZNK10BDirectory10GetStatForEPKcP4stat@LIBBE_BASE");
638
639	// Haiku GetStatFor()
640	B_DEFINE_SYMBOL_VERSION("_ZNK10BDirectory11_GetStatForEPKcP4stat",
641		"_ZNK10BDirectory10GetStatForEPKcP4stat@@LIBBE_1_ALPHA1");
642
643#	endif	// gcc 4
644#endif	// !HAIKU_TARGET_PLATFORM_LIBBE_TEST
645