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