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