diff --git a/docs/develop/file_systems/overview.txt b/docs/develop/file_systems/overview.txt index 919a899751..fff1190e02 100644 --- a/docs/develop/file_systems/overview.txt +++ b/docs/develop/file_systems/overview.txt @@ -6,6 +6,38 @@ Filesystem drivers are in src/add-ons/kernel/file_system A filesystem usually relies on an underlying block device, but that's not required. For example, NFS is a network filesystem, so it doesn't need one. +Implementation notes +-------------------- +Each filesystem driver must define a few structures which act as the +interface between the VFS and the filesystem implementation. These +structures contain function pointers, some of which are optional, +which should point to functions defined by the implementer that +perform the appropriate filesystem peration as defined by the +documentation. + +See docs/user/drivers/fs_interface.dox for more detailed documentation +of this interface. + +It's important to note that while there may be some similarities in +the interface with that of other operations systems, one should not +make any assumptions about the desired behavior based soley on the +function prototypes defined in fs_interface.h. + +The following is a list of notes calling out some potential mistakes. + +# fs_vnode_ops.read_symlink + +Defining this function means that the filesystem driver supports +symbolic links, and the function that f_vnode_ops.read_symlink points +to should read the contents of a symlink from the specified node. + +This may seem similar to the posix function readlink(), but it is +slightly different. Unlike readlink(), which returns the number of +bytes copied into the output buffer, fs_vnode_ops.read_symlink is +expected to always return the length of the symlink contents, even if +the provided buffer is not large enough to contain the entire symlink +contents. + Development tools ----------------- diff --git a/docs/user/drivers/fs_interface.dox b/docs/user/drivers/fs_interface.dox index 6ed3ed74f7..cf27d1fa8e 100644 --- a/docs/user/drivers/fs_interface.dox +++ b/docs/user/drivers/fs_interface.dox @@ -961,8 +961,8 @@ to hold the complete string, only the first \c *_bufferSize bytes of the string shall be written to the buffer; the buffer shall not be null-terminated in this case. Furthermore the variable \a _bufferSize - points to shall be set to the length of the string written to the buffer, - not including any terminating null character (if written). + shall be set to the length of the symlink contents, even if the entire + contents did not fit in the provided \a buffer. \param volume The volume object. \param link The node object. @@ -970,8 +970,7 @@ written to. \param _bufferSize Pointer to a pre-allocated variable containing the size of the buffer supplied to the function. Upon successful completion the - hook shall store the number of bytes actually written into the buffer - in the variable. + hook shall store the length of the symlink contents. \retval B_OK Everything went fine. \retval B_BAD_VALUE \a link does not identify a symbolic link. */ diff --git a/docs/user/storage/SymLink.dox b/docs/user/storage/SymLink.dox index 936bbabefc..a89eb1409f 100644 --- a/docs/user/storage/SymLink.dox +++ b/docs/user/storage/SymLink.dox @@ -113,10 +113,16 @@ The string written to the buffer is guaranteed to be \c NULL terminated. + This function does not return the number of bytes written into + the provided buffer. It returns the length of the symlink's + contents, even if that contents does not fit within the + provided buffer. If the buffer cannot contain the entire + contents then it will be truncated to \a size. + \param buffer The buffer to read the symlink's contents into. \param size The size of \a buffer. - \return The number of bytes written into the buffer or an error code. + \return The length of the symlink's contents or an error code. \retval B_BAD_VALUE \a buf was \c NULL or the object didn't refer to a symbolic link. \retval B_FILE_ERROR The object was not initialized. diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index 7fee9bb606..6b9faf2375 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -1592,21 +1592,21 @@ bfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, RETURN_ERROR(B_BAD_VALUE); if ((inode->Flags() & INODE_LONG_SYMLINK) != 0) { - if ((uint64)inode->Size() < (uint64)*_bufferSize) - *_bufferSize = inode->Size(); - status_t status = inode->ReadAt(0, (uint8*)buffer, _bufferSize); if (status < B_OK) RETURN_ERROR(status); + *_bufferSize = inode->Size(); return B_OK; } - size_t linkLen = strlen(inode->Node().short_symlink); - if (linkLen < *_bufferSize) - *_bufferSize = linkLen; + size_t linkLength = strlen(inode->Node().short_symlink); - return user_memcpy(buffer, inode->Node().short_symlink, *_bufferSize); + size_t bytesToCopy = min_c(linkLength, *_bufferSize); + + *_bufferSize = linkLength; + + return user_memcpy(buffer, inode->Node().short_symlink, bytesToCopy); } diff --git a/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp index bb2575e531..bbe0c32b6d 100644 --- a/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp @@ -489,7 +489,17 @@ btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, size_t* _bufferSize) { Inode* inode = (Inode*)_node->private_node; - return inode->ReadAt(0, (uint8*)buffer, _bufferSize); + + if (!inode->IsSymLink()) + return B_BAD_VALUE; + + status_t result = inode->ReadAt(0, reinterpret_cast(buffer), + _bufferSize); + if (result != B_OK) + return result; + + *_bufferSize = inode->Size(); + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp b/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp index 45a5f1e36d..e9c88caa2a 100644 --- a/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/exfat/kernel_interface.cpp @@ -534,7 +534,18 @@ exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, size_t *_bufferSize) { Inode* inode = (Inode*)_node->private_node; - return inode->ReadAt(0, (uint8*)buffer, _bufferSize); + + if (!inode->IsSymLink()) + return B_BAD_VALUE; + + status_t result = inode->ReadAt(0, reinterpret_cast(buffer), + _bufferSize); + if (result != B_OK) + return result; + + *_bufferSize = inode->Size(); + + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp index 6eecf9bcce..1bc83cd64e 100644 --- a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp @@ -5,6 +5,7 @@ */ +#include #include #include #include @@ -1298,13 +1299,22 @@ ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, if (!inode->IsSymLink()) return B_BAD_VALUE; - if (inode->Size() < (off_t)*_bufferSize) - *_bufferSize = inode->Size(); + if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) { + status_t result = inode->ReadAt(0, reinterpret_cast(buffer), + _bufferSize); + if (result != B_OK) + return result; + } else { + size_t bytesToCopy = std::min(static_cast(inode->Size()), + *_bufferSize); - if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) - return inode->ReadAt(0, (uint8 *)buffer, _bufferSize); + status_t result = user_memcpy(buffer, inode->Node().symlink, + bytesToCopy); + if (result != B_OK) + return result; + } - memcpy(buffer, inode->Node().symlink, *_bufferSize); + *_bufferSize = inode->Size(); return B_OK; } diff --git a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp index 0a9bd987b4..0e963506bd 100644 --- a/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/iso9660/kernel_interface.cpp @@ -8,6 +8,7 @@ */ +#include #include #ifndef FS_SHELL @@ -563,14 +564,12 @@ fs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, return B_BAD_VALUE; size_t length = strlen(node->attr.slName); - if (length > *_bufferSize) - memcpy(buffer, node->attr.slName, *_bufferSize); - else { - memcpy(buffer, node->attr.slName, length); - *_bufferSize = length; - } - return B_OK; + size_t bytesToCopy = std::min(length, *_bufferSize); + + *_bufferSize = length; + + return user_memcpy(buffer, node->attr.slName, bytesToCopy); } diff --git a/src/add-ons/kernel/file_systems/netfs/client/netfs.cpp b/src/add-ons/kernel/file_systems/netfs/client/netfs.cpp index 5a0dca1fa4..2f7669e668 100644 --- a/src/add-ons/kernel/file_systems/netfs/client/netfs.cpp +++ b/src/add-ons/kernel/file_systems/netfs/client/netfs.cpp @@ -576,6 +576,10 @@ netfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize) Node* node = (Node*)_node; PRINT("netfs_read_link(%p, %p, %p, %lu)\n", ns, node, buffer, *bufferSize); + + // TODO: If this were to be implemented (which it isn't, it currently just + // returns B_BAD_VALUE) then this will need to be changed to return the + // length of the node and not the number of bytes read into buffer. status_t error = node->GetVolume()->ReadLink(node, buffer, *bufferSize, bufferSize); PRINT("netfs_read_link() done: (%" B_PRIx32 ", %lu)\n", error, diff --git a/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c b/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c index 9b32a3cd28..d7a97677a1 100644 --- a/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c +++ b/src/add-ons/kernel/file_systems/nfs/nfs_add_on.c @@ -2290,13 +2290,12 @@ fs_readlink(fs_volume *_volume, fs_vnode *_node, char *buf, size_t *bufsize) length = XDRInPacketGetDynamic(&reply, data); - length = min_c(length, *bufsize); - memcpy(buf, data, length); + status_t result = user_memcpy(buf, data, min_c(length, *bufsize)); *bufsize = length; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); - return B_OK; + return result; } static status_t diff --git a/src/add-ons/kernel/file_systems/packagefs/nodes/UnpackingLeafNode.cpp b/src/add-ons/kernel/file_systems/packagefs/nodes/UnpackingLeafNode.cpp index eb2ad11cd0..cd8f76b6c3 100644 --- a/src/add-ons/kernel/file_systems/packagefs/nodes/UnpackingLeafNode.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/nodes/UnpackingLeafNode.cpp @@ -272,11 +272,13 @@ UnpackingLeafNode::ReadSymlink(void* buffer, size_t* bufferSize) return B_OK; } - size_t toCopy = std::min(strlen(linkPath), *bufferSize); - memcpy(buffer, linkPath, toCopy); - *bufferSize = toCopy; + size_t linkLength = strnlen(linkPath, B_PATH_NAME_LENGTH); - return B_OK; + size_t bytesToCopy = std::min(linkLength, *bufferSize); + + *bufferSize = linkLength; + + return user_memcpy(buffer, linkPath, bytesToCopy); } diff --git a/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkSymlink.cpp b/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkSymlink.cpp index 1480a779dc..06045a7653 100644 --- a/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkSymlink.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkSymlink.cpp @@ -159,11 +159,13 @@ PackageLinkSymlink::Read(io_request* request) status_t PackageLinkSymlink::ReadSymlink(void* buffer, size_t* bufferSize) { - size_t toCopy = std::min(strlen(fLinkPath), *bufferSize); - memcpy(buffer, fLinkPath, toCopy); - *bufferSize = toCopy; + size_t linkLength = strnlen(fLinkPath, B_PATH_NAME_LENGTH); - return B_OK; + size_t bytesToCopy = std::min(linkLength, *bufferSize); + + *bufferSize = linkLength; + + return user_memcpy(buffer, fLinkPath, bytesToCopy); } diff --git a/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp index f4a32711e0..287ef24dd3 100644 --- a/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ramfs/kernel_interface.cpp @@ -375,8 +375,11 @@ ramfs_read_symlink(fs_volume* _volume, fs_vnode* _node, char *buffer, size_t toRead = min(*bufferSize, symLink->GetLinkedPathLength()); if (toRead > 0) - memcpy(buffer, symLink->GetLinkedPath(), toRead); - *bufferSize = toRead; + error = user_memcpy(buffer, symLink->GetLinkedPath(), + toRead); + + if (error == B_OK) + *bufferSize = symLink->GetLinkedPathLength(); } else { FATAL("Node %" B_PRIdINO " pretends to be a SymLink, but isn't!\n", node->GetID()); diff --git a/src/add-ons/kernel/file_systems/reiserfs/Iterators.cpp b/src/add-ons/kernel/file_systems/reiserfs/Iterators.cpp index a4a34dc2e8..e28b7ca503 100644 --- a/src/add-ons/kernel/file_systems/reiserfs/Iterators.cpp +++ b/src/add-ons/kernel/file_systems/reiserfs/Iterators.cpp @@ -1522,7 +1522,12 @@ StreamReader::_ReadIndirectItem(off_t offset, void *buffer, size_t bufferSize) off_t blockOffset = i * (off_t)fBlockSize; uint32 localOffset = max_c(0LL, offset - blockOffset); uint32 toRead = min_c(fBlockSize - localOffset, bufferSize); - memcpy(buffer, (uint8*)block->GetData() + localOffset, toRead); + status_t copyResult = user_memcpy(buffer, + reinterpret_cast(block->GetData()) + localOffset, + toRead); + if (copyResult != B_OK) + return copyResult; + block->Put(); bufferSize -= toRead; buffer = (uint8*)buffer + toRead; @@ -1542,7 +1547,7 @@ StreamReader::_ReadDirectItem(off_t offset, void *buffer, size_t bufferSize) { //PRINT(("StreamReader::_ReadDirectItem(%Ld, %p, %lu)\n", offset, buffer, bufferSize)); // copy the data into the buffer - memcpy(buffer, (uint8*)fItem.GetData() + offset, bufferSize); - return B_OK; + return user_memcpy(buffer, + reinterpret_cast(fItem.GetData()) + offset, bufferSize); } diff --git a/src/add-ons/kernel/file_systems/reiserfs/Iterators.h b/src/add-ons/kernel/file_systems/reiserfs/Iterators.h index eb69dc37df..5952b4d720 100644 --- a/src/add-ons/kernel/file_systems/reiserfs/Iterators.h +++ b/src/add-ons/kernel/file_systems/reiserfs/Iterators.h @@ -226,6 +226,8 @@ public: uint32 GetObjectID() const { return fItemIterator.GetObjectID(); } uint64 GetOffset() const { return fItemIterator.GetOffset(); } + off_t StreamSize() const { return fStreamSize; } + status_t ReadAt(off_t position, void *buffer, size_t bufferSize, size_t *bytesRead); diff --git a/src/add-ons/kernel/file_systems/reiserfs/Volume.cpp b/src/add-ons/kernel/file_systems/reiserfs/Volume.cpp index 84fbb349ad..2122a7ee6b 100644 --- a/src/add-ons/kernel/file_systems/reiserfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/reiserfs/Volume.cpp @@ -406,37 +406,6 @@ Volume::FindDirEntry(VNode *dir, const char *entryName, VNode *foundNode, return error; } -// ReadObject -/*! \brief Reads data from an object. - - For subsequent reads better use a StreamReader. - - \param node The object to be read from. - \param offset The file offset at which to start with reading. - \param buffer The buffer into which the data shall be written. - \param bufferSize The number of bytes to be read. - \param bytesRead Pointer to a size_t to be set to the number of bytes - actually read. - \return \c B_OK, if everything went fine. -*/ -status_t -Volume::ReadObject(VNode *node, off_t offset, void *buffer, size_t bufferSize, - size_t *bytesRead) -{ - status_t error = (node && buffer && bytesRead && offset >= 0 - ? B_OK : B_BAD_VALUE); - // read files or symlinks only - if (error == B_OK && !(node->IsFile() || node->IsSymlink())) - error = B_BAD_VALUE; - // let a StreamReader do the actual work - if (error == B_OK) { - StreamReader reader(fTree, node->GetDirID(), node->GetObjectID()); - error = reader.InitCheck(); - if (error == B_OK) - error = reader.ReadAt(offset, buffer, bufferSize, bytesRead); - } - return error; -} // ReadLink /*! \brief Reads a symlink. @@ -447,15 +416,30 @@ Volume::ReadObject(VNode *node, off_t offset, void *buffer, size_t bufferSize, \param node The symlink to be read from. \param buffer The buffer into which the data shall be written. \param bufferSize The number of bytes to be read. - \param bytesRead Pointer to a size_t to be set to the number of bytes - actually read. + \param linkLength Pointer to a size_t to be set to the length of the link. \return \c B_OK, if everything went fine. */ status_t Volume::ReadLink(VNode *node, char *buffer, size_t bufferSize, - size_t *bytesRead) + size_t *linkLength) { - return ReadObject(node, 0, buffer, bufferSize, bytesRead); + if (node == NULL || linkLength == NULL) + return B_BAD_VALUE; + + if (!node->IsSymlink()) + return B_BAD_VALUE; + + StreamReader reader(fTree, node->GetDirID(), node->GetObjectID()); + status_t result = reader.InitCheck(); + if (result != B_OK) + return result; + + size_t bytesCopied = bufferSize; + result = reader.ReadAt(0, buffer, bufferSize, &bytesCopied); + + *linkLength = reader.StreamSize(); + + return result; } // FindEntry diff --git a/src/add-ons/kernel/file_systems/reiserfs/Volume.h b/src/add-ons/kernel/file_systems/reiserfs/Volume.h index 230d6b8108..793cbd5d5c 100644 --- a/src/add-ons/kernel/file_systems/reiserfs/Volume.h +++ b/src/add-ons/kernel/file_systems/reiserfs/Volume.h @@ -72,10 +72,9 @@ public: status_t FindDirEntry(VNode *dir, const char *entryName, VNode *foundNode, bool failIfHidden = false); - status_t ReadObject(VNode *node, off_t offset, void *buffer, - size_t bufferSize, size_t *bytesRead); + status_t ReadLink(VNode *node, char *buffer, size_t bufferSize, - size_t *bytesRead); + size_t *linkLength); status_t FindEntry(const VNode *rootDir, const char *path, VNode *foundNode); diff --git a/src/build/libroot/fs.cpp b/src/build/libroot/fs.cpp index c5f59a566e..517668a90c 100644 --- a/src/build/libroot/fs.cpp +++ b/src/build/libroot/fs.cpp @@ -1070,19 +1070,28 @@ _kern_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize) if (error != B_OK) return error; - // readlink ssize_t bytesRead = readlink(realPath.c_str(), buffer, *_bufferSize); if (bytesRead < 0) return errno; - if (*_bufferSize > 0) { - if ((size_t)bytesRead == *_bufferSize) - bytesRead--; - + // On Haiku _kern_read_link will return the length of the link, *not* + // the number of bytes written to the buffer parameter. To emulate that + // here, we use readlink() to read the links contents, and then if it is + // possible that the link contents didn't fit in buffer, we'll fall back + // to lstat() to get the full length of the link. + if (static_cast(bytesRead) < *_bufferSize) { buffer[bytesRead] = '\0'; - } + *_bufferSize = bytesRead; + } else { + // The number of bytes copied by readlink() tells us that the entire + // link might not have fit into buffer. Fall back to getting the full + // length of the link using lstat. + struct stat linkStat; + if (lstat(realPath.c_str(), &linkStat) != 0) + return errno; - *_bufferSize = bytesRead; + *_bufferSize = linkStat.st_size; + } return B_OK; } diff --git a/src/kits/storage/SymLink.cpp b/src/kits/storage/SymLink.cpp index 2b6b490ced..991826d8fb 100644 --- a/src/kits/storage/SymLink.cpp +++ b/src/kits/storage/SymLink.cpp @@ -94,11 +94,10 @@ BSymLink::ReadLink(char* buffer, size_t size) if (result < B_OK) return result; - // null-terminate - if (linkLen >= size) - return B_BUFFER_OVERFLOW; - - buffer[linkLen] = '\0'; + if (linkLen < size) + buffer[linkLen] = '\0'; + else if (size > 0) + buffer[size - 1] = '\0'; return linkLen; } diff --git a/src/system/libroot/posix/unistd/link.c b/src/system/libroot/posix/unistd/link.c index 74ab991040..78919280f7 100644 --- a/src/system/libroot/posix/unistd/link.c +++ b/src/system/libroot/posix/unistd/link.c @@ -34,7 +34,12 @@ readlinkat(int fd, const char *path, char *buffer, size_t bufferSize) if (linkLen < bufferSize) buffer[linkLen] = '\0'; - return linkLen; + // _kern_read_link() returns the length of the link, but readlinkat() is + // supposed to return the number of bytes placed into buffer. If the + // buffer is larger than the link contents, then linkLen is the number + // of bytes written to the buffer. Otherwise, bufferSize bytes will have + // been written. + return min_c(linkLen, bufferSize); } diff --git a/src/tests/kits/storage/SymLinkTest.cpp b/src/tests/kits/storage/SymLinkTest.cpp index 782944c70a..3014d9299d 100644 --- a/src/tests/kits/storage/SymLinkTest.cpp +++ b/src/tests/kits/storage/SymLinkTest.cpp @@ -285,7 +285,7 @@ SymLinkTest::InitTest1() BDirectory pathDir(dirSuperLink); CPPUNIT_ASSERT( pathDir.InitCheck() == B_OK ); BSymLink link(&pathDir, ""); - CPPUNIT_ASSERT( link.InitCheck() == B_OK ); + CPPUNIT_ASSERT(link.InitCheck() == B_ENTRY_NOT_FOUND); } NextSubTest(); { @@ -467,8 +467,8 @@ SymLinkTest::InitTest2() // NextSubTest(); CPPUNIT_ASSERT( pathDir.SetTo(dirSuperLink) == B_OK ); - CPPUNIT_ASSERT( link.SetTo(&pathDir, "") == B_OK ); - CPPUNIT_ASSERT( link.InitCheck() == B_OK ); + CPPUNIT_ASSERT(link.SetTo(&pathDir, "") == B_ENTRY_NOT_FOUND); + CPPUNIT_ASSERT(link.InitCheck() == B_ENTRY_NOT_FOUND); // NextSubTest(); CPPUNIT_ASSERT( pathDir.SetTo(existingSuperFile) == B_OK ); @@ -556,9 +556,19 @@ SymLinkTest::ReadLinkTest() NextSubTest(); char smallBuffer[2]; CPPUNIT_ASSERT( link.SetTo(dirLink) == B_OK ); - CPPUNIT_ASSERT( link.ReadLink(smallBuffer, sizeof(smallBuffer)) - == (ssize_t)strlen(dirLink) ); - CPPUNIT_ASSERT( strncmp(smallBuffer, existingDir, sizeof(smallBuffer)) == 0 ); + ssize_t linkLength = link.ReadLink(smallBuffer, sizeof(smallBuffer)); + CPPUNIT_ASSERT(linkLength == static_cast(strlen(existingDir))); + CPPUNIT_ASSERT_EQUAL('/', smallBuffer[0]); + CPPUNIT_ASSERT_EQUAL('\0', smallBuffer[1]); + + // Invoke with one extra byte of length to ensure that the result is NULL + // terminated. + NextSubTest(); + buffer[17] = 'x'; + CPPUNIT_ASSERT(link.ReadLink(buffer, 18)); + CPPUNIT_ASSERT_EQUAL('\0', buffer[17]); + CPPUNIT_ASSERT_EQUAL(strcmp(buffer, "/tmp/existing-dir"), 0); + // bad args NextSubTest(); CPPUNIT_ASSERT( link.SetTo(fileLink) == B_OK );