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