xref: /haiku/src/kits/storage/Directory.cpp (revision 8f03af00f8119491a5d79d1a9859f2247a6d2bcf)
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 
30 BDirectory::BDirectory()
31 	:
32 	fDirFd(-1)
33 {
34 }
35 
36 
37 BDirectory::BDirectory(const BDirectory& dir)
38 	:
39 	fDirFd(-1)
40 {
41 	*this = dir;
42 }
43 
44 
45 BDirectory::BDirectory(const entry_ref* ref)
46 	:
47 	fDirFd(-1)
48 {
49 	SetTo(ref);
50 }
51 
52 
53 BDirectory::BDirectory(const node_ref* nref)
54 	:
55 	fDirFd(-1)
56 {
57 	SetTo(nref);
58 }
59 
60 
61 BDirectory::BDirectory(const BEntry* entry)
62 	:
63 	fDirFd(-1)
64 {
65 	SetTo(entry);
66 }
67 
68 
69 BDirectory::BDirectory(const char* path)
70 	:
71 	fDirFd(-1)
72 {
73 	SetTo(path);
74 }
75 
76 
77 BDirectory::BDirectory(const BDirectory* dir, const char* path)
78 	:
79 	fDirFd(-1)
80 {
81 	SetTo(dir, path);
82 }
83 
84 
85 BDirectory::~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 
96 status_t
97 BDirectory::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 
119 status_t
120 BDirectory::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 
133 status_t
134 BDirectory::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 
161 status_t
162 BDirectory::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 
184 status_t
185 BDirectory::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 
223 status_t
224 BDirectory::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 
234 bool
235 BDirectory::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 
247 status_t
248 BDirectory::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 
272 bool
273 BDirectory::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 
292 bool
293 BDirectory::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 
331 status_t
332 BDirectory::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 
347 status_t
348 BDirectory::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 
372 int32
373 BDirectory::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 
383 status_t
384 BDirectory::Rewind()
385 {
386 	if (InitCheck() != B_OK)
387 		return B_FILE_ERROR;
388 	return _kern_rewind_dir(fDirFd);
389 }
390 
391 
392 int32
393 BDirectory::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 
412 status_t
413 BDirectory::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 
435 status_t
436 BDirectory::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 
457 status_t
458 BDirectory::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 
481 BDirectory&
482 BDirectory::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 
493 status_t
494 BDirectory::_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 
510 status_t
511 BDirectory::_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
524 void BDirectory::_ErectorDirectory1() {}
525 void BDirectory::_ErectorDirectory2() {}
526 void BDirectory::_ErectorDirectory3() {}
527 void BDirectory::_ErectorDirectory4() {}
528 void BDirectory::_ErectorDirectory5() {}
529 void BDirectory::_ErectorDirectory6() {}
530 
531 
532 //! Closes the BDirectory's file descriptor.
533 void
534 BDirectory::close_fd()
535 {
536 	if (fDirFd >= 0) {
537 		_kern_close(fDirFd);
538 		fDirFd = -1;
539 	}
540 	BNode::close_fd();
541 }
542 
543 
544 int
545 BDirectory::get_fd() const
546 {
547 	return fDirFd;
548 }
549 
550 
551 //	#pragma mark - C functions
552 
553 
554 // TODO: Check this method for efficiency.
555 status_t
556 create_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