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