KPath: Added LAZY_ALLOC flag.

* This allows KPath to not allocate a buffer when initialized
  without path.
* Added test cases for this.
* Added test for LockBuffer().
* Enhanced tests to allow building them in debug mode.
* Moved calling vfs_normalize_path() into own private method.
* Improved error codes; B_NO_MEMORY is now only returned if the
  allocation actually failed.
* If used with LAZY_ALLOC, Path() and LockBuffer() are now allowed
  to return a NULL path.
This commit is contained in:
Axel Dörfler 2017-04-30 16:31:28 +02:00
parent f94671c33d
commit e9843da357
4 changed files with 262 additions and 52 deletions

View File

@ -13,12 +13,14 @@
namespace BPrivate { namespace BPrivate {
namespace DiskDevice { namespace DiskDevice {
class KPath { class KPath {
public: public:
enum { enum {
DEFAULT = 0, DEFAULT = 0,
NORMALIZE = 0x01, NORMALIZE = 0x01,
TRAVERSE_LEAF_LINK = 0x02, TRAVERSE_LEAF_LINK = 0x02,
LAZY_ALLOC = 0x04
}; };
public: public:
KPath(size_t bufferSize = B_PATH_NAME_LENGTH); KPath(size_t bufferSize = B_PATH_NAME_LENGTH);
@ -41,7 +43,7 @@ public:
size_t BufferSize() const size_t BufferSize() const
{ return fBufferSize; } { return fBufferSize; }
char* LockBuffer(); char* LockBuffer(bool force = false);
void UnlockBuffer(); void UnlockBuffer();
char* DetachBuffer(); char* DetachBuffer();
@ -64,12 +66,19 @@ public:
bool operator!=(const char* path) const; bool operator!=(const char* path) const;
private: private:
status_t _AllocateBuffer();
status_t _Normalize(const char* path,
bool traverseLeafLink);
void _ChopTrailingSlashes(); void _ChopTrailingSlashes();
private:
char* fBuffer; char* fBuffer;
size_t fBufferSize; size_t fBufferSize;
size_t fPathLength; size_t fPathLength;
bool fLocked; bool fLocked;
bool fLazy;
bool fFailed;
bool fIsNull;
}; };

View File

@ -27,9 +27,12 @@ KPath::KPath(size_t bufferSize)
fBuffer(NULL), fBuffer(NULL),
fBufferSize(0), fBufferSize(0),
fPathLength(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), fBuffer(NULL),
fBufferSize(0), fBufferSize(0),
fPathLength(0), fPathLength(0),
fLocked(false) fLocked(false),
fLazy(false),
fFailed(false),
fIsNull(false)
{ {
SetTo(path, flags, bufferSize); SetTo(path, flags, bufferSize);
} }
@ -49,7 +55,10 @@ KPath::KPath(const KPath& other)
fBuffer(NULL), fBuffer(NULL),
fBufferSize(0), fBufferSize(0),
fPathLength(0), fPathLength(0),
fLocked(false) fLocked(false),
fLazy(false),
fFailed(false),
fIsNull(false)
{ {
*this = other; *this = other;
} }
@ -73,17 +82,18 @@ KPath::SetTo(const char* path, int32 flags, size_t bufferSize)
fBuffer = NULL; fBuffer = NULL;
fBufferSize = 0; fBufferSize = 0;
} }
fPathLength = 0; fPathLength = 0;
fLocked = false; fLocked = false;
// allocate buffer
if (fBuffer == NULL)
fBuffer = (char*)malloc(bufferSize);
if (fBuffer == NULL)
return B_NO_MEMORY;
fBufferSize = bufferSize; 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); return SetPath(path, flags);
} }
@ -97,37 +107,61 @@ KPath::Adopt(KPath& other)
fBuffer = other.fBuffer; fBuffer = other.fBuffer;
fBufferSize = other.fBufferSize; fBufferSize = other.fBufferSize;
fPathLength = other.fPathLength; fPathLength = other.fPathLength;
fLazy = other.fLazy;
fFailed = other.fFailed;
fIsNull = other.fIsNull;
other.fBuffer = NULL; other.fBuffer = NULL;
other.fBufferSize = 0; if (!other.fLazy)
other.fBufferSize = 0;
other.fPathLength = 0; other.fPathLength = 0;
other.fFailed = false;
other.fIsNull = other.fLazy;
} }
status_t status_t
KPath::InitCheck() const 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 status_t
KPath::SetPath(const char* path, int32 flags) KPath::SetPath(const char* path, int32 flags)
{ {
if (fBuffer == NULL) if (path == NULL && fLazy && fBuffer == NULL) {
return B_NO_INIT; 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 (path != NULL) {
if ((flags & NORMALIZE) != 0) { if ((flags & NORMALIZE) != 0) {
// normalize path // normalize path
status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, status_t status = _Normalize(path,
(flags & TRAVERSE_LEAF_LINK) != 0, (flags & TRAVERSE_LEAF_LINK) != 0);
team_get_kernel_team_id() == team_get_current_team_id()); if (status != B_OK)
if (error != B_OK) { return status;
SetPath(NULL);
return error;
}
fPathLength = strlen(fBuffer);
} else { } else {
// don't normalize path // don't normalize path
size_t length = strlen(path); size_t length = strlen(path);
@ -141,6 +175,8 @@ KPath::SetPath(const char* path, int32 flags)
} else { } else {
fBuffer[0] = '\0'; fBuffer[0] = '\0';
fPathLength = 0; fPathLength = 0;
if (fLazy)
fIsNull = true;
} }
return B_OK; return B_OK;
} }
@ -149,17 +185,31 @@ KPath::SetPath(const char* path, int32 flags)
const char* const char*
KPath::Path() const 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* char*
KPath::LockBuffer() KPath::LockBuffer(bool force)
{ {
if (fBuffer == NULL && fLazy) {
if (fIsNull && !force)
return NULL;
_AllocateBuffer();
}
if (fBuffer == NULL || fLocked) if (fBuffer == NULL || fLocked)
return NULL; return NULL;
fLocked = true; fLocked = true;
fIsNull = false;
return fBuffer; return fBuffer;
} }
@ -171,7 +221,12 @@ KPath::UnlockBuffer()
TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
return; return;
} }
fLocked = false; fLocked = false;
if (fBuffer == NULL)
return;
fPathLength = strnlen(fBuffer, fBufferSize); fPathLength = strnlen(fBuffer, fBufferSize);
if (fPathLength == fBufferSize) { if (fPathLength == fBufferSize) {
TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
@ -189,7 +244,6 @@ KPath::DetachBuffer()
if (fBuffer != NULL) { if (fBuffer != NULL) {
fBuffer = NULL; fBuffer = NULL;
fBufferSize = 0;
fPathLength = 0; fPathLength = 0;
fLocked = false; fLocked = false;
} }
@ -295,26 +349,15 @@ KPath::Normalize(bool traverseLeafLink)
if (fPathLength == 0) if (fPathLength == 0)
return B_BAD_VALUE; return B_BAD_VALUE;
status_t error = vfs_normalize_path(fBuffer, fBuffer, fBufferSize, return _Normalize(fBuffer, traverseLeafLink);
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;
} }
KPath& KPath&
KPath::operator=(const KPath& other) KPath::operator=(const KPath& other)
{ {
SetTo(other.fBuffer, false, other.fBufferSize); SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT,
other.fBufferSize);
return *this; return *this;
} }
@ -333,7 +376,7 @@ KPath::operator==(const KPath& other) const
if (fBuffer == NULL) if (fBuffer == NULL)
return !other.fBuffer; return !other.fBuffer;
return other.fBuffer return other.fBuffer != NULL
&& fPathLength == other.fPathLength && fPathLength == other.fPathLength
&& strcmp(fBuffer, other.fBuffer) == 0; && strcmp(fBuffer, other.fBuffer) == 0;
} }
@ -343,9 +386,9 @@ bool
KPath::operator==(const char* path) const KPath::operator==(const char* path) const
{ {
if (fBuffer == NULL) 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 void
KPath::_ChopTrailingSlashes() KPath::_ChopTrailingSlashes()
{ {

View File

@ -14,6 +14,9 @@
#include <cppunit/TestSuite.h> #include <cppunit/TestSuite.h>
typedef void* mutex;
// Kernel stubs // 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 - // #pragma mark -
@ -57,22 +90,22 @@ KPathTest::TestSetToAndPath()
{ {
KPath path; KPath path;
status_t status = path.InitCheck(); status_t status = path.InitCheck();
// CPPUNIT_ASSERT(status == B_NO_INIT); CPPUNIT_ASSERT_MESSAGE("1. ", status == B_OK);
status = path.SetTo("a/b/c"); 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(strcmp(path.Path(), "a/b/c") == 0);
CPPUNIT_ASSERT(path.Length() == 5); CPPUNIT_ASSERT(path.Length() == 5);
CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH);
status = path.SetPath("abc/def"); 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(strcmp(path.Path(), "abc/def") == 0);
CPPUNIT_ASSERT(path.Length() == 7); CPPUNIT_ASSERT(path.Length() == 7);
CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH); CPPUNIT_ASSERT(path.BufferSize() == B_PATH_NAME_LENGTH);
status = path.SetTo("a/b/c", false, 10); 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(strcmp(path.Path(), "a/b/c") == 0);
CPPUNIT_ASSERT(path.Length() == 5); CPPUNIT_ASSERT(path.Length() == 5);
CPPUNIT_ASSERT(path.BufferSize() == 10); CPPUNIT_ASSERT(path.BufferSize() == 10);
@ -81,6 +114,38 @@ KPathTest::TestSetToAndPath()
CPPUNIT_ASSERT(status == B_BUFFER_OVERFLOW); CPPUNIT_ASSERT(status == B_BUFFER_OVERFLOW);
CPPUNIT_ASSERT(strcmp(path.Path(), "a/b/c") == 0); CPPUNIT_ASSERT(strcmp(path.Path(), "a/b/c") == 0);
CPPUNIT_ASSERT(path.Length() == 5); 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; KPath path;
status_t status = path.ReplaceLeaf("x"); status_t status = path.ReplaceLeaf("x");
// CPPUNIT_ASSERT(status == B_NO_INIT); CPPUNIT_ASSERT(status == B_OK);
path.SetTo("/a/b/c"); path.SetTo("/a/b/c");
CPPUNIT_ASSERT(path.Length() == 6); CPPUNIT_ASSERT(path.Length() == 6);
@ -194,7 +259,53 @@ KPathTest::TestAdopt()
CPPUNIT_ASSERT(strcmp(one.Path(), "second") == 0); CPPUNIT_ASSERT(strcmp(one.Path(), "second") == 0);
CPPUNIT_ASSERT(two.Length() == 0); CPPUNIT_ASSERT(two.Length() == 0);
CPPUNIT_ASSERT(two.BufferSize() == 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(strcmp(buffer, "test") == 0);
CPPUNIT_ASSERT(path.Path() == NULL); 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() != NULL);
CPPUNIT_ASSERT(path.Path()[0] == '\0'); CPPUNIT_ASSERT(path.Path()[0] == '\0');
CPPUNIT_ASSERT(path.Path() == path.Leaf()); 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>( suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestSetToAndPath", &KPathTest::TestSetToAndPath)); "KPathTest::TestSetToAndPath", &KPathTest::TestSetToAndPath));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLazyAlloc", &KPathTest::TestLazyAlloc));
suite.addTest(new CppUnit::TestCaller<KPathTest>( suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLeaf", &KPathTest::TestLeaf)); "KPathTest::TestLeaf", &KPathTest::TestLeaf));
suite.addTest(new CppUnit::TestCaller<KPathTest>( suite.addTest(new CppUnit::TestCaller<KPathTest>(
@ -293,6 +412,8 @@ KPathTest::AddTests(BTestSuite& parent)
"KPathTest::TestRemoveLeaf", &KPathTest::TestRemoveLeaf)); "KPathTest::TestRemoveLeaf", &KPathTest::TestRemoveLeaf));
suite.addTest(new CppUnit::TestCaller<KPathTest>( suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestAdopt", &KPathTest::TestAdopt)); "KPathTest::TestAdopt", &KPathTest::TestAdopt));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLockBuffer", &KPathTest::TestLockBuffer));
suite.addTest(new CppUnit::TestCaller<KPathTest>( suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestDetachBuffer", &KPathTest::TestDetachBuffer)); "KPathTest::TestDetachBuffer", &KPathTest::TestDetachBuffer));
suite.addTest(new CppUnit::TestCaller<KPathTest>( suite.addTest(new CppUnit::TestCaller<KPathTest>(

View File

@ -16,10 +16,12 @@ public:
virtual ~KPathTest(); virtual ~KPathTest();
void TestSetToAndPath(); void TestSetToAndPath();
void TestLazyAlloc();
void TestLeaf(); void TestLeaf();
void TestReplaceLeaf(); void TestReplaceLeaf();
void TestRemoveLeaf(); void TestRemoveLeaf();
void TestAdopt(); void TestAdopt();
void TestLockBuffer();
void TestDetachBuffer(); void TestDetachBuffer();
void TestNormalize(); void TestNormalize();
void TestAssign(); void TestAssign();