xref: /haiku/src/kits/storage/Directory.cpp (revision 54624bda43f13312a491fe1c91d22834be3374f5)
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 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 
371 int32
372 BDirectory::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 
382 status_t
383 BDirectory::Rewind()
384 {
385 	if (InitCheck() != B_OK)
386 		return B_FILE_ERROR;
387 	return _kern_rewind_dir(fDirFd);
388 }
389 
390 
391 int32
392 BDirectory::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 
410 status_t
411 BDirectory::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 
433 status_t
434 BDirectory::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 
455 status_t
456 BDirectory::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 
479 BDirectory&
480 BDirectory::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 
491 status_t
492 BDirectory::_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 
508 status_t
509 BDirectory::_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
522 void BDirectory::_ErectorDirectory1() {}
523 void BDirectory::_ErectorDirectory2() {}
524 void BDirectory::_ErectorDirectory3() {}
525 void BDirectory::_ErectorDirectory4() {}
526 void BDirectory::_ErectorDirectory5() {}
527 void BDirectory::_ErectorDirectory6() {}
528 
529 
530 //! Closes the BDirectory's file descriptor.
531 void
532 BDirectory::close_fd()
533 {
534 	if (fDirFd >= 0) {
535 		_kern_close(fDirFd);
536 		fDirFd = -1;
537 	}
538 	BNode::close_fd();
539 }
540 
541 
542 int
543 BDirectory::get_fd() const
544 {
545 	return fDirFd;
546 }
547 
548 
549 //	#pragma mark - C functions
550 
551 
552 // TODO: Check this method for efficiency.
553 status_t
554 create_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