diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index b94ce09284..ec190fe343 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -90,7 +90,7 @@ typedef struct file_system_info { uint8 event, selectsync *sync); status_t (*fsync)(fs_volume fs, fs_vnode v); - status_t (*read_link)(fs_volume fs, fs_vnode link, char *buffer, size_t bufferSize); + status_t (*read_link)(fs_volume fs, fs_vnode link, char *buffer, size_t *_bufferSize); status_t (*write_link)(fs_volume fs, fs_vnode link, char *toPath); status_t (*create_symlink)(fs_volume fs, fs_vnode dir, const char *name, const char *path, int mode); diff --git a/headers/private/kernel/syscalls.h b/headers/private/kernel/syscalls.h index 39c6736448..3afac5cb85 100644 --- a/headers/private/kernel/syscalls.h +++ b/headers/private/kernel/syscalls.h @@ -134,8 +134,8 @@ extern off_t _kern_seek(int fd, off_t pos, int seekType); extern status_t _kern_create_dir_entry_ref(dev_t device, ino_t inode, const char *name, int perms); extern status_t _kern_create_dir(int fd, const char *path, int perms); extern status_t _kern_remove_dir(const char *path); -extern ssize_t _kern_read_link(int fd, const char *path, char *buffer, - size_t bufferSize); +extern status_t _kern_read_link(int fd, const char *path, char *buffer, + size_t *_bufferSize); extern status_t _kern_write_link(const char *path, const char *toPath); extern status_t _kern_create_symlink(int fd, const char *path, const char *toPath, int mode); diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index 44ba220115..0cc85e78ab 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -119,7 +119,7 @@ off_t _user_seek(int fd, off_t pos, int seekType); status_t _user_create_dir_entry_ref(dev_t device, ino_t inode, const char *name, int perms); status_t _user_create_dir(int fd, const char *path, int perms); status_t _user_remove_dir(const char *path); -ssize_t _user_read_link(int fd, const char *path, char *buffer, size_t bufferSize); +status_t _user_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize); status_t _user_write_link(const char *path, const char *toPath); status_t _user_create_symlink(int fd, const char *path, const char *toPath, int mode); 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 47f3dd2b4e..dd900ec4d6 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -1269,29 +1269,33 @@ bfs_access(void *_ns, void *_node, int accessMode) static status_t -bfs_read_link(void *_ns, void *_node, char *buffer, size_t bufferSize) +bfs_read_link(void *_ns, void *_node, char *buffer, size_t *_bufferSize) { FUNCTION(); Inode *inode = (Inode *)_node; - + size_t bufferSize = *_bufferSize; + if (!inode->IsSymLink()) RETURN_ERROR(B_BAD_VALUE); if (inode->Flags() & INODE_LONG_SYMLINK) { // we also need space for the terminating null byte - if (inode->Size() >= bufferSize) + if (inode->Size() >= bufferSize) { + *_bufferSize = inode->Size(); return B_BUFFER_OVERFLOW; + } - status_t status = inode->ReadAt(0, (uint8 *)buffer, &bufferSize); + status_t status = inode->ReadAt(0, (uint8 *)buffer, _bufferSize); if (status < B_OK) RETURN_ERROR(status); - buffer[bufferSize] = '\0'; + buffer[*_bufferSize] = '\0'; return B_OK; } - if (strlcpy(buffer, inode->Node().short_symlink, bufferSize) > bufferSize) + *_bufferSize = strlcpy(buffer, inode->Node().short_symlink, bufferSize); + if (*_bufferSize > bufferSize) return B_BUFFER_OVERFLOW; return B_OK; diff --git a/src/kernel/core/fs/devfs.cpp b/src/kernel/core/fs/devfs.cpp index 4bb87fda33..4fc3979730 100644 --- a/src/kernel/core/fs/devfs.cpp +++ b/src/kernel/core/fs/devfs.cpp @@ -1115,15 +1115,17 @@ devfs_fsync(fs_volume _fs, fs_vnode _v) static status_t -devfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t bufferSize) +devfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t *_bufferSize) { struct devfs_vnode *link = (struct devfs_vnode *)_link; if (!S_ISLNK(link->stream.type)) return B_BAD_VALUE; - if (bufferSize <= link->stream.u.symlink.length) - return B_NAME_TOO_LONG; + if (*_bufferSize <= link->stream.u.symlink.length) { + *_bufferSize = link->stream.u.symlink.length + 1; + return B_BUFFER_OVERFLOW; + } memcpy(buffer, link->stream.u.symlink.path, link->stream.u.symlink.length + 1); return B_OK; diff --git a/src/kernel/core/fs/rootfs.c b/src/kernel/core/fs/rootfs.c index 23eecd3603..2a8c43656c 100644 --- a/src/kernel/core/fs/rootfs.c +++ b/src/kernel/core/fs/rootfs.c @@ -1,10 +1,11 @@ /* -** Copyright 2002-2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the Haiku License. -** -** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. -** Distributed under the terms of the NewOS License. -*/ + * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ + #include #include @@ -768,27 +769,29 @@ rootfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie cookie) static status_t rootfs_read_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes) { - return EPERM; + return B_NOT_ALLOWED; } static status_t rootfs_write_pages(fs_volume _fs, fs_vnode _v, fs_cookie cookie, off_t pos, const iovec *vecs, size_t count, size_t *_numBytes) { - return EPERM; + return B_NOT_ALLOWED; } static status_t -rootfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t bufferSize) +rootfs_read_link(fs_volume _fs, fs_vnode _link, char *buffer, size_t *_bufferSize) { struct rootfs_vnode *link = _link; - + if (!S_ISLNK(link->stream.type)) return B_BAD_VALUE; - if (bufferSize <= link->stream.symlink.length) - return B_NAME_TOO_LONG; + if (*_bufferSize <= link->stream.symlink.length) { + *_bufferSize = link->stream.symlink.length + 1; + return B_BUFFER_OVERFLOW; + } memcpy(buffer, link->stream.symlink.path, link->stream.symlink.length + 1); return B_OK; diff --git a/src/kernel/core/fs/vfs.cpp b/src/kernel/core/fs/vfs.cpp index e8c2cf306e..96e90a3eb8 100644 --- a/src/kernel/core/fs/vfs.cpp +++ b/src/kernel/core/fs/vfs.cpp @@ -1358,6 +1358,7 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink, // If the new node is a symbolic link, resolve it (if we've been told to do it) if (S_ISLNK(type) && !(!traverseLeafLink && nextPath[0] == '\0')) { + size_t bufferSize; char *buffer; PRINT(("traverse link\n")); @@ -1368,13 +1369,14 @@ vnode_path_to_vnode(struct vnode *vnode, char *path, bool traverseLeafLink, goto resolve_link_error; } - buffer = (char *)malloc(B_PATH_NAME_LENGTH); + buffer = (char *)malloc(bufferSize = B_PATH_NAME_LENGTH); if (buffer == NULL) { status = B_NO_MEMORY; goto resolve_link_error; } - status = FS_CALL(nextVnode, read_link)(nextVnode->mount->cookie, nextVnode->private_node, buffer, B_PATH_NAME_LENGTH); + status = FS_CALL(nextVnode, read_link)(nextVnode->mount->cookie, + nextVnode->private_node, buffer, &bufferSize); if (status < B_OK) { free(buffer); @@ -3573,7 +3575,7 @@ common_unlock_node(int fd, bool kernel) static status_t -common_read_link(int fd, char *path, char *buffer, size_t bufferSize, +common_read_link(int fd, char *path, char *buffer, size_t *_bufferSize, bool kernel) { struct vnode *vnode; @@ -3583,9 +3585,10 @@ common_read_link(int fd, char *path, char *buffer, size_t bufferSize, if (status < B_OK) return status; - if (FS_CALL(vnode, read_link) != NULL) - status = FS_CALL(vnode, read_link)(vnode->mount->cookie, vnode->private_node, buffer, bufferSize); - else + if (FS_CALL(vnode, read_link) != NULL) { + status = FS_CALL(vnode, read_link)(vnode->mount->cookie, + vnode->private_node, buffer, _bufferSize); + } else status = B_BAD_VALUE; put_vnode(vnode); @@ -5261,17 +5264,19 @@ _kern_remove_dir(const char *path) * symlink to be read. If both are given and the path is absolute, \a fd is * ignored; a relative path is reckoned off of the directory (!) identified * by \a fd. + * If this function fails with B_BUFFER_OVERFLOW, the \a _bufferSize pointer + * will still be updated to reflect the required buffer size. * * \param fd The FD. May be < 0. * \param path The absolute or relative path. May be \c NULL. * \param buffer The buffer into which the contents of the symlink shall be * written. - * \param bufferSize The size of the supplied buffer. + * \param _bufferSize A pointer to the size of the supplied buffer. * \return The length of the link on success or an appropriate error code */ -ssize_t -_kern_read_link(int fd, const char *path, char *buffer, size_t bufferSize) +status_t +_kern_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize) { status_t status; @@ -5279,15 +5284,10 @@ _kern_read_link(int fd, const char *path, char *buffer, size_t bufferSize) char pathBuffer[B_PATH_NAME_LENGTH + 1]; strlcpy(pathBuffer, path, B_PATH_NAME_LENGTH); - status = common_read_link(fd, pathBuffer, buffer, bufferSize, true); - } else - status = common_read_link(fd, NULL, buffer, bufferSize, true); - - if (status < B_OK) - return status; + return common_read_link(fd, pathBuffer, buffer, _bufferSize, true); + } - // Unlike what POSIX wants, our file systems must always null terminate links - return strlen(buffer); + return common_read_link(fd, NULL, buffer, _bufferSize, true); } @@ -6083,14 +6083,16 @@ _user_remove_dir(const char *userPath) } -ssize_t -_user_read_link(int fd, const char *userPath, char *userBuffer, size_t bufferSize) +status_t +_user_read_link(int fd, const char *userPath, char *userBuffer, size_t *userBufferSize) { char path[B_PATH_NAME_LENGTH + 1]; char buffer[B_PATH_NAME_LENGTH]; + size_t bufferSize; int status; - if (!IS_USER_ADDRESS(userBuffer)) + if (!IS_USER_ADDRESS(userBuffer) || !IS_USER_ADDRESS(userBufferSize) + || user_memcpy(&bufferSize, userBufferSize, sizeof(size_t)) < B_OK) return B_BAD_ADDRESS; if (userPath) { @@ -6103,18 +6105,22 @@ _user_read_link(int fd, const char *userPath, char *userBuffer, size_t bufferSiz if (bufferSize > B_PATH_NAME_LENGTH) bufferSize = B_PATH_NAME_LENGTH; + } - status = common_read_link(fd, path, buffer, bufferSize, false); - } else - status = common_read_link(fd, NULL, buffer, bufferSize, false); + status = common_read_link(fd, userPath ? path : NULL, buffer, &bufferSize, false); + + // we also update the bufferSize in case of errors + // (the real length will be returned in case of B_BUFFER_OVERFLOW) + if (user_memcpy(userBufferSize, &bufferSize, sizeof(size_t)) < B_OK) + return B_BAD_ADDRESS; if (status < B_OK) return status; - status = user_strlcpy(userBuffer, buffer, bufferSize); - if (status < 0) - return status; - return (status >= (int)bufferSize ? bufferSize : status + 1); + if (user_strlcpy(userBuffer, buffer, bufferSize) < 0) + return B_BAD_ADDRESS; + + return B_OK; } diff --git a/src/kernel/libroot/posix/unistd/link.c b/src/kernel/libroot/posix/unistd/link.c index 067321b1e6..92a420b3a8 100644 --- a/src/kernel/libroot/posix/unistd/link.c +++ b/src/kernel/libroot/posix/unistd/link.c @@ -1,5 +1,5 @@ /* - * Copyright 2002-2004, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -20,9 +20,13 @@ ssize_t readlink(const char *path, char *buffer, size_t bufferSize) { - int status = _kern_read_link(-1, path, buffer, bufferSize); + status_t status = _kern_read_link(-1, path, buffer, &bufferSize); + if (status < B_OK && status != B_BUFFER_OVERFLOW) { + errno = status; + return -1; + } - RETURN_AND_SET_ERRNO(status); + return bufferSize; } diff --git a/src/kits/storage/Entry.cpp b/src/kits/storage/Entry.cpp index a6b5f7cc2e..77e51e8430 100644 --- a/src/kits/storage/Entry.cpp +++ b/src/kits/storage/Entry.cpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------- -// This software is part of the OpenBeOS distribution and is covered -// by the OpenBeOS license. +// This software is part of the Haiku distribution and is covered +// by the MIT license. //--------------------------------------------------------------------- /*! \file Entry.cpp @@ -925,10 +925,10 @@ BEntry::set(int dirFD, const char *path, bool traverse) // we need to traverse the symlink if (--linkLimit < 0) return B_LINK_LIMIT; - ssize_t readBytes = _kern_read_link(dirFD, leafName, tmpPath, - B_PATH_NAME_LENGTH); - if (readBytes < 0) - return readBytes; + size_t bufferSize = B_PATH_NAME_LENGTH; + error = _kern_read_link(dirFD, leafName, tmpPath, &bufferSize); + if (error < 0) + return error; path = tmpPath; // next round... } diff --git a/src/kits/storage/LibBeAdapter.cpp b/src/kits/storage/LibBeAdapter.cpp index d321f4c7a7..1917686298 100644 --- a/src/kits/storage/LibBeAdapter.cpp +++ b/src/kits/storage/LibBeAdapter.cpp @@ -529,14 +529,16 @@ _kern_rewind_dir(int fd) // _kern_read_link extern "C" -ssize_t -_kern_read_link(int fd, const char *path, char *buffer, size_t bufferSize) +status_t +_kern_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize) { - ssize_t result = _kreadlink_(fd, path, buffer, bufferSize); - if (result >= 0) - buffer[result] = '\0'; + ssize_t result = _kreadlink_(fd, path, buffer, *_bufferSize); + if (result < 0) + return result; - return result; + buffer[result] = '\0'; + *_bufferSize = result; + return B_OK; } // _kern_unlink diff --git a/src/kits/storage/SymLink.cpp b/src/kits/storage/SymLink.cpp index 7a2a197bad..77e11322d0 100644 --- a/src/kits/storage/SymLink.cpp +++ b/src/kits/storage/SymLink.cpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------- -// This software is part of the OpenBeOS distribution and is covered -// by the OpenBeOS license. +// This software is part of the Haiku distribution and is covered +// by the MIT license. //--------------------------------------------------------------------- /*! \file SymLink.cpp @@ -103,13 +103,18 @@ BSymLink::~BSymLink() - some other error code */ ssize_t -BSymLink::ReadLink(char *buf, size_t size) +BSymLink::ReadLink(char *buffer, size_t size) { - if (!buf) + if (!buffer) return B_BAD_VALUE; if (InitCheck() != B_OK) return B_FILE_ERROR; - return _kern_read_link(get_fd(), NULL, buf, size); + + status_t error = _kern_read_link(get_fd(), NULL, buffer, &size); + if (error < B_OK) + return error; + + return size; } // MakeLinkedPath