git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34287 a95241bf-73f2-0310-859d-f6bbb57e9c96
1024 lines
20 KiB
C++
1024 lines
20 KiB
C++
/*
|
|
* Copyright 2005-2008, Ingo Weinhold, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <BeOSBuildCompatibility.h>
|
|
|
|
#include "fs_impl.h"
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <utime.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <map>
|
|
#include <string>
|
|
|
|
#include <fs_attr.h>
|
|
#include <NodeMonitor.h> // for B_STAT_* flags
|
|
#include <syscalls.h>
|
|
|
|
#include "fs_descriptors.h"
|
|
#include "NodeRef.h"
|
|
|
|
#if defined(HAIKU_HOST_PLATFORM_FREEBSD)
|
|
# include "fs_freebsd.h"
|
|
#endif
|
|
|
|
|
|
using namespace std;
|
|
using namespace BPrivate;
|
|
|
|
|
|
#if defined(HAIKU_HOST_PLATFORM_FREEBSD)
|
|
# define haiku_host_platform_read haiku_freebsd_read
|
|
# define haiku_host_platform_write haiku_freebsd_write
|
|
# define haiku_host_platform_readv haiku_freebsd_readv
|
|
# define haiku_host_platform_writev haiku_freebsd_writev
|
|
#else
|
|
# define haiku_host_platform_read read
|
|
# define haiku_host_platform_write write
|
|
# define haiku_host_platform_readv readv
|
|
# define haiku_host_platform_writev writev
|
|
#endif
|
|
|
|
|
|
static status_t get_path(dev_t device, ino_t node, const char *name,
|
|
string &path);
|
|
|
|
|
|
// find_dir_entry
|
|
static status_t
|
|
find_dir_entry(DIR *dir, const char *path, NodeRef ref, string &name,
|
|
bool skipDot)
|
|
{
|
|
// find the entry
|
|
bool found = false;
|
|
while (dirent *entry = readdir(dir)) {
|
|
if ((!skipDot && strcmp(entry->d_name, ".") == 0)
|
|
|| strcmp(entry->d_name, "..") == 0) {
|
|
// skip "." and ".."
|
|
} else /*if (entry->d_ino == ref.node)*/ {
|
|
// Note: Linux doesn't seem to translate dirent::d_ino of
|
|
// mount points. Thus we always have to lstat().
|
|
// We also need to compare the device, which is generally not
|
|
// included in the dirent structure. Hence we lstat().
|
|
string entryPath(path);
|
|
entryPath += '/';
|
|
entryPath += entry->d_name;
|
|
struct stat st;
|
|
if (lstat(entryPath.c_str(), &st) == 0) {
|
|
if (NodeRef(st) == ref) {
|
|
name = entry->d_name;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// find_dir_entry
|
|
static status_t
|
|
find_dir_entry(const char *path, NodeRef ref, string &name, bool skipDot)
|
|
{
|
|
// open dir
|
|
DIR *dir = opendir(path);
|
|
if (!dir)
|
|
return errno;
|
|
|
|
status_t error = find_dir_entry(dir, path, ref, name, skipDot);
|
|
|
|
// close dir
|
|
closedir(dir);
|
|
|
|
return error;
|
|
}
|
|
|
|
// normalize_dir_path
|
|
static status_t
|
|
normalize_dir_path(string path, NodeRef ref, string &normalizedPath)
|
|
{
|
|
// get parent path
|
|
path += "/..";
|
|
|
|
// stat the parent dir
|
|
struct stat st;
|
|
if (lstat(path.c_str(), &st) < 0)
|
|
return errno;
|
|
|
|
// root dir?
|
|
NodeRef parentRef(st);
|
|
if (parentRef == ref) {
|
|
normalizedPath = "/";
|
|
return 0;
|
|
}
|
|
|
|
// find the entry
|
|
string name;
|
|
status_t error = find_dir_entry(path.c_str(), ref, name, true) ;
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// recurse to get the parent dir path, if found
|
|
error = normalize_dir_path(path, parentRef, normalizedPath);
|
|
if (error != 0)
|
|
return error;
|
|
|
|
// construct the normalizedPath
|
|
if (normalizedPath.length() > 1) // don't append "/", if parent is root
|
|
normalizedPath += '/';
|
|
normalizedPath += name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// normalize_dir_path
|
|
static status_t
|
|
normalize_dir_path(const char *path, string &normalizedPath)
|
|
{
|
|
// stat() the dir
|
|
struct stat st;
|
|
if (stat(path, &st) < 0)
|
|
return errno;
|
|
|
|
return normalize_dir_path(path, NodeRef(st), normalizedPath);
|
|
}
|
|
|
|
// normalize_entry_path
|
|
static status_t
|
|
normalize_entry_path(const char *path, string &normalizedPath)
|
|
{
|
|
const char *dirPath = NULL;
|
|
const char *leafName = NULL;
|
|
|
|
string dirPathString;
|
|
if (const char *lastSlash = strrchr(path, '/')) {
|
|
// found a slash: decompose into dir path and leaf name
|
|
leafName = lastSlash + 1;
|
|
if (leafName[0] == '\0') {
|
|
// slash is at the end: the whole path is a dir name
|
|
leafName = NULL;
|
|
} else {
|
|
dirPathString = string(path, leafName - path);
|
|
dirPath = dirPathString.c_str();
|
|
}
|
|
|
|
} else {
|
|
// path contains no slash, so it is a path relative to the current dir
|
|
dirPath = ".";
|
|
leafName = path;
|
|
}
|
|
|
|
// catch special case: no leaf, or leaf is a directory
|
|
if (!leafName || strcmp(leafName, ".") == 0 || strcmp(leafName, "..") == 0)
|
|
return normalize_dir_path(path, normalizedPath);
|
|
|
|
// normalize the dir path
|
|
status_t error = normalize_dir_path(dirPath, normalizedPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// append the leaf name
|
|
if (normalizedPath.length() > 1) // don't append "/", if parent is root
|
|
normalizedPath += '/';
|
|
normalizedPath += leafName;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
typedef map<NodeRef, string> DirPathMap;
|
|
static DirPathMap sDirPathMap;
|
|
|
|
// get_path
|
|
static status_t
|
|
get_path(const NodeRef *ref, const char *name, string &path)
|
|
{
|
|
if (!ref && !name)
|
|
return B_BAD_VALUE;
|
|
|
|
// no ref or absolute path
|
|
if (!ref || (name && name[0] == '/')) {
|
|
path = name;
|
|
return B_OK;
|
|
}
|
|
|
|
// get the dir path
|
|
if (ref) {
|
|
DirPathMap::iterator it = sDirPathMap.find(*ref);
|
|
if (it == sDirPathMap.end())
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
path = it->second;
|
|
|
|
// stat the path to check, if it is still valid
|
|
struct stat st;
|
|
if (lstat(path.c_str(), &st) < 0) {
|
|
sDirPathMap.erase(it);
|
|
return errno;
|
|
}
|
|
|
|
// compare the NodeRef
|
|
if (NodeRef(st) != *ref) {
|
|
sDirPathMap.erase(it);
|
|
return B_ENTRY_NOT_FOUND;
|
|
}
|
|
|
|
// still a directory?
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
sDirPathMap.erase(it);
|
|
return B_NOT_A_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
// if there's a name, append it
|
|
if (name) {
|
|
path += '/';
|
|
path += name;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// get_path
|
|
status_t
|
|
BPrivate::get_path(int fd, const char *name, string &path)
|
|
{
|
|
// get the node ref for the fd, if any, and the path part is not absolute
|
|
if (fd >= 0 && !(name && name[0] == '/')) {
|
|
// get descriptor
|
|
Descriptor *descriptor = get_descriptor(fd);
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// get node ref for the descriptor
|
|
NodeRef ref;
|
|
status_t error = descriptor->GetNodeRef(ref);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return ::get_path(&ref, name, path);
|
|
|
|
} else // no descriptor or absolute path
|
|
return ::get_path((NodeRef*)NULL, name, path);
|
|
}
|
|
|
|
// get_path
|
|
static status_t
|
|
get_path(dev_t device, ino_t directory, const char *name, string &path)
|
|
{
|
|
NodeRef ref;
|
|
ref.device = device;
|
|
ref.node = directory;
|
|
|
|
return get_path(&ref, name, path);
|
|
}
|
|
|
|
// add_dir_path
|
|
static void
|
|
add_dir_path(const char *path, const NodeRef &ref)
|
|
{
|
|
// add the normalized path
|
|
string normalizedPath;
|
|
if (normalize_dir_path(path, normalizedPath) == B_OK)
|
|
sDirPathMap[ref] = normalizedPath;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// _kern_entry_ref_to_path
|
|
status_t
|
|
_kern_entry_ref_to_path(dev_t device, ino_t node, const char *leaf,
|
|
char *userPath, size_t pathLength)
|
|
{
|
|
// get the path
|
|
string path;
|
|
status_t error = get_path(device, node, leaf, path);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// copy it back to the user buffer
|
|
if (strlcpy(userPath, path.c_str(), pathLength) >= pathLength)
|
|
return B_BUFFER_OVERFLOW;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// _kern_create_dir
|
|
status_t
|
|
_kern_create_dir(int fd, const char *path, int perms)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// mkdir
|
|
if (mkdir(realPath.c_str(), perms) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_create_dir_entry_ref
|
|
status_t
|
|
_kern_create_dir_entry_ref(dev_t device, ino_t node, const char *name,
|
|
int perms)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(device, node, name, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// mkdir
|
|
if (mkdir(realPath.c_str(), perms) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// open_dir
|
|
static int
|
|
open_dir(const char *path)
|
|
{
|
|
// open the dir
|
|
DIR *dir = opendir(path);
|
|
if (!dir)
|
|
return errno;
|
|
|
|
// stat the entry
|
|
struct stat st;
|
|
if (stat(path, &st) < 0) {
|
|
closedir(dir);
|
|
return errno;
|
|
}
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
closedir(dir);
|
|
return B_NOT_A_DIRECTORY;
|
|
}
|
|
|
|
// cache dir path
|
|
NodeRef ref(st);
|
|
add_dir_path(path, ref);
|
|
|
|
// create descriptor
|
|
DirectoryDescriptor *descriptor = new DirectoryDescriptor(dir, ref);
|
|
return add_descriptor(descriptor);
|
|
}
|
|
|
|
// _kern_open_dir
|
|
int
|
|
_kern_open_dir(int fd, const char *path)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return open_dir(realPath.c_str());
|
|
}
|
|
|
|
// _kern_open_dir_entry_ref
|
|
int
|
|
_kern_open_dir_entry_ref(dev_t device, ino_t node, const char *name)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(device, node, name, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return open_dir(realPath.c_str());
|
|
}
|
|
|
|
// _kern_open_parent_dir
|
|
int
|
|
_kern_open_parent_dir(int fd, char *name, size_t nameLength)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, NULL, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// stat the entry
|
|
struct stat st;
|
|
if (stat(realPath.c_str(), &st) < 0)
|
|
return errno;
|
|
|
|
if (!S_ISDIR(st.st_mode))
|
|
return B_NOT_A_DIRECTORY;
|
|
|
|
// get the entry name
|
|
realPath += "/..";
|
|
string entryName;
|
|
error = find_dir_entry(realPath.c_str(), NodeRef(st),
|
|
entryName, false);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
if (strlcpy(name, entryName.c_str(), nameLength) >= nameLength)
|
|
return B_BUFFER_OVERFLOW;
|
|
|
|
// open the parent directory
|
|
|
|
return open_dir(realPath.c_str());
|
|
}
|
|
|
|
// _kern_read_dir
|
|
ssize_t
|
|
_kern_read_dir(int fd, struct dirent *buffer, size_t bufferSize,
|
|
uint32 maxCount)
|
|
{
|
|
if (maxCount <= 0)
|
|
return B_BAD_VALUE;
|
|
|
|
// get the descriptor
|
|
DirectoryDescriptor *descriptor
|
|
= dynamic_cast<DirectoryDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// get the next entry
|
|
dirent *entry;
|
|
if (dynamic_cast<AttrDirDescriptor*>(descriptor))
|
|
entry = fs_read_attr_dir(descriptor->dir);
|
|
else
|
|
entry = readdir(descriptor->dir);
|
|
if (!entry)
|
|
return errno;
|
|
|
|
// copy the entry
|
|
int entryLen = &entry->d_name[strlen(entry->d_name) + 1] - (char*)entry;
|
|
if (entryLen > (int)bufferSize)
|
|
return B_BUFFER_OVERFLOW;
|
|
|
|
memcpy(buffer, entry, entryLen);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// _kern_rewind_dir
|
|
status_t
|
|
_kern_rewind_dir(int fd)
|
|
{
|
|
// get the descriptor
|
|
DirectoryDescriptor *descriptor
|
|
= dynamic_cast<DirectoryDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// rewind
|
|
if (dynamic_cast<AttrDirDescriptor*>(descriptor))
|
|
fs_rewind_attr_dir(descriptor->dir);
|
|
else
|
|
rewinddir(descriptor->dir);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// open_file
|
|
static int
|
|
open_file(const char *path, int openMode, int perms)
|
|
{
|
|
// stat the node
|
|
bool exists = true;
|
|
struct stat st;
|
|
if (lstat(path, &st) < 0) {
|
|
exists = false;
|
|
if (!(openMode & O_CREAT))
|
|
return errno;
|
|
}
|
|
|
|
Descriptor *descriptor;
|
|
if (exists && S_ISLNK(st.st_mode) && (openMode & O_NOTRAVERSE) != 0) {
|
|
// a symlink not to be followed: create a special descriptor
|
|
// normalize path first
|
|
string normalizedPath;
|
|
status_t error = normalize_entry_path(path, normalizedPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
descriptor = new SymlinkDescriptor(normalizedPath.c_str());
|
|
} else {
|
|
// open the file
|
|
openMode &= ~O_NOTRAVERSE;
|
|
int newFD = open(path, openMode, perms);
|
|
if (newFD < 0)
|
|
return errno;
|
|
|
|
descriptor = new FileDescriptor(newFD);
|
|
}
|
|
|
|
// cache path, if this is a directory
|
|
if (exists && S_ISDIR(st.st_mode))
|
|
add_dir_path(path, NodeRef(st));
|
|
|
|
return add_descriptor(descriptor);
|
|
}
|
|
|
|
// _kern_open
|
|
int
|
|
_kern_open(int fd, const char *path, int openMode, int perms)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return open_file(realPath.c_str(), openMode, perms);
|
|
}
|
|
|
|
// _kern_open_entry_ref
|
|
int
|
|
_kern_open_entry_ref(dev_t device, ino_t node, const char *name,
|
|
int openMode, int perms)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(device, node, name, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return open_file(realPath.c_str(), openMode, perms);
|
|
}
|
|
|
|
// _kern_seek
|
|
off_t
|
|
_kern_seek(int fd, off_t pos, int seekType)
|
|
{
|
|
// get the descriptor
|
|
FileDescriptor *descriptor
|
|
= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// seek
|
|
off_t result = lseek(descriptor->fd, pos, seekType);
|
|
if (result < 0)
|
|
return errno;
|
|
|
|
return result;
|
|
}
|
|
|
|
// _kern_read
|
|
ssize_t
|
|
_kern_read(int fd, off_t pos, void *buffer, size_t bufferSize)
|
|
{
|
|
// get the descriptor
|
|
FileDescriptor *descriptor
|
|
= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// seek
|
|
if (pos != -1) {
|
|
off_t result = lseek(descriptor->fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
}
|
|
|
|
// read
|
|
ssize_t bytesRead = haiku_host_platform_read(descriptor->fd, buffer,
|
|
bufferSize);
|
|
if (bytesRead < 0)
|
|
return errno;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
// _kern_write
|
|
ssize_t
|
|
_kern_write(int fd, off_t pos, const void *buffer, size_t bufferSize)
|
|
{
|
|
// get the descriptor
|
|
FileDescriptor *descriptor
|
|
= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// seek
|
|
if (pos != -1) {
|
|
off_t result = lseek(descriptor->fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
}
|
|
|
|
// read
|
|
ssize_t bytesWritten = haiku_host_platform_write(descriptor->fd, buffer,
|
|
bufferSize);
|
|
if (bytesWritten < 0)
|
|
return errno;
|
|
|
|
return bytesWritten;
|
|
}
|
|
|
|
// _kern_close
|
|
status_t
|
|
_kern_close(int fd)
|
|
{
|
|
return delete_descriptor(fd);
|
|
}
|
|
|
|
// _kern_dup
|
|
int
|
|
_kern_dup(int fd)
|
|
{
|
|
// get the descriptor
|
|
Descriptor *descriptor = get_descriptor(fd);
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// clone it
|
|
Descriptor *clone;
|
|
status_t error = descriptor->Dup(clone);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
return add_descriptor(clone);
|
|
}
|
|
|
|
// _kern_fsync
|
|
status_t
|
|
_kern_fsync(int fd)
|
|
{
|
|
// get the descriptor
|
|
FileDescriptor *descriptor
|
|
= dynamic_cast<FileDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// sync
|
|
if (fsync(descriptor->fd) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_read_stat
|
|
status_t
|
|
_kern_read_stat(int fd, const char *path, bool traverseLink,
|
|
struct stat *st, size_t statSize)
|
|
{
|
|
if (path) {
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// stat
|
|
int result;
|
|
if (traverseLink)
|
|
result = stat(realPath.c_str(), st);
|
|
else
|
|
result = lstat(realPath.c_str(), st);
|
|
|
|
if (result < 0)
|
|
return errno;
|
|
} else {
|
|
Descriptor *descriptor = get_descriptor(fd);
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
return descriptor->GetStat(traverseLink, st);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_write_stat
|
|
status_t
|
|
_kern_write_stat(int fd, const char *path, bool traverseLink,
|
|
const struct stat *st, size_t statSize, int statMask)
|
|
{
|
|
// get a usable path
|
|
int realFD = -1;
|
|
string realPath;
|
|
status_t error;
|
|
bool isSymlink = false;
|
|
if (path) {
|
|
error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// stat it to see, if it is a symlink
|
|
struct stat tmpStat;
|
|
if (lstat(realPath.c_str(), &tmpStat) < 0)
|
|
return errno;
|
|
|
|
isSymlink = S_ISLNK(tmpStat.st_mode);
|
|
|
|
} else {
|
|
Descriptor *descriptor = get_descriptor(fd);
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
if (FileDescriptor *fileFD
|
|
= dynamic_cast<FileDescriptor*>(descriptor)) {
|
|
realFD = fileFD->fd;
|
|
|
|
} else if (dynamic_cast<DirectoryDescriptor*>(descriptor)) {
|
|
error = get_path(fd, NULL, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
} else if (SymlinkDescriptor *linkFD
|
|
= dynamic_cast<SymlinkDescriptor*>(descriptor)) {
|
|
realPath = linkFD->path;
|
|
isSymlink = true;
|
|
|
|
} else
|
|
return B_FILE_ERROR;
|
|
}
|
|
|
|
// We're screwed, if the node to manipulate is a symlink. All the
|
|
// available functions traverse symlinks.
|
|
if (isSymlink && !traverseLink)
|
|
return B_ERROR;
|
|
|
|
if (realFD >= 0) {
|
|
if (statMask & B_STAT_MODE) {
|
|
if (fchmod(realFD, st->st_mode) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_UID) {
|
|
if (fchown(realFD, st->st_uid, (gid_t)-1) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_GID) {
|
|
if (fchown(realFD, (uid_t)-1, st->st_gid) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_SIZE) {
|
|
if (ftruncate(realFD, st->st_size) < 0)
|
|
return errno;
|
|
}
|
|
|
|
// The timestamps can only be set via utime(), but that requires a
|
|
// path we don't have.
|
|
if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
|
|
| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME)) {
|
|
return B_ERROR;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
if (statMask & B_STAT_MODE) {
|
|
if (chmod(realPath.c_str(), st->st_mode) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_UID) {
|
|
if (chown(realPath.c_str(), st->st_uid, (gid_t)-1) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_GID) {
|
|
if (chown(realPath.c_str(), (uid_t)-1, st->st_gid) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & B_STAT_SIZE) {
|
|
if (truncate(realPath.c_str(), st->st_size) < 0)
|
|
return errno;
|
|
}
|
|
|
|
if (statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
|
|
// Grab the previous mod and access times so we only overwrite
|
|
// the specified time and not both
|
|
struct stat oldStat;
|
|
if (~statMask & (B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME)) {
|
|
if (stat(realPath.c_str(), &oldStat) < 0)
|
|
return errno;
|
|
}
|
|
|
|
utimbuf buffer;
|
|
buffer.actime = (statMask & B_STAT_ACCESS_TIME) ? st->st_atime : oldStat.st_atime;
|
|
buffer.modtime = (statMask & B_STAT_MODIFICATION_TIME) ? st->st_mtime : oldStat.st_mtime;
|
|
if (utime(realPath.c_str(), &buffer) < 0)
|
|
return errno;
|
|
}
|
|
|
|
// not supported
|
|
if (statMask & (B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME))
|
|
return B_ERROR;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// _kern_create_symlink
|
|
status_t
|
|
_kern_create_symlink(int fd, const char *path, const char *toPath, int mode)
|
|
{
|
|
// Note: path must not be NULL, so this will always work.
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// symlink
|
|
if (symlink(toPath, realPath.c_str()) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_read_link
|
|
status_t
|
|
_kern_read_link(int fd, const char *path, char *buffer, size_t *_bufferSize)
|
|
{
|
|
// get the descriptor
|
|
SymlinkDescriptor *descriptor
|
|
= dynamic_cast<SymlinkDescriptor*>(get_descriptor(fd));
|
|
if (!descriptor)
|
|
return B_FILE_ERROR;
|
|
|
|
// readlink
|
|
ssize_t bytesRead = readlink(descriptor->path.c_str(), buffer,
|
|
*_bufferSize);
|
|
if (bytesRead < 0)
|
|
return errno;
|
|
|
|
if (*_bufferSize > 0) {
|
|
if ((size_t)bytesRead == *_bufferSize)
|
|
bytesRead--;
|
|
|
|
buffer[bytesRead] = '\0';
|
|
}
|
|
|
|
*_bufferSize = bytesRead;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_unlink
|
|
status_t
|
|
_kern_unlink(int fd, const char *path)
|
|
{
|
|
// get a usable path
|
|
string realPath;
|
|
status_t error = get_path(fd, path, realPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// unlink
|
|
if (unlink(realPath.c_str()) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// _kern_rename
|
|
status_t
|
|
_kern_rename(int oldDir, const char *oldPath, int newDir, const char *newPath)
|
|
{
|
|
// get usable paths
|
|
string realOldPath;
|
|
status_t error = get_path(oldDir, oldPath, realOldPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
string realNewPath;
|
|
error = get_path(newDir, newPath, realNewPath);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// rename
|
|
if (rename(realOldPath.c_str(), realNewPath.c_str()) < 0)
|
|
return errno;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// _kern_lock_node
|
|
status_t
|
|
_kern_lock_node(int fd)
|
|
{
|
|
return B_ERROR;
|
|
}
|
|
|
|
// _kern_unlock_node
|
|
status_t
|
|
_kern_unlock_node(int fd)
|
|
{
|
|
return B_ERROR;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// read_pos
|
|
ssize_t
|
|
read_pos(int fd, off_t pos, void *buffer, size_t bufferSize)
|
|
{
|
|
// seek
|
|
off_t result = lseek(fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
|
|
// read
|
|
ssize_t bytesRead = haiku_host_platform_read(fd, buffer, bufferSize);
|
|
if (bytesRead < 0) {
|
|
errno = bytesRead;
|
|
return -1;
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
// write_pos
|
|
ssize_t
|
|
write_pos(int fd, off_t pos, const void *buffer, size_t bufferSize)
|
|
{
|
|
// seek
|
|
off_t result = lseek(fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
|
|
// read
|
|
ssize_t bytesWritten = haiku_host_platform_write(fd, buffer, bufferSize);
|
|
if (bytesWritten < 0) {
|
|
errno = bytesWritten;
|
|
return -1;
|
|
}
|
|
|
|
return bytesWritten;
|
|
}
|
|
|
|
// readv_pos
|
|
ssize_t
|
|
readv_pos(int fd, off_t pos, const struct iovec *vec, size_t count)
|
|
{
|
|
// seek
|
|
off_t result = lseek(fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
|
|
// read
|
|
ssize_t bytesRead = haiku_host_platform_readv(fd, vec, count);
|
|
if (bytesRead < 0) {
|
|
errno = bytesRead;
|
|
return -1;
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
// writev_pos
|
|
ssize_t
|
|
writev_pos(int fd, off_t pos, const struct iovec *vec, size_t count)
|
|
{
|
|
// seek
|
|
off_t result = lseek(fd, pos, SEEK_SET);
|
|
if (result < 0)
|
|
return errno;
|
|
|
|
// read
|
|
ssize_t bytesWritten = haiku_host_platform_writev(fd, vec, count);
|
|
if (bytesWritten < 0) {
|
|
errno = bytesWritten;
|
|
return -1;
|
|
}
|
|
|
|
return bytesWritten;
|
|
}
|