Add the IMG Debug Adapter File System (DAFS) for metag, which uses SWITCH operations to communicate with a file server on a host computer via a JTAG debug adapter. Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx> Cc: Alexander Viro <viro@xxxxxxxxxxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Cc: linux-metag@xxxxxxxxxxxxxxx --- I haven't received any feedback on this patch since v6 in March. If it's acceptable, how should it find its way into mainline? I'm happy to take it in the arch/metag tree if somebody on the fs side would be willing to ACK it. v10 (Dec 2013): * add release() callback to write back file mappings (same problem as fixed in hostfs by commit 65984ff9d217 um: hostfs: Fix writeback) * implement basic fsync() to write back file mappings * use simple_dentry_operations, like hostfs change in commit b26d4cd385fc (consolidate simple ->d_delete() instances) v9 (Jul 2013): * updated dafs_readdir to use iterate() file operation and dir_emit() to match changes in v3.11-rc1. v8 (May 2013): * enabled building as a module (commit e605ff8 merged during v3.10 merge window exports the necessary symbols) v7 (Mar 2013): * use explicit sized types in da_stat and da_finddata (Al Viro) * move IMGDAFS_SUPER_MAGIC to <linux/magic.h> (Al Viro) * use fmode_t instead of int in a couple of places (Al Viro) * removed "will unlock" comment (Al Viro) * use kmalloc and init hi->mode explicitly (Al Viro) * kmalloc instead of using dynamic array for path (Al Viro) * use file_inode() helper in dafs_readdir (Al Viro) v6 (Mar 2013): * use file_inode() helper introduced in v3.9-rc1 * remove dafs_iops as it only contained directory methods * add MODULE_ALIAS_FS() v5 (Mar 2013): * removed arch/metag changes (the arch/metag patchset it was previously part of is now mainline with those changes) v4 (Jan 2013): * clean up fscall similarly to chancall in tty/metag_da v2 (Dec 2012): * fixed fserrno problem (Al Viro) * renamed to imgdafs --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/imgdafs/Kconfig | 6 + fs/imgdafs/Makefile | 7 + fs/imgdafs/imgdafs.h | 82 +++++ fs/imgdafs/inode.c | 843 +++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/magic.h | 1 + 7 files changed, 941 insertions(+) create mode 100644 fs/imgdafs/Kconfig create mode 100644 fs/imgdafs/Makefile create mode 100644 fs/imgdafs/imgdafs.h create mode 100644 fs/imgdafs/inode.c diff --git a/fs/Kconfig b/fs/Kconfig index c229f828eb01..839ef10842eb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -212,6 +212,7 @@ source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" source "fs/f2fs/Kconfig" source "fs/efivarfs/Kconfig" +source "fs/imgdafs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index 4fe6df3ec28f..25a93c793b74 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -126,3 +126,4 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ +obj-$(CONFIG_IMGDAFS_FS) += imgdafs/ diff --git a/fs/imgdafs/Kconfig b/fs/imgdafs/Kconfig new file mode 100644 index 000000000000..844e6cf0e9ed --- /dev/null +++ b/fs/imgdafs/Kconfig @@ -0,0 +1,6 @@ +config IMGDAFS_FS + tristate "Meta DA filesystem support" + depends on METAG_DA + help + This enables the DA filesystem, which allows Linux to + to access files on a system attached via a debug adapter. diff --git a/fs/imgdafs/Makefile b/fs/imgdafs/Makefile new file mode 100644 index 000000000000..169a3c6b5fa4 --- /dev/null +++ b/fs/imgdafs/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for DAfs the Debug Adapter filesystem. +# + +obj-$(CONFIG_IMGDAFS_FS) += imgdafs.o + +imgdafs-objs := inode.o diff --git a/fs/imgdafs/imgdafs.h b/fs/imgdafs/imgdafs.h new file mode 100644 index 000000000000..0350220eb321 --- /dev/null +++ b/fs/imgdafs/imgdafs.h @@ -0,0 +1,82 @@ +#ifndef _IMGDAFS_H_ +#define _IMGDAFS_H_ + +#include <linux/types.h> + +#define DA_OP_OPEN 0 +#define DA_OP_CREAT 1 +#define DA_OP_READ 2 +#define DA_OP_WRITE 3 +#define DA_OP_CLOSE 4 +#define DA_OP_LINK 5 +#define DA_OP_LSEEK 6 +#define DA_OP_UNLINK 7 +#define DA_OP_ISATTY 8 +#define DA_OP_FCNTL 9 +#define DA_OP_STAT 10 +#define DA_OP_FSTAT 11 +#define DA_OP_GETCWD 12 +#define DA_OP_CHDIR 13 +#define DA_OP_MKDIR 14 +#define DA_OP_RMDIR 15 +#define DA_OP_FINDFIRST 16 +#define DA_OP_FINDNEXT 17 +#define DA_OP_FINDCLOSE 18 +#define DA_OP_CHMOD 19 +#define DA_OP_PREAD 20 +#define DA_OP_PWRITE 21 + +#define OS_TYPE_FILE 1 +#define OS_TYPE_DIR 2 +#define OS_TYPE_SYMLINK 3 +#define OS_TYPE_CHARDEV 4 +#define OS_TYPE_BLOCKDEV 5 +#define OS_TYPE_FIFO 6 +#define OS_TYPE_SOCK 7 + +#define DA_O_RDONLY 0 +#define DA_O_WRONLY 1 +#define DA_O_RDWR 2 +#define DA_O_APPEND 8 +#define DA_O_CREAT 0x0200 +#define DA_O_TRUNC 0x0400 +#define DA_O_EXCL 0x0800 + +#define DA_O_AFFINITY_THREAD_0 0x10000 +#define DA_O_AFFINITY_THREAD_1 0x20000 +#define DA_O_AFFINITY_THREAD_2 0x40000 +#define DA_O_AFFINITY_THREAD_3 0x80000 +#define DA_O_AFFINITY_SHIFT 16 + +#define DA_S_IWUSR 0200 /* 0x80 */ +#define DA_S_IRUSR 0400 /* 0x100 */ + +struct da_stat { + s16 st_dev; + u16 st_ino; + u32 st_mode; + u16 st_nlink; + u16 st_uid; + u16 st_gid; + s16 st_rdev; + s32 st_size; + s32 st_atime; + s32 st_spare1; + s32 st_mtime; + s32 st_spare2; + s32 st_ctime; + s32 st_spare3; + s32 st_blksize; + s32 st_blocks; + s32 st_spare4[2]; +}; + +#define _A_SUBDIR 0x10 + +struct da_finddata { + u32 size; + u32 attrib; + u8 name[260]; +}; + +#endif diff --git a/fs/imgdafs/inode.c b/fs/imgdafs/inode.c new file mode 100644 index 000000000000..f444f67af0d8 --- /dev/null +++ b/fs/imgdafs/inode.c @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2008-2013 Imagination Technologies Ltd. + * Licensed under the GPL + * + * Based on hostfs for UML. + * + */ + +#include <linux/fs.h> +#include <linux/magic.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/seq_file.h> +#include <linux/mount.h> +#include <linux/slab.h> + +#include <asm/da.h> +#include <asm/hwthread.h> + +#include "imgdafs.h" + +static int fscall(int in_system_call, int in_arg1, int in_arg2, int in_arg3, + int in_arg4, int in_arg5, int *fserrno) +{ + register int arg1 asm("D1Ar1") = in_arg1; + register int arg2 asm("D0Ar2") = in_arg2; + register int arg3 asm("D1Ar3") = in_arg3; + register int arg4 asm("D0Ar4") = in_arg4; + register int arg5 asm("D1Ar5") = in_arg5; + register int system_call asm("D0Ar6") = in_system_call; + register int result asm("D0Re0"); + register int errno asm("D1Re0"); + + asm volatile ( + "MSETL [A0StP++], %7,%5,%3\n\t" + "ADD A0StP, A0StP, #8\n\t" + "SWITCH #0x0C00208\n\t" + "GETL %0, %1, [A0StP+#-8]\n\t" + "SUB A0StP, A0StP, #(4*6)+8\n\t" + : "=r" (result), + "=r" (errno) + : "r" (arg1), + "r" (arg2), + "r" (arg3), + "r" (arg4), + "r" (arg5), + "r" (system_call) + : "memory"); + + if (fserrno) + *fserrno = errno; + + return result; +} + +struct dafs_inode_info { + int fd; + fmode_t mode; + struct inode vfs_inode; +}; + +static inline struct dafs_inode_info *DAFS_I(struct inode *inode) +{ + return container_of(inode, struct dafs_inode_info, vfs_inode); +} + +#define FILE_DAFS_I(file) DAFS_I(file_inode(file)) + +static const struct inode_operations dafs_dir_iops; + +static char *__dentry_name(struct dentry *dentry, char *name) +{ + char *p = dentry_path_raw(dentry, name, PATH_MAX); + char *root; + size_t len; + + root = dentry->d_sb->s_fs_info; + len = strlen(root); + if (IS_ERR(p)) { + __putname(name); + return NULL; + } + + strlcpy(name, root, PATH_MAX); + if (len > p - name) { + __putname(name); + return NULL; + } + if (p > name + len) { + char *s = name + len; + while ((*s++ = *p++) != '\0') + ; + } + return name; +} + +static char *dentry_name(struct dentry *dentry) +{ + char *name = __getname(); + if (!name) + return NULL; + + return __dentry_name(dentry, name); +} + +static int stat_file(const char *path, struct da_stat *p, int fd) +{ + int ret; + int fserrno; + memset(p, 0, sizeof(*p)); + + if (fd >= 0) { + ret = fscall(DA_OP_FSTAT, fd, (int)p, 0, 0, 0, &fserrno); + if (ret < 0) { + /* Some versions of Codescape do not fill out errno. */ + if (ret < 0 && fserrno == 0) + fserrno = ENOENT; + return -fserrno; + } + } else { + ret = fscall(DA_OP_STAT, (int)path, (int)p, strlen(path), 0, 0, + &fserrno); + if (ret < 0) { + /* Some versions of Codescape do not fill out errno. */ + if (ret < 0 && fserrno == 0) + fserrno = ENOENT; + return -fserrno; + } + } + + return 0; +} + +static struct inode *dafs_iget(struct super_block *sb) +{ + struct inode *inode = new_inode(sb); + if (!inode) + return ERR_PTR(-ENOMEM); + return inode; +} + +static struct inode *dafs_alloc_inode(struct super_block *sb) +{ + struct dafs_inode_info *hi; + + hi = kmalloc(sizeof(*hi), GFP_KERNEL); + if (hi == NULL) + return NULL; + + hi->fd = -1; + hi->mode = 0; + inode_init_once(&hi->vfs_inode); + return &hi->vfs_inode; +} + +static void close_file(void *stream) +{ + int fd = *((int *) stream); + + fscall(DA_OP_CLOSE, fd, 0, 0, 0, 0, NULL); +} + +static void dafs_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + if (DAFS_I(inode)->fd != -1) { + close_file(&DAFS_I(inode)->fd); + DAFS_I(inode)->fd = -1; + } +} + +static void dafs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + kfree(DAFS_I(inode)); +} + +static void dafs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, dafs_i_callback); +} + +static const struct super_operations dafs_sbops = { + .alloc_inode = dafs_alloc_inode, + .drop_inode = generic_delete_inode, + .evict_inode = dafs_evict_inode, + .destroy_inode = dafs_destroy_inode, +}; + +static int open_dir(char *path, struct da_finddata *finddata, int *fserrno) +{ + int len = strlen(path); + char *buf; + int ret; + + buf = kmalloc(len + 3, GFP_KERNEL); + if (!buf) { + *fserrno = ENOMEM; + return -1; + } + + strcpy(buf, path); + if (buf[len - 1] != '/') + strcat(buf, "/*"); + else + strcat(buf, "*"); + + ret = fscall(DA_OP_FINDFIRST, (int)buf, (int)finddata, 0, 0, 0, + fserrno); + kfree(buf); + return ret; +} + +static void close_dir(int handle) +{ + fscall(DA_OP_FINDCLOSE, handle, 0, 0, 0, 0, NULL); +} + +static int read_dir(int handle, struct da_finddata *finddata) +{ + return fscall(DA_OP_FINDNEXT, handle, (int)finddata, 0, 0, 0, NULL); +} + +static int dafs_readdir(struct file *file, struct dir_context *ctx) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + char *name; + int handle; + int fserrno; + unsigned long long next, ino; + int error = 0; + struct da_finddata finddata; + + name = dentry_name(file->f_path.dentry); + if (name == NULL) + return -ENOMEM; + handle = open_dir(name, &finddata, &fserrno); + __putname(name); + if (handle == -1) + return -fserrno; + + next = 1; + + if (ctx->pos == 0) { + if (!dir_emit(ctx, ".", ctx->pos + 1, inode->i_ino, DT_DIR)) + goto out; + ctx->pos++; + } + + while (1) { + error = read_dir(handle, &finddata); + if (error) + break; + + if (next >= ctx->pos) { + size_t len = strlen(finddata.name); + ino = iunique(sb, 100); + if (!dir_emit(ctx, finddata.name, len, ino, + (finddata.attrib & _A_SUBDIR) ? + DT_DIR : DT_REG)) + break; + ctx->pos++; + } + next++; + } +out: + close_dir(handle); + return 0; +} + +static int dafs_file_open(struct inode *ino, struct file *file) +{ + static DEFINE_MUTEX(open_mutex); + char *name; + fmode_t mode, fmode; + int flags = 0, r = 0, w = 0, fd; + int cpu; + + fmode = file->f_mode & (FMODE_READ | FMODE_WRITE); + if ((fmode & DAFS_I(ino)->mode) == fmode) + return 0; + + mode = ino->i_mode & (DA_S_IWUSR | DA_S_IRUSR); + + mode |= DAFS_I(ino)->mode; + + DAFS_I(ino)->mode |= fmode; + if (DAFS_I(ino)->mode & FMODE_READ) + r = 1; + if (DAFS_I(ino)->mode & FMODE_WRITE) { + w = 1; + r = 1; + } + +retry: + if (r && !w) + flags |= DA_O_RDONLY; + else if (!r && w) + flags |= DA_O_WRONLY; + else if (r && w) + flags |= DA_O_RDWR; + + if (file->f_flags & O_CREAT) + flags |= DA_O_CREAT; + + if (file->f_flags & O_TRUNC) + flags |= DA_O_TRUNC; + + /* + * Set the affinity for this file handle to all CPUs. If we + * don't do this then, if the process that opened the file + * migrates to a different cpu, the FileServer will not accept + * the file handle. + */ + for_each_possible_cpu(cpu) { + u8 hwthread = cpu_2_hwthread_id[cpu]; + flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread)); + } + + name = dentry_name(file->f_path.dentry); + if (name == NULL) + return -ENOMEM; + + fd = fscall(DA_OP_OPEN, (int)name, flags, mode, strlen(name), 0, NULL); + __putname(name); + if (fd < 0) + return fd; + + mutex_lock(&open_mutex); + /* somebody else had handled it first? */ + if ((mode & DAFS_I(ino)->mode) == mode) { + mutex_unlock(&open_mutex); + return 0; + } + if ((mode | DAFS_I(ino)->mode) != mode) { + mode |= DAFS_I(ino)->mode; + mutex_unlock(&open_mutex); + close_file(&fd); + goto retry; + } + DAFS_I(ino)->fd = fd; + DAFS_I(ino)->mode = mode; + mutex_unlock(&open_mutex); + + return 0; +} + +static int dafs_file_release(struct inode *inode, struct file *file) +{ + filemap_write_and_wait(inode->i_mapping); + + return 0; +} + +static int dafs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = file->f_mapping->host; + + return filemap_write_and_wait_range(inode->i_mapping, start, end); +} + +static const struct file_operations dafs_file_fops = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .splice_read = generic_file_splice_read, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .write = do_sync_write, + .mmap = generic_file_mmap, + .open = dafs_file_open, + .release = dafs_file_release, + .fsync = dafs_fsync, +}; + +static const struct file_operations dafs_dir_fops = { + .llseek = generic_file_llseek, + .iterate = dafs_readdir, + .read = generic_read_dir, +}; + +static int read_file(int fd, unsigned long long *offset, const char *buf, + int len) +{ + int n; + int fserrno; + + n = fscall(DA_OP_PREAD, fd, (int)buf, len, (int)*offset, 0, &fserrno); + + if (n < 0) + return -fserrno; + + return n; +} + +static int write_file(int fd, unsigned long long *offset, const char *buf, + int len) +{ + int n; + int fserrno; + + n = fscall(DA_OP_PWRITE, fd, (int)buf, len, (int)*offset, 0, &fserrno); + + if (n < 0) + return -fserrno; + + return n; +} + +static int dafs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + unsigned long long base; + int count = PAGE_CACHE_SIZE; + int end_index = inode->i_size >> PAGE_CACHE_SHIFT; + int err; + + if (page->index >= end_index) + count = inode->i_size & (PAGE_CACHE_SIZE-1); + + buffer = kmap(page); + base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; + + err = write_file(DAFS_I(inode)->fd, &base, buffer, count); + if (err != count) { + ClearPageUptodate(page); + goto out; + } + + if (base > inode->i_size) + inode->i_size = base; + + if (PageError(page)) + ClearPageError(page); + err = 0; + + out: + kunmap(page); + + unlock_page(page); + return err; +} + +static int dafs_readpage(struct file *file, struct page *page) +{ + char *buffer; + long long start; + int err = 0; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + err = read_file(FILE_DAFS_I(file)->fd, &start, buffer, + PAGE_CACHE_SIZE); + if (err < 0) + goto out; + + memset(&buffer[err], 0, PAGE_CACHE_SIZE - err); + + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) + ClearPageError(page); + err = 0; + out: + kunmap(page); + unlock_page(page); + return err; +} + +static int dafs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + + *pagep = grab_cache_page_write_begin(mapping, index, flags); + if (!*pagep) + return -ENOMEM; + return 0; +} + +static int dafs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + struct inode *inode = mapping->host; + void *buffer; + unsigned from = pos & (PAGE_CACHE_SIZE - 1); + int err; + + buffer = kmap(page); + err = write_file(FILE_DAFS_I(file)->fd, &pos, buffer + from, copied); + kunmap(page); + + if (!PageUptodate(page) && err == PAGE_CACHE_SIZE) + SetPageUptodate(page); + + /* + * If err > 0, write_file has added err to pos, so we are comparing + * i_size against the last byte written. + */ + if (err > 0 && (pos > inode->i_size)) + inode->i_size = pos; + unlock_page(page); + page_cache_release(page); + + return err; +} + +static const struct address_space_operations dafs_aops = { + .writepage = dafs_writepage, + .readpage = dafs_readpage, + .set_page_dirty = __set_page_dirty_nobuffers, + .write_begin = dafs_write_begin, + .write_end = dafs_write_end, +}; + +static int read_name(struct inode *ino, char *name) +{ + dev_t rdev; + struct da_stat st; + int err = stat_file(name, &st, -1); + if (err) + return err; + + /* No valid maj and min from DA.*/ + rdev = MKDEV(0, 0); + + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + ino->i_op = &dafs_dir_iops; + ino->i_fop = &dafs_dir_fops; + break; + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + init_special_inode(ino, st.st_mode & S_IFMT, rdev); + break; + + case S_IFLNK: + default: + ino->i_fop = &dafs_file_fops; + ino->i_mapping->a_ops = &dafs_aops; + } + + ino->i_ino = st.st_ino; + ino->i_mode = st.st_mode; + set_nlink(ino, st.st_nlink); + + i_uid_write(ino, st.st_uid); + i_gid_write(ino, st.st_gid); + ino->i_atime.tv_sec = st.st_atime; + ino->i_atime.tv_nsec = 0; + ino->i_mtime.tv_sec = st.st_mtime; + ino->i_mtime.tv_nsec = 0; + ino->i_ctime.tv_sec = st.st_ctime; + ino->i_ctime.tv_nsec = 0; + ino->i_size = st.st_size; + ino->i_blocks = st.st_blocks; + return 0; +} + +static int dafs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + struct inode *inode; + char *name; + int error, fd; + int damode; + int creat_flags = DA_O_TRUNC | DA_O_CREAT | DA_O_WRONLY; + int cpu; + + inode = dafs_iget(dir->i_sb); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + goto out; + } + + damode = mode & (DA_S_IWUSR | DA_S_IRUSR); + + error = -ENOMEM; + name = dentry_name(dentry); + if (name == NULL) + goto out_put; + + /* + * creat() will only create text mode files on a Windows host + * at present. Replicate the creat() functionality with an + * open() call, which always creates binary files. Set the + * affinity to all hardware threads. + */ + for_each_possible_cpu(cpu) { + u8 hwthread = cpu_2_hwthread_id[cpu]; + creat_flags |= (1 << (DA_O_AFFINITY_SHIFT + hwthread)); + } + + fd = fscall(DA_OP_OPEN, (int)name, creat_flags, damode, strlen(name), + 0, NULL); + if (fd < 0) + error = fd; + else + error = read_name(inode, name); + + kfree(name); + if (error) + goto out_put; + + DAFS_I(inode)->fd = fd; + DAFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; + d_instantiate(dentry, inode); + return 0; + + out_put: + iput(inode); + out: + return error; +} + +static struct dentry *dafs_lookup(struct inode *ino, struct dentry *dentry, + unsigned int flags) +{ + struct inode *inode; + char *name; + int err; + + inode = dafs_iget(ino->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + err = -ENOMEM; + name = dentry_name(dentry); + if (name == NULL) + goto out_put; + + err = read_name(inode, name); + + __putname(name); + if (err == -ENOENT) { + iput(inode); + inode = NULL; + } else if (err) + goto out_put; + + d_add(dentry, inode); + return NULL; + + out_put: + iput(inode); + out: + return ERR_PTR(err); +} + +static int dafs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + char *from_name, *to_name; + int err; + + from_name = dentry_name(from); + if (from_name == NULL) + return -ENOMEM; + to_name = dentry_name(to); + if (to_name == NULL) { + __putname(from_name); + return -ENOMEM; + } + err = -EINVAL; + __putname(from_name); + __putname(to_name); + return err; +} + +static int dafs_unlink(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + int fserrno; + + file = dentry_name(dentry); + if (file == NULL) + return -ENOMEM; + + err = fscall(DA_OP_UNLINK, (int)file, 0, 0, 0, 0, &fserrno); + __putname(file); + if (err) + return -fserrno; + return 0; +} + +static int do_mkdir(const char *file, int mode) +{ + int err; + int fserrno; + + err = fscall(DA_OP_MKDIR, (int)file, mode, strlen(file), 0, 0, + &fserrno); + if (err) + return -fserrno; + return 0; +} + +static int dafs_mkdir(struct inode *ino, struct dentry *dentry, umode_t mode) +{ + char *file; + int err; + + file = dentry_name(dentry); + if (file == NULL) + return -ENOMEM; + err = do_mkdir(file, mode); + __putname(file); + return err; +} + +static int do_rmdir(const char *file) +{ + int err; + int fserrno; + + err = fscall(DA_OP_RMDIR, (int)file, strlen(file), 0, 0, 0, &fserrno); + if (err) + return -fserrno; + return 0; +} + +static int dafs_rmdir(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + file = dentry_name(dentry); + if (file == NULL) + return -ENOMEM; + err = do_rmdir(file); + __putname(file); + return err; +} + +static int dafs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + char *from_name, *to_name; + int err; + + from_name = dentry_name(from); + if (from_name == NULL) + return -ENOMEM; + to_name = dentry_name(to); + if (to_name == NULL) { + __putname(from_name); + return -ENOMEM; + } + err = -EINVAL; + __putname(from_name); + __putname(to_name); + return err; +} + +static const struct inode_operations dafs_dir_iops = { + .create = dafs_create, + .lookup = dafs_lookup, + .link = dafs_link, + .unlink = dafs_unlink, + .mkdir = dafs_mkdir, + .rmdir = dafs_rmdir, + .rename = dafs_rename, +}; + +static char *host_root_path = "."; + +static int dafs_fill_sb_common(struct super_block *sb, void *d, int silent) +{ + struct inode *root_inode; + int err; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = IMGDAFS_SUPER_MAGIC; + sb->s_op = &dafs_sbops; + sb->s_d_op = &simple_dentry_operations; + sb->s_maxbytes = MAX_LFS_FILESIZE; + + err = -ENOMEM; + + root_inode = new_inode(sb); + if (!root_inode) + goto out; + + err = read_name(root_inode, host_root_path); + if (err) + goto out_put; + + err = -ENOMEM; + sb->s_fs_info = host_root_path; + sb->s_root = d_make_root(root_inode); + if (sb->s_root == NULL) + goto out; + + return 0; + +out_put: + iput(root_inode); +out: + return err; +} + +static struct dentry *dafs_read_sb(struct file_system_type *type, + int flags, const char *dev_name, + void *data) +{ + if (!metag_da_enabled()) + return ERR_PTR(-ENODEV); + return mount_nodev(type, flags, data, dafs_fill_sb_common); +} + +static struct file_system_type dafs_type = { + .owner = THIS_MODULE, + .name = "imgdafs", + .mount = dafs_read_sb, + .kill_sb = kill_anon_super, + .fs_flags = 0, +}; +MODULE_ALIAS_FS("imgdafs"); + +static int __init init_dafs(void) +{ + return register_filesystem(&dafs_type); +} + +static void __exit exit_dafs(void) +{ + unregister_filesystem(&dafs_type); +} + +module_init(init_dafs) +module_exit(exit_dafs) +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 77c60311a6c6..2487967f5e96 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -31,6 +31,7 @@ #define PSTOREFS_MAGIC 0x6165676C #define EFIVARFS_MAGIC 0xde5e81e4 #define HOSTFS_SUPER_MAGIC 0x00c0ffee +#define IMGDAFS_SUPER_MAGIC 0xdadadaf5 #define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ #define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-metag" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html