xref: /haiku/src/build/libbe/storage/Directory.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
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 
BDirectory()27 BDirectory::BDirectory()
28 	:
29 	fDirFd(-1)
30 {
31 }
32 
33 
BDirectory(const BDirectory & dir)34 BDirectory::BDirectory(const BDirectory& dir)
35 	:
36 	fDirFd(-1)
37 {
38 	*this = dir;
39 }
40 
41 
BDirectory(const entry_ref * ref)42 BDirectory::BDirectory(const entry_ref* ref)
43 	:
44 	fDirFd(-1)
45 {
46 	SetTo(ref);
47 }
48 
49 
BDirectory(const node_ref * nref)50 BDirectory::BDirectory(const node_ref* nref)
51 	:
52 	fDirFd(-1)
53 {
54 	SetTo(nref);
55 }
56 
57 
BDirectory(const BEntry * entry)58 BDirectory::BDirectory(const BEntry* entry)
59 	:
60 	fDirFd(-1)
61 {
62 	SetTo(entry);
63 }
64 
65 
BDirectory(const char * path)66 BDirectory::BDirectory(const char* path)
67 	:
68 	fDirFd(-1)
69 {
70 	SetTo(path);
71 }
72 
73 
BDirectory(const BDirectory * dir,const char * path)74 BDirectory::BDirectory(const BDirectory* dir, const char* path)
75 	:
76 	fDirFd(-1)
77 {
78 	SetTo(dir, path);
79 }
80 
81 
~BDirectory()82 BDirectory::~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 
93 status_t
SetTo(const entry_ref * ref)94 BDirectory::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 
115 status_t
SetTo(const node_ref * nref)116 BDirectory::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 
129 status_t
SetTo(const BEntry * entry)130 BDirectory::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 
156 status_t
SetTo(const char * path)157 BDirectory::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 
178 status_t
SetTo(const BDirectory * dir,const char * path)179 BDirectory::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 
216 status_t
GetEntry(BEntry * entry) const217 BDirectory::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 
227 status_t
FindEntry(const char * path,BEntry * entry,bool traverse) const228 BDirectory::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 
252 bool
Contains(const char * path,int32 nodeFlags) const253 BDirectory::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 
272 bool
Contains(const BEntry * entry,int32 nodeFlags) const273 BDirectory::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 
311 status_t
GetNextEntry(BEntry * entry,bool traverse)312 BDirectory::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 
327 status_t
GetNextRef(entry_ref * ref)328 BDirectory::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 
352 int32
GetNextDirents(dirent * buf,size_t bufSize,int32 count)353 BDirectory::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 
363 status_t
Rewind()364 BDirectory::Rewind()
365 {
366 	if (InitCheck() != B_OK)
367 		return B_FILE_ERROR;
368 	return _kern_rewind_dir(fDirFd);
369 }
370 
371 
372 int32
CountEntries()373 BDirectory::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 
392 status_t
CreateDirectory(const char * path,BDirectory * dir)393 BDirectory::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 
415 status_t
CreateFile(const char * path,BFile * file,bool failIfExists)416 BDirectory::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 
437 status_t
CreateSymLink(const char * path,const char * linkToPath,BSymLink * link)438 BDirectory::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 
461 BDirectory&
operator =(const BDirectory & dir)462 BDirectory::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 
473 status_t
GetStatFor(const char * path,struct stat * st) const474 BDirectory::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
_ErectorDirectory1()491 void BDirectory::_ErectorDirectory1() {}
_ErectorDirectory2()492 void BDirectory::_ErectorDirectory2() {}
_ErectorDirectory3()493 void BDirectory::_ErectorDirectory3() {}
_ErectorDirectory4()494 void BDirectory::_ErectorDirectory4() {}
_ErectorDirectory5()495 void BDirectory::_ErectorDirectory5() {}
_ErectorDirectory6()496 void BDirectory::_ErectorDirectory6() {}
497 
498 
499 //! Closes the BDirectory's file descriptor.
500 void
close_fd()501 BDirectory::close_fd()
502 {
503 	if (fDirFd >= 0) {
504 		_kern_close(fDirFd);
505 		fDirFd = -1;
506 	}
507 	BNode::close_fd();
508 }
509 
510 
511 int
get_fd() const512 BDirectory::get_fd() const
513 {
514 	return fDirFd;
515 }
516 
517 
518 status_t
set_dir_fd(int fd)519 BDirectory::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.
538 status_t
create_directory(const char * path,mode_t mode)539 create_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