1 /* 2 * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2008-2017, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 /*! A simple class wrapping a path. Has a fixed-sized buffer. */ 9 10 11 #include <fs/KPath.h> 12 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <team.h> 17 #include <vfs.h> 18 #include <slab/Slab.h> 19 20 21 // debugging 22 #define TRACE(x) ; 23 //#define TRACE(x) dprintf x 24 25 26 #ifdef _KERNEL_MODE 27 extern object_cache* sPathNameCache; 28 #endif 29 30 31 KPath::KPath(size_t bufferSize) 32 : 33 fBuffer(NULL), 34 fBufferSize(0), 35 fPathLength(0), 36 fLocked(false), 37 fLazy(false), 38 fFailed(false), 39 fIsNull(false) 40 { 41 SetTo(NULL, DEFAULT, bufferSize); 42 } 43 44 45 KPath::KPath(const char* path, int32 flags, size_t bufferSize) 46 : 47 fBuffer(NULL), 48 fBufferSize(0), 49 fPathLength(0), 50 fLocked(false), 51 fLazy(false), 52 fFailed(false), 53 fIsNull(false) 54 { 55 SetTo(path, flags, bufferSize); 56 } 57 58 59 KPath::KPath(const KPath& other) 60 : 61 fBuffer(NULL), 62 fBufferSize(0), 63 fPathLength(0), 64 fLocked(false), 65 fLazy(false), 66 fFailed(false), 67 fIsNull(false) 68 { 69 *this = other; 70 } 71 72 73 KPath::~KPath() 74 { 75 _FreeBuffer(); 76 } 77 78 79 status_t 80 KPath::SetTo(const char* path, int32 flags, size_t bufferSize) 81 { 82 if (bufferSize == 0) 83 bufferSize = B_PATH_NAME_LENGTH + 1; 84 85 // free the previous buffer, if the buffer size differs 86 if (fBuffer != NULL && fBufferSize != bufferSize) { 87 _FreeBuffer(); 88 fBufferSize = 0; 89 } 90 91 fPathLength = 0; 92 fLocked = false; 93 fBufferSize = bufferSize; 94 fLazy = (flags & LAZY_ALLOC) != 0; 95 fIsNull = path == NULL; 96 97 if (path != NULL || !fLazy) { 98 status_t status = _AllocateBuffer(); 99 if (status != B_OK) 100 return status; 101 } 102 103 return SetPath(path, flags); 104 } 105 106 107 void 108 KPath::Adopt(KPath& other) 109 { 110 _FreeBuffer(); 111 112 fBuffer = other.fBuffer; 113 fBufferSize = other.fBufferSize; 114 fPathLength = other.fPathLength; 115 fLazy = other.fLazy; 116 fFailed = other.fFailed; 117 fIsNull = other.fIsNull; 118 119 other.fBuffer = NULL; 120 if (!other.fLazy) 121 other.fBufferSize = 0; 122 other.fPathLength = 0; 123 other.fFailed = false; 124 other.fIsNull = other.fLazy; 125 } 126 127 128 status_t 129 KPath::InitCheck() const 130 { 131 if (fBuffer != NULL || (fLazy && !fFailed && fBufferSize != 0)) 132 return B_OK; 133 134 return fFailed ? B_NO_MEMORY : B_NO_INIT; 135 } 136 137 138 /*! \brief Sets the buffer to \a path. 139 140 \param flags Understands the following two options: 141 - \c NORMALIZE 142 - \c TRAVERSE_LEAF_LINK 143 */ 144 status_t 145 KPath::SetPath(const char* path, int32 flags) 146 { 147 if (path == NULL && fLazy && fBuffer == NULL) { 148 fIsNull = true; 149 return B_OK; 150 } 151 152 if (fBuffer == NULL) { 153 if (fLazy) { 154 status_t status = _AllocateBuffer(); 155 if (status != B_OK) 156 return B_NO_MEMORY; 157 } else 158 return B_NO_INIT; 159 } 160 161 fIsNull = false; 162 163 if (path != NULL) { 164 if ((flags & NORMALIZE) != 0) { 165 // normalize path 166 status_t status = _Normalize(path, 167 (flags & TRAVERSE_LEAF_LINK) != 0); 168 if (status != B_OK) 169 return status; 170 } else { 171 // don't normalize path 172 size_t length = strlen(path); 173 if (length >= fBufferSize) 174 return B_BUFFER_OVERFLOW; 175 176 memcpy(fBuffer, path, length + 1); 177 fPathLength = length; 178 _ChopTrailingSlashes(); 179 } 180 } else { 181 fBuffer[0] = '\0'; 182 fPathLength = 0; 183 if (fLazy) 184 fIsNull = true; 185 } 186 return B_OK; 187 } 188 189 190 const char* 191 KPath::Path() const 192 { 193 return fIsNull ? NULL : fBuffer; 194 } 195 196 197 /*! \brief Locks the buffer for external changes. 198 199 \param force In lazy mode, this will allocate a buffer when set. 200 Otherwise, \c NULL will be returned if set to NULL. 201 */ 202 char* 203 KPath::LockBuffer(bool force) 204 { 205 if (fBuffer == NULL && fLazy) { 206 if (fIsNull && !force) 207 return NULL; 208 209 _AllocateBuffer(); 210 } 211 212 if (fBuffer == NULL || fLocked) 213 return NULL; 214 215 fLocked = true; 216 fIsNull = false; 217 218 return fBuffer; 219 } 220 221 222 void 223 KPath::UnlockBuffer() 224 { 225 if (!fLocked) { 226 #ifdef _KERNEL_MODE 227 panic("KPath::UnlockBuffer(): Buffer not locked!"); 228 #endif 229 return; 230 } 231 232 fLocked = false; 233 234 if (fBuffer == NULL) 235 return; 236 237 fPathLength = strnlen(fBuffer, fBufferSize); 238 if (fPathLength == fBufferSize) { 239 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); 240 fPathLength--; 241 fBuffer[fPathLength] = '\0'; 242 } 243 _ChopTrailingSlashes(); 244 } 245 246 247 char* 248 KPath::DetachBuffer() 249 { 250 char* buffer = fBuffer; 251 252 if (fBufferSize == (B_PATH_NAME_LENGTH + 1)) { 253 buffer = (char*)malloc(fBufferSize); 254 memcpy(buffer, fBuffer, fBufferSize); 255 _FreeBuffer(); 256 } 257 258 if (fBuffer != NULL) { 259 fBuffer = NULL; 260 fPathLength = 0; 261 fLocked = false; 262 } 263 264 return buffer; 265 } 266 267 268 const char* 269 KPath::Leaf() const 270 { 271 if (fBuffer == NULL) 272 return NULL; 273 274 for (int32 i = fPathLength - 1; i >= 0; i--) { 275 if (fBuffer[i] == '/') 276 return fBuffer + i + 1; 277 } 278 279 return fBuffer; 280 } 281 282 283 status_t 284 KPath::ReplaceLeaf(const char* newLeaf) 285 { 286 const char* leaf = Leaf(); 287 if (leaf == NULL) 288 return B_NO_INIT; 289 290 int32 leafIndex = leaf - fBuffer; 291 // chop off the current leaf (don't replace "/", though) 292 if (leafIndex != 0 || fBuffer[leafIndex - 1]) { 293 fBuffer[leafIndex] = '\0'; 294 fPathLength = leafIndex; 295 _ChopTrailingSlashes(); 296 } 297 298 // if a leaf was given, append it 299 if (newLeaf != NULL) 300 return Append(newLeaf); 301 return B_OK; 302 } 303 304 305 bool 306 KPath::RemoveLeaf() 307 { 308 // get the leaf -- bail out, if not initialized or only the "/" is left 309 const char* leaf = Leaf(); 310 if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0') 311 return false; 312 313 // chop off the leaf 314 int32 leafIndex = leaf - fBuffer; 315 fBuffer[leafIndex] = '\0'; 316 fPathLength = leafIndex; 317 _ChopTrailingSlashes(); 318 319 return true; 320 } 321 322 323 status_t 324 KPath::Append(const char* component, bool isComponent) 325 { 326 // check initialization and parameter 327 if (fBuffer == NULL) 328 return B_NO_INIT; 329 if (component == NULL) 330 return B_BAD_VALUE; 331 if (fPathLength == 0) 332 return SetPath(component); 333 334 // get component length 335 size_t componentLength = strlen(component); 336 if (componentLength < 1) 337 return B_OK; 338 339 // if our current path is empty, we just copy the supplied one 340 // compute the result path len 341 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' 342 && component[0] != '/'; 343 size_t resultPathLength = fPathLength + componentLength 344 + (insertSlash ? 1 : 0); 345 if (resultPathLength >= fBufferSize) 346 return B_BUFFER_OVERFLOW; 347 348 // compose the result path 349 if (insertSlash) 350 fBuffer[fPathLength++] = '/'; 351 memcpy(fBuffer + fPathLength, component, componentLength + 1); 352 fPathLength = resultPathLength; 353 return B_OK; 354 } 355 356 357 status_t 358 KPath::Normalize(bool traverseLeafLink) 359 { 360 if (fBuffer == NULL) 361 return B_NO_INIT; 362 if (fPathLength == 0) 363 return B_BAD_VALUE; 364 365 return _Normalize(fBuffer, traverseLeafLink); 366 } 367 368 369 KPath& 370 KPath::operator=(const KPath& other) 371 { 372 if (other.fBuffer == fBuffer) 373 return *this; 374 375 SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT, 376 other.fBufferSize); 377 return *this; 378 } 379 380 381 KPath& 382 KPath::operator=(const char* path) 383 { 384 SetPath(path); 385 return *this; 386 } 387 388 389 bool 390 KPath::operator==(const KPath& other) const 391 { 392 if (fBuffer == NULL) 393 return !other.fBuffer; 394 395 return other.fBuffer != NULL 396 && fPathLength == other.fPathLength 397 && strcmp(fBuffer, other.fBuffer) == 0; 398 } 399 400 401 bool 402 KPath::operator==(const char* path) const 403 { 404 if (fBuffer == NULL) 405 return path == NULL; 406 407 return path != NULL && strcmp(fBuffer, path) == 0; 408 } 409 410 411 bool 412 KPath::operator!=(const KPath& other) const 413 { 414 return !(*this == other); 415 } 416 417 418 bool 419 KPath::operator!=(const char* path) const 420 { 421 return !(*this == path); 422 } 423 424 425 status_t 426 KPath::_AllocateBuffer() 427 { 428 if (fBuffer == NULL && fBufferSize != 0) { 429 #ifdef _KERNEL_MODE 430 if (fBufferSize == (B_PATH_NAME_LENGTH + 1)) 431 fBuffer = (char*)object_cache_alloc(sPathNameCache, 0); 432 else 433 #endif 434 fBuffer = (char*)malloc(fBufferSize); 435 } 436 if (fBuffer == NULL) { 437 fFailed = true; 438 return B_NO_MEMORY; 439 } 440 441 memset(fBuffer, 0, fBufferSize); 442 fFailed = false; 443 return B_OK; 444 } 445 446 447 void 448 KPath::_FreeBuffer() 449 { 450 #ifdef _KERNEL_MODE 451 if (fBufferSize == (B_PATH_NAME_LENGTH + 1)) 452 object_cache_free(sPathNameCache, fBuffer, 0); 453 else 454 #endif 455 free(fBuffer); 456 fBuffer = NULL; 457 } 458 459 460 status_t 461 KPath::_Normalize(const char* path, bool traverseLeafLink) 462 { 463 status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, 464 traverseLeafLink, 465 team_get_kernel_team_id() == team_get_current_team_id()); 466 if (error != B_OK) { 467 // vfs_normalize_path() might have screwed up the previous 468 // path -- unset it completely to avoid weird problems. 469 fBuffer[0] = '\0'; 470 fPathLength = 0; 471 return error; 472 } 473 474 fPathLength = strlen(fBuffer); 475 return B_OK; 476 } 477 478 479 void 480 KPath::_ChopTrailingSlashes() 481 { 482 if (fBuffer != NULL) { 483 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 484 fBuffer[--fPathLength] = '\0'; 485 } 486 } 487 488