| /* |
| * 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: |
| */ |