blob: 6f5b1c91fb7290a6736c0ee633d7099d750db1b1 [file] [log] [blame]
/*
* Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
* Licensed under the GPL
*/
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <utime.h>
#include <sys/vfs.h>
#include "os.h"
#include "user.h"
#include "hostfs.h"
extern int append;
char *get_path(const char *path[], char *buf, int size)
{
const char **s;
char *p;
int new = 1;
for(s = path; *s != NULL; s++){
new += strlen(*s);
if((*(s + 1) != NULL) && (strlen(*s) > 0) &&
((*s)[strlen(*s) - 1] != '/'))
new++;
}
if(new > size){
buf = um_kmalloc(new);
if(buf == NULL)
return(NULL);
}
p = buf;
for(s = path; *s != NULL; s++){
strcpy(p, *s);
p += strlen(*s);
if((*(s + 1) != NULL) && (strlen(*s) > 0) &&
((*s)[strlen(*s) - 1] != '/'))
strcpy(p++, "/");
}
return(buf);
}
void free_path(const char *buf, char *tmp)
{
if((buf != tmp) && (buf != NULL))
kfree((char *) buf);
}
int host_open_file(const char *path[], int r, int w)
{
char tmp[HOSTFS_BUFSIZE], *file;
int mode = 0, fd;
if(r && !w)
mode = O_RDONLY;
else if(!r && w)
mode = O_WRONLY;
else if(r && w)
mode = O_RDWR;
else {
printk("Impossible mode in host_open_file - r = %d, w = %d",
r, w);
return(-EINVAL);
}
if(append)
mode |= O_APPEND;
fd = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
fd = open64(file, mode);
if(fd < 0)
fd = -errno;
out:
free_path(file, tmp);
return(fd);
}
void *host_open_dir(const char *path[], int *err_out)
{
char tmp[HOSTFS_BUFSIZE], *file;
DIR *dir = NULL;
*err_out = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
dir = opendir(file);
*err_out = errno;
out:
free_path(file, tmp);
return(dir);
}
char *host_read_dir(void *stream, unsigned long long *pos,
unsigned long long *ino_out, int *len_out)
{
DIR *dir = stream;
struct dirent *ent;
seekdir(dir, *pos);
ent = readdir(dir);
if(ent == NULL) return(NULL);
*len_out = strlen(ent->d_name);
*ino_out = ent->d_ino;
*pos = telldir(dir);
return(ent->d_name);
}
void host_close_dir(void *stream)
{
closedir(stream);
}
int host_file_type(const char *path[], int *rdev)
{
char tmp[HOSTFS_BUFSIZE], *file;
struct stat64 buf;
int ret;
ret = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
if(lstat64(file, &buf) < 0){
ret = -errno;
goto out;
}
if(rdev != NULL)
*rdev = buf.st_rdev;
if(S_ISDIR(buf.st_mode)) ret = OS_TYPE_DIR;
else if(S_ISLNK(buf.st_mode)) ret = OS_TYPE_SYMLINK;
else if(S_ISCHR(buf.st_mode)) ret = OS_TYPE_CHARDEV;
else if(S_ISBLK(buf.st_mode)) ret = OS_TYPE_BLOCKDEV;
else if(S_ISFIFO(buf.st_mode))ret = OS_TYPE_FIFO;
else if(S_ISSOCK(buf.st_mode))ret = OS_TYPE_SOCK;
else ret = OS_TYPE_FILE;
out:
free_path(file, tmp);
return(ret);
}
int host_file_create(const char *path[], int ur, int uw, int ux,
int gr, int gw, int gx, int or, int ow, int ox)
{
char tmp[HOSTFS_BUFSIZE], *file;
int mode = 0, fd;
mode |= ur ? S_IRUSR : 0;
mode |= uw ? S_IWUSR : 0;
mode |= ux ? S_IXUSR : 0;
mode |= gr ? S_IRGRP : 0;
mode |= gw ? S_IWGRP : 0;
mode |= gx ? S_IXGRP : 0;
mode |= or ? S_IROTH : 0;
mode |= ow ? S_IWOTH : 0;
mode |= ox ? S_IXOTH : 0;
fd = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
fd = open64(file, O_CREAT | O_RDWR, mode);
out:
free_path(file, tmp);
return(fd);
}
static int do_stat_file(const char *path, int *dev_out,
unsigned long long *inode_out, int *mode_out,
int *nlink_out, int *uid_out, int *gid_out,
unsigned long long *size_out, unsigned long *atime_out,
unsigned long *mtime_out, unsigned long *ctime_out,
int *blksize_out, unsigned long long *blocks_out)
{
struct stat64 buf;
if(lstat64(path, &buf) < 0)
return(-errno);
if(dev_out != NULL) *dev_out = buf.st_dev;
/* See the Makefile for why STAT64_INO_FIELD is passed in
* by the build
*/
if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD;
if(mode_out != NULL) *mode_out = buf.st_mode;
if(nlink_out != NULL) *nlink_out = buf.st_nlink;
if(uid_out != NULL) *uid_out = buf.st_uid;
if(gid_out != NULL) *gid_out = buf.st_gid;
if(size_out != NULL) *size_out = buf.st_size;
if(atime_out != NULL) *atime_out = buf.st_atime;
if(mtime_out != NULL) *mtime_out = buf.st_mtime;
if(ctime_out != NULL) *ctime_out = buf.st_ctime;
if(blksize_out != NULL) *blksize_out = buf.st_blksize;
if(blocks_out != NULL) *blocks_out = buf.st_blocks;
return(0);
}
int host_stat_file(const char *path[], int *dev_out,
unsigned long long *inode_out, int *mode_out,
int *nlink_out, int *uid_out, int *gid_out,
unsigned long long *size_out, unsigned long *atime_out,
unsigned long *mtime_out, unsigned long *ctime_out,
int *blksize_out, unsigned long long *blocks_out)
{
char tmp[HOSTFS_BUFSIZE], *file;
int err;
err = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = do_stat_file(file, dev_out, inode_out, mode_out, nlink_out,
uid_out, gid_out, size_out, atime_out, mtime_out,
ctime_out, blksize_out, blocks_out);
out:
free_path(file, tmp);
return(err);
}
int host_set_attr(const char *path[], struct hostfs_iattr *attrs)
{
char tmp[HOSTFS_BUFSIZE], *file;
struct utimbuf buf;
int err = 0, ma;
if(append && (attrs->ia_valid & HOSTFS_ATTR_SIZE))
return(-EPERM);
err = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
if(attrs->ia_valid & HOSTFS_ATTR_MODE){
if(chmod(file, attrs->ia_mode) != 0){
err = -errno;
goto out;
}
}
if(attrs->ia_valid & HOSTFS_ATTR_UID){
if(chown(file, attrs->ia_uid, -1)){
err = -errno;
goto out;
}
}
if(attrs->ia_valid & HOSTFS_ATTR_GID){
if(chown(file, -1, attrs->ia_gid)){
err = -errno;
goto out;
}
}
if(attrs->ia_valid & HOSTFS_ATTR_SIZE){
if(truncate(file, attrs->ia_size)){
err = -errno;
goto out;
}
}
ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET;
if((attrs->ia_valid & ma) == ma){
buf.actime = attrs->ia_atime;
buf.modtime = attrs->ia_mtime;
if(utime(file, &buf) != 0){
err = -errno;
goto out;
}
}
else {
if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){
err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &buf.modtime,
NULL, NULL, NULL);
if(err != 0)
goto out;
buf.actime = attrs->ia_atime;
if(utime(file, &buf) != 0){
err = -errno;
goto out;
}
}
if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){
err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &buf.actime, NULL,
NULL, NULL, NULL);
if(err != 0)
goto out;
buf.modtime = attrs->ia_mtime;
if(utime(file, &buf) != 0){
err = -errno;
goto out;
}
}
}
if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ;
if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){
err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &attrs->ia_atime,
&attrs->ia_mtime, NULL, NULL, NULL);
if(err != 0)
goto out;
}
err = 0;
out:
free_path(file, tmp);
return(err);
}
int host_make_symlink(const char *from[], const char *to)
{
char tmp[HOSTFS_BUFSIZE], *file;
int err = -ENOMEM;
file = get_path(from, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = symlink(to, file);
if(err)
err = -errno;
out:
free_path(file, tmp);
return(err);
}
int host_unlink_file(const char *path[])
{
char tmp[HOSTFS_BUFSIZE], *file;
int err = -ENOMEM;
if(append)
return(-EPERM);
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = unlink(file);
if(err)
err = -errno;
out:
free_path(file, tmp);
return(err);
}
int host_mkdir(const char *path[], int mode)
{
char tmp[HOSTFS_BUFSIZE], *file;
int err = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = mkdir(file, mode);
if(err)
err = -errno;
out:
free_path(file, tmp);
return(err);
}
int host_rmdir(const char *path[])
{
char tmp[HOSTFS_BUFSIZE], *file;
int err = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = rmdir(file);
if(err)
err = -errno;
out:
free_path(file, tmp);
return(err);
}
int host_link_file(const char *to[], const char *from[])
{
char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
int err = -ENOMEM;
f = get_path(from, from_tmp, sizeof(from_tmp));
t = get_path(to, to_tmp, sizeof(to_tmp));
if((f == NULL) || (t == NULL))
goto out;
err = link(t, f);
if(err)
err = -errno;
out:
free_path(f, from_tmp);
free_path(t, to_tmp);
return(err);
}
int host_readlink(const char *path[], char *buf, int size)
{
char tmp[HOSTFS_BUFSIZE], *file;
int n = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
n = readlink(file, buf, size);
if(n < 0)
n = -errno;
if(n < size)
buf[n] = '\0';
out:
free_path(file, tmp);
return(n);
}
int host_rename_file(const char *from[], const char *to[])
{
char from_tmp[HOSTFS_BUFSIZE], *f, to_tmp[HOSTFS_BUFSIZE], *t;
int err = -ENOMEM;
f = get_path(from, from_tmp, sizeof(from_tmp));
t = get_path(to, to_tmp, sizeof(to_tmp));
if((f == NULL) || (t == NULL))
goto out;
err = rename(f, t);
if(err < 0)
err = -errno;
out:
free_path(f, from_tmp);
free_path(t, to_tmp);
return(err);
}
int host_statfs(const char *path[], long *bsize_out, long long *blocks_out,
long long *bfree_out, long long *bavail_out,
long long *files_out, long long *ffree_out, void *fsid_out,
int fsid_size, long *namelen_out, long *spare_out)
{
char tmp[HOSTFS_BUFSIZE], *file;
struct statfs64 buf;
int err = -ENOMEM;
file = get_path(path, tmp, sizeof(tmp));
if(file == NULL)
goto out;
err = statfs64(file, &buf);
if(err < 0){
err = -errno;
goto out;
}
*bsize_out = buf.f_bsize;
*blocks_out = buf.f_blocks;
*bfree_out = buf.f_bfree;
*bavail_out = buf.f_bavail;
*files_out = buf.f_files;
*ffree_out = buf.f_ffree;
memcpy(fsid_out, &buf.f_fsid,
sizeof(buf.f_fsid) > fsid_size ? fsid_size :
sizeof(buf.f_fsid));
*namelen_out = buf.f_namelen;
spare_out[0] = buf.f_spare[0];
spare_out[1] = buf.f_spare[1];
spare_out[2] = buf.f_spare[2];
spare_out[3] = buf.f_spare[3];
spare_out[4] = buf.f_spare[4];
spare_out[5] = buf.f_spare[5];
out:
free_path(file, tmp);
return(err);
}
char *generic_host_read_dir(void *stream, unsigned long long *pos,
unsigned long long *ino_out, int *len_out,
void *mount)
{
return(host_read_dir(stream, pos, ino_out, len_out));
}
int generic_host_read_file(int fd, unsigned long long offset, char *buf,
int len, void *mount)
{
return(host_read_file(fd, offset, buf, len));
}
int generic_host_write_file(int fd, unsigned long long offset, const char *buf,
int len, void *mount)
{
return(host_write_file(fd, offset, buf, len));
}
void generic_host_close_file(void *stream, unsigned long long size, void *mount)
{
return(host_close_file(stream));
}
void generic_host_close_dir(void *stream, void *mount)
{
return(host_close_dir(stream));
}
int generic_host_truncate_file(int fd, __u64 size, void *m)
{
return(os_truncate_fd(fd, size));
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/