diff --git a/headers/private/kernel/fs/KPath.h b/headers/private/kernel/fs/KPath.h index cfa0642f02..1298f81007 100644 --- a/headers/private/kernel/fs/KPath.h +++ b/headers/private/kernel/fs/KPath.h @@ -13,12 +13,14 @@ namespace BPrivate { namespace DiskDevice { + class KPath { public: enum { DEFAULT = 0, NORMALIZE = 0x01, TRAVERSE_LEAF_LINK = 0x02, + LAZY_ALLOC = 0x04 }; public: KPath(size_t bufferSize = B_PATH_NAME_LENGTH); @@ -41,7 +43,7 @@ public: size_t BufferSize() const { return fBufferSize; } - char* LockBuffer(); + char* LockBuffer(bool force = false); void UnlockBuffer(); char* DetachBuffer(); @@ -64,12 +66,19 @@ public: bool operator!=(const char* path) const; private: + status_t _AllocateBuffer(); + status_t _Normalize(const char* path, + bool traverseLeafLink); void _ChopTrailingSlashes(); +private: char* fBuffer; size_t fBufferSize; size_t fPathLength; bool fLocked; + bool fLazy; + bool fFailed; + bool fIsNull; }; diff --git a/src/system/kernel/fs/KPath.cpp b/src/system/kernel/fs/KPath.cpp index 815b0dafbc..cacceb482a 100644 --- a/src/system/kernel/fs/KPath.cpp +++ b/src/system/kernel/fs/KPath.cpp @@ -27,9 +27,12 @@ KPath::KPath(size_t bufferSize) fBuffer(NULL), fBufferSize(0), fPathLength(0), - fLocked(false) + fLocked(false), + fLazy(false), + fFailed(false), + fIsNull(false) { - SetTo(NULL, KPath::DEFAULT, bufferSize); + SetTo(NULL, DEFAULT, bufferSize); } @@ -38,7 +41,10 @@ KPath::KPath(const char* path, int32 flags, size_t bufferSize) fBuffer(NULL), fBufferSize(0), fPathLength(0), - fLocked(false) + fLocked(false), + fLazy(false), + fFailed(false), + fIsNull(false) { SetTo(path, flags, bufferSize); } @@ -49,7 +55,10 @@ KPath::KPath(const KPath& other) fBuffer(NULL), fBufferSize(0), fPathLength(0), - fLocked(false) + fLocked(false), + fLazy(false), + fFailed(false), + fIsNull(false) { *this = other; } @@ -73,17 +82,18 @@ KPath::SetTo(const char* path, int32 flags, size_t bufferSize) fBuffer = NULL; fBufferSize = 0; } + fPathLength = 0; fLocked = false; - - // allocate buffer - if (fBuffer == NULL) - fBuffer = (char*)malloc(bufferSize); - if (fBuffer == NULL) - return B_NO_MEMORY; - fBufferSize = bufferSize; - fBuffer[0] = '\0'; + fLazy = (flags & LAZY_ALLOC) != 0; + fIsNull = path == NULL; + + if (path != NULL || !fLazy) { + status_t status = _AllocateBuffer(); + if (status != B_OK) + return status; + } return SetPath(path, flags); } @@ -97,37 +107,61 @@ KPath::Adopt(KPath& other) fBuffer = other.fBuffer; fBufferSize = other.fBufferSize; fPathLength = other.fPathLength; + fLazy = other.fLazy; + fFailed = other.fFailed; + fIsNull = other.fIsNull; other.fBuffer = NULL; - other.fBufferSize = 0; + if (!other.fLazy) + other.fBufferSize = 0; other.fPathLength = 0; + other.fFailed = false; + other.fIsNull = other.fLazy; } status_t KPath::InitCheck() const { - return fBuffer != NULL ? B_OK : B_NO_MEMORY; + if (fBuffer != NULL || fLazy && !fFailed && fBufferSize != 0) + return B_OK; + + return fFailed ? B_NO_MEMORY : B_NO_INIT; } +/*! \brief Sets the buffer to \a path. + + \param flags Understands the following two options: + - \c NORMALIZE + - \c TRAVERSE_LEAF_LINK +*/ status_t KPath::SetPath(const char* path, int32 flags) { - if (fBuffer == NULL) - return B_NO_INIT; + if (path == NULL && fLazy && fBuffer == NULL) { + fIsNull = true; + return B_OK; + } + + if (fBuffer == NULL) { + if (fLazy) { + status_t status = _AllocateBuffer(); + if (status != B_OK) + return B_NO_MEMORY; + } else + return B_NO_INIT; + } + + fIsNull = false; if (path != NULL) { if ((flags & NORMALIZE) != 0) { // normalize path - status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, - (flags & TRAVERSE_LEAF_LINK) != 0, - team_get_kernel_team_id() == team_get_current_team_id()); - if (error != B_OK) { - SetPath(NULL); - return error; - } - fPathLength = strlen(fBuffer); + status_t status = _Normalize(path, + (flags & TRAVERSE_LEAF_LINK) != 0); + if (status != B_OK) + return status; } else { // don't normalize path size_t length = strlen(path); @@ -141,6 +175,8 @@ KPath::SetPath(const char* path, int32 flags) } else { fBuffer[0] = '\0'; fPathLength = 0; + if (fLazy) + fIsNull = true; } return B_OK; } @@ -149,17 +185,31 @@ KPath::SetPath(const char* path, int32 flags) const char* KPath::Path() const { - return fBuffer; + return fIsNull ? NULL : fBuffer; } +/*! \brief Locks the buffer for external changes. + + \param force In lazy mode, this will allocate a buffer when set. + Otherwise, \c NULL will be returned if set to NULL. +*/ char* -KPath::LockBuffer() +KPath::LockBuffer(bool force) { + if (fBuffer == NULL && fLazy) { + if (fIsNull && !force) + return NULL; + + _AllocateBuffer(); + } + if (fBuffer == NULL || fLocked) return NULL; fLocked = true; + fIsNull = false; + return fBuffer; } @@ -171,7 +221,12 @@ KPath::UnlockBuffer() TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); return; } + fLocked = false; + + if (fBuffer == NULL) + return; + fPathLength = strnlen(fBuffer, fBufferSize); if (fPathLength == fBufferSize) { TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); @@ -189,7 +244,6 @@ KPath::DetachBuffer() if (fBuffer != NULL) { fBuffer = NULL; - fBufferSize = 0; fPathLength = 0; fLocked = false; } @@ -295,26 +349,15 @@ KPath::Normalize(bool traverseLeafLink) if (fPathLength == 0) return B_BAD_VALUE; - status_t error = vfs_normalize_path(fBuffer, fBuffer, fBufferSize, - traverseLeafLink, - team_get_kernel_team_id() == team_get_current_team_id()); - if (error != B_OK) { - // vfs_normalize_path() might have screwed up the previous path -- unset - // it completely to avoid weird problems. - fBuffer[0] = '\0'; - fPathLength = 0; - return error; - } - - fPathLength = strlen(fBuffer); - return B_OK; + return _Normalize(fBuffer, traverseLeafLink); } KPath& KPath::operator=(const KPath& other) { - SetTo(other.fBuffer, false, other.fBufferSize); + SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT, + other.fBufferSize); return *this; } @@ -333,7 +376,7 @@ KPath::operator==(const KPath& other) const if (fBuffer == NULL) return !other.fBuffer; - return other.fBuffer + return other.fBuffer != NULL && fPathLength == other.fPathLength && strcmp(fBuffer, other.fBuffer) == 0; } @@ -343,9 +386,9 @@ bool KPath::operator==(const char* path) const { if (fBuffer == NULL) - return (!path); + return path == NULL; - return path && strcmp(fBuffer, path) == 0; + return path != NULL && strcmp(fBuffer, path) == 0; } @@ -363,6 +406,41 @@ KPath::operator!=(const char* path) const } +status_t +KPath::_AllocateBuffer() +{ + if (fBuffer == NULL && fBufferSize != 0) + fBuffer = (char*)malloc(fBufferSize); + if (fBuffer == NULL) { + fFailed = true; + return B_NO_MEMORY; + } + + fBuffer[0] = '\0'; + fFailed = false; + return B_OK; +} + + +status_t +KPath::_Normalize(const char* path, bool traverseLeafLink) +{ + status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, + traverseLeafLink, + team_get_kernel_team_id() == team_get_current_team_id()); + if (error != B_OK) { + // vfs_normalize_path() might have screwed up the previous + // path -- unset it completely to avoid weird problems. + fBuffer[0] = '\0'; + fPathLength = 0; + return error; + } + + fPathLength = strlen(fBuffer); + return B_OK; +} + + void KPath::_ChopTrailingSlashes() { diff --git a/src/tests/system/kernel/fs/KPathTest.cpp b/src/tests/system/kernel/fs/KPathTest.cpp index 9617c4bcfa..5265330ed9 100644 --- a/src/tests/system/kernel/fs/KPathTest.cpp +++ b/src/tests/system/kernel/fs/KPathTest.cpp @@ -14,6 +14,9 @@ #include +typedef void* mutex; + + // Kernel stubs @@ -39,6 +42,36 @@ vfs_normalize_path(const char* path, char* buffer, size_t bufferSize, } +// #pragma mark - DEBUG only + + +extern "C" void* +memalign_etc(size_t alignment, size_t size, uint32 flags) +{ + return malloc(size); +} + + +extern "C" status_t +_mutex_lock(mutex* lock, void* locker) +{ + return B_OK; +} + + +extern "C" status_t +_mutex_trylock(mutex* lock) +{ + return B_OK; +} + + +extern "C" void +_mutex_unlock(mutex* lock) +{ +} + + // #pragma mark - @@ -57,22 +90,22 @@ KPathTest::TestSetToAndPath() { KPath path; status_t status = path.InitCheck(); -// CPPUNIT_ASSERT(status == B_NO_INIT); + CPPUNIT_ASSERT_MESSAGE("1. ", status == B_OK); status = path.SetTo("a/b/c"); - CPPUNIT_ASSERT(status == B_OK); + CPPUNIT_ASSERT_MESSAGE("2. ", status == B_OK); CPPUNIT_ASSERT(strcmp(path.Path(), "a/b/c") == 0); CPPUNIT_ASSERT(path.Length() == 5); CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); status = path.SetPath("abc/def"); - CPPUNIT_ASSERT(status == B_OK); + CPPUNIT_ASSERT_MESSAGE("3. ", status == B_OK); CPPUNIT_ASSERT(strcmp(path.Path(), "abc/def") == 0); CPPUNIT_ASSERT(path.Length() == 7); CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); status = path.SetTo("a/b/c", false, 10); - CPPUNIT_ASSERT(status == B_OK); + CPPUNIT_ASSERT_MESSAGE("4. ", status == B_OK); CPPUNIT_ASSERT(strcmp(path.Path(), "a/b/c") == 0); CPPUNIT_ASSERT(path.Length() == 5); CPPUNIT_ASSERT(path.BufferSize() == 10); @@ -81,6 +114,38 @@ KPathTest::TestSetToAndPath() CPPUNIT_ASSERT(status == B_BUFFER_OVERFLOW); CPPUNIT_ASSERT(strcmp(path.Path(), "a/b/c") == 0); CPPUNIT_ASSERT(path.Length() == 5); + + status = path.SetTo(NULL, KPath::DEFAULT, SIZE_MAX); + CPPUNIT_ASSERT(status == B_NO_MEMORY); +} + + +void +KPathTest::TestLazyAlloc() +{ + KPath path(NULL, KPath::LAZY_ALLOC); + CPPUNIT_ASSERT(path.Path() == NULL); + CPPUNIT_ASSERT(path.Length() == 0); + CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); + CPPUNIT_ASSERT(path.InitCheck() == B_OK); + + path.SetPath("/b"); + CPPUNIT_ASSERT(path.Path() != NULL); + CPPUNIT_ASSERT(strcmp(path.Path(), "/b") == 0); + CPPUNIT_ASSERT(path.Length() == 2); + CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); + + KPath second("yo", KPath::LAZY_ALLOC); + CPPUNIT_ASSERT(second.Path() != NULL); + CPPUNIT_ASSERT(strcmp(second.Path(), "yo") == 0); + CPPUNIT_ASSERT(second.Length() == 2); + CPPUNIT_ASSERT(second.BufferSize() == B_PATH_NAME_LENGTH); + + status_t status = path.SetTo(NULL, KPath::LAZY_ALLOC, SIZE_MAX); + CPPUNIT_ASSERT(status == B_OK); + status = path.SetPath("test"); + CPPUNIT_ASSERT(status == B_NO_MEMORY); + CPPUNIT_ASSERT(path.InitCheck() == B_NO_MEMORY); } @@ -118,7 +183,7 @@ KPathTest::TestReplaceLeaf() { KPath path; status_t status = path.ReplaceLeaf("x"); -// CPPUNIT_ASSERT(status == B_NO_INIT); + CPPUNIT_ASSERT(status == B_OK); path.SetTo("/a/b/c"); CPPUNIT_ASSERT(path.Length() == 6); @@ -194,7 +259,53 @@ KPathTest::TestAdopt() CPPUNIT_ASSERT(strcmp(one.Path(), "second") == 0); CPPUNIT_ASSERT(two.Length() == 0); CPPUNIT_ASSERT(two.BufferSize() == 0); -// CPPUNIT_ASSERT(two.InitCheck() == B_NO_INIT); + CPPUNIT_ASSERT(two.InitCheck() == B_NO_INIT); + + two.SetTo(NULL, KPath::LAZY_ALLOC); + CPPUNIT_ASSERT(two.InitCheck() == B_OK); + CPPUNIT_ASSERT(two.Path() == NULL); + CPPUNIT_ASSERT(two.Length() == 0); + one.Adopt(two); + + CPPUNIT_ASSERT(two.InitCheck() == B_OK); + CPPUNIT_ASSERT(one.Path() == NULL); + CPPUNIT_ASSERT(one.Length() == 0); + one.SetPath("test"); + CPPUNIT_ASSERT(one.Path() != NULL); + CPPUNIT_ASSERT(strcmp(one.Path(), "test") == 0); + CPPUNIT_ASSERT(one.Length() == 4); +} + + +void +KPathTest::TestLockBuffer() +{ + KPath path; + CPPUNIT_ASSERT(path.Path() != NULL); + CPPUNIT_ASSERT(path.Length() == 0); + char* buffer = path.LockBuffer(); + CPPUNIT_ASSERT(path.Path() == buffer); + strcpy(buffer, "test"); + CPPUNIT_ASSERT(path.Length() == 0); + path.UnlockBuffer(); + CPPUNIT_ASSERT(path.Length() == 4); + + KPath second(NULL, KPath::LAZY_ALLOC); + CPPUNIT_ASSERT(second.Path() == NULL); + CPPUNIT_ASSERT(second.Length() == 0); + buffer = second.LockBuffer(); + CPPUNIT_ASSERT(second.Path() == NULL); + CPPUNIT_ASSERT(buffer == NULL); + + KPath third(NULL, KPath::LAZY_ALLOC); + CPPUNIT_ASSERT(third.Path() == NULL); + buffer = third.LockBuffer(true); + CPPUNIT_ASSERT(third.Path() != NULL); + CPPUNIT_ASSERT(buffer != NULL); + strcpy(buffer, "test"); + CPPUNIT_ASSERT(third.Length() == 0); + third.UnlockBuffer(); + CPPUNIT_ASSERT(third.Length() == 4); } @@ -209,7 +320,7 @@ KPathTest::TestDetachBuffer() CPPUNIT_ASSERT(strcmp(buffer, "test") == 0); CPPUNIT_ASSERT(path.Path() == NULL); -// CPPUNIT_ASSERT(path.InitCheck() == B_NO_INIT); + CPPUNIT_ASSERT(path.InitCheck() == B_NO_INIT); } @@ -225,6 +336,12 @@ KPathTest::TestNormalize() CPPUNIT_ASSERT(path.Path() != NULL); CPPUNIT_ASSERT(path.Path()[0] == '\0'); CPPUNIT_ASSERT(path.Path() == path.Leaf()); + + status = path.SetTo("test/../in", KPath::NORMALIZE); + CPPUNIT_ASSERT(status == B_NOT_SUPPORTED); + CPPUNIT_ASSERT(path.Path() != NULL); + CPPUNIT_ASSERT(path.Path()[0] == '\0'); + CPPUNIT_ASSERT(path.Path() == path.Leaf()); } @@ -285,6 +402,8 @@ KPathTest::AddTests(BTestSuite& parent) suite.addTest(new CppUnit::TestCaller( "KPathTest::TestSetToAndPath", &KPathTest::TestSetToAndPath)); + suite.addTest(new CppUnit::TestCaller( + "KPathTest::TestLazyAlloc", &KPathTest::TestLazyAlloc)); suite.addTest(new CppUnit::TestCaller( "KPathTest::TestLeaf", &KPathTest::TestLeaf)); suite.addTest(new CppUnit::TestCaller( @@ -293,6 +412,8 @@ KPathTest::AddTests(BTestSuite& parent) "KPathTest::TestRemoveLeaf", &KPathTest::TestRemoveLeaf)); suite.addTest(new CppUnit::TestCaller( "KPathTest::TestAdopt", &KPathTest::TestAdopt)); + suite.addTest(new CppUnit::TestCaller( + "KPathTest::TestLockBuffer", &KPathTest::TestLockBuffer)); suite.addTest(new CppUnit::TestCaller( "KPathTest::TestDetachBuffer", &KPathTest::TestDetachBuffer)); suite.addTest(new CppUnit::TestCaller( diff --git a/src/tests/system/kernel/fs/KPathTest.h b/src/tests/system/kernel/fs/KPathTest.h index fceea30ae9..54a30d8c7c 100644 --- a/src/tests/system/kernel/fs/KPathTest.h +++ b/src/tests/system/kernel/fs/KPathTest.h @@ -16,10 +16,12 @@ public: virtual ~KPathTest(); void TestSetToAndPath(); + void TestLazyAlloc(); void TestLeaf(); void TestReplaceLeaf(); void TestRemoveLeaf(); void TestAdopt(); + void TestLockBuffer(); void TestDetachBuffer(); void TestNormalize(); void TestAssign();