1 /*
2 * Copyright 2002-2012, Haiku Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Tyler Dauwalder
7 * Axel Dörfler, axeld@pinc-software.de
8 * Ingo Weinhold, bonefish@users.sf.net
9 */
10
11
12 #include <Path.h>
13
14 #include <new>
15
16 #include <Directory.h>
17 #include <Entry.h>
18 #include <StorageDefs.h>
19 #include <String.h>
20
21 #include <syscalls.h>
22
23 #include "storage_support.h"
24
25
26 using namespace std;
27
28
29 // Creates an uninitialized BPath object.
BPath()30 BPath::BPath()
31 :
32 fName(NULL),
33 fCStatus(B_NO_INIT)
34 {
35 }
36
37
38 // Creates a copy of the given BPath object.
BPath(const BPath & path)39 BPath::BPath(const BPath& path)
40 :
41 fName(NULL),
42 fCStatus(B_NO_INIT)
43 {
44 *this = path;
45 }
46
47
48 // Creates a BPath object and initializes it to the filesystem entry
49 // specified by the passed in entry_ref struct.
BPath(const entry_ref * ref)50 BPath::BPath(const entry_ref* ref)
51 :
52 fName(NULL),
53 fCStatus(B_NO_INIT)
54 {
55 SetTo(ref);
56 }
57
58
59 // Creates a BPath object and initializes it to the filesystem entry
60 // specified by the passed in BEntry object.
BPath(const BEntry * entry)61 BPath::BPath(const BEntry* entry)
62 :
63 fName(NULL),
64 fCStatus(B_NO_INIT)
65 {
66 SetTo(entry);
67 }
68
69
70 // Creates a BPath object and initializes it to the specified path or
71 // path and filename combination.
BPath(const char * dir,const char * leaf,bool normalize)72 BPath::BPath(const char* dir, const char* leaf, bool normalize)
73 :
74 fName(NULL),
75 fCStatus(B_NO_INIT)
76 {
77 SetTo(dir, leaf, normalize);
78 }
79
80
81 // Creates a BPath object and initializes it to the specified directory
82 // and filename combination.
BPath(const BDirectory * dir,const char * leaf,bool normalize)83 BPath::BPath(const BDirectory* dir, const char* leaf, bool normalize)
84 :
85 fName(NULL),
86 fCStatus(B_NO_INIT)
87 {
88 SetTo(dir, leaf, normalize);
89 }
90
91
92 // Destroys the BPath object and frees any of its associated resources.
~BPath()93 BPath::~BPath()
94 {
95 Unset();
96 }
97
98
99 // Checks whether or not the object was properly initialized.
100 status_t
InitCheck() const101 BPath::InitCheck() const
102 {
103 return fCStatus;
104 }
105
106
107 // Reinitializes the object to the filesystem entry specified by the
108 // passed in entry_ref struct.
109 status_t
SetTo(const entry_ref * ref)110 BPath::SetTo(const entry_ref* ref)
111 {
112 Unset();
113 if (!ref)
114 return fCStatus = B_BAD_VALUE;
115
116 char path[B_PATH_NAME_LENGTH];
117 fCStatus = _kern_entry_ref_to_path(ref->device, ref->directory,
118 ref->name, path, sizeof(path));
119 if (fCStatus != B_OK)
120 return fCStatus;
121
122 fCStatus = _SetPath(path);
123 // the path is already normalized
124 return fCStatus;
125 }
126
127
128 // Reinitializes the object to the specified filesystem entry.
129 status_t
SetTo(const BEntry * entry)130 BPath::SetTo(const BEntry* entry)
131 {
132 Unset();
133 if (entry == NULL)
134 return B_BAD_VALUE;
135
136 entry_ref ref;
137 fCStatus = entry->GetRef(&ref);
138 if (fCStatus == B_OK)
139 fCStatus = SetTo(&ref);
140
141 return fCStatus;
142 }
143
144
145 // Reinitializes the object to the passed in path or path and
146 // leaf combination.
147 status_t
SetTo(const char * path,const char * leaf,bool normalize)148 BPath::SetTo(const char* path, const char* leaf, bool normalize)
149 {
150 status_t error = (path ? B_OK : B_BAD_VALUE);
151 if (error == B_OK && leaf && BPrivate::Storage::is_absolute_path(leaf))
152 error = B_BAD_VALUE;
153 char newPath[B_PATH_NAME_LENGTH];
154 if (error == B_OK) {
155 // we always normalize relative paths
156 normalize |= !BPrivate::Storage::is_absolute_path(path);
157 // build a new path from path and leaf
158 // copy path first
159 uint32 pathLen = strlen(path);
160 if (pathLen >= sizeof(newPath))
161 error = B_NAME_TOO_LONG;
162 if (error == B_OK)
163 strcpy(newPath, path);
164 // append leaf, if supplied
165 if (error == B_OK && leaf) {
166 bool needsSeparator = (pathLen > 0 && path[pathLen - 1] != '/');
167 uint32 wholeLen = pathLen + (needsSeparator ? 1 : 0)
168 + strlen(leaf);
169 if (wholeLen >= sizeof(newPath))
170 error = B_NAME_TOO_LONG;
171 if (error == B_OK) {
172 if (needsSeparator) {
173 newPath[pathLen] = '/';
174 pathLen++;
175 }
176 strcpy(newPath + pathLen, leaf);
177 }
178 }
179 // check, if necessary to normalize
180 if (error == B_OK && !normalize)
181 normalize = _MustNormalize(newPath, &error);
182
183 // normalize the path, if necessary, otherwise just set it
184 if (error == B_OK) {
185 if (normalize) {
186 // create a BEntry and initialize us with this entry
187 BEntry entry;
188 error = entry.SetTo(newPath, false);
189 if (error == B_OK)
190 return SetTo(&entry);
191 } else
192 error = _SetPath(newPath);
193 }
194 }
195 // cleanup, if something went wrong
196 if (error != B_OK)
197 Unset();
198 fCStatus = error;
199 return error;
200 }
201
202
203 // Reinitializes the object to the passed in dir and relative path combination.
204 status_t
SetTo(const BDirectory * dir,const char * path,bool normalize)205 BPath::SetTo(const BDirectory* dir, const char* path, bool normalize)
206 {
207 status_t error = (dir && dir->InitCheck() == B_OK ? B_OK : B_BAD_VALUE);
208 // get the path of the BDirectory
209 BEntry entry;
210 if (error == B_OK)
211 error = dir->GetEntry(&entry);
212 BPath dirPath;
213 if (error == B_OK)
214 error = dirPath.SetTo(&entry);
215 // let the other version do the work
216 if (error == B_OK)
217 error = SetTo(dirPath.Path(), path, normalize);
218 if (error != B_OK)
219 Unset();
220 fCStatus = error;
221 return error;
222 }
223
224
225 // Returns the object to an uninitialized state.
226 void
Unset()227 BPath::Unset()
228 {
229 _SetPath(NULL);
230 fCStatus = B_NO_INIT;
231 }
232
233
234 // Appends the passed in relative path to the end of the current path.
235 status_t
Append(const char * path,bool normalize)236 BPath::Append(const char* path, bool normalize)
237 {
238 status_t error = (InitCheck() == B_OK ? B_OK : B_BAD_VALUE);
239 if (error == B_OK)
240 error = SetTo(Path(), path, normalize);
241 if (error != B_OK)
242 Unset();
243 fCStatus = error;
244 return error;
245 }
246
247
248 // Gets the entire path of the object.
249 const char*
Path() const250 BPath::Path() const
251 {
252 return fName;
253 }
254
255
256 // Gets the leaf portion of the path.
257 const char*
Leaf() const258 BPath::Leaf() const
259 {
260 if (InitCheck() != B_OK)
261 return NULL;
262
263 const char* result = fName + strlen(fName);
264 // There should be no need for the second condition, since we deal
265 // with absolute paths only and those contain at least one '/'.
266 // However, it doesn't harm.
267 while (*result != '/' && result > fName)
268 result--;
269 result++;
270
271 return result;
272 }
273
274
275 // Initializes path with the parent directory of the BPath object.
276 status_t
GetParent(BPath * path) const277 BPath::GetParent(BPath* path) const
278 {
279 if (path == NULL)
280 return B_BAD_VALUE;
281
282 status_t error = InitCheck();
283 if (error != B_OK)
284 return error;
285
286 int32 length = strlen(fName);
287 if (length == 1) {
288 // handle "/" (path is supposed to be absolute)
289 return B_ENTRY_NOT_FOUND;
290 }
291
292 char parentPath[B_PATH_NAME_LENGTH];
293 length--;
294 while (fName[length] != '/' && length > 0)
295 length--;
296 if (length == 0) {
297 // parent dir is "/"
298 length++;
299 }
300 memcpy(parentPath, fName, length);
301 parentPath[length] = '\0';
302
303 return path->SetTo(parentPath);
304 }
305
306
307 // Gets whether or not the path is absolute or relative.
308 bool
IsAbsolute() const309 BPath::IsAbsolute() const
310 {
311 if (InitCheck() != B_OK)
312 return false;
313
314 return fName[0] == '/';
315 }
316
317
318 // Performs a simple (string-wise) comparison of paths for equality.
319 bool
operator ==(const BPath & item) const320 BPath::operator==(const BPath& item) const
321 {
322 return *this == item.Path();
323 }
324
325
326 // Performs a simple (string-wise) comparison of paths for equality.
327 bool
operator ==(const char * path) const328 BPath::operator==(const char* path) const
329 {
330 return (InitCheck() != B_OK && path == NULL)
331 || (fName != NULL && path != NULL && strcmp(fName, path) == 0);
332 }
333
334
335 // Performs a simple (string-wise) comparison of paths for inequality.
336 bool
operator !=(const BPath & item) const337 BPath::operator!=(const BPath& item) const
338 {
339 return !(*this == item);
340 }
341
342
343 // Performs a simple (string-wise) comparison of paths for inequality.
344 bool
operator !=(const char * path) const345 BPath::operator!=(const char* path) const
346 {
347 return !(*this == path);
348 }
349
350
351 // Initializes the object as a copy of item.
352 BPath&
operator =(const BPath & item)353 BPath::operator=(const BPath& item)
354 {
355 if (this != &item)
356 *this = item.Path();
357 return *this;
358 }
359
360
361 // Initializes the object with the passed in path.
362 BPath&
operator =(const char * path)363 BPath::operator=(const char* path)
364 {
365 if (path == NULL)
366 Unset();
367 else
368 SetTo(path);
369 return *this;
370 }
371
372
373 // #pragma mark - BFlattenable functionality
374
375
376 // that's the layout of a flattened entry_ref
377 struct flattened_entry_ref {
378 dev_t device;
379 ino_t directory;
380 char name[1];
381 };
382
383 // base size of a flattened entry ref
384 static const size_t flattened_entry_ref_size
385 = sizeof(dev_t) + sizeof(ino_t);
386
387
388 // Overrides BFlattenable::IsFixedSize()
389 bool
IsFixedSize() const390 BPath::IsFixedSize() const
391 {
392 return false;
393 }
394
395
396 // Overrides BFlattenable::TypeCode()
397 type_code
TypeCode() const398 BPath::TypeCode() const
399 {
400 return B_REF_TYPE;
401 }
402
403
404 // Gets the size of the flattened entry_ref struct that represents
405 // the path in bytes.
406 ssize_t
FlattenedSize() const407 BPath::FlattenedSize() const
408 {
409 ssize_t size = flattened_entry_ref_size;
410 BEntry entry;
411 entry_ref ref;
412 if (InitCheck() == B_OK
413 && entry.SetTo(Path()) == B_OK
414 && entry.GetRef(&ref) == B_OK) {
415 size += strlen(ref.name) + 1;
416 }
417 return size;
418 }
419
420
421 // Converts the path of the object to an entry_ref and writes it into buffer.
422 status_t
Flatten(void * buffer,ssize_t size) const423 BPath::Flatten(void* buffer, ssize_t size) const
424 {
425 if (buffer == NULL)
426 return B_BAD_VALUE;
427
428 // ToDo: Reimplement for performance reasons: Don't call FlattenedSize().
429 ssize_t flattenedSize = FlattenedSize();
430 if (flattenedSize < 0)
431 return flattenedSize;
432 if (size < flattenedSize)
433 return B_BAD_VALUE;
434
435 // convert the path to an entry_ref
436 BEntry entry;
437 entry_ref ref;
438
439 if (Path() != NULL) {
440 status_t status = entry.SetTo(Path());
441 if (status == B_OK)
442 status = entry.GetRef(&ref);
443 if (status != B_OK)
444 return status;
445 }
446
447 // store the entry_ref in the buffer
448 flattened_entry_ref& fref = *(flattened_entry_ref*)buffer;
449 fref.device = ref.device;
450 fref.directory = ref.directory;
451 if (ref.name)
452 strcpy(fref.name, ref.name);
453
454 return B_OK;
455 }
456
457
458 // Checks if type code is equal to B_REF_TYPE.
459 bool
AllowsTypeCode(type_code code) const460 BPath::AllowsTypeCode(type_code code) const
461 {
462 return code == B_REF_TYPE;
463 }
464
465
466 // Initializes the object with the flattened entry_ref data from the passed
467 // in buffer.
468 status_t
Unflatten(type_code code,const void * buffer,ssize_t size)469 BPath::Unflatten(type_code code, const void* buffer, ssize_t size)
470 {
471 Unset();
472 status_t error = B_OK;
473 // check params
474 if (!(code == B_REF_TYPE && buffer != NULL
475 && size >= (ssize_t)flattened_entry_ref_size)) {
476 error = B_BAD_VALUE;
477 }
478 if (error == B_OK) {
479 if (size == (ssize_t)flattened_entry_ref_size) {
480 // already Unset();
481 } else {
482 // reconstruct the entry_ref from the buffer
483 const flattened_entry_ref& fref
484 = *(const flattened_entry_ref*)buffer;
485 BString name(fref.name, size - flattened_entry_ref_size);
486 entry_ref ref(fref.device, fref.directory, name.String());
487 error = SetTo(&ref);
488 }
489 }
490 if (error != B_OK)
491 fCStatus = error;
492 return error;
493 }
494
495
_WarPath1()496 void BPath::_WarPath1() {}
_WarPath2()497 void BPath::_WarPath2() {}
_WarPath3()498 void BPath::_WarPath3() {}
499
500
501 /*! Sets the supplied path.
502
503 The path is copied, if \a path is \c NULL the path of the object is set to
504 \c NULL as well. The old path is deleted.
505
506 \param path the path to be set
507
508 \returns A status code.
509 \retval B_OK Everything went fine.
510 \retval B_NO_MEMORY Insufficient memory.
511 */
512 status_t
_SetPath(const char * path)513 BPath::_SetPath(const char* path)
514 {
515 status_t error = B_OK;
516 const char* oldPath = fName;
517 // set the new path
518 if (path) {
519 fName = new(nothrow) char[strlen(path) + 1];
520 if (fName)
521 strcpy(fName, path);
522 else
523 error = B_NO_MEMORY;
524 } else
525 fName = NULL;
526
527 // delete the old one
528 delete[] oldPath;
529 return error;
530 }
531
532
533 /*! Checks a path to see if normalization is required.
534
535 The following items require normalization:
536 - Relative pathnames (after concatenation; e.g. "boot/ltj")
537 - The presence of "." or ".." ("/boot/ltj/../ltj/./gwar")
538 - Redundant slashes ("/boot//ltj")
539 - A trailing slash ("/boot/ltj/")
540
541 \param _error A pointer to an error variable that will be set if the input
542 is not a valid path.
543
544 \return \c true if \a path requires normalization, \c false otherwise.
545 */
546 bool
_MustNormalize(const char * path,status_t * _error)547 BPath::_MustNormalize(const char* path, status_t* _error)
548 {
549 // Check for useless input
550 if (path == NULL || path[0] == 0) {
551 if (_error != NULL)
552 *_error = B_BAD_VALUE;
553 return false;
554 }
555
556 int len = strlen(path);
557
558 /* Look for anything in the string that forces us to normalize:
559 + No leading /
560 + any occurence of /./ or /../ or //, or a trailing /. or /..
561 + a trailing /
562 */;
563 if (path[0] != '/')
564 return true; // not "/*"
565 else if (len == 1)
566 return false; // "/"
567 else if (len > 1 && path[len-1] == '/')
568 return true; // "*/"
569 else {
570 enum ParseState {
571 NoMatch,
572 InitialSlash,
573 OneDot,
574 TwoDots
575 } state = NoMatch;
576
577 for (int i = 0; path[i] != 0; i++) {
578 switch (state) {
579 case NoMatch:
580 if (path[i] == '/')
581 state = InitialSlash;
582 break;
583
584 case InitialSlash:
585 if (path[i] == '/')
586 return true; // "*//*"
587
588 if (path[i] == '.')
589 state = OneDot;
590 else
591 state = NoMatch;
592 break;
593
594 case OneDot:
595 if (path[i] == '/')
596 return true; // "*/./*"
597
598 if (path[i] == '.')
599 state = TwoDots;
600 else
601 state = NoMatch;
602 break;
603
604 case TwoDots:
605 if (path[i] == '/')
606 return true; // "*/../*"
607
608 state = NoMatch;
609 break;
610 }
611 }
612
613 // If we hit the end of the string while in either
614 // of these two states, there was a trailing /. or /..
615 if (state == OneDot || state == TwoDots)
616 return true;
617
618 return false;
619 }
620 }
621