From: Mark Huang Date: Fri, 20 Aug 2004 18:50:31 +0000 (+0000) Subject: Initial revision X-Git-Tag: after-ipod-patch~7 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=fcaa72a25bf2a04dead51eadb349516d2a2383d1;p=linux-2.6.git Initial revision --- diff --git a/arch/um/include/aio.h b/arch/um/include/aio.h new file mode 100644 index 000000000..6096f4f4e --- /dev/null +++ b/arch/um/include/aio.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef AIO_H__ +#define AIO_H__ + +enum aio_type { AIO_READ, AIO_WRITE, AIO_MMAP }; + +struct aio_thread_reply { + void *data; + int err; +}; + +struct aio_context { + int reply_fd; +}; + +#define INIT_AIO_CONTEXT { .reply_fd = -1 } + +extern int submit_aio(enum aio_type type, int fd, char *buf, int len, + unsigned long long offset, int reply_fd, void *data); + +#endif + +/* + * 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: + */ diff --git a/arch/um/include/filehandle.h b/arch/um/include/filehandle.h new file mode 100644 index 000000000..adc51087a --- /dev/null +++ b/arch/um/include/filehandle.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __FILEHANDLE_H__ +#define __FILEHANDLE_H__ + +#include "linux/list.h" +#include "linux/fs.h" +#include "os.h" + +struct file_handle { + struct list_head list; + int fd; + char *(*get_name)(struct inode *); + struct inode *inode; + struct openflags flags; +}; + +extern struct file_handle bad_filehandle; + +extern int open_file(char *name, struct openflags flags, int mode); +extern void *open_dir(char *file); +extern int open_filehandle(char *name, struct openflags flags, int mode, + struct file_handle *fh); +extern int read_file(struct file_handle *fh, unsigned long long offset, + char *buf, int len); +extern int write_file(struct file_handle *fh, unsigned long long offset, + const char *buf, int len); +extern int truncate_file(struct file_handle *fh, unsigned long long size); +extern int close_file(struct file_handle *fh); +extern void not_reclaimable(struct file_handle *fh); +extern void is_reclaimable(struct file_handle *fh, + char *(name_proc)(struct inode *), + struct inode *inode); +extern int filehandle_fd(struct file_handle *fh); +extern int make_pipe(struct file_handle *fhs); + +#endif + +/* + * 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: + */ diff --git a/arch/um/kernel/filehandle.c b/arch/um/kernel/filehandle.c new file mode 100644 index 000000000..a44dccf08 --- /dev/null +++ b/arch/um/kernel/filehandle.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/list.h" +#include "linux/spinlock.h" +#include "linux/fs.h" +#include "linux/errno.h" +#include "filehandle.h" +#include "os.h" +#include "kern_util.h" + +static spinlock_t open_files_lock = SPIN_LOCK_UNLOCKED; +static struct list_head open_files = LIST_HEAD_INIT(open_files); + +#define NUM_RECLAIM 128 + +static void reclaim_fds(void) +{ + struct file_handle *victim; + int closed = NUM_RECLAIM; + + spin_lock(&open_files_lock); + while(!list_empty(&open_files) && closed--){ + victim = list_entry(open_files.prev, struct file_handle, list); + os_close_file(victim->fd); + victim->fd = -1; + list_del_init(&victim->list); + } + spin_unlock(&open_files_lock); +} + +int open_file(char *name, struct openflags flags, int mode) +{ + int fd; + + fd = os_open_file(name, flags, mode); + if(fd != -EMFILE) + return(fd); + + reclaim_fds(); + fd = os_open_file(name, flags, mode); + + return(fd); +} + +void *open_dir(char *file) +{ + void *dir; + int err; + + dir = os_open_dir(file, &err); + if(dir != NULL) + return(dir); + if(err != -EMFILE) + return(ERR_PTR(err)); + + reclaim_fds(); + + dir = os_open_dir(file, &err); + if(dir == NULL) + dir = ERR_PTR(err); + + return(dir); +} + +void not_reclaimable(struct file_handle *fh) +{ + char *name; + + if(fh->get_name == NULL) + return; + + if(list_empty(&fh->list)){ + name = (*fh->get_name)(fh->inode); + if(name != NULL){ + fh->fd = open_file(name, fh->flags, 0); + kfree(name); + } + else printk("File descriptor %d has no name\n", fh->fd); + } + else { + spin_lock(&open_files_lock); + list_del_init(&fh->list); + spin_unlock(&open_files_lock); + } +} + +void is_reclaimable(struct file_handle *fh, char *(name_proc)(struct inode *), + struct inode *inode) +{ + fh->get_name = name_proc; + fh->inode = inode; + + spin_lock(&open_files_lock); + list_add(&fh->list, &open_files); + spin_unlock(&open_files_lock); +} + +static int active_handle(struct file_handle *fh) +{ + int fd; + char *name; + + if(!list_empty(&fh->list)) + list_move(&fh->list, &open_files); + + if(fh->fd != -1) + return(0); + + if(fh->inode == NULL) + return(-ENOENT); + + name = (*fh->get_name)(fh->inode); + if(name == NULL) + return(-ENOMEM); + + fd = open_file(name, fh->flags, 0); + kfree(name); + if(fd < 0) + return(fd); + + fh->fd = fd; + is_reclaimable(fh, fh->get_name, fh->inode); + + return(0); +} + +int filehandle_fd(struct file_handle *fh) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + return(fh->fd); +} + +static void init_fh(struct file_handle *fh, int fd, struct openflags flags) +{ + flags.c = 0; + *fh = ((struct file_handle) { .list = LIST_HEAD_INIT(fh->list), + .fd = fd, + .get_name = NULL, + .inode = NULL, + .flags = flags }); +} + +int open_filehandle(char *name, struct openflags flags, int mode, + struct file_handle *fh) +{ + int fd; + + fd = open_file(name, flags, mode); + if(fd < 0) + return(fd); + + init_fh(fh, fd, flags); + return(0); +} + +int close_file(struct file_handle *fh) +{ + spin_lock(&open_files_lock); + list_del(&fh->list); + spin_unlock(&open_files_lock); + + os_close_file(fh->fd); + + fh->fd = -1; + return(0); +} + +int read_file(struct file_handle *fh, unsigned long long offset, char *buf, + int len) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + err = os_seek_file(fh->fd, offset); + if(err) + return(err); + + return(os_read_file(fh->fd, buf, len)); +} + +int write_file(struct file_handle *fh, unsigned long long offset, + const char *buf, int len) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + if(offset != -1) + err = os_seek_file(fh->fd, offset); + if(err) + return(err); + + return(os_write_file(fh->fd, buf, len)); +} + +int truncate_file(struct file_handle *fh, unsigned long long size) +{ + int err; + + err = active_handle(fh); + if(err) + return(err); + + return(os_truncate_fd(fh->fd, size)); +} + +int make_pipe(struct file_handle *fhs) +{ + int fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err && (err != -EMFILE)) + return(err); + + if(err){ + reclaim_fds(); + err = os_pipe(fds, 1, 1); + } + if(err) + return(err); + + init_fh(&fhs[0], fds[0], OPENFLAGS()); + init_fh(&fhs[1], fds[1], OPENFLAGS()); + return(0); +} + +/* + * 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: + */ diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c new file mode 100644 index 000000000..56b378276 --- /dev/null +++ b/arch/um/os-Linux/aio.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include "os.h" +#include "helper.h" +#include "aio.h" +#include "init.h" +#include "user.h" +#include "mode.h" + +struct aio_thread_req { + enum aio_type type; + int io_fd; + unsigned long long offset; + char *buf; + int len; + int reply_fd; + void *data; +}; + +static int aio_req_fd_r = -1; +static int aio_req_fd_w = -1; + +#if defined(HAVE_AIO_ABI) +#include + +/* If we have the headers, we are going to build with AIO enabled. + * If we don't have aio in libc, we define the necessary stubs here. + */ + +#if !defined(HAVE_AIO_LIBC) + +#define __NR_io_setup 245 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 + +static long io_setup(int n, aio_context_t *ctxp) +{ + return(syscall(__NR_io_setup, n, ctxp)); +} + +static long io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) +{ + return(syscall(__NR_io_submit, ctx, nr, iocbpp)); +} + +static long io_getevents(aio_context_t ctx_id, long min_nr, long nr, + struct io_event *events, struct timespec *timeout) +{ + return(syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout)); +} + +#endif + +/* The AIO_MMAP cases force the mmapped page into memory here + * rather than in whatever place first touches the data. I used + * to do this by touching the page, but that's delicate because + * gcc is prone to optimizing that away. So, what's done here + * is we read from the descriptor from which the page was + * mapped. The caller is required to pass an offset which is + * inside the page that was mapped. Thus, when the read + * returns, we know that the page is in the page cache, and + * that it now backs the mmapped area. + */ + +static int do_aio(aio_context_t ctx, enum aio_type type, int fd, char *buf, + int len, unsigned long long offset, void *data) +{ + struct iocb iocb, *iocbp = &iocb; + char c; + int err; + + iocb = ((struct iocb) { .aio_data = (unsigned long) data, + .aio_reqprio = 0, + .aio_fildes = fd, + .aio_buf = (unsigned long) buf, + .aio_nbytes = len, + .aio_offset = offset, + .aio_reserved1 = 0, + .aio_reserved2 = 0, + .aio_reserved3 = 0 }); + + switch(type){ + case AIO_READ: + iocb.aio_lio_opcode = IOCB_CMD_PREAD; + err = io_submit(ctx, 1, &iocbp); + break; + case AIO_WRITE: + iocb.aio_lio_opcode = IOCB_CMD_PWRITE; + err = io_submit(ctx, 1, &iocbp); + break; + case AIO_MMAP: + iocb.aio_lio_opcode = IOCB_CMD_PREAD; + iocb.aio_buf = (unsigned long) &c; + iocb.aio_nbytes = sizeof(c); + err = io_submit(ctx, 1, &iocbp); + break; + default: + printk("Bogus op in do_aio - %d\n", type); + err = -EINVAL; + break; + } + if(err > 0) + err = 0; + + return(err); +} + +static aio_context_t ctx = 0; + +static int aio_thread(void *arg) +{ + struct aio_thread_reply reply; + struct io_event event; + int err, n, reply_fd; + + signal(SIGWINCH, SIG_IGN); + + while(1){ + n = io_getevents(ctx, 1, 1, &event, NULL); + if(n < 0){ + if(errno == EINTR) + continue; + printk("aio_thread - io_getevents failed, " + "errno = %d\n", errno); + } + else { + reply = ((struct aio_thread_reply) + { .data = (void *) event.data, + .err = event.res }); + reply_fd = + ((struct aio_context *) event.data)->reply_fd; + err = os_write_file(reply_fd, &reply, sizeof(reply)); + if(err != sizeof(reply)) + printk("not_aio_thread - write failed, " + "fd = %d, err = %d\n", + aio_req_fd_r, -err); + } + } + return(0); +} + +#endif + +static int do_not_aio(struct aio_thread_req *req) +{ + char c; + int err; + + switch(req->type){ + case AIO_READ: + err = os_seek_file(req->io_fd, req->offset); + if(err) + goto out; + + err = os_read_file(req->io_fd, req->buf, req->len); + break; + case AIO_WRITE: + err = os_seek_file(req->io_fd, req->offset); + if(err) + goto out; + + err = os_write_file(req->io_fd, req->buf, req->len); + break; + case AIO_MMAP: + err = os_seek_file(req->io_fd, req->offset); + if(err) + goto out; + + err = os_read_file(req->io_fd, &c, sizeof(c)); + break; + default: + printk("do_not_aio - bad request type : %d\n", req->type); + err = -EINVAL; + break; + } + + out: + return(err); +} + +static int not_aio_thread(void *arg) +{ + struct aio_thread_req req; + struct aio_thread_reply reply; + int err; + + signal(SIGWINCH, SIG_IGN); + while(1){ + err = os_read_file(aio_req_fd_r, &req, sizeof(req)); + if(err != sizeof(req)){ + if(err < 0) + printk("not_aio_thread - read failed, fd = %d, " + "err = %d\n", aio_req_fd_r, -err); + else { + printk("not_aio_thread - short read, fd = %d, " + "length = %d\n", aio_req_fd_r, err); + } + continue; + } + err = do_not_aio(&req); + reply = ((struct aio_thread_reply) { .data = req.data, + .err = err }); + err = os_write_file(req.reply_fd, &reply, sizeof(reply)); + if(err != sizeof(reply)) + printk("not_aio_thread - write failed, fd = %d, " + "err = %d\n", aio_req_fd_r, -err); + } +} + +static int aio_pid = -1; + +static int init_aio_24(void) +{ + unsigned long stack; + int fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err) + goto out; + + aio_req_fd_w = fds[0]; + aio_req_fd_r = fds[1]; + err = run_helper_thread(not_aio_thread, NULL, + CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0); + if(err < 0) + goto out_close_pipe; + + aio_pid = err; + goto out; + + out_close_pipe: + os_close_file(fds[0]); + os_close_file(fds[1]); + aio_req_fd_w = -1; + aio_req_fd_r = -1; + out: + return(0); +} + +#ifdef HAVE_AIO_ABI +#define DEFAULT_24_AIO 0 +static int init_aio_26(void) +{ + unsigned long stack; + int err; + + if(io_setup(256, &ctx)){ + printk("aio_thread failed to initialize context, err = %d\n", + errno); + return(-errno); + } + + err = run_helper_thread(aio_thread, NULL, + CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0); + if(err < 0) + return(-errno); + + aio_pid = err; + err = 0; + out: + return(err); +} + +int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len, + unsigned long long offset, int reply_fd, void *data) +{ + struct aio_thread_reply reply; + int err; + + ((struct aio_context *) data)->reply_fd = reply_fd; + + err = do_aio(ctx, type, io_fd, buf, len, offset, data); + if(err){ + reply = ((struct aio_thread_reply) { .data = data, + .err = err }); + err = os_write_file(reply_fd, &reply, sizeof(reply)); + if(err != sizeof(reply)) + printk("submit_aio_26 - write failed, " + "fd = %d, err = %d\n", reply_fd, -err); + else err = 0; + } + + return(err); +} + +#else +#define DEFAULT_24_AIO 1 +static int init_aio_26(void) +{ + return(-ENOSYS); +} + +int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len, + unsigned long long offset, int reply_fd, void *data) +{ + return(-ENOSYS); +} +#endif + +static int aio_24 = DEFAULT_24_AIO; + +static int __init set_aio_24(char *name, int *add) +{ + aio_24 = 1; + return(0); +} + +__uml_setup("aio=2.4", set_aio_24, +"aio=2.4\n" +" This is used to force UML to use 2.4-style AIO even when 2.6 AIO is\n" +" available. 2.4 AIO is a single thread that handles one request at a\n" +" time, synchronously. 2.6 AIO is a thread which uses 2.5 AIO interface\n" +" to handle an arbitrary number of pending requests. 2.6 AIO is not\n" +" available in tt mode, on 2.4 hosts, or when UML is built with\n" +" /usr/include/linux/aio_abi no available.\n\n" +); + +static int init_aio(void) +{ + int err; + + CHOOSE_MODE(({ + if(!aio_24){ + printk("Disabling 2.6 AIO in tt mode\n"); + aio_24 = 1; + } }), (void) 0); + + if(!aio_24){ + err = init_aio_26(); + if(err && (errno == ENOSYS)){ + printk("2.6 AIO not supported on the host - " + "reverting to 2.4 AIO\n"); + aio_24 = 1; + } + else return(err); + } + + if(aio_24) + return(init_aio_24()); + + return(0); +} + +__initcall(init_aio); + +static void exit_aio(void) +{ + if(aio_pid != -1) + os_kill_process(aio_pid, 1); +} + +__uml_exitcall(exit_aio); + +int submit_aio_24(enum aio_type type, int io_fd, char *buf, int len, + unsigned long long offset, int reply_fd, void *data) +{ + struct aio_thread_req req = { .type = type, + .io_fd = io_fd, + .offset = offset, + .buf = buf, + .len = len, + .reply_fd = reply_fd, + .data = data, + }; + int err; + + err = os_write_file(aio_req_fd_w, &req, sizeof(req)); + if(err == sizeof(req)) + err = 0; + + return(err); +} + +int submit_aio(enum aio_type type, int io_fd, char *buf, int len, + unsigned long long offset, int reply_fd, void *data) +{ + if(aio_24) + return(submit_aio_24(type, io_fd, buf, len, offset, reply_fd, + data)); + else { + return(submit_aio_26(type, io_fd, buf, len, offset, reply_fd, + data)); + } +} + +/* + * 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: + */ diff --git a/arch/um/sys-i386/semaphore.c b/arch/um/sys-i386/semaphore.c new file mode 100644 index 000000000..073912cfc --- /dev/null +++ b/arch/um/sys-i386/semaphore.c @@ -0,0 +1,301 @@ +/* + * i386 semaphore implementation. + * + * (C) Copyright 1999 Linus Torvalds + * + * Portions Copyright 1999 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * rw semaphores implemented November 1999 by Benjamin LaHaise + */ +#include +#include +#include +#include +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is protected + * by the spinlock in the semaphore's waitqueue head. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +asmlinkage void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +asmlinkage void __sched __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * the wait_queue_head. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + tsk->state = TASK_RUNNING; +} + +asmlinkage int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * wait_queue_head. The "-1" is because we're + * still hoping to get the semaphore. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_INTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + + tsk->state = TASK_RUNNING; + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + * + * We could have done the trylock with a + * single "cmpxchg" without failure cases, + * but then it wouldn't work on a 386. + */ +asmlinkage int __down_trylock(struct semaphore * sem) +{ + int sleepers; + unsigned long flags; + + spin_lock_irqsave(&sem->wait.lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock in the + * wait_queue_head. + */ + if (!atomic_add_negative(sleepers, &sem->count)) { + wake_up_locked(&sem->wait); + } + + spin_unlock_irqrestore(&sem->wait.lock, flags); + return 1; +} + + +/* + * The semaphore operations have a special calling sequence that + * allow us to do a simpler in-line version of them. These routines + * need to convert that sequence back into the C sequence when + * there is contention on the semaphore. + * + * %ecx contains the semaphore pointer on entry. Save the C-clobbered + * registers (%eax, %edx and %ecx) except %eax when used as a return + * value.. + */ +asm( +".section .sched.text\n" +".align 4\n" +".globl __down_failed\n" +"__down_failed:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif + "pushl %eax\n\t" + "pushl %edx\n\t" + "pushl %ecx\n\t" + "call __down\n\t" + "popl %ecx\n\t" + "popl %edx\n\t" + "popl %eax\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif + "ret" +); + +asm( +".section .sched.text\n" +".align 4\n" +".globl __down_failed_interruptible\n" +"__down_failed_interruptible:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif + "pushl %edx\n\t" + "pushl %ecx\n\t" + "call __down_interruptible\n\t" + "popl %ecx\n\t" + "popl %edx\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif + "ret" +); + +asm( +".section .sched.text\n" +".align 4\n" +".globl __down_failed_trylock\n" +"__down_failed_trylock:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif + "pushl %edx\n\t" + "pushl %ecx\n\t" + "call __down_trylock\n\t" + "popl %ecx\n\t" + "popl %edx\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif + "ret" +); + +asm( +".section .sched.text\n" +".align 4\n" +".globl __up_wakeup\n" +"__up_wakeup:\n\t" + "pushl %eax\n\t" + "pushl %edx\n\t" + "pushl %ecx\n\t" + "call __up\n\t" + "popl %ecx\n\t" + "popl %edx\n\t" + "popl %eax\n\t" + "ret" +); + +/* + * rw spinlock fallbacks + */ +#if defined(CONFIG_SMP) +asm( +".section .sched.text\n" +".align 4\n" +".globl __write_lock_failed\n" +"__write_lock_failed:\n\t" + LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax)\n" +"1: rep; nop\n\t" + "cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" + "jne 1b\n\t" + LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" + "jnz __write_lock_failed\n\t" + "ret" +); + +asm( +".section .sched.text\n" +".align 4\n" +".globl __read_lock_failed\n" +"__read_lock_failed:\n\t" + LOCK "incl (%eax)\n" +"1: rep; nop\n\t" + "cmpl $1,(%eax)\n\t" + "js 1b\n\t" + LOCK "decl (%eax)\n\t" + "js __read_lock_failed\n\t" + "ret" +); +#endif diff --git a/fs/hostfs/externfs.c b/fs/hostfs/externfs.c new file mode 100644 index 000000000..884c33c83 --- /dev/null +++ b/fs/hostfs/externfs.c @@ -0,0 +1,1317 @@ +/* + * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "kern.h" +#include "user_util.h" +#include "2_5compat.h" +#include "mem.h" +#include "filehandle.h" + +struct externfs { + struct list_head list; + struct externfs_mount_ops *mount_ops; + struct file_system_type type; +}; + +static inline struct externfs_inode *EXTERNFS_I(struct inode *inode) +{ + return(container_of(inode, struct externfs_inode, vfs_inode)); +} + +#define file_externfs_i(file) EXTERNFS_I((file)->f_dentry->d_inode) + +int externfs_d_delete(struct dentry *dentry) +{ + return(1); +} + +struct dentry_operations externfs_dentry_ops = { +}; + +#define EXTERNFS_SUPER_MAGIC 0x00c0ffee + +static struct inode_operations externfs_iops; +static struct inode_operations externfs_dir_iops; +static struct address_space_operations externfs_link_aops; + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *name; + int len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + len += parent->d_name.len + 1; + parent = parent->d_parent; + } + + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + len -= parent->d_name.len + 1; + name[len] = '/'; + strncpy(&name[len + 1], parent->d_name.name, + parent->d_name.len); + parent = parent->d_parent; + } + + return(name); +} + +char *inode_name(struct inode *ino, int extra) +{ + struct dentry *dentry; + + dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); + return(dentry_name(dentry, extra)); +} + +char *inode_name_prefix(struct inode *inode, char *prefix) +{ + int len; + char *name; + + len = strlen(prefix); + name = inode_name(inode, len); + if(name == NULL) + return(name); + + memmove(&name[len], name, strlen(name) + 1); + memcpy(name, prefix, strlen(prefix)); + return(name); +} + +static int read_name(struct inode *ino, char *name) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + /* The non-int inode fields are copied into ints by stat_file and + * then copied into the inode because passing the actual pointers + * in and having them treated as int * breaks on big-endian machines + */ + dev_t i_rdev; + int err; + int i_mode, i_nlink, i_blksize; + unsigned long atime, mtime, ctime; + unsigned long long i_size; + unsigned long long i_ino; + unsigned long long i_blocks; + + err = (*ops->stat_file)(name, ino->i_sb->s_fs_info, &i_rdev, &i_ino, + &i_mode, &i_nlink, &ino->i_uid, &ino->i_gid, + &i_size, &atime, &mtime, &ctime, &i_blksize, + &i_blocks); + if(err) return(err); + + ino->i_atime.tv_sec = atime; + ino->i_atime.tv_nsec = 0; + + ino->i_ctime.tv_sec = ctime; + ino->i_ctime.tv_nsec = 0; + + ino->i_mtime.tv_sec = mtime; + ino->i_mtime.tv_nsec = 0; + + ino->i_ino = i_ino; + ino->i_rdev = i_rdev; + ino->i_mode = i_mode; + ino->i_nlink = i_nlink; + ino->i_size = i_size; + ino->i_blksize = i_blksize; + ino->i_blocks = i_blocks; + return(0); +} + +static char *follow_link(char *link, + int (*do_read_link)(char *path, int uid, int gid, + char *buf, int size, + struct externfs_data *ed), + int uid, int gid, struct externfs_data *ed) +{ + int len, n; + char *name, *resolved, *end; + + len = 64; + while(1){ + n = -ENOMEM; + name = kmalloc(len, GFP_KERNEL); + if(name == NULL) + goto out; + + n = (*do_read_link)(link, uid, gid, name, len, ed); + if(n < len) + break; + len *= 2; + kfree(name); + } + if(n < 0) + goto out_free; + + if(*name == '/') + return(name); + + end = strrchr(link, '/'); + if(end == NULL) + return(name); + + *(end + 1) = '\0'; + len = strlen(link) + strlen(name) + 1; + + resolved = kmalloc(len, GFP_KERNEL); + if(resolved == NULL){ + n = -ENOMEM; + goto out_free; + } + + sprintf(resolved, "%s%s", link, name); + kfree(name); + return(resolved); + + out_free: + kfree(name); + out: + return(ERR_PTR(n)); +} + +static int read_inode(struct inode *ino) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *name, *new; + int err = 0, type; + + /* Unfortunately, we are called from iget() when we don't have a dentry + * allocated yet. + */ + if(list_empty(&ino->i_dentry)) + goto out; + + err = -ENOMEM; + name = inode_name(ino, 0); + if(name == NULL) + goto out; + + type = (*ops->file_type)(name, NULL, ed); + if(type < 0){ + err = type; + goto out_free; + } + + if(type == OS_TYPE_SYMLINK){ + new = follow_link(name, ops->read_link, current->fsuid, + current->fsgid, ed); + if(IS_ERR(new)){ + err = PTR_ERR(new); + goto out_free; + } + kfree(name); + name = new; + } + + err = read_name(ino, name); + out_free: + kfree(name); + out: + return(err); +} + +int externfs_statfs(struct super_block *sb, struct kstatfs *sf) +{ + /* do_statfs uses struct statfs64 internally, but the linux kernel + * struct statfs still has 32-bit versions for most of these fields, + * so we convert them here + */ + int err; + long long f_blocks; + long long f_bfree; + long long f_bavail; + long long f_files; + long long f_ffree; + struct externfs_data *ed = sb->s_fs_info; + + err = (*ed->file_ops->statfs)(&sf->f_bsize, &f_blocks, &f_bfree, + &f_bavail, &f_files, &f_ffree, + &sf->f_fsid, sizeof(sf->f_fsid), + &sf->f_namelen, sf->f_spare, ed); + if(err) + return(err); + + sf->f_blocks = f_blocks; + sf->f_bfree = f_bfree; + sf->f_bavail = f_bavail; + sf->f_files = f_files; + sf->f_ffree = f_ffree; + sf->f_type = EXTERNFS_SUPER_MAGIC; + return(0); +} + +static struct inode *externfs_alloc_inode(struct super_block *sb) +{ + struct externfs_data *ed = sb->s_fs_info; + struct externfs_inode *ext; + + ext = (*ed->mount_ops->init_file)(ed); + if(ext == NULL) + return(NULL); + + *ext = ((struct externfs_inode) { .ops = ed->file_ops }); + + inode_init_once(&ext->vfs_inode); + return(&ext->vfs_inode); +} + +static void externfs_destroy_inode(struct inode *inode) +{ + struct externfs_inode *ext = EXTERNFS_I(inode); + + (*ext->ops->close_file)(ext, inode->i_size); +} + +static void externfs_read_inode(struct inode *inode) +{ + read_inode(inode); +} + +static struct super_operations externfs_sbops = { + .alloc_inode = externfs_alloc_inode, + .destroy_inode = externfs_destroy_inode, + .read_inode = externfs_read_inode, + .statfs = externfs_statfs, +}; + +int externfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + void *dir; + char *name; + unsigned long long next, ino; + int error, len; + struct externfs_file_ops *ops = file_externfs_i(file)->ops; + struct externfs_data *ed = file->f_dentry->d_inode->i_sb->s_fs_info; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) + return(-ENOMEM); + + dir = (*ops->open_dir)(name, current->fsuid, current->fsgid, ed); + kfree(name); + if(IS_ERR(dir)) + return(PTR_ERR(dir)); + + next = file->f_pos; + while((name = (*ops->read_dir)(dir, &next, &ino, &len, ed)) != NULL){ + error = (*filldir)(ent, name, len, file->f_pos, ino, + DT_UNKNOWN); + if(error) + break; + file->f_pos = next; + } + (*ops->close_dir)(dir, ed); + return(0); +} + +int externfs_file_open(struct inode *ino, struct file *file) +{ + ino->i_nlink++; + return(0); +} + +int externfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct externfs_file_ops *ops = file_externfs_i(file)->ops; + struct inode *inode = dentry->d_inode; + struct externfs_data *ed = inode->i_sb->s_fs_info; + + return((*ops->truncate_file)(EXTERNFS_I(inode), inode->i_size, ed)); +} + +static struct file_operations externfs_file_fops = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .open = externfs_file_open, + .release = NULL, + .fsync = externfs_fsync, +}; + +static struct file_operations externfs_dir_fops = { + .readdir = externfs_readdir, + .read = generic_read_dir, +}; + +struct wp_info { + struct page *page; + int count; + unsigned long long start; + unsigned long long size; + int (*truncate)(struct externfs_inode *ext, __u64 size, + struct externfs_data *ed); + struct externfs_inode *ei; + struct externfs_data *ed; +}; + +static void externfs_finish_writepage(char *buffer, int res, void *arg) +{ + struct wp_info *wp = arg; + + if(res == wp->count){ + ClearPageError(wp->page); + if(wp->start + res > wp->size) + (*wp->truncate)(wp->ei, wp->size, wp->ed); + } + else { + SetPageError(wp->page); + ClearPageUptodate(wp->page); + } + + kunmap(wp->page); + unlock_page(wp->page); + kfree(wp); +} + +int externfs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops; + struct wp_info *wp; + struct externfs_data *ed = inode->i_sb->s_fs_info; + char *buffer; + unsigned long long base; + int count = PAGE_CACHE_SIZE; + int end_index = inode->i_size >> PAGE_CACHE_SHIFT; + int err, offset; + + base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; + + /* If we are entirely outside the file, then return an error */ + err = -EIO; + offset = inode->i_size & (PAGE_CACHE_SIZE-1); + if (page->index > end_index || + ((page->index == end_index) && !offset)) + goto out_unlock; + + err = -ENOMEM; + wp = kmalloc(sizeof(*wp), GFP_KERNEL); + if(wp == NULL) + goto out_unlock; + + *wp = ((struct wp_info) { .page = page, + .count = count, + .start = base, + .size = inode->i_size, + .truncate = ops->truncate_file, + .ei = EXTERNFS_I(inode), + .ed = ed }); + + buffer = kmap(page); + err = (*ops->write_file)(EXTERNFS_I(inode), base, buffer, 0, + count, externfs_finish_writepage, wp, ed); + + return err; + + out_unlock: + unlock_page(page); + return(err); +} + +static void externfs_finish_readpage(char *buffer, int res, void *arg) +{ + struct page *page = arg; + struct inode *inode; + + if(res < 0){ + SetPageError(page); + goto out; + } + + inode = page->mapping->host; + if(inode->i_size >> PAGE_CACHE_SHIFT == page->index) + res = inode->i_size % PAGE_CACHE_SIZE; + + memset(&buffer[res], 0, PAGE_CACHE_SIZE - res); + + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) + ClearPageError(page); + out: + kunmap(page); + unlock_page(page); +} + +static int externfs_readpage(struct file *file, struct page *page) +{ + struct inode *ino = page->mapping->host; + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *buffer; + long long start; + int err = 0; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + + if(ops->map_file_page != NULL){ + /* XXX What happens when PAGE_SIZE != PAGE_CACHE_SIZE? */ + err = (*ops->map_file_page)(file_externfs_i(file), start, + buffer, file->f_mode & FMODE_WRITE, + ed); + if(!err) + err = PAGE_CACHE_SIZE; + } + else err = (*ops->read_file)(file_externfs_i(file), start, buffer, + PAGE_CACHE_SIZE, 0, 0, + externfs_finish_readpage, page, ed); + + if(err > 0) + err = 0; + return(err); +} + +struct writepage_info { + struct semaphore sem; + int res; +}; + +static void externfs_finish_prepare(char *buffer, int res, void *arg) +{ + struct writepage_info *wp = arg; + + wp->res = res; + up(&wp->sem); +} + +int externfs_prepare_write(struct file *file, struct page *page, + unsigned int from, unsigned int to) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops; + struct externfs_data *ed = inode->i_sb->s_fs_info; + char *buffer; + long long start; + int err; + struct writepage_info wp; + + if(PageUptodate(page)) + return(0); + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + + if(ops->map_file_page != NULL){ + err = (*ops->map_file_page)(file_externfs_i(file), start, + buffer, file->f_mode & FMODE_WRITE, + ed); + goto out; + + } + + init_MUTEX_LOCKED(&wp.sem); + err = (*ops->read_file)(file_externfs_i(file), start, buffer, + PAGE_CACHE_SIZE, from, to, + externfs_finish_prepare, &wp, ed); + down(&wp.sem); + if(err < 0) + goto out; + + err = wp.res; + if(err < 0) + goto out; + + if(from > 0) + memset(buffer, 0, from); + if(to < PAGE_CACHE_SIZE) + memset(buffer + to, 0, PAGE_CACHE_SIZE - to); + + SetPageUptodate(page); + err = 0; + out: + kunmap(page); + return(err); +} + +static int externfs_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + struct externfs_file_ops *ops = EXTERNFS_I(inode)->ops; + unsigned long long size; + long long start; + int err; + + start = (long long) (page->index << PAGE_CACHE_SHIFT); + + if(ops->map_file_page != NULL) + err = to - from; + else { + size = start + to; + if(size > inode->i_size){ + inode->i_size = size; + mark_inode_dirty(inode); + } + } + + set_page_dirty(page); + return(to - from); +} + +static int externfs_removepage(struct page *page, int gfpmask) +{ + physmem_remove_mapping(page_address(page)); + return(0); +} + +static struct address_space_operations externfs_aops = { + .writepage = externfs_writepage, + .readpage = externfs_readpage, + .releasepage = externfs_removepage, +/* .set_page_dirty = __set_page_dirty_nobuffers, */ + .prepare_write = externfs_prepare_write, + .commit_write = externfs_commit_write +}; + +static int init_inode(struct inode *inode, struct dentry *dentry) +{ + char *name = NULL; + int type, err = -ENOMEM, rdev; + struct externfs_inode *ext = EXTERNFS_I(inode); + struct externfs_file_ops *ops = ext->ops; + struct externfs_data *ed = inode->i_sb->s_fs_info; + + if(dentry){ + name = dentry_name(dentry, 0); + if(name == NULL) + goto out; + type = (*ops->file_type)(name, &rdev, ed); + } + else type = OS_TYPE_DIR; + + err = 0; + if(type == OS_TYPE_SYMLINK) + inode->i_op = &page_symlink_inode_operations; + else if(type == OS_TYPE_DIR) + inode->i_op = &externfs_dir_iops; + else inode->i_op = &externfs_iops; + + if(type == OS_TYPE_DIR) inode->i_fop = &externfs_dir_fops; + else inode->i_fop = &externfs_file_fops; + + if(type == OS_TYPE_SYMLINK) + inode->i_mapping->a_ops = &externfs_link_aops; + else inode->i_mapping->a_ops = &externfs_aops; + + switch (type) { + case OS_TYPE_CHARDEV: + init_special_inode(inode, S_IFCHR, rdev); + break; + case OS_TYPE_BLOCKDEV: + init_special_inode(inode, S_IFBLK, rdev); + break; + case OS_TYPE_FIFO: + init_special_inode(inode, S_IFIFO, 0); + break; + case OS_TYPE_SOCK: + init_special_inode(inode, S_IFSOCK, 0); + break; + case OS_TYPE_SYMLINK: + inode->i_mode = S_IFLNK | S_IRWXUGO; + } + + err = (*ops->open_file)(ext, name, current->fsuid, current->fsgid, + inode, ed); + if((err != -EISDIR) && (err != -ENOENT) && (err != -ENXIO)) + goto out_put; + + err = 0; + + out_free: + kfree(name); + out: + return(err); + + out_put: + iput(inode); + goto out_free; +} + +int externfs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct externfs_inode *ext = EXTERNFS_I(dir); + struct externfs_file_ops *ops = ext->ops; + struct inode *inode; + struct externfs_data *ed = dir->i_sb->s_fs_info; + char *name; + int err = -ENOMEM; + + inode = iget(dir->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + err = (*ops->create_file)(ext, name, mode, current->fsuid, + current->fsuid, inode, ed); + if(err) + goto out_free; + + err = read_name(inode, name); + if(err) + goto out_rm; + + inode->i_nlink++; + d_instantiate(dentry, inode); + kfree(name); + out: + return(err); + + out_rm: + (*ops->unlink_file)(name, ed); + out_free: + kfree(name); + out_put: + inode->i_nlink = 0; + iput(inode); + goto out; +} + +struct dentry *externfs_lookup(struct inode *ino, struct dentry *dentry, + struct nameidata *nd) +{ + struct inode *inode; + char *name; + int err = -ENOMEM; + + inode = iget(ino->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + err = read_name(inode, name); + kfree(name); + if(err){ + if(err != -ENOENT) + goto out_put; + + inode->i_nlink = 0; + iput(inode); + inode = NULL; + } + d_add(dentry, inode); + dentry->d_op = &externfs_dentry_ops; + return(NULL); + + out_put: + inode->i_nlink = 0; + iput(inode); + out: + return(ERR_PTR(err)); +} + +static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) +{ + char *file; + int len; + + file = inode_name(ino, dentry->d_name.len + 1); + if(file == NULL) return(NULL); + strcat(file, "/"); + len = strlen(file); + strncat(file, dentry->d_name.name, dentry->d_name.len); + file[len + dentry->d_name.len] = '\0'; + return(file); +} + +int externfs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *from_name, *to_name; + int err = -ENOMEM; + + from_name = inode_dentry_name(ino, from); + if(from_name == NULL) + goto out; + + to_name = dentry_name(to, 0); + if(to_name == NULL) + goto out_free_from; + + err = (*ops->link_file)(to_name, from_name, current->fsuid, + current->fsgid, ed); + if(err) + goto out_free_to; + + d_instantiate(from, to->d_inode); + to->d_inode->i_nlink++; + atomic_inc(&to->d_inode->i_count); + + out_free_to: + kfree(to_name); + out_free_from: + kfree(from_name); + out: + return(err); +} + +int externfs_unlink(struct inode *ino, struct dentry *dentry) +{ + struct inode *inode; + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *file; + int err; + + file = inode_dentry_name(ino, dentry); + if(file == NULL) + return(-ENOMEM); + + inode = dentry->d_inode; + if((inode->i_nlink == 1) && (ops->invisible != NULL)) + (*ops->invisible)(EXTERNFS_I(inode)); + + err = (*ops->unlink_file)(file, ed); + kfree(file); + + inode->i_nlink--; + + return(err); +} + +int externfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct inode *inode; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *file; + int err; + + file = inode_dentry_name(ino, dentry); + if(file == NULL) + return(-ENOMEM); + err = (*ops->make_symlink)(file, to, current->fsuid, current->fsgid, + ed); + kfree(file); + if(err) + goto out; + + err = -ENOMEM; + inode = iget(ino->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + d_instantiate(dentry, inode); + inode->i_nlink++; + out: + return(err); + + out_put: + iput(inode); + goto out; +} + +int externfs_make_dir(struct inode *ino, struct dentry *dentry, int mode) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct inode *inode; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *file; + int err = -ENOMEM; + + file = inode_dentry_name(ino, dentry); + if(file == NULL) + goto out; + err = (*ops->make_dir)(file, mode, current->fsuid, current->fsgid, ed); + + err = -ENOMEM; + inode = iget(ino->i_sb, 0); + if(inode == NULL) + goto out_free; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = read_name(inode, file); + if(err) + goto out_put; + + kfree(file); + d_instantiate(dentry, inode); + inode->i_nlink = 2; + inode->i_mode = S_IFDIR | mode; + + ino->i_nlink++; + out: + return(err); + out_put: + inode->i_nlink = 0; + iput(inode); + out_free: + kfree(file); + goto out; +} + +int externfs_remove_dir(struct inode *ino, struct dentry *dentry) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *file; + int err; + + file = inode_dentry_name(ino, dentry); + if(file == NULL) + return(-ENOMEM); + err = (*ops->remove_dir)(file, current->fsuid, current->fsgid, ed); + kfree(file); + + dentry->d_inode->i_nlink = 0; + ino->i_nlink--; + return(err); +} + +int externfs_make_node(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + struct externfs_file_ops *ops = EXTERNFS_I(dir)->ops; + struct externfs_data *ed = dir->i_sb->s_fs_info; + struct inode *inode; + char *name; + int err = -ENOMEM; + + inode = iget(dir->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + init_special_inode(inode, mode, dev); + err = (*ops->make_node)(name, mode & S_IRWXUGO, current->fsuid, + current->fsgid, mode & S_IFMT, MAJOR(dev), + MINOR(dev), ed); + if(err) + goto out_free; + + err = read_name(inode, name); + if(err) + goto out_rm; + + inode->i_nlink++; + d_instantiate(dentry, inode); + kfree(name); + out: + return(err); + + out_rm: + (*ops->unlink_file)(name, ed); + out_free: + kfree(name); + out_put: + inode->i_nlink = 0; + iput(inode); + goto out; +} + +int externfs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + struct externfs_file_ops *ops = EXTERNFS_I(from_ino)->ops; + struct externfs_data *ed = from_ino->i_sb->s_fs_info; + char *from_name, *to_name; + int err; + + from_name = inode_dentry_name(from_ino, from); + if(from_name == NULL) + return(-ENOMEM); + to_name = inode_dentry_name(to_ino, to); + if(to_name == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = (*ops->rename_file)(from_name, to_name, ed); + kfree(from_name); + kfree(to_name); + + from_ino->i_nlink--; + to_ino->i_nlink++; + return(err); +} + +void externfs_truncate(struct inode *ino) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + + (*ops->truncate_file)(EXTERNFS_I(ino), ino->i_size, ed); +} + +int externfs_permission(struct inode *ino, int desired, struct nameidata *nd) +{ + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *name; + int r = 0, w = 0, x = 0, err; + + if(ops->access_file == NULL) + return(vfs_permission(ino, desired)); + + if(desired & MAY_READ) r = 1; + if(desired & MAY_WRITE) w = 1; + if(desired & MAY_EXEC) x = 1; + name = inode_name(ino, 0); + if(name == NULL) + return(-ENOMEM); + + err = (*ops->access_file)(name, r, w, x, current->fsuid, + current->fsgid, ed); + kfree(name); + + if(!err) + err = vfs_permission(ino, desired); + return(err); +} + +int externfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct externfs_file_ops *ops = EXTERNFS_I(dentry->d_inode)->ops; + struct externfs_data *ed = dentry->d_inode->i_sb->s_fs_info; + struct externfs_iattr attrs; + char *name; + int err; + + attrs.ia_valid = 0; + if(attr->ia_valid & ATTR_MODE){ + attrs.ia_valid |= EXTERNFS_ATTR_MODE; + attrs.ia_mode = attr->ia_mode; + } + if(attr->ia_valid & ATTR_UID){ + attrs.ia_valid |= EXTERNFS_ATTR_UID; + attrs.ia_uid = attr->ia_uid; + } + if(attr->ia_valid & ATTR_GID){ + attrs.ia_valid |= EXTERNFS_ATTR_GID; + attrs.ia_gid = attr->ia_gid; + } + if(attr->ia_valid & ATTR_SIZE){ + attrs.ia_valid |= EXTERNFS_ATTR_SIZE; + attrs.ia_size = attr->ia_size; + } + if(attr->ia_valid & ATTR_ATIME){ + attrs.ia_valid |= EXTERNFS_ATTR_ATIME; + attrs.ia_atime = attr->ia_atime.tv_sec; + } + if(attr->ia_valid & ATTR_MTIME){ + attrs.ia_valid |= EXTERNFS_ATTR_MTIME; + attrs.ia_mtime = attr->ia_mtime.tv_sec; + } + if(attr->ia_valid & ATTR_CTIME){ + attrs.ia_valid |= EXTERNFS_ATTR_CTIME; + attrs.ia_ctime = attr->ia_ctime.tv_sec; + } + if(attr->ia_valid & ATTR_ATIME_SET){ + attrs.ia_valid |= EXTERNFS_ATTR_ATIME_SET; + attrs.ia_atime = attr->ia_atime.tv_sec; + } + if(attr->ia_valid & ATTR_MTIME_SET){ + attrs.ia_valid |= EXTERNFS_ATTR_MTIME_SET; + } + name = dentry_name(dentry, 0); + if(name == NULL) + return(-ENOMEM); + err = (*ops->set_attr)(name, &attrs, ed); + kfree(name); + if(err) + return(err); + + return(inode_setattr(dentry->d_inode, attr)); +} + +int externfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + generic_fillattr(dentry->d_inode, stat); + return(0); +} + +static struct inode_operations externfs_iops = { + .create = externfs_create, + .link = externfs_link, + .unlink = externfs_unlink, + .symlink = externfs_symlink, + .mkdir = externfs_make_dir, + .rmdir = externfs_remove_dir, + .mknod = externfs_make_node, + .rename = externfs_rename, + .truncate = externfs_truncate, + .permission = externfs_permission, + .setattr = externfs_setattr, + .getattr = externfs_getattr, +}; + +static struct inode_operations externfs_dir_iops = { + .create = externfs_create, + .lookup = externfs_lookup, + .link = externfs_link, + .unlink = externfs_unlink, + .symlink = externfs_symlink, + .mkdir = externfs_make_dir, + .rmdir = externfs_remove_dir, + .mknod = externfs_make_node, + .rename = externfs_rename, + .truncate = externfs_truncate, + .permission = externfs_permission, + .setattr = externfs_setattr, + .getattr = externfs_getattr, +}; + +int externfs_link_readpage(struct file *file, struct page *page) +{ + struct inode *ino = page->mapping->host; + struct externfs_file_ops *ops = EXTERNFS_I(ino)->ops; + struct externfs_data *ed = ino->i_sb->s_fs_info; + char *buffer, *name; + long long start; + int err; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + name = inode_name(ino, 0); + if(name == NULL) + return(-ENOMEM); + + err = (*ops->read_link)(name, current->fsuid, current->fsgid, buffer, + PAGE_CACHE_SIZE, ed); + + kfree(name); + if(err == PAGE_CACHE_SIZE) + err = -E2BIG; + else if(err > 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + } + kunmap(page); + unlock_page(page); + return(err); +} + +static int externfs_flushpage(struct page *page, unsigned long offset) +{ + return(externfs_writepage(page, NULL)); +} + +struct externfs_data *inode_externfs_info(struct inode *inode) +{ + return(inode->i_sb->s_fs_info); +} + +static struct address_space_operations externfs_link_aops = { + .readpage = externfs_link_readpage, + .releasepage = externfs_removepage, + .invalidatepage = externfs_flushpage, +}; + +DECLARE_MUTEX(externfs_sem); +struct list_head externfses = LIST_HEAD_INIT(externfses); + +static struct externfs *find_externfs(struct file_system_type *type) +{ + struct list_head *ele; + struct externfs *fs; + + down(&externfs_sem); + list_for_each(ele, &externfses){ + fs = list_entry(ele, struct externfs, list); + if(&fs->type == type) + goto out; + } + fs = NULL; + out: + up(&externfs_sem); + return(fs); +} + +#define DEFAULT_ROOT "/" + +char *host_root_filename(char *mount_arg) +{ + char *root = DEFAULT_ROOT; + + if((mount_arg != NULL) && (*mount_arg != '\0')) + root = mount_arg; + + return(uml_strdup(root)); +} + +static int externfs_fill_sb(struct super_block *sb, void *data, int silent) +{ + struct externfs *fs; + struct inode *root_inode; + struct externfs_data *sb_data; + int err = -EINVAL; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = EXTERNFS_SUPER_MAGIC; + sb->s_op = &externfs_sbops; + + fs = find_externfs(sb->s_type); + if(fs == NULL){ + printk("Couldn't find externfs for filesystem '%s'\n", + sb->s_type->name); + goto out; + } + + sb_data = (*fs->mount_ops->mount)(data); + if(IS_ERR(sb_data)){ + err = PTR_ERR(sb_data); + goto out; + } + + sb->s_fs_info = sb_data; + sb_data->mount_ops = fs->mount_ops; + + root_inode = iget(sb, 0); + if(root_inode == NULL) + goto out; + + err = init_inode(root_inode, NULL); + if(err) + goto out_put; + + err = -ENOMEM; + sb->s_root = d_alloc_root(root_inode); + if(sb->s_root == NULL) + goto out_put; + + err = read_inode(root_inode); + if(err) + goto out_put; + + out: + return(err); + + out_put: + iput(root_inode); + goto out; +} + +struct super_block *externfs_read_super(struct file_system_type *type, + int flags, const char *dev_name, + void *data) +{ + return(get_sb_nodev(type, flags, data, externfs_fill_sb)); +} + +void init_externfs(struct externfs_data *ed, struct externfs_file_ops *ops) +{ + ed->file_ops = ops; +} + +int register_externfs(char *name, struct externfs_mount_ops *mount_ops) +{ + struct externfs *new; + int err = -ENOMEM; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if(new == NULL) + goto out; + + memset(new, 0, sizeof(*new)); + *new = ((struct externfs) { .list = LIST_HEAD_INIT(new->list), + .mount_ops = mount_ops, + .type = { .name = name, + .get_sb = externfs_read_super, + .kill_sb = kill_anon_super, + .fs_flags = 0, + .owner = THIS_MODULE } }); + list_add(&new->list, &externfses); + + err = register_filesystem(&new->type); + if(err) + goto out_del; + return(0); + + out_del: + list_del(&new->list); + kfree(new); + out: + return(err); +} + +void unregister_externfs(char *name) +{ + struct list_head *ele; + struct externfs *fs; + + down(&externfs_sem); + list_for_each(ele, &externfses){ + fs = list_entry(ele, struct externfs, list); + if(!strcmp(fs->type.name, name)){ + list_del(ele); + up(&externfs_sem); + return; + } + } + up(&externfs_sem); + printk("Unregister_externfs - filesystem '%s' not found\n", name); +} + +/* + * 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: + */ diff --git a/fs/hostfs/host_file.c b/fs/hostfs/host_file.c new file mode 100644 index 000000000..e8eb90165 --- /dev/null +++ b/fs/hostfs/host_file.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/string.h" +#include "linux/errno.h" +#include "linux/types.h" +#include "linux/slab.h" +#include "linux/fs.h" +#include "asm/fcntl.h" +#include "hostfs.h" +#include "filehandle.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 = kmalloc(new, GFP_KERNEL); + 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, struct file_handle *fh) +{ + char tmp[HOSTFS_BUFSIZE], *file; + int mode = 0, err; + struct openflags flags = OPENFLAGS(); + + if (r) + flags = of_read(flags); + if (w) + flags = of_write(flags); + if(append) + flags = of_append(flags); + + err = -ENOMEM; + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = open_filehandle(file, flags, mode, fh); + out: + free_path(file, tmp); + return(err); +} + +void *host_open_dir(const char *path[]) +{ + char tmp[HOSTFS_BUFSIZE], *file; + void *dir = ERR_PTR(-ENOMEM); + + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + dir = open_dir(file); + 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) +{ + int err; + char *name; + + err = os_seek_dir(stream, *pos); + if(err) + return(ERR_PTR(err)); + + err = os_read_dir(stream, ino_out, &name); + if(err) + return(ERR_PTR(err)); + + if(name == NULL) + return(NULL); + + *len_out = strlen(name); + *pos = os_tell_dir(stream); + return(name); +} + +int host_file_type(const char *path[], int *rdev) +{ + char tmp[HOSTFS_BUFSIZE], *file; + struct uml_stat buf; + int ret; + + ret = -ENOMEM; + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + if(rdev != NULL){ + ret = os_lstat_file(file, &buf); + if(ret) + goto out; + *rdev = MKDEV(buf.ust_rmajor, buf.ust_rminor); + } + + ret = os_file_type(file); + out: + free_path(file, tmp); + return(ret); +} + +int host_create_file(const char *path[], int mode, struct file_handle *fh) +{ + char tmp[HOSTFS_BUFSIZE], *file; + int err = -ENOMEM; + + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = open_filehandle(file, of_create(of_rdwr(OPENFLAGS())), mode, fh); + out: + free_path(file, tmp); + return(err); +} + +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 uml_stat buf; + int err; + + err = os_lstat_file(path, &buf); + if(err < 0) + return(err); + + if(dev_out != NULL) *dev_out = MKDEV(buf.ust_major, buf.ust_minor); + if(inode_out != NULL) *inode_out = buf.ust_ino; + if(mode_out != NULL) *mode_out = buf.ust_mode; + if(nlink_out != NULL) *nlink_out = buf.ust_nlink; + if(uid_out != NULL) *uid_out = buf.ust_uid; + if(gid_out != NULL) *gid_out = buf.ust_gid; + if(size_out != NULL) *size_out = buf.ust_size; + if(atime_out != NULL) *atime_out = buf.ust_atime; + if(mtime_out != NULL) *mtime_out = buf.ust_mtime; + if(ctime_out != NULL) *ctime_out = buf.ust_ctime; + if(blksize_out != NULL) *blksize_out = buf.ust_blksize; + if(blocks_out != NULL) *blocks_out = buf.ust_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 externfs_iattr *attrs) +{ + char tmp[HOSTFS_BUFSIZE], *file; + unsigned long time; + int err = 0, ma; + + if(append && (attrs->ia_valid & EXTERNFS_ATTR_SIZE)) + return(-EPERM); + + err = -ENOMEM; + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + if(attrs->ia_valid & EXTERNFS_ATTR_MODE){ + err = os_set_file_perms(file, attrs->ia_mode); + if(err < 0) + goto out; + } + if(attrs->ia_valid & EXTERNFS_ATTR_UID){ + err = os_set_file_owner(file, attrs->ia_uid, -1); + if(err < 0) + goto out; + } + if(attrs->ia_valid & EXTERNFS_ATTR_GID){ + err = os_set_file_owner(file, -1, attrs->ia_gid); + if(err < 0) + goto out; + } + if(attrs->ia_valid & EXTERNFS_ATTR_SIZE){ + err = os_truncate_file(file, attrs->ia_size); + if(err < 0) + goto out; + } + ma = EXTERNFS_ATTR_ATIME_SET | EXTERNFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ + err = os_set_file_time(file, attrs->ia_atime, attrs->ia_mtime); + if(err) + goto out; + } + else { + if(attrs->ia_valid & EXTERNFS_ATTR_ATIME_SET){ + err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &time, + NULL, NULL, NULL); + if(err != 0) + goto out; + + err = os_set_file_time(file, attrs->ia_atime, time); + if(err) + goto out; + } + if(attrs->ia_valid & EXTERNFS_ATTR_MTIME_SET){ + err = do_stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &time, NULL, + NULL, NULL, NULL); + if(err != 0) + goto out; + + err = os_set_file_time(file, time, attrs->ia_mtime); + if(err) + goto out; + } + } + if(attrs->ia_valid & EXTERNFS_ATTR_CTIME) ; + if(attrs->ia_valid & (EXTERNFS_ATTR_ATIME | EXTERNFS_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 = os_make_symlink(to, file); + 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 = os_remove_file(file); + out: + free_path(file, tmp); + return(err); +} + +int host_make_dir(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 = os_make_dir(file, mode); + out: + free_path(file, tmp); + return(err); +} + +int host_remove_dir(const char *path[]) +{ + char tmp[HOSTFS_BUFSIZE], *file; + int err = -ENOMEM; + + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_remove_dir(file); + 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 = os_link_file(t, f); + out: + free_path(f, from_tmp); + free_path(t, to_tmp); + return(err); +} + +int host_read_link(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 = os_read_symlink(file, buf, size); + 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 = os_move_file(f, t); + out: + free_path(f, from_tmp); + free_path(t, to_tmp); + return(err); +} + +int host_stat_fs(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; + int err = -ENOMEM; + + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_stat_filesystem(file, bsize_out, blocks_out, bfree_out, + bavail_out, files_out, ffree_out, fsid_out, + fsid_size, namelen_out, spare_out); + 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_truncate_file(struct file_handle *fh, __u64 size, void *m) +{ + return(truncate_file(fh, 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: + */ diff --git a/fs/hostfs/host_fs.c b/fs/hostfs/host_fs.c new file mode 100644 index 000000000..c059539c5 --- /dev/null +++ b/fs/hostfs/host_fs.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2000 - 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/string.h" +#include "linux/types.h" +#include "linux/errno.h" +#include "linux/slab.h" +#include "linux/init.h" +#include "linux/fs.h" +#include "linux/stat.h" +#include "hostfs.h" +#include "kern.h" +#include "init.h" +#include "kern_util.h" +#include "filehandle.h" +#include "os.h" + +/* Changed in hostfs_args before the kernel starts running */ +static char *jail_dir = "/"; +int append = 0; + +static int __init hostfs_args(char *options, int *add) +{ + char *ptr; + + ptr = strchr(options, ','); + if(ptr != NULL) + *ptr++ = '\0'; + if(*options != '\0') + jail_dir = options; + + options = ptr; + while(options){ + ptr = strchr(options, ','); + if(ptr != NULL) + *ptr++ = '\0'; + if(*options != '\0'){ + if(!strcmp(options, "append")) + append = 1; + else printf("hostfs_args - unsupported option - %s\n", + options); + } + options = ptr; + } + return(0); +} + +__uml_setup("hostfs=", hostfs_args, +"hostfs=,,...\n" +" This is used to set hostfs parameters. The root directory argument\n" +" is used to confine all hostfs mounts to within the specified directory\n" +" tree on the host. If this isn't specified, then a user inside UML can\n" +" mount anything on the host that's accessible to the user that's running\n" +" it.\n" +" The only flag currently supported is 'append', which specifies that all\n" +" files opened by hostfs will be opened in append mode.\n\n" +); + +struct hostfs_data { + struct externfs_data ext; + char *mount; +}; + +struct hostfs_file { + struct externfs_inode ext; + struct file_handle fh; +}; + +static int hostfs_access_file(char *file, int uid, int w, int x, int gid, + int r, struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + char tmp[HOSTFS_BUFSIZE]; + int err, mode = 0; + + if(r) mode = OS_ACC_R_OK; + if(w) mode |= OS_ACC_W_OK; + if(x) mode |= OS_ACC_X_OK; + + err = -ENOMEM; + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_access(file, mode); + free_path(file, tmp); + out: + return(err); +} + +static int hostfs_make_node(const char *file, int mode, int uid, int gid, + int type, int major, int minor, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + char tmp[HOSTFS_BUFSIZE]; + int err = -ENOMEM; + + file = get_path(path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + /* XXX Pass type in an OS-independent way */ + mode |= type; + + err = os_make_dev(file, mode, major, minor); + free_path(file, tmp); + out: + return(err); +} + +static int hostfs_stat_file(const char *file, struct externfs_data *ed, + dev_t *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 *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + /* XXX Why pretend everything is owned by root? */ + *uid_out = 0; + *gid_out = 0; + return(host_stat_file(path, dev_out, inode_out, mode_out, nlink_out, + NULL, NULL, size_out, atime_out, mtime_out, + ctime_out, blksize_out, blocks_out)); +} + +static int hostfs_file_type(const char *file, int *rdev, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_file_type(path, rdev)); +} + +static char *hostfs_name(struct inode *inode) +{ + struct externfs_data *ed = inode_externfs_info(inode); + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + + return(inode_name_prefix(inode, mount)); +} + +static struct externfs_inode *hostfs_init_file(struct externfs_data *ed) +{ + struct hostfs_file *hf; + + hf = kmalloc(sizeof(*hf), GFP_KERNEL); + if(hf == NULL) + return(NULL); + + hf->fh.fd = -1; + return(&hf->ext); +} + +static int hostfs_open_file(struct externfs_inode *ext, char *file, + int uid, int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext); + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + int err; + + err = host_open_file(path, 1, 1, &hf->fh); + if(err == -EISDIR) + goto out; + + if(err == -EACCES) + err = host_open_file(path, 1, 0, &hf->fh); + + if(err) + goto out; + + is_reclaimable(&hf->fh, hostfs_name, inode); + out: + return(err); +} + +static void *hostfs_open_dir(char *file, int uid, int gid, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_open_dir(path)); +} + +static void hostfs_close_dir(void *stream, struct externfs_data *ed) +{ + os_close_dir(stream); +} + +static char *hostfs_read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + + return(generic_host_read_dir(stream, pos, ino_out, len_out, mount)); +} + +static int hostfs_read_file(struct externfs_inode *ext, + unsigned long long offset, char *buf, int len, + int ignore_start, int ignore_end, + void (*completion)(char *, int, void *), void *arg, + struct externfs_data *ed) +{ + struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext); + int err = 0; + + if(ignore_start != 0){ + err = read_file(&hf->fh, offset, buf, ignore_start); + if(err < 0) + goto out; + } + + if(ignore_end != len) + err = read_file(&hf->fh, offset + ignore_end, buf + ignore_end, + len - ignore_end); + + out: + + (*completion)(buf, err, arg); + if (err > 0) + err = 0; + return(err); +} + +static int hostfs_write_file(struct externfs_inode *ext, + unsigned long long offset, const char *buf, + int start, int len, + void (*completion)(char *, int, void *), + void *arg, struct externfs_data *ed) +{ + struct file_handle *fh; + int err; + + fh = &container_of(ext, struct hostfs_file, ext)->fh; + err = write_file(fh, offset + start, buf + start, len); + + (*completion)((char *) buf, err, arg); + if (err > 0) + err = 0; + + return(err); +} + +static int hostfs_create_file(struct externfs_inode *ext, char *file, int mode, + int uid, int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct hostfs_file *hf = container_of(ext, struct hostfs_file, + ext); + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + int err = -ENOMEM; + + err = host_create_file(path, mode, &hf->fh); + if(err) + goto out; + + is_reclaimable(&hf->fh, hostfs_name, inode); + out: + return(err); +} + +static int hostfs_set_attr(const char *file, struct externfs_iattr *attrs, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_set_attr(path, attrs)); +} + +static int hostfs_make_symlink(const char *from, const char *to, int uid, + int gid, struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, from, NULL }; + + return(host_make_symlink(path, to)); +} + +static int hostfs_link_file(const char *to, const char *from, int uid, int gid, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *to_path[] = { jail_dir, mount, to, NULL }; + const char *from_path[] = { jail_dir, mount, from, NULL }; + + return(host_link_file(to_path, from_path)); +} + +static int hostfs_unlink_file(const char *file, struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_unlink_file(path)); +} + +static int hostfs_make_dir(const char *file, int mode, int uid, int gid, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_make_dir(path, mode)); +} + +static int hostfs_remove_dir(const char *file, int uid, int gid, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_remove_dir(path)); +} + +static int hostfs_read_link(char *file, int uid, int gid, char *buf, int size, + struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, file, NULL }; + + return(host_read_link(path, buf, size)); +} + +static int hostfs_rename_file(char *from, char *to, struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *to_path[] = { jail_dir, mount, to, NULL }; + const char *from_path[] = { jail_dir, mount, from, NULL }; + + return(host_rename_file(from_path, to_path)); +} + +static int hostfs_stat_fs(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, struct externfs_data *ed) +{ + char *mount = container_of(ed, struct hostfs_data, ext)->mount; + const char *path[] = { jail_dir, mount, NULL }; + + return(host_stat_fs(path, bsize_out, blocks_out, bfree_out, bavail_out, + files_out, ffree_out, fsid_out, fsid_size, + namelen_out, spare_out)); +} + +void hostfs_close_file(struct externfs_inode *ext, + unsigned long long size) +{ + struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext); + + if(hf->fh.fd != -1){ + truncate_file(&hf->fh, size); + close_file(&hf->fh); + } + + kfree(hf); +} + +int hostfs_truncate_file(struct externfs_inode *ext, __u64 size, + struct externfs_data *ed) +{ + struct hostfs_file *hf = container_of(ext, struct hostfs_file, ext); + + return(truncate_file(&hf->fh, size)); +} + +static struct externfs_file_ops hostfs_file_ops = { + .stat_file = hostfs_stat_file, + .file_type = hostfs_file_type, + .access_file = hostfs_access_file, + .open_file = hostfs_open_file, + .open_dir = hostfs_open_dir, + .read_dir = hostfs_read_dir, + .read_file = hostfs_read_file, + .write_file = hostfs_write_file, + .map_file_page = NULL, + .close_file = hostfs_close_file, + .close_dir = hostfs_close_dir, + .invisible = NULL, + .create_file = hostfs_create_file, + .set_attr = hostfs_set_attr, + .make_symlink = hostfs_make_symlink, + .unlink_file = hostfs_unlink_file, + .make_dir = hostfs_make_dir, + .remove_dir = hostfs_remove_dir, + .make_node = hostfs_make_node, + .link_file = hostfs_link_file, + .read_link = hostfs_read_link, + .rename_file = hostfs_rename_file, + .statfs = hostfs_stat_fs, + .truncate_file = hostfs_truncate_file +}; + +static struct externfs_data *mount_fs(char *mount_arg) +{ + struct hostfs_data *hd; + int err = -ENOMEM; + + hd = kmalloc(sizeof(*hd), GFP_KERNEL); + if(hd == NULL) + goto out; + + hd->mount = host_root_filename(mount_arg); + if(hd->mount == NULL) + goto out_free; + + init_externfs(&hd->ext, &hostfs_file_ops); + + return(&hd->ext); + + out_free: + kfree(hd); + out: + return(ERR_PTR(err)); +} + +static struct externfs_mount_ops hostfs_mount_ops = { + .init_file = hostfs_init_file, + .mount = mount_fs, +}; + +static int __init init_hostfs(void) +{ + return(register_externfs("hostfs", &hostfs_mount_ops)); +} + +static void __exit exit_hostfs(void) +{ + unregister_externfs("hostfs"); +} + +__initcall(init_hostfs); +__exitcall(exit_hostfs); + +#if 0 +module_init(init_hostfs) +module_exit(exit_hostfs) +MODULE_LICENSE("GPL"); +#endif + +/* + * 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: + */ diff --git a/fs/hostfs/humfs.c b/fs/hostfs/humfs.c new file mode 100644 index 000000000..7878be5a3 --- /dev/null +++ b/fs/hostfs/humfs.c @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/init.h" +#include "linux/workqueue.h" +#include +#include "hostfs.h" +#include "mem.h" +#include "os.h" +#include "mode.h" +#include "aio.h" +#include "irq_user.h" +#include "irq_kern.h" +#include "filehandle.h" +#include "metadata.h" + +#define HUMFS_VERSION 2 + +static int humfs_stat_file(const char *path, struct externfs_data *ed, + dev_t *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 humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err, mode, perms, major, minor; + char type; + + err = host_stat_file(data_path, NULL, inode_out, mode_out, + nlink_out, NULL, NULL, size_out, atime_out, + mtime_out, ctime_out, blksize_out, blocks_out); + if(err) + return(err); + + err = (*mount->meta->ownerships)(path, &perms, uid_out, gid_out, + &type, &major, &minor, mount); + if(err) + return(err); + + *mode_out = (*mode_out & ~S_IRWXUGO) | perms; + + mode = 0; + switch(type){ + case 'c': + mode = S_IFCHR; + *dev_out = MKDEV(major, minor); + break; + case 'b': + mode = S_IFBLK; + *dev_out = MKDEV(major, minor); + break; + case 's': + mode = S_IFSOCK; + break; + default: + break; + } + + if(mode != 0) + *mode_out = (*mode_out & ~S_IFMT) | mode; + + return(0); +} + +static int meta_type(const char *path, int *dev_out, void *m) +{ + struct humfs *mount = m; + int err, type, maj, min; + char c; + + err = (*mount->meta->ownerships)(path, NULL, NULL, NULL, &c, &maj, + &min, mount); + if(err) + return(err); + + if(c == 0) + return(0); + + if(dev_out) + *dev_out = MKDEV(maj, min); + + switch(c){ + case 'c': + type = OS_TYPE_CHARDEV; + break; + case 'b': + type = OS_TYPE_BLOCKDEV; + break; + case 'p': + type = OS_TYPE_FIFO; + break; + case 's': + type = OS_TYPE_SOCK; + break; + default: + type = -EINVAL; + break; + } + + return(type); +} + +static int humfs_file_type(const char *path, int *dev_out, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int type; + + type = meta_type(path, dev_out, mount); + if(type != 0) + return(type); + + return(host_file_type(data_path, dev_out)); +} + +static char *humfs_data_name(struct inode *inode) +{ + struct externfs_data *ed = inode_externfs_info(inode); + struct humfs *mount = container_of(ed, struct humfs, ext); + + return(inode_name_prefix(inode, mount->data)); +} + +static struct externfs_inode *humfs_init_file(struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf; + + hf = (*mount->meta->init_file)(); + if(hf == NULL) + return(NULL); + + hf->data.fd = -1; + return(&hf->ext); +} + +static int humfs_open_file(struct externfs_inode *ext, char *path, int uid, + int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + const char *data_path[3] = { mount->data, path, NULL }; + struct openflags flags; + char tmp[HOSTFS_BUFSIZE], *file; + int err = -ENOMEM; + + file = get_path(data_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + flags = of_rdwr(OPENFLAGS()); + if(mount->direct) + flags = of_direct(flags); + + if(path == NULL) + path = ""; + err = (*mount->meta->open_file)(hf, path, inode, mount); + if(err) + goto out_free; + + err = open_filehandle(file, flags, 0, &hf->data); + if(err == -EISDIR) + goto out; + else if(err == -EPERM){ + flags = of_set_rw(flags, 1, 0); + err = open_filehandle(file, flags, 0, &hf->data); + } + + if(err) + goto out_close; + + hf->mount = mount; + is_reclaimable(&hf->data, humfs_data_name, inode); + + out_free: + free_path(file, tmp); + out: + return(err); + + out_close: + (*mount->meta->close_file)(hf); + goto out_free; +} + +static void *humfs_open_dir(char *path, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + + return(host_open_dir(data_path)); +} + +static void humfs_close_dir(void *stream, struct externfs_data *ed) +{ + os_close_dir(stream); +} + +static char *humfs_read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + + return(generic_host_read_dir(stream, pos, ino_out, len_out, mount)); +} + +LIST_HEAD(humfs_replies); + +struct humfs_aio { + struct aio_context aio; + struct list_head list; + void (*completion)(char *, int, void *); + char *buf; + int real_len; + int err; + void *data; +}; + +static int humfs_reply_fd = -1; + +struct humfs_aio last_task_aio, last_intr_aio; +struct humfs_aio *last_task_aio_ptr, *last_intr_aio_ptr; + +void humfs_work_proc(void *unused) +{ + struct humfs_aio *aio; + unsigned long flags; + + while(!list_empty(&humfs_replies)){ + local_irq_save(flags); + aio = list_entry(humfs_replies.next, struct humfs_aio, list); + + last_task_aio = *aio; + last_task_aio_ptr = aio; + + list_del(&aio->list); + local_irq_restore(flags); + + if(aio->err >= 0) + aio->err = aio->real_len; + (*aio->completion)(aio->buf, aio->err, aio->data); + kfree(aio); + } +} + +DECLARE_WORK(humfs_work, humfs_work_proc, NULL); + +static irqreturn_t humfs_interrupt(int irq, void *dev_id, + struct pt_regs *unused) +{ + struct aio_thread_reply reply; + struct humfs_aio *aio; + int err, fd = (int) dev_id; + + while(1){ + err = os_read_file(fd, &reply, sizeof(reply)); + if(err < 0){ + if(err == -EAGAIN) + break; + printk("humfs_interrupt - read returned err %d\n", + -err); + return(IRQ_HANDLED); + } + aio = reply.data; + aio->err = reply.err; + list_add(&aio->list, &humfs_replies); + last_intr_aio = *aio; + last_intr_aio_ptr = aio; + } + + if(!list_empty(&humfs_replies)) + schedule_work(&humfs_work); + reactivate_fd(fd, HUMFS_IRQ); + return(IRQ_HANDLED); +} + +static int init_humfs_aio(void) +{ + int fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err){ + printk("init_humfs_aio - pipe failed, err = %d\n", -err); + goto out; + } + + err = um_request_irq(HUMFS_IRQ, fds[0], IRQ_READ, humfs_interrupt, + SA_INTERRUPT | SA_SAMPLE_RANDOM, "humfs", + (void *) fds[0]); + if(err){ + printk("init_humfs_aio - : um_request_irq failed, err = %d\n", + err); + goto out_close; + } + + humfs_reply_fd = fds[1]; + goto out; + + out_close: + os_close_file(fds[0]); + os_close_file(fds[1]); + out: + return(0); +} + +__initcall(init_humfs_aio); + +static int humfs_aio(enum aio_type type, int fd, unsigned long long offset, + char *buf, int len, int real_len, + void (*completion)(char *, int, void *), void *arg) +{ + struct humfs_aio *aio; + int err = -ENOMEM; + + aio = kmalloc(sizeof(*aio), GFP_KERNEL); + if(aio == NULL) + goto out; + *aio = ((struct humfs_aio) { .aio = INIT_AIO_CONTEXT, + .list = LIST_HEAD_INIT(aio->list), + .completion= completion, + .buf = buf, + .err = 0, + .real_len = real_len, + .data = arg }); + + err = submit_aio(type, fd, buf, len, offset, humfs_reply_fd, aio); + if(err) + (*completion)(buf, err, arg); + + out: + return(err); +} + +static int humfs_read_file(struct externfs_inode *ext, + unsigned long long offset, char *buf, int len, + int ignore_start, int ignore_end, + void (*completion)(char *, int, void *), void *arg, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int fd = filehandle_fd(&hf->data); + + if(fd < 0){ + (*completion)(buf, fd, arg); + return(fd); + } + + return(humfs_aio(AIO_READ, fd, offset, buf, len, len, completion, + arg)); +} + +static int humfs_write_file(struct externfs_inode *ext, + unsigned long long offset, + const char *buf, int start, int len, + void (*completion)(char *, int, void *), void *arg, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int err, orig_len = len, fd = filehandle_fd(&hf->data); + + if(fd < 0){ + (*completion)((char *) buf, fd, arg); + return(fd); + } + + if(mount->direct) + len = PAGE_SIZE; + else { + offset += start; + buf += start; + } + + err = humfs_aio(AIO_WRITE, fd, offset, (char *) buf, len, orig_len, + completion, arg); + + if(err < 0) + return(err); + + if(mount->direct) + err = orig_len; + + return(err); +} + +static int humfs_map_file_page(struct externfs_inode *ext, + unsigned long long offset, char *buf, int w, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + unsigned long long size, need; + int err, fd = filehandle_fd(&hf->data); + + if(fd < 0) + return(fd); + + err = os_fd_size(fd, &size); + if(err) + return(err); + + need = offset + PAGE_SIZE; + if(size < need){ + err = os_truncate_fd(fd, need); + if(err) + return(err); + } + + return(physmem_subst_mapping(buf, fd, offset, w)); +} + +static void humfs_close_file(struct externfs_inode *ext, + unsigned long long size) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + int fd; + + if(hf->data.fd == -1) + return; + + fd = filehandle_fd(&hf->data); + physmem_forget_descriptor(fd); + truncate_file(&hf->data, size); + close_file(&hf->data); + + (*hf->mount->meta->close_file)(hf); +} + +/* XXX Assumes that you can't make a normal file */ + +static int humfs_make_node(const char *path, int mode, int uid, int gid, + int type, int major, int minor, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct file_handle fh; + const char *data_path[3] = { mount->data, path, NULL }; + int err; + char t; + + err = host_create_file(data_path, S_IRWXUGO, &fh); + if(err) + goto out; + + close_file(&fh); + + switch(type){ + case S_IFCHR: + t = 'c'; + break; + case S_IFBLK: + t = 'b'; + break; + case S_IFIFO: + t = 'p'; + break; + case S_IFSOCK: + t = 's'; + break; + default: + err = -EINVAL; + printk("make_node - bad node type : %d\n", type); + goto out_rm; + } + + err = (*mount->meta->make_node)(path, mode, uid, gid, t, major, minor, + mount); + if(err) + goto out_rm; + + out: + return(err); + + out_rm: + host_unlink_file(data_path); + goto out; +} + +static int humfs_create_file(struct externfs_inode *ext, char *path, int mode, + int uid, int gid, struct inode *inode, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->create_file)(hf, path, mode, uid, gid, inode, + mount); + if(err) + goto out; + + err = host_create_file(data_path, S_IRWXUGO, &hf->data); + if(err) + goto out_rm; + + + is_reclaimable(&hf->data, humfs_data_name, inode); + + return(0); + + out_rm: + (*mount->meta->remove_file)(path, mount); + (*mount->meta->close_file)(hf); + out: + return(err); +} + +static int humfs_set_attr(const char *path, struct externfs_iattr *attrs, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int (*chown)(const char *, int, int, int, struct humfs *); + int err; + + chown = mount->meta->change_ownerships; + if(attrs->ia_valid & EXTERNFS_ATTR_MODE){ + err = (*chown)(path, attrs->ia_mode, -1, -1, mount); + if(err) + return(err); + } + if(attrs->ia_valid & EXTERNFS_ATTR_UID){ + err = (*chown)(path, -1, attrs->ia_uid, -1, mount); + if(err) + return(err); + } + if(attrs->ia_valid & EXTERNFS_ATTR_GID){ + err = (*chown)(path, -1, -1, attrs->ia_gid, mount); + if(err) + return(err); + } + + attrs->ia_valid &= ~(EXTERNFS_ATTR_MODE | EXTERNFS_ATTR_UID | + EXTERNFS_ATTR_GID); + + return(host_set_attr(data_path, attrs)); +} + +static int humfs_make_symlink(const char *from, const char *to, int uid, + int gid, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + struct humfs_file *hf; + const char *data_path[3] = { mount->data, from, NULL }; + int err = -ENOMEM; + + hf = (*mount->meta->init_file)(); + if(hf == NULL) + goto out; + + err = (*mount->meta->create_file)(hf, from, S_IRWXUGO, uid, gid, NULL, + mount); + if(err) + goto out_close; + + err = host_make_symlink(data_path, to); + if(err) + (*mount->meta->remove_file)(from, mount); + + out_close: + (*mount->meta->close_file)(hf); + out: + return(err); +} + +static int humfs_link_file(const char *to, const char *from, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path_from[3] = { mount->data, from, NULL }; + const char *data_path_to[3] = { mount->data, to, NULL }; + int err; + + err = (*mount->meta->create_link)(to, from, mount); + if(err) + return(err); + + err = host_link_file(data_path_to, data_path_from); + if(err) + (*mount->meta->remove_file)(from, mount); + + return(err); +} + +static int humfs_unlink_file(const char *path, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->remove_file)(path, mount); + if (err) + return err; + + (*mount->meta->remove_file)(path, mount); + return(host_unlink_file(data_path)); +} + +static void humfs_invisible(struct externfs_inode *ext) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + struct humfs *mount = hf->mount; + + (*mount->meta->invisible)(hf); + not_reclaimable(&hf->data); +} + +static int humfs_make_dir(const char *path, int mode, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = (*mount->meta->create_dir)(path, mode, uid, gid, mount); + if(err) + return(err); + + err = host_make_dir(data_path, S_IRWXUGO); + if(err) + (*mount->meta->remove_dir)(path, mount); + + return(err); +} + +static int humfs_remove_dir(const char *path, int uid, int gid, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, path, NULL }; + int err; + + err = host_remove_dir(data_path); + if (err) + return err; + + (*mount->meta->remove_dir)(path, mount); + + return(err); +} + +static int humfs_read_link(char *file, int uid, int gid, char *buf, int size, + struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, file, NULL }; + + return(host_read_link(data_path, buf, size)); +} + +struct humfs *inode_humfs_info(struct inode *inode) +{ + return(container_of(inode_externfs_info(inode), struct humfs, ext)); +} + +static int humfs_rename_file(char *from, char *to, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path_from[3] = { mount->data, from, NULL }; + const char *data_path_to[3] = { mount->data, to, NULL }; + int err; + + err = (*mount->meta->rename_file)(from, to, mount); + if(err) + return(err); + + err = host_rename_file(data_path_from, data_path_to); + if(err) + (*mount->meta->rename_file)(to, from, mount); + + return(err); +} + +static int humfs_stat_fs(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, struct externfs_data *ed) +{ + struct humfs *mount = container_of(ed, struct humfs, ext); + const char *data_path[3] = { mount->data, NULL }; + int err; + + /* XXX Needs to maintain this info as metadata */ + err = host_stat_fs(data_path, bsize_out, blocks_out, bfree_out, + bavail_out, files_out, ffree_out, fsid_out, + fsid_size, namelen_out, spare_out); + if(err) + return(err); + + *blocks_out = mount->total / *bsize_out; + *bfree_out = (mount->total - mount->used) / *bsize_out; + *bavail_out = (mount->total - mount->used) / *bsize_out; + return(0); +} + +int humfs_truncate_file(struct externfs_inode *ext, __u64 size, + struct externfs_data *ed) +{ + struct humfs_file *hf = container_of(ext, struct humfs_file, ext); + + return(truncate_file(&hf->data, size)); +} + +char *humfs_path(char *dir, char *file) +{ + int need_slash, len = strlen(dir) + strlen(file); + char *new; + + need_slash = (dir[strlen(dir) - 1] != '/'); + if(need_slash) + len++; + + new = kmalloc(len + 1, GFP_KERNEL); + if(new == NULL) + return(NULL); + + strcpy(new, dir); + if(need_slash) + strcat(new, "/"); + strcat(new, file); + + return(new); +} + +DECLARE_MUTEX(meta_sem); +struct list_head metas = LIST_HEAD_INIT(metas); + +static struct humfs_meta_ops *find_meta(const char *name) +{ + struct list_head *ele; + struct humfs_meta_ops *m; + + down(&meta_sem); + list_for_each(ele, &metas){ + m = list_entry(ele, struct humfs_meta_ops, list); + if(!strcmp(m->name, name)) + goto out; + } + m = NULL; + out: + up(&meta_sem); + return(m); +} + +void register_meta(struct humfs_meta_ops *ops) +{ + down(&meta_sem); + list_add(&ops->list, &metas); + up(&meta_sem); +} + +void unregister_meta(struct humfs_meta_ops *ops) +{ + down(&meta_sem); + list_del(&ops->list); + up(&meta_sem); +} + +static struct humfs *read_superblock(char *root) +{ + struct humfs *mount; + struct humfs_meta_ops *meta = NULL; + struct file_handle *fh; + const char *path[] = { root, "superblock", NULL }; + u64 used, total; + char meta_buf[33], line[HOSTFS_BUFSIZE], *newline; + unsigned long long pos; + int version, i, n, err; + + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if(fh == NULL) + return(ERR_PTR(-ENOMEM)); + + err = host_open_file(path, 1, 0, fh); + if(err){ + printk("Failed to open %s/%s, errno = %d\n", path[0], + path[1], err); + return(ERR_PTR(err)); + } + + used = 0; + total = 0; + pos = 0; + i = 0; + while(1){ + n = read_file(fh, pos, &line[i], sizeof(line) - i - 1); + if((n == 0) && (i == 0)) + break; + if(n < 0) + return(ERR_PTR(n)); + + pos += n; + if(n > 0) + line[n + i] = '\0'; + + newline = strchr(line, '\n'); + if(newline == NULL){ + printk("read_superblock - line too long : '%s'\n", + line); + return(ERR_PTR(-EINVAL)); + } + newline++; + + if(sscanf(line, "version %d\n", &version) == 1){ + if(version != HUMFS_VERSION){ + printk("humfs version mismatch - want version " + "%d, got version %d.\n", HUMFS_VERSION, + version); + return(ERR_PTR(-EINVAL)); + } + } + else if(sscanf(line, "used %Lu\n", &used) == 1) ; + else if(sscanf(line, "total %Lu\n", &total) == 1) ; + else if(sscanf(line, "metadata %32s\n", meta_buf) == 1){ + meta = find_meta(meta_buf); + if(meta == NULL){ + printk("read_superblock - meta api \"%s\" not " + "registered\n", meta_buf); + return(ERR_PTR(-EINVAL)); + } + } + + else { + printk("read_superblock - bogus line : '%s'\n", line); + return(ERR_PTR(-EINVAL)); + } + + i = newline - line; + memmove(line, newline, sizeof(line) - i); + i = strlen(line); + } + + if(used == 0){ + printk("read_superblock - used not specified or set to " + "zero\n"); + return(ERR_PTR(-EINVAL)); + } + if(total == 0){ + printk("read_superblock - total not specified or set to " + "zero\n"); + return(ERR_PTR(-EINVAL)); + } + if(used > total){ + printk("read_superblock - used is greater than total\n"); + return(ERR_PTR(-EINVAL)); + } + + if(meta == NULL){ + meta = find_meta("shadow_fs"); + } + + if(meta == NULL){ + printk("read_superblock - valid meta api was not specified\n"); + return(ERR_PTR(-EINVAL)); + } + + mount = (*meta->init_mount)(root); + if(IS_ERR(mount)) + return(mount); + + *mount = ((struct humfs) { .total = total, + .used = used, + .meta = meta }); + return(mount); +} + +struct externfs_file_ops humfs_no_mmap_file_ops = { + .stat_file = humfs_stat_file, + .file_type = humfs_file_type, + .access_file = NULL, + .open_file = humfs_open_file, + .open_dir = humfs_open_dir, + .read_dir = humfs_read_dir, + .read_file = humfs_read_file, + .write_file = humfs_write_file, + .map_file_page = NULL, + .close_file = humfs_close_file, + .close_dir = humfs_close_dir, + .invisible = humfs_invisible, + .create_file = humfs_create_file, + .set_attr = humfs_set_attr, + .make_symlink = humfs_make_symlink, + .unlink_file = humfs_unlink_file, + .make_dir = humfs_make_dir, + .remove_dir = humfs_remove_dir, + .make_node = humfs_make_node, + .link_file = humfs_link_file, + .read_link = humfs_read_link, + .rename_file = humfs_rename_file, + .statfs = humfs_stat_fs, + .truncate_file = humfs_truncate_file +}; + +struct externfs_file_ops humfs_mmap_file_ops = { + .stat_file = humfs_stat_file, + .file_type = humfs_file_type, + .access_file = NULL, + .open_file = humfs_open_file, + .open_dir = humfs_open_dir, + .invisible = humfs_invisible, + .read_dir = humfs_read_dir, + .read_file = humfs_read_file, + .write_file = humfs_write_file, + .map_file_page = humfs_map_file_page, + .close_file = humfs_close_file, + .close_dir = humfs_close_dir, + .create_file = humfs_create_file, + .set_attr = humfs_set_attr, + .make_symlink = humfs_make_symlink, + .unlink_file = humfs_unlink_file, + .make_dir = humfs_make_dir, + .remove_dir = humfs_remove_dir, + .make_node = humfs_make_node, + .link_file = humfs_link_file, + .read_link = humfs_read_link, + .rename_file = humfs_rename_file, + .statfs = humfs_stat_fs, + .truncate_file = humfs_truncate_file +}; + +static struct externfs_data *mount_fs(char *mount_arg) +{ + char *root, *data, *flags; + struct humfs *mount; + struct externfs_file_ops *file_ops; + int err, do_mmap = 0; + + if(mount_arg == NULL){ + printk("humfs - no host directory specified\n"); + return(NULL); + } + + flags = strchr((char *) mount_arg, ','); + if(flags != NULL){ + do { + *flags++ = '\0'; + + if(!strcmp(flags, "mmap")) + do_mmap = 1; + + flags = strchr(flags, ','); + } while(flags != NULL); + } + + err = -ENOMEM; + root = host_root_filename(mount_arg); + if(root == NULL) + goto err; + + mount = read_superblock(root); + if(IS_ERR(mount)){ + err = PTR_ERR(mount); + goto err_free_root; + } + + data = humfs_path(root, "data/"); + if(data == NULL) + goto err_free_mount; + + if(CHOOSE_MODE(do_mmap, 0)){ + printk("humfs doesn't support mmap in tt mode\n"); + do_mmap = 0; + } + + mount->data = data; + mount->mmap = do_mmap; + + file_ops = do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops; + init_externfs(&mount->ext, file_ops); + + return(&mount->ext); + + err_free_mount: + kfree(mount); + err_free_root: + kfree(root); + err: + return(NULL); +} + +struct externfs_mount_ops humfs_mount_ops = { + .init_file = humfs_init_file, + .mount = mount_fs, +}; + +static int __init init_humfs(void) +{ + return(register_externfs("humfs", &humfs_mount_ops)); +} + +static void __exit exit_humfs(void) +{ + unregister_externfs("humfs"); +} + +__initcall(init_humfs); +__exitcall(exit_humfs); + +/* + * 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: + */ diff --git a/fs/hostfs/meta_fs.c b/fs/hostfs/meta_fs.c new file mode 100644 index 000000000..74641496e --- /dev/null +++ b/fs/hostfs/meta_fs.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include +#include +#include "hostfs.h" +#include "metadata.h" +#include "kern_util.h" + +#define METADATA_FILE_PATH(meta) (meta)->root, "file_metadata" +#define METADATA_DIR_PATH(meta) (meta)->root, "dir_metadata" + +struct meta_fs { + struct humfs humfs; + char *root; +}; + +struct meta_file { + struct humfs_file humfs; + struct file_handle fh; +}; + +static int meta_file_path(const char *path, struct meta_fs *meta, + const char *path_out[]) +{ + const char *data_path[] = { meta->root, "data", path, NULL }; + char data_tmp[HOSTFS_BUFSIZE]; + char *data_file = get_path(data_path, data_tmp, sizeof(data_tmp)); + + if(data_file == NULL) + return(-ENOMEM); + + path_out[0] = meta->root; + path_out[2] = path; + if(os_file_type(data_file) == OS_TYPE_DIR){ + path_out[1] = "dir_metadata"; + path_out[3] = "metadata"; + path_out[4] = NULL; + } + else { + path_out[1] = "file_metadata"; + path_out[3] = NULL; + } + + return(0); +} + +static int open_meta_file(const char *path, struct humfs *humfs, + struct file_handle *fh) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *meta_path[5]; + char meta_tmp[HOSTFS_BUFSIZE]; + char *meta_file; + int err; + + err = meta_file_path(path, meta, meta_path); + if(err) + goto out; + + meta_file = get_path(meta_path, meta_tmp, sizeof(meta_tmp)); + if(meta_file == NULL) + goto out; + + err = open_filehandle(meta_file, of_rdwr(OPENFLAGS()), 0, fh); + + out: + return(err); +} + +static char *meta_fs_name(struct inode *inode) +{ + struct humfs *mount = inode_humfs_info(inode); + struct meta_fs *meta = container_of(mount, struct meta_fs, humfs); + const char *metadata_path[5]; + char tmp[HOSTFS_BUFSIZE], *name, *file; + + if(meta_file_path("", meta, metadata_path)) + return(NULL); + + file = get_path(metadata_path, tmp, sizeof(tmp)); + if(file == NULL) + return(NULL); + + name = inode_name_prefix(inode, file); + + free_path(file, tmp); + return(name); +} + +static void metafs_invisible(struct humfs_file *hf) +{ + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + + not_reclaimable(&mf->fh); +} + +static struct humfs_file *metafs_init_file(void) +{ + struct meta_file *mf; + int err = -ENOMEM; + + mf = kmalloc(sizeof(*mf), GFP_KERNEL); + if(mf == NULL) + return(ERR_PTR(err)); + + return(&mf->humfs); +} + +static int metafs_open_file(struct humfs_file *hf, const char *path, + struct inode *inode, struct humfs *humfs) +{ + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + int err; + + err = open_meta_file(path, humfs, &mf->fh); + if(err) + return(err); + + is_reclaimable(&mf->fh, meta_fs_name, inode); + + return(0); +} + +static void metafs_close_file(struct humfs_file *hf) +{ + struct meta_file *meta = container_of(hf, struct meta_file, humfs); + + close_file(&meta->fh); + kfree(meta); +} + +static int metafs_create_file(struct humfs_file *hf, const char *path, + int mode, int uid, int gid, struct inode *inode, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + struct meta_file *mf = container_of(hf, struct meta_file, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *file = get_path(metadata_path, tmp, sizeof(tmp)); + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg")]; + int err = -ENOMEM; + + if(file == NULL) + goto out; + + err = open_filehandle(file, of_write(of_create(OPENFLAGS())), 0644, + &mf->fh); + if(err) + goto out_free_path; + + if(inode != NULL) + is_reclaimable(&mf->fh, meta_fs_name, inode); + + sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + err = write_file(&mf->fh, 0, buf, strlen(buf)); + if(err < 0) + goto out_rm; + + free_path(file, tmp); + return(0); + + out_rm: + close_file(&mf->fh); + os_remove_file(file); + out_free_path: + free_path(file, tmp); + out: + return(err); +} + +static int metafs_create_link(const char *to, const char *from, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *path_to[] = { METADATA_FILE_PATH(meta), to, NULL }; + const char *path_from[] = { METADATA_FILE_PATH(meta), from, NULL }; + + return(host_link_file(path_to, path_from)); +} + +static int metafs_remove_file(const char *path, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *file = get_path(metadata_path, tmp, sizeof(tmp)); + int err = -ENOMEM; + + if(file == NULL) + goto out; + + err = os_remove_file(file); + + out: + free_path(file, tmp); + return(err); +} + +static int metafs_create_directory(const char *path, int mode, int uid, + int gid, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE]; + const char *dir_path[] = { METADATA_DIR_PATH(meta), path, NULL, NULL }; + const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL, + NULL }; + char *file, dir_meta[sizeof("mmmm uuuuuuuuuu gggggggggg\n")]; + int err, fd; + + err = host_make_dir(dir_path, 0755); + if(err) + goto out; + + err = host_make_dir(file_path, 0755); + if(err) + goto out_rm; + + /* This to make the index independent of the number of elements in + * METADATA_DIR_PATH(). + */ + dir_path[sizeof(dir_path) / sizeof(dir_path[0]) - 2] = "metadata"; + + err = -ENOMEM; + file = get_path(dir_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + fd = os_open_file(file, of_create(of_rdwr(OPENFLAGS())), 0644); + if(fd < 0){ + err = fd; + goto out_free; + } + + sprintf(dir_meta, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + err = os_write_file(fd, dir_meta, strlen(dir_meta)); + if(err > 0) + err = 0; + + os_close_file(fd); + + out_free: + free_path(file, tmp); + out_rm: + host_remove_dir(dir_path); + out: + return(err); +} + +static int metafs_remove_directory(const char *path, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + char tmp[HOSTFS_BUFSIZE], *file; + const char *dir_path[] = { METADATA_DIR_PATH(meta), path, "metadata", + NULL }; + const char *file_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + char *slash; + int err; + + err = -ENOMEM; + file = get_path(dir_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_remove_file(file); + if(err) + goto out_free; + + slash = strrchr(file, '/'); + if(slash == NULL){ + printk("remove_shadow_directory failed to find last slash\n"); + goto out_free; + } + *slash = '\0'; + err = os_remove_dir(file); + free_path(file, tmp); + + file = get_path(file_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = os_remove_dir(file); + if(err) + goto out_free; + + out: + return(err); + out_free: + free_path(file, tmp); + goto out; +} + +static int metafs_make_node(const char *path, int mode, int uid, int gid, + int type, int maj, int min, struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + struct file_handle fh; + char tmp[HOSTFS_BUFSIZE]; + const char *metadata_path[] = { METADATA_FILE_PATH(meta), path, NULL }; + int err; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")], *file; + + sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, type, + maj, min); + + err = -ENOMEM; + file = get_path(metadata_path, tmp, sizeof(tmp)); + if(file == NULL) + goto out; + + err = open_filehandle(file, + of_create(of_rdwr(OPENFLAGS())), 0644, &fh); + if(err) + goto out_free; + + err = write_file(&fh, 0, buf, strlen(buf)); + if(err > 0) + err = 0; + + close_file(&fh); + + out_free: + free_path(file, tmp); + out: + return(err); +} + +static int metafs_ownerships(const char *path, int *mode_out, int *uid_out, + int *gid_out, char *type_out, int *maj_out, + int *min_out, struct humfs *humfs) +{ + struct file_handle fh; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")]; + int err, n, mode, uid, gid, maj, min; + char type; + + err = open_meta_file(path, humfs, &fh); + if(err) + goto out; + + err = os_read_file(fh.fd, buf, sizeof(buf) - 1); + if(err < 0) + goto out_close; + + buf[err] = '\0'; + err = 0; + + n = sscanf(buf, "%d %d %d %c %d %d", &mode, &uid, &gid, &type, &maj, + &min); + if(n == 3){ + maj = -1; + min = -1; + type = 0; + err = 0; + } + else if(n != 6) + err = -EINVAL; + + if(mode_out != NULL) + *mode_out = mode; + if(uid_out != NULL) + *uid_out = uid; + if(gid_out != NULL) + *gid_out = uid; + if(type_out != NULL) + *type_out = type; + if(maj_out != NULL) + *maj_out = maj; + if(min_out != NULL) + *min_out = min; + + out_close: + close_file(&fh); + out: + return(err); +} + +static int metafs_change_ownerships(const char *path, int mode, int uid, + int gid, struct humfs *humfs) +{ + struct file_handle fh; + char type; + char buf[sizeof("mmmm uuuuuuuuuu gggggggggg x nnn mmm\n")]; + int err = -ENOMEM, old_mode, old_uid, old_gid, n, maj, min; + + err = open_meta_file(path, humfs, &fh); + if(err) + goto out; + + err = read_file(&fh, 0, buf, sizeof(buf) - 1); + if(err < 0) + goto out_close; + + buf[err] = '\0'; + + n = sscanf(buf, "%d %d %d %c %d %d\n", &old_mode, &old_uid, &old_gid, + &type, &maj, &min); + if((n != 3) && (n != 6)){ + err = -EINVAL; + goto out_close; + } + + if(mode == -1) + mode = old_mode; + if(uid == -1) + uid = old_uid; + if(gid == -1) + gid = old_gid; + + if(n == 3) + sprintf(buf, "%d %d %d\n", mode & S_IRWXUGO, uid, gid); + else + sprintf(buf, "%d %d %d %c %d %d\n", mode & S_IRWXUGO, uid, gid, + type, maj, min); + + err = write_file(&fh, 0, buf, strlen(buf)); + if(err > 0) + err = 0; + + err = truncate_file(&fh, strlen(buf)); + + out_close: + close_file(&fh); + out: + return(err); +} + +static int metafs_rename_file(const char *from, const char *to, + struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + const char *metadata_path_from[5], *metadata_path_to[5]; + int err; + + err = meta_file_path(from, meta, metadata_path_from); + if(err) + return(err); + + err = meta_file_path(to, meta, metadata_path_to); + if(err) + return(err); + + return(host_rename_file(metadata_path_from, metadata_path_to)); +} + +static struct humfs *metafs_init_mount(char *root) +{ + struct meta_fs *meta; + int err = -ENOMEM; + + meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if(meta == NULL) + goto out; + + meta->root = uml_strdup(root); + if(meta->root == NULL) + goto out_free_meta; + + return(&meta->humfs); + + out_free_meta: + kfree(meta); + out: + return(ERR_PTR(err)); +} + +static void metafs_free_mount(struct humfs *humfs) +{ + struct meta_fs *meta = container_of(humfs, struct meta_fs, humfs); + + kfree(meta); +} + +struct humfs_meta_ops hum_fs_meta_fs_ops = { + .list = LIST_HEAD_INIT(hum_fs_meta_fs_ops.list), + .name = "shadow_fs", + .init_file = metafs_init_file, + .open_file = metafs_open_file, + .close_file = metafs_close_file, + .ownerships = metafs_ownerships, + .make_node = metafs_make_node, + .create_file = metafs_create_file, + .create_link = metafs_create_link, + .remove_file = metafs_remove_file, + .create_dir = metafs_create_directory, + .remove_dir = metafs_remove_directory, + .change_ownerships = metafs_change_ownerships, + .rename_file = metafs_rename_file, + .invisible = metafs_invisible, + .init_mount = metafs_init_mount, + .free_mount = metafs_free_mount, +}; + +static int __init init_meta_fs(void) +{ + register_meta(&hum_fs_meta_fs_ops); + return(0); +} + +static void __exit exit_meta_fs(void) +{ + unregister_meta(&hum_fs_meta_fs_ops); +} + +__initcall(init_meta_fs); +__exitcall(exit_meta_fs); + +/* + * 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: + */ diff --git a/fs/hostfs/metadata.h b/fs/hostfs/metadata.h new file mode 100644 index 000000000..924fb5bf2 --- /dev/null +++ b/fs/hostfs/metadata.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2004 Piotr Neuman (sikkh@wp.pl) and + * Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __UM_FS_METADATA +#define __UM_FS_METADATA + +#include "linux/fs.h" +#include "linux/list.h" +#include "os.h" +#include "hostfs.h" + +struct humfs { + struct externfs_data ext; + __u64 used; + __u64 total; + char *data; + int mmap; + int direct; + struct humfs_meta_ops *meta; +}; + +struct humfs_file { + struct humfs *mount; + struct file_handle data; + struct externfs_inode ext; +}; + +struct humfs_meta_ops { + struct list_head list; + char *name; + struct humfs_file *(*init_file)(void); + int (*open_file)(struct humfs_file *hf, const char *path, + struct inode *inode, struct humfs *humfs); + int (*create_file)(struct humfs_file *hf, const char *path, int mode, + int uid, int gid, struct inode *inode, + struct humfs *humfs); + void (*close_file)(struct humfs_file *humfs); + int (*ownerships)(const char *path, int *mode_out, int *uid_out, + int *gid_out, char *type_out, int *maj_out, + int *min_out, struct humfs *humfs); + int (*make_node)(const char *path, int mode, int uid, int gid, + int type, int major, int minor, struct humfs *humfs); + int (*create_link)(const char *to, const char *from, + struct humfs *humfs); + int (*remove_file)(const char *path, struct humfs *humfs); + int (*create_dir)(const char *path, int mode, int uid, int gid, + struct humfs *humfs); + int (*remove_dir)(const char *path, struct humfs *humfs); + int (*change_ownerships)(const char *path, int mode, int uid, int gid, + struct humfs *humfs); + int (*rename_file)(const char *from, const char *to, + struct humfs *humfs); + void (*invisible)(struct humfs_file *hf); + struct humfs *(*init_mount)(char *root); + void (*free_mount)(struct humfs *humfs); +}; + +extern void register_meta(struct humfs_meta_ops *ops); +extern void unregister_meta(struct humfs_meta_ops *ops); + +extern char *humfs_path(char *dir, char *file); +extern char *humfs_name(struct inode *inode, char *prefix); +extern struct humfs *inode_humfs_info(struct inode *inode); + +#endif + +/* + * 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: + */