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 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;
};

View File

@ -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()
{

View File

@ -14,6 +14,9 @@
#include <cppunit/TestSuite.h>
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>(
"KPathTest::TestSetToAndPath", &KPathTest::TestSetToAndPath));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLazyAlloc", &KPathTest::TestLazyAlloc));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLeaf", &KPathTest::TestLeaf));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
@ -293,6 +412,8 @@ KPathTest::AddTests(BTestSuite& parent)
"KPathTest::TestRemoveLeaf", &KPathTest::TestRemoveLeaf));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestAdopt", &KPathTest::TestAdopt));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestLockBuffer", &KPathTest::TestLockBuffer));
suite.addTest(new CppUnit::TestCaller<KPathTest>(
"KPathTest::TestDetachBuffer", &KPathTest::TestDetachBuffer));
suite.addTest(new CppUnit::TestCaller<KPathTest>(

View File

@ -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();