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 27extern object_cache* sPathNameCache; 28#endif 29 30 31KPath::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 45KPath::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 59KPath::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 73KPath::~KPath() 74{ 75 _FreeBuffer(); 76} 77 78 79status_t 80KPath::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 107void 108KPath::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 128status_t 129KPath::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*/ 144status_t 145KPath::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 190const char* 191KPath::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*/ 202char* 203KPath::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 222void 223KPath::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 247char* 248KPath::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 268const char* 269KPath::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 283status_t 284KPath::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 305bool 306KPath::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 323status_t 324KPath::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 357status_t 358KPath::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 369KPath& 370KPath::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 381KPath& 382KPath::operator=(const char* path) 383{ 384 SetPath(path); 385 return *this; 386} 387 388 389bool 390KPath::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 401bool 402KPath::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 411bool 412KPath::operator!=(const KPath& other) const 413{ 414 return !(*this == other); 415} 416 417 418bool 419KPath::operator!=(const char* path) const 420{ 421 return !(*this == path); 422} 423 424 425status_t 426KPath::_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 447void 448KPath::_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 460status_t 461KPath::_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 479void 480KPath::_ChopTrailingSlashes() 481{ 482 if (fBuffer != NULL) { 483 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 484 fBuffer[--fPathLength] = '\0'; 485 } 486} 487 488