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. 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. 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. 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. 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. 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. 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. 93 BPath::~BPath() 94 { 95 Unset(); 96 } 97 98 99 // Checks whether or not the object was properly initialized. 100 status_t 101 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 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 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 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 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 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 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* 250 BPath::Path() const 251 { 252 return fName; 253 } 254 255 256 // Gets the leaf portion of the path. 257 const char* 258 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 277 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 309 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 320 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 328 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 337 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 345 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& 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& 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 390 BPath::IsFixedSize() const 391 { 392 return false; 393 } 394 395 396 // Overrides BFlattenable::TypeCode() 397 type_code 398 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 407 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 423 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 460 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 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 496 void BPath::_WarPath1() {} 497 void BPath::_WarPath2() {} 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 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 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