Index: fs/Kconfig =================================================================== RCS file: /cvs/l26/fs/Kconfig,v retrieving revision 1.1.1.2 retrieving revision 1.3 diff -c -p -c -3 -p -r1.1.1.2 -r1.3 *** fs/Kconfig 7 Apr 2004 07:34:50 -0000 1.1.1.2 --- fs/Kconfig 20 May 2004 19:15:50 -0000 1.3 *************** config AFS_FS *** 1614,1619 **** --- 1614,1640 ---- config RXRPC tristate + config ZFS_FS + tristate "ZFS support (Experimental)" + depends on INET && EXPERIMENTAL + help + ZFS is an advanced network file system, similar to NFS in that it + enables you to mount file systems of a remote server and access them + with regular Unix commands as if they were sitting on your hard + disk. ZFS has several advantages over NFS: support for + disconnected operation (e.g. for laptops), persistent client caches + and write back caching. + + If you say Y here, your Linux box will be able to act as a ZFS + *client*. You will need user level daemon as well, both for the + client and server. Servers are currently user level, i.e. they need + no kernel support. + + To compile the ZFS client support as a module, choose M here: the + module will be called zfs. + + If unsure, say N. + endmenu menu "Partition Types" Index: fs/Makefile =================================================================== RCS file: /cvs/l26/fs/Makefile,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -c -p -c -3 -p -r1.1.1.2 -r1.2 *** fs/Makefile 7 Apr 2004 07:34:50 -0000 1.1.1.2 --- fs/Makefile 27 Apr 2004 11:03:07 -0000 1.2 *************** obj-$(CONFIG_JFS_FS) += jfs/ *** 92,94 **** --- 92,95 ---- obj-$(CONFIG_XFS_FS) += xfs/ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_BEFS_FS) += befs/ + obj-$(CONFIG_ZFS_FS) += zfs/ Index: fs/zfs/.cvsignore =================================================================== RCS file: fs/zfs/.cvsignore diff -N fs/zfs/.cvsignore *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/.cvsignore 15 May 2004 14:07:39 -0000 1.1 *************** *** 0 **** --- 1,4 ---- + .cvsignore + *.cmd + zfs.ko + zfs.mod.c Index: fs/zfs/Makefile =================================================================== RCS file: fs/zfs/Makefile diff -N fs/zfs/Makefile *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/Makefile 4 May 2004 13:54:43 -0000 1.3 *************** *** 0 **** --- 1,10 ---- + # + # Makefile for the Linux ZFS filesystem routines. + # + + obj-$(CONFIG_ZFS_FS) += zfs.o + zfs-objs := chardev.o data-coding.o dir.o file.o inode.o super.o zfs_prot.o zfsd_call.o + + # If you want debugging output, please uncomment the following line. + + EXTRA_CFLAGS += -DDEBUG Index: fs/zfs/chardev.c =================================================================== RCS file: fs/zfs/chardev.c diff -N fs/zfs/chardev.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/chardev.c 28 May 2004 09:58:51 -0000 1.13 *************** *** 0 **** --- 1,296 ---- + /* + Chardev operations - communication channel between this kernel + module and ZFSd. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + #include + #include + #include + #include + #include + #include + #include + + #include "zfs.h" + #include "data-coding.h" + #include "zfs_prot.h" + + + static ssize_t zfs_chardev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *off) + { + struct request *req; + + TRACE("%u: reading %u bytes (going to sleep if no data avaible)", current->pid, nbytes); + + NEXT_REQUEST: + /* Wait for a request. */ + down_interruptible(&channel.req_pending_count); + + if (signal_pending(current)) { + TRACE("%u: interrupt", current->pid); + return -EINTR; + } + if (!channel.connected) { + TRACE("%u: zfsd closed communication device", current->pid); + return -EIO; + } + + /* Remove the first request from the queue of pending requests if copy_to_user() succeeds. */ + down(&channel.req_pending_lock); + + req = list_entry(channel.req_pending.next, struct request, item); + if (down_trylock(&req->lock)) { + /* ZFS thread (running in send_request()) is being interrupted. */ + up(&channel.req_pending_lock); + up(&channel.req_pending_count); + goto NEXT_REQUEST; + } + + if (req->length > nbytes) { + WARN("%u: reading only %u bytes of %u in message", current->pid, nbytes, req->length); + req->length = nbytes; + } else + nbytes = req->length; + + if (copy_to_user(buf, req->dc->buffer, nbytes)) { + up(&channel.req_pending_lock); + up(&channel.req_pending_count); + return -EFAULT; + } + + list_del(&req->item); + + up(&channel.req_pending_lock); + + /* Put the data coding buffer (DC), we do not need it any more. */ + dc_put(req->dc); + req->dc = NULL; + + /* Add the request to the queue of processing requests. We need this to be able to compare id of the request with id of the reply later. */ + down(&channel.req_processing_lock); + list_add_tail(&req->item, &channel.req_processing[INDEX(req->id)]); + up(&channel.req_processing_lock); + + req->state = REQ_PROCESSING; + + up(&req->lock); + + TRACE("%u: %u bytes read", current->pid, nbytes); + + return nbytes; + } + + extern struct inode *zfs_ilookup(struct super_block *sb, zfs_fh *fh); + + static ssize_t zfs_chardev_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *off) + { + struct list_head *item; + struct request *req; + DC *dc; + direction dir; + unsigned int id; + int error = 0; + + TRACE("%u: writting %u bytes", current->pid, nbytes); + + if (nbytes > DC_SIZE) { + WARN("%u: zfsd has written %u bytes but max. %u is allowed in message", current->pid, nbytes, DC_SIZE); + return -EINVAL; + } + + /* Get a new DC for the reply. */ + dc = dc_get(); + if (!dc) + return -ENOMEM; + + if (copy_from_user(dc->buffer, buf, nbytes)) + error = -EFAULT; + else if (!start_decoding(dc) + || !decode_direction(dc, &dir) + || !decode_request_id(dc, &id)) + error = -EINVAL; + else + switch (dir) { + case DIR_REQUEST: + /* TODO: ZFSd wants something, we must send a reply with the same id. */ + break; + case DIR_REPLY: + /* Find the request the reply belongs to. */ + down(&channel.req_processing_lock); + list_for_each(item, &channel.req_processing[INDEX(id)]) { + req = list_entry(item, struct request, item); + if (down_trylock(&req->lock)) + continue; + if (id == req->id) { + TRACE("%u: request corresponding to reply id %u found", current->pid, id); + + /* Remove the request from the queue of processing requests. */ + list_del(item); + up(&channel.req_processing_lock); + + req->state = REQ_DEQUEUED; + + /* Store the DC of the reply. */ + req->dc = dc; + + /* Wake up the thread which is waiting for the reply. */ + wake_up(&req->waitq); + + up(&req->lock); + + return nbytes; + } + up(&req->lock); + } + up(&channel.req_processing_lock); + + WARN("%u: no request corresponding to reply id %u found", current->pid, id); + + break; + case DIR_ONEWAY: + { + uint32_t fn; + + if (!decode_function(dc, &fn)) + error = -EINVAL; + else + switch (fn) { + case ZFS_PROC_INVALIDATE: + if (!zfs_sb) + error = -EIO; + else { + invalidate_args args; + + if (!decode_invalidate_args(dc, &args) + || !finish_decoding(dc)) + error = -EPROTO; + else { + struct inode *inode; + + TRACE("%u: invalidate [sid: %u, vid: %u, dev: %u, ino: %u, gen: %u]", current->pid, args.fh.sid, args.fh.vid, args.fh.dev, args.fh.ino, args.fh.gen); + + inode = zfs_ilookup(zfs_sb, &args.fh); + if (inode) { + TRACE("%u: %p invalidated", current->pid, inode); + make_bad_inode(inode); + } else + TRACE("%u: no inode invalidated", current->pid); + } + } + break; + default: + error = -EINVAL; + break; + } + } + break; + default: + break; + } + + dc_put(dc); + + if (error) + TRACE("%u: %d", current->pid, error); + else + TRACE("%u: %u bytes written", current->pid, nbytes); + + return error ? error : nbytes; + } + + static int zfs_chardev_open(struct inode *inode, struct file *file) + { + int i; + + TRACE("%u", current->pid); + + down(&channel.lock); + + if (channel.connected) { + up(&channel.lock); + return -EBUSY; + } + + init_MUTEX(&channel.request_id_lock); + channel.request_id = 0; + + init_MUTEX_LOCKED(&channel.req_pending_count); + + init_MUTEX(&channel.req_pending_lock); + INIT_LIST_HEAD(&channel.req_pending); + + init_MUTEX(&channel.req_processing_lock); + for (i = 0; i < REQ_PROCESSING_TABSIZE; i++) + INIT_LIST_HEAD(&channel.req_processing[i]); + + channel.connected = 1; + + up(&channel.lock); + + return 0; + } + + static int zfs_chardev_release(struct inode *inode, struct file *file) + { + struct list_head *item; + struct request *req; + int i; + + TRACE("%u", current->pid); + + down(&channel.lock); + + channel.connected = 0; + + wake_up_all(&channel.req_pending_count.wait); + /* No up(&req_pending_count) is neccessary - the samaphore will be initialized in the next call of zfs_chardev_open(). */ + + down(&channel.req_pending_lock); + list_for_each(item, &channel.req_pending) { + req = list_entry(item, struct request, item); + wake_up(&req->waitq); + } + up(&channel.req_pending_lock); + + down(&channel.req_processing_lock); + for (i = 0; i < REQ_PROCESSING_TABSIZE; i++) + list_for_each(item, &channel.req_processing[i]) { + req = list_entry(item, struct request, item); + wake_up(&req->waitq); + } + up(&channel.req_processing_lock); + + up(&channel.lock); + + /* Free all allocated DCs. */ + dc_destroy_all(); + + return 0; + } + + struct file_operations zfs_chardev_file_operations = { + .owner = THIS_MODULE, + .read = zfs_chardev_read, + .write = zfs_chardev_write, + .open = zfs_chardev_open, + .release = zfs_chardev_release, + }; Index: fs/zfs/dir.c =================================================================== RCS file: fs/zfs/dir.c diff -N fs/zfs/dir.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/dir.c 22 May 2004 11:22:33 -0000 1.8 *************** *** 0 **** --- 1,61 ---- + /* + Directory operations. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + + #include "zfs.h" + #include "zfs_prot.h" + #include "zfsd_call.h" + + + static int zfs_readdir(struct file *file, void *dirent, filldir_t filldir) + { + struct inode *inode = file->f_dentry->d_inode; + read_dir_args args; + int error; + + TRACE("'%s'", file->f_dentry->d_name.name); + + if (file->f_pos == -1) + return 0; + + args.cap = *CAP(file->private_data); + args.cookie = file->f_pos ? *COOKIE(file->private_data) : 0; + args.count = ZFS_MAXDATA; + + error = zfsd_readdir(&args, file, dirent, filldir); + if ((error == -ESTALE) && !IS_ROOT_INODE(inode)) + make_bad_inode(inode); + + return error; + } + + extern int zfs_open(struct inode *inode, struct file *file); + extern int zfs_release(struct inode *inode, struct file *file); + + struct file_operations zfs_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = zfs_readdir, + .open = zfs_open, + .release = zfs_release, + }; Index: fs/zfs/file.c =================================================================== RCS file: fs/zfs/file.c diff -N fs/zfs/file.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/file.c 22 May 2004 11:22:33 -0000 1.11 *************** *** 0 **** --- 1,180 ---- + /* + File operations. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + #include + #include + #include + #include + #include + + #include "zfs.h" + #include "zfs_prot.h" + #include "zfsd_call.h" + + + static ssize_t zfs_read(struct file *file, char __user *buf, size_t nbytes, loff_t *off) + { + struct inode *inode = file->f_dentry->d_inode; + read_args args; + int error; + + TRACE("'%s': %lld", file->f_dentry->d_name.name, *off); + + args.cap = *CAP(file->private_data); + args.offset = *off; + args.count = (nbytes > ZFS_MAXDATA) ? ZFS_MAXDATA : nbytes; + + error = zfsd_read(buf, &args); + if (error > 0) { + *off += error; + inode->i_atime = CURRENT_TIME; + } else if (error == -ESTALE) + make_bad_inode(inode); + + return error; + } + + static ssize_t zfs_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *off) + { + struct inode *inode = file->f_dentry->d_inode; + write_args args; + int error; + + TRACE("'%s': %lld", file->f_dentry->d_name.name, *off); + + args.cap = *CAP(file->private_data); + args.offset = *off; + args.data.len = (nbytes > ZFS_MAXDATA) ? ZFS_MAXDATA : nbytes; + args.data.buf = buf; + + error = zfsd_write(&args); + if (error > 0) { + *off += error; + inode->i_mtime = CURRENT_TIME; + if (*off > inode->i_size) { + inode->i_size = *off; + inode->i_ctime = CURRENT_TIME; + } + } else if (error == -ESTALE) + make_bad_inode(inode); + + return error; + } + + int zfs_open(struct inode *inode, struct file *file) + { + struct dentry *dentry = file->f_dentry; + zfs_cap *cap; + open_args args; + int error; + + TRACE("'%s'", dentry->d_name.name); + + if ((file->f_flags & O_CREAT) && dentry->d_fsdata) { + /* We already have CAP for the file (zfs_create() has found it out). */ + file->private_data = dentry->d_fsdata; + dentry->d_fsdata = NULL; + } else { + cap = kmalloc(sizeof(zfs_cap) + (S_ISDIR(inode->i_mode) ? sizeof(int32_t) : 0), GFP_KERNEL); + if (!cap) + return -ENOMEM; + + args.file = ZFS_I(inode)->fh; + args.flags = file->f_flags; + + error = zfsd_open(cap, &args); + if (error) { + kfree(cap); + if ((error == -ESTALE) && !IS_ROOT_INODE(inode)) + make_bad_inode(inode); + return error; + } + + file->private_data = cap; + } + + return 0; + } + + int zfs_release(struct inode *inode, struct file *file) + { + int error; + + TRACE("'%s'", file->f_dentry->d_name.name); + + error = zfsd_close(CAP(file->private_data)); + + kfree(file->private_data); + + return error; + } + + static int zfs_readpage(struct file *file, struct page *page) + { + char *kaddr; + read_args args; + int error = 0; + + TRACE("'%s': %lu", file->f_dentry->d_name.name, page->index); + + if (PageUptodate(page)) + goto out; + + args.cap = *CAP(file->private_data); + args.offset = page->index << PAGE_CACHE_SHIFT; + args.count = PAGE_CACHE_SIZE; + + kaddr = kmap(page); + + error = zfsd_readpage(kaddr, &args); + if (error > 0) { + if (error < PAGE_CACHE_SIZE) + /* Zero the rest of the page. */ + memset(kaddr + error, 0, PAGE_CACHE_SIZE - error); + + SetPageUptodate(page); + + error = 0; + } else if (error == -ESTALE) + make_bad_inode(file->f_dentry->d_inode); + + kunmap(kaddr); + + out: + unlock_page(page); + + return error; + } + + struct file_operations zfs_file_operations = { + .llseek = generic_file_llseek, + .read = zfs_read, + .write = zfs_write, + .mmap = generic_file_readonly_mmap, + .open = zfs_open, + .release = zfs_release, + }; + + struct address_space_operations zfs_file_address_space_operations = { + .readpage = zfs_readpage, + }; Index: fs/zfs/inode.c =================================================================== RCS file: fs/zfs/inode.c diff -N fs/zfs/inode.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/inode.c 27 May 2004 19:30:36 -0000 1.19 *************** *** 0 **** --- 1,601 ---- + /* + Inode operations. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + #include + #include + #include + #include + #include + #include + + #include "zfs.h" + #include "zfs_prot.h" + #include "zfsd_call.h" + + + static void zfs_iattr_to_sattr(sattr *attr, struct iattr *iattr) + { + unsigned int valid = iattr->ia_valid; + + attr->mode = (valid & ATTR_MODE) ? (iattr->ia_mode & S_IALLUGO) : -1; + attr->uid = (valid & ATTR_UID) ? iattr->ia_uid : -1; + attr->gid = (valid & ATTR_GID) ? iattr->ia_gid : -1; + attr->size = (valid & ATTR_SIZE) ? iattr->ia_size : -1; + attr->atime = (valid & ATTR_ATIME) ? iattr->ia_atime.tv_sec : -1; + attr->mtime = (valid & ATTR_MTIME) ? iattr->ia_mtime.tv_sec : -1; + } + + static void zfs_attr_to_iattr(struct inode *inode, fattr *attr) + { + inode->i_ino = attr->ino; + inode->i_version = attr->version; + inode->i_mode = ftype2mode[attr->type] | attr->mode; + inode->i_nlink = attr->nlink; + inode->i_uid = attr->uid; + inode->i_gid = attr->gid; + inode->i_rdev = attr->rdev; + inode->i_size = attr->size; + inode->i_blocks = attr->blocks; + inode->i_blksize = attr->blksize; + inode->i_atime.tv_sec = attr->atime; + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = attr->mtime; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = attr->ctime; + inode->i_ctime.tv_nsec = 0; + } + + static ftype zfs_mode_to_ftype(int mode) + { + switch (mode & S_IFMT) { + case S_IFSOCK: + return FT_SOCK; + case S_IFLNK: + return FT_LNK; + case S_IFREG: + return FT_REG; + case S_IFBLK: + return FT_BLK; + case S_IFDIR: + return FT_DIR; + case S_IFCHR: + return FT_CHR; + case S_IFIFO: + return FT_FIFO; + default: + return FT_BAD; + } + } + + static struct inode_operations zfs_dir_inode_operations, zfs_file_inode_operations, zfs_symlink_inode_operations; + extern struct file_operations zfs_dir_operations, zfs_file_operations; + extern struct address_space_operations zfs_file_address_space_operations; + + static void zfs_fill_inode(struct inode *inode, fattr *attr) + { + zfs_attr_to_iattr(inode, attr); + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op = &zfs_file_inode_operations; + inode->i_fop = &zfs_file_operations; + inode->i_data.a_ops = &zfs_file_address_space_operations; + break; + case S_IFDIR: + inode->i_op = &zfs_dir_inode_operations; + inode->i_fop = &zfs_dir_operations; + break; + case S_IFLNK: + inode->i_op = &zfs_symlink_inode_operations; + break; + default: + init_special_inode(inode, inode->i_mode, huge_decode_dev(inode->i_rdev)); + break; + } + } + + static int zfs_test_inode(struct inode *inode, void *data) + { + return !memcmp(&ZFS_I(inode)->fh, data, sizeof(zfs_fh)); + } + + static int zfs_set_inode(struct inode *inode, void *data) + { + ZFS_I(inode)->fh = *(zfs_fh *)data; + + return 0; + } + + struct inode *zfs_ilookup(struct super_block *sb, zfs_fh *fh) + { + return ilookup5(sb, HASH(fh), zfs_test_inode, fh); + } + + struct inode *zfs_iget(struct super_block *sb, zfs_fh *fh, fattr *attr) + { + struct inode *inode; + + TRACE("%u", fh->ino); + + inode = iget5_locked(sb, HASH(fh), zfs_test_inode, zfs_set_inode, fh); + + if (inode && (inode->i_state & I_NEW)) { + zfs_fill_inode(inode, attr); + unlock_new_inode(inode); + } + + return inode; + } + + static int zfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) + { + struct inode *inode = dentry->d_inode; + fattr attr; + + TRACE("'%s'", dentry->d_name.name); + + if (!inode) + return 1; + + if (is_bad_inode(inode)) + return 0; + + if (time_after(jiffies, dentry->d_time + ZFS_DENTRY_MAXAGE * HZ)) { + /* The dentry is too old, so revalidate it. */ + if (zfsd_getattr(&attr, &ZFS_I(inode)->fh) + || (attr.version != inode->i_version)) { + make_bad_inode(dentry->d_inode); + return 0; + } + + zfs_attr_to_iattr(inode, &attr); + dentry->d_time = jiffies; + } + + return 1; + } + + struct dentry_operations zfs_dentry_operations = { + .d_revalidate = zfs_d_revalidate, + }; + + static int zfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) + { + create_args args; + create_res res; + struct inode *inode; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.where.dir = ZFS_I(dir)->fh; + args.where.name.str = (char *)dentry->d_name.name; + args.where.name.len = dentry->d_name.len; + args.flags = nd->intent.open.flags; + if (args.flags & O_ACCMODE) + args.flags--; + args.attr.mode = mode & S_IALLUGO; + args.attr.uid = current->fsuid; + if (dir->i_mode & S_ISGID) + args.attr.gid = dir->i_gid; + else + args.attr.gid = current->fsgid; + args.attr.size = -1; + args.attr.atime = -1; + args.attr.mtime = -1; + + error = zfsd_create(&res, &args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode = zfs_iget(dir->i_sb, &res.file, &res.attr); + if (!inode) + return -ENOMEM; + + dentry->d_fsdata = kmalloc(sizeof(zfs_cap), GFP_KERNEL); + if (!dentry->d_fsdata) + return -ENOMEM; + *(zfs_cap *)dentry->d_fsdata = res.cap; + + d_instantiate(dentry, inode); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static struct dentry *zfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) + { + dir_op_args args; + dir_op_res res; + struct inode *inode; + int error; + + TRACE("'%s'", dentry->d_name.name); + + if (dentry->d_name.len > ZFS_MAXNAMELEN) + return ERR_PTR(-ENAMETOOLONG); + + args.dir = ZFS_I(dir)->fh; + args.name.str = (char *)dentry->d_name.name; + args.name.len = dentry->d_name.len; + + error = zfsd_lookup(&res, &args); + if (!error) { + inode = zfs_iget(dir->i_sb, &res.file, &res.attr); + if (!inode) + return ERR_PTR(-ENOMEM); + } else if (error != -ENOENT) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return ERR_PTR(error); + } else + inode = NULL; + + dentry->d_time = jiffies; + dentry->d_op = &zfs_dentry_operations; + + d_add(dentry, inode); + + return NULL; + } + + static int zfs_link(struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) + { + struct inode *inode = src_dentry->d_inode; + link_args args; + int error; + + TRACE("'%s' -> '%s'", dentry->d_name.name, src_dentry->d_name.name); + + args.from = ZFS_I(inode)->fh; + args.to.dir = ZFS_I(dir)->fh; + args.to.name.str = (char *)dentry->d_name.name; + args.to.name.len = dentry->d_name.len; + + error = zfsd_link(&args); + if (error) { + if (error == -ESTALE) { + /* We do not know which one (dir or inode) is bad, so invalidate both. */ + if (!IS_ROOT_INODE(dir)) + make_bad_inode(dir); + if (!IS_ROOT_INODE(inode)) + make_bad_inode(inode); + } + return error; + } + + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + + atomic_inc(&inode->i_count); + d_instantiate(dentry, inode); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_unlink(struct inode *dir, struct dentry *dentry) + { + struct inode *inode = dentry->d_inode; + dir_op_args args; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.dir = ZFS_I(dir)->fh; + args.name.str = (char *)dentry->d_name.name; + args.name.len = dentry->d_name.len; + + error = zfsd_unlink(&args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode->i_nlink--; + inode->i_ctime = CURRENT_TIME; + + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_symlink(struct inode *dir, struct dentry *dentry, const char *old_name) + { + symlink_args args; + dir_op_res res; + struct inode *inode; + int error; + + TRACE("'%s' -> '%s'", dentry->d_name.name, old_name); + + if (strlen(old_name) > ZFS_MAXPATHLEN) + return -ENAMETOOLONG; + + args.from.dir = ZFS_I(dir)->fh; + args.from.name.str = (char *)dentry->d_name.name; + args.from.name.len = dentry->d_name.len; + args.to.str = (char *)old_name; + args.to.len = strlen(old_name); + args.attr.mode = -1; + args.attr.uid = current->fsuid; + if (dir->i_mode & S_ISGID) + args.attr.gid = dir->i_gid; + else + args.attr.gid = current->fsgid; + args.attr.size = -1; + args.attr.atime = -1; + args.attr.mtime = -1; + + error = zfsd_symlink(&res, &args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode = zfs_iget(dir->i_sb, &res.file, &res.attr); + if (!inode) + return -ENOMEM; + + d_instantiate(dentry, inode); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) + { + mkdir_args args; + dir_op_res res; + struct inode *inode; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.where.dir = ZFS_I(dir)->fh; + args.where.name.str = (char *)dentry->d_name.name; + args.where.name.len = dentry->d_name.len; + args.attr.mode = mode & (S_IRWXUGO | S_ISVTX); + args.attr.uid = current->fsuid; + if (dir->i_mode & S_ISGID) { + args.attr.gid = dir->i_gid; + args.attr.mode |= S_ISGID; + } else + args.attr.gid = current->fsgid; + args.attr.size = -1; + args.attr.atime = -1; + args.attr.mtime = -1; + + error = zfsd_mkdir(&res, &args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode = zfs_iget(dir->i_sb, &res.file, &res.attr); + if (!inode) + return -ENOMEM; + + d_instantiate(dentry, inode); + + dir->i_nlink++; + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_rmdir(struct inode *dir, struct dentry *dentry) + { + struct inode *inode = dentry->d_inode; + dir_op_args args; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.dir = ZFS_I(dir)->fh; + args.name.str = (char *)dentry->d_name.name; + args.name.len = dentry->d_name.len; + + error = zfsd_rmdir(&args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode->i_nlink--; + + dir->i_nlink--; + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) + { + mknod_args args; + dir_op_res res; + struct inode *inode; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.where.dir = ZFS_I(dir)->fh; + args.where.name.str = (char *)dentry->d_name.name; + args.where.name.len = dentry->d_name.len; + args.attr.mode = mode & S_IALLUGO; + args.attr.uid = current->fsuid; + if (dir->i_mode & S_ISGID) + args.attr.gid = dir->i_gid; + else + args.attr.gid = current->fsgid; + args.attr.size = -1; + args.attr.atime = -1; + args.attr.mtime = -1; + args.type = zfs_mode_to_ftype(mode); + args.rdev = huge_encode_dev(rdev); + + error = zfsd_mknod(&res, &args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(dir)) + make_bad_inode(dir); + return error; + } + + inode = zfs_iget(dir->i_sb, &res.file, &res.attr); + if (!inode) + return -ENOMEM; + + d_instantiate(dentry, inode); + + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) + { + struct inode *old_inode = old_dentry->d_inode; + rename_args args; + int error; + + TRACE("'%s' -> '%s'", old_dentry->d_name.name, new_dentry->d_name.name); + + args.from.dir = ZFS_I(old_dir)->fh; + args.from.name.str = (char *)old_dentry->d_name.name; + args.from.name.len = old_dentry->d_name.len; + args.to.dir = ZFS_I(new_dir)->fh; + args.to.name.str = (char *)new_dentry->d_name.name; + args.to.name.len = new_dentry->d_name.len; + + error = zfsd_rename(&args); + if (error) { + if (error == -ESTALE) { + if (!IS_ROOT_INODE(old_dir)) + make_bad_inode(old_dir); + if (!IS_ROOT_INODE(new_dir)) + make_bad_inode(new_dir); + } + return error; + } + + if (S_ISDIR(old_inode->i_mode)) { + old_dir->i_nlink--; + new_dir->i_nlink++; + } + old_dir->i_mtime = old_dir->i_ctime = CURRENT_TIME; + new_dir->i_mtime = new_dir->i_ctime = CURRENT_TIME; + + return 0; + } + + static int zfs_setattr(struct dentry *dentry, struct iattr *iattr) + { + struct inode *inode = dentry->d_inode; + fattr attr; + sattr_args args; + int error; + + TRACE("'%s'", dentry->d_name.name); + + args.file = ZFS_I(inode)->fh; + zfs_iattr_to_sattr(&args.attr, iattr); + + error = zfsd_setattr(&attr, &args); + if (error) { + if ((error == -ESTALE) && !IS_ROOT_INODE(inode)) + make_bad_inode(inode); + return error; + } + + zfs_attr_to_iattr(inode, &attr); + + return 0; + } + + static int zfs_readlink(struct dentry *dentry, char __user *buf, int buflen) + { + struct inode *inode = dentry->d_inode; + read_link_res res; + int error; + + TRACE("'%s'", dentry->d_name.name); + + error = zfsd_readlink(&res, &ZFS_I(inode)->fh); + if (error) { + if (error == -ESTALE) + make_bad_inode(inode); + return error; + } + + return vfs_readlink(dentry, buf, buflen, res.path.str); + } + + static int zfs_follow_link(struct dentry *dentry, struct nameidata *nd) + { + struct inode *inode = dentry->d_inode; + read_link_res res; + int error; + + TRACE("'%s'", dentry->d_name.name); + + error = zfsd_readlink(&res, &ZFS_I(inode)->fh); + if (error) { + if (error == -ESTALE) + make_bad_inode(inode); + return error; + } + + return vfs_follow_link(nd, res.path.str); + } + + static struct inode_operations zfs_dir_inode_operations = { + .create = zfs_create, + .lookup = zfs_lookup, + .link = zfs_link, + .unlink = zfs_unlink, + .symlink = zfs_symlink, + .mkdir = zfs_mkdir, + .rmdir = zfs_rmdir, + .mknod = zfs_mknod, + .rename = zfs_rename, + .setattr = zfs_setattr, + }; + + static struct inode_operations zfs_file_inode_operations = { + .setattr = zfs_setattr, + }; + + static struct inode_operations zfs_symlink_inode_operations = { + .readlink = zfs_readlink, + .follow_link = zfs_follow_link, + .setattr = zfs_setattr, + }; Index: fs/zfs/super.c =================================================================== RCS file: fs/zfs/super.c diff -N fs/zfs/super.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/super.c 27 May 2004 19:30:36 -0000 1.11 *************** *** 0 **** --- 1,220 ---- + /* + Superblock operations. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + #include + #include + #include + #include + #include + #include + #include + + #include "zfs.h" + #include "zfs_prot.h" + #include "zfsd_call.h" + + + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV_MAJOR(ZFS_CHARDEV_MAJOR); + + struct super_block *zfs_sb; + struct channel channel; + + extern struct file_operations zfs_chardev_file_operations; + + static kmem_cache_t *zfs_inode_cachep; + + static struct inode *zfs_alloc_inode(struct super_block *sb) + { + struct zfs_inode_info *ei; + + ei = kmem_cache_alloc(zfs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + + TRACE("%p", &ei->vfs_inode); + + return &ei->vfs_inode; + } + + static void zfs_destroy_inode(struct inode *inode) + { + TRACE("%p", inode); + + kmem_cache_free(zfs_inode_cachep, ZFS_I(inode)); + } + + static void zfs_init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) + { + struct zfs_inode_info *ei = (struct zfs_inode_info *)foo; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); + } + + static int zfs_init_inodecache(void) + { + zfs_inode_cachep = kmem_cache_create("zfs_inode_cache", + sizeof(struct zfs_inode_info), + 0, + SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT, + zfs_init_once, + NULL); + if (!zfs_inode_cachep) + return -ENOMEM; + + return 0; + } + + static void zfs_destroy_inodecache(void) + { + if (kmem_cache_destroy(zfs_inode_cachep)) + INFO("zfs_inode_cache: not all structures were freed"); + } + + static void zfs_put_super(struct super_block *sb) + { + INFO("UMOUNT"); + + zfs_sb = NULL; + } + + static int zfs_statfs(struct super_block *sb, struct kstatfs *buf) + { + buf->f_type = ZFS_SUPER_MAGIC; + buf->f_bsize = ZFS_MAXDATA; + buf->f_namelen = ZFS_MAXNAMELEN; + + return 0; + } + + static struct super_operations zfs_super_operations = { + .alloc_inode = zfs_alloc_inode, + .destroy_inode = zfs_destroy_inode, + .put_super = zfs_put_super, + .statfs = zfs_statfs, + }; + + extern struct inode *zfs_iget(struct super_block *sb, zfs_fh *fh, fattr *attr); + + static int zfs_fill_super(struct super_block *sb, void *data, int silent) + { + zfs_fh root_fh; + fattr root_attr; + struct inode *root_inode; + int error; + + INFO("MOUNT"); + + if (!channel.connected) { + ERROR("zfsd has not opened communication device!"); + return -EIO; + } + + sb->s_op = &zfs_super_operations; + sb->s_magic = ZFS_SUPER_MAGIC; + + /* Get root file handle. */ + error = zfsd_root(&root_fh); + if (error) + return error; + + /* Get root inode attributes. */ + error = zfsd_getattr(&root_attr, &root_fh); + if (error) + return error; + + /* Make root inode. */ + root_inode = zfs_iget(sb, &root_fh, &root_attr); + if (!root_inode) + return -ENOMEM; + + /* Create root dentry. */ + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + return -ENOMEM; + + zfs_sb = sb; + + return 0; + } + + static struct super_block *zfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) + { + return get_sb_single(fs_type, flags, data, zfs_fill_super); + } + + static struct file_system_type zfs_type = { + .owner = THIS_MODULE, + .name = "zfs", + .get_sb = zfs_get_sb, + .kill_sb = kill_anon_super, + .fs_flags = 0, + }; + + static int __init zfs_init(void) + { + int error; + + INFO("INIT"); + + /* Register communication device. */ + error = register_chrdev(ZFS_CHARDEV_MAJOR, "zfs", &zfs_chardev_file_operations); + if (error) { + ERROR("unable to register chardev major %d!", ZFS_CHARDEV_MAJOR); + return error; + } + + /* Initialize inode cache. */ + error = zfs_init_inodecache(); + if (error) { + unregister_chrdev(ZFS_CHARDEV_MAJOR, "zfs"); + ERROR("unable to create zfs inode cache!"); + return error; + } + + /* Register ZFS file system. */ + error = register_filesystem(&zfs_type); + if (error) { + zfs_destroy_inodecache(); + unregister_chrdev(ZFS_CHARDEV_MAJOR, "zfs"); + ERROR("unable to register filesystem!"); + return error; + } + + init_MUTEX(&channel.lock); + + return 0; + } + + static void __exit zfs_exit(void) + { + INFO("EXIT"); + + unregister_filesystem(&zfs_type); + zfs_destroy_inodecache(); + unregister_chrdev(ZFS_CHARDEV_MAJOR, "zfs"); + } + + module_init(zfs_init); + module_exit(zfs_exit); Index: fs/zfs/zfs.h =================================================================== RCS file: fs/zfs/zfs.h diff -N fs/zfs/zfs.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/zfs.h 27 May 2004 19:30:36 -0000 1.13 *************** *** 0 **** --- 1,119 ---- + /* + Module definitions. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #ifndef _ZFS_H + #define _ZFS_H + + #include + #include + #include + #include + #include + + #include "constant.h" + #include "data-coding.h" + #include "zfs_prot.h" + + + #define ERROR(format, ...) printk(KERN_ERR "zfs: " format "\n", ##__VA_ARGS__) + #define WARN(format, ...) printk(KERN_WARNING "zfs: " format "\n", ##__VA_ARGS__) + #define INFO(format, ...) printk(KERN_INFO "zfs: " format "\n", ##__VA_ARGS__) + + #ifdef DEBUG + # define TRACE(format, ...) printk(KERN_INFO "zfs: %s(): " format "\n", __func__, ##__VA_ARGS__) + #else + # define TRACE(...) + #endif + + #define ZFS_SUPER_MAGIC *((uint32_t *)"zfs") + #define ZFS_CHARDEV_MAJOR 251 + + /* Timeout in seconds for request. */ + #define ZFS_TIMEOUT (REQUEST_TIMEOUT + 5) + + /* Maximum age of dentry in seconds after that revalidation is requered. */ + #define ZFS_DENTRY_MAXAGE 10 + + /* Is INODE root inode? */ + #define IS_ROOT_INODE(inode) (inode == inode->i_sb->s_root->d_inode) + + #define CAP(p) ((zfs_cap *)p) + #define COOKIE(p) ((int32_t *)&((zfs_cap *)p)[1]) + + /* Hash function which returns (not neccessary unique) inode number. */ + #define ROTATE_LEFT(x, nbites) ((x << nbites) | (x >> (32 - nbites))) + #define HASH(fh) (ROTATE_LEFT(fh->sid, 22) ^ ROTATE_LEFT(fh->dev, 12) ^ fh->ino) + + /* ZFS super block. */ + extern struct super_block *zfs_sb; + + /* ZFS inode. */ + #define ZFS_I(inode) ((struct zfs_inode_info *)inode) + struct zfs_inode_info { + struct inode vfs_inode; + zfs_fh fh; + }; + + /* Size of hash table of processing requests. */ + #define REQ_PROCESSING_TABSIZE 32 + + /* Hash function which returns index of queue in the hash table. */ + #define INDEX(key) (key % REQ_PROCESSING_TABSIZE) + + /* Communication channel between this kernel module and ZFSd. */ + extern struct channel { + struct semaphore lock; + volatile int connected; + + struct semaphore request_id_lock; + uint32_t request_id; + + struct semaphore req_pending_count; + /* count of requests in the req_pending queue */ + struct semaphore req_pending_lock; + struct list_head req_pending; + /* queue of requests which have been prepared + but not sent to zfsd yet */ + + struct semaphore req_processing_lock; + struct list_head req_processing[REQ_PROCESSING_TABSIZE]; + /* hashtable of requests which have been sent to ZFSd + but corresponding reply has not been received yet */ + } channel; + + enum request_state {REQ_PENDING, REQ_PROCESSING, REQ_DEQUEUED}; + + /* Request to ZFSd. */ + struct request { + struct semaphore lock; + enum request_state state; + unsigned int id; /* unique request id */ + DC *dc; /* the message */ + unsigned int length; /* length of request body (dc.buffer) */ + struct list_head item; /* item in req_pending + or req_processing[] list */ + wait_queue_head_t waitq; + /* wait queue of kernel threads (actualy only current thread) + which have prepared the request but not received the reply */ + }; + + #endif Index: fs/zfs/zfsd_call.c =================================================================== RCS file: fs/zfs/zfsd_call.c diff -N fs/zfs/zfsd_call.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/zfsd_call.c 22 May 2004 12:54:55 -0000 1.14 *************** *** 0 **** --- 1,639 ---- + /* + Functions to call ZFSd. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "zfs.h" + #include "data-coding.h" + #include "zfs_prot.h" + + + /* + Send the request and wait for the reply. + */ + int send_request(struct request *req) { + DECLARE_WAITQUEUE(wait, current); + long timeout_left; + + TRACE("%u", req->id); + + if (!channel.connected) { + TRACE("%u: zfsd closed communication device", req->id); + return -EIO; + } + + init_MUTEX(&req->lock); + init_waitqueue_head(&req->waitq); + + TRACE("%u: sending %u bytes", req->id, req->length); + + /* Add the request to the queue of pending requests. */ + down(&channel.req_pending_lock); + list_add_tail(&req->item, &channel.req_pending); + up(&channel.req_pending_lock); + + req->state = REQ_PENDING; + + TRACE("%u: waiting for reply", req->id); + + add_wait_queue(&req->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* Wake up a thread waiting for a request. */ + up(&channel.req_pending_count); + + /* FIXME: If pre-emptible kernel reschedule right now, this thread will be sleeping probably without timeout. + Does exist some kernel lock, or something like down_interruptible_timeout()? */ + + timeout_left = schedule_timeout(ZFS_TIMEOUT * HZ); + remove_wait_queue(&req->waitq, &wait); + + down(&req->lock); + + /* If some error (interrupt or timeout) occurs, remove the request from appropriate queue. */ + switch (req->state) { + case REQ_PENDING: + down(&channel.req_pending_count); + down(&channel.req_pending_lock); + list_del(&req->item); + up(&channel.req_pending_lock); + break; + case REQ_PROCESSING: + down(&channel.req_processing_lock); + list_del(&req->item); + up(&channel.req_processing_lock); + break; + default: + break; + } + + if (signal_pending(current)) { + TRACE("%u: interrupt", req->id); + return -EINTR; + } + if (!timeout_left) { + TRACE("%u: timeout", req->id); + return -ESTALE; + } + if (!channel.connected) { + TRACE("%u: zfsd closed communication device", req->id); + return -EIO; + } + + TRACE("%u: receiving corresponding reply", req->id); + + /* No up(&req->lock) is neccessary - no critical section follows and the request will be destroyed soon. */ + + return 0; + } + + int zfsd_root(zfs_fh *fh) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_root_zfsd(&dc, NULL); + if (!error + && (!decode_zfs_fh(dc, fh) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_getattr(fattr *attr, zfs_fh *fh) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_getattr_zfsd(&dc, fh); + if (!error + && (!decode_fattr(dc, attr) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_setattr(fattr *attr, sattr_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_setattr_zfsd(&dc, args); + if (!error + && (!decode_fattr(dc, attr) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_create(create_res *res, create_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_create_zfsd(&dc, args); + if (!error + && (!decode_create_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_lookup(dir_op_res *res, dir_op_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_lookup_zfsd(&dc, args); + if (!error + && (!decode_dir_op_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_link(link_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_link_zfsd(&dc, args); + if (!error + && !finish_decoding(dc)) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_unlink(dir_op_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_unlink_zfsd(&dc, args); + if (!error + && !finish_decoding(dc)) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_symlink(dir_op_res *res, symlink_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_symlink_zfsd(&dc, args); + if (!error + && (!decode_dir_op_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_mkdir(dir_op_res *res, mkdir_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_mkdir_zfsd(&dc, args); + if (!error + && (!decode_dir_op_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_rmdir(dir_op_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_rmdir_zfsd(&dc, args); + if (!error + && !finish_decoding(dc)) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_mknod(dir_op_res *res, mknod_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_mknod_zfsd(&dc, args); + if (!error + && (!decode_dir_op_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_rename(rename_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_rename_zfsd(&dc, args); + if (!error + && !finish_decoding(dc)) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_readlink(read_link_res *res, zfs_fh *fh) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_readlink_zfsd(&dc, fh); + if (!error + && (!decode_read_link_res(dc, res) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_open(zfs_cap *cap, open_args *args) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_open_zfsd(&dc, args); + if (!error + && (!decode_zfs_cap(dc, cap) + || !finish_decoding(dc))) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_close(zfs_cap *cap) + { + DC *dc; + int error; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_close_zfsd(&dc, cap); + if (!error + && !finish_decoding(dc)) + error = -EPROTO; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_readdir(read_dir_args *args, struct file *file, void *dirent, filldir_t filldir) + { + DC *dc; + dir_list list; + dir_entry entry; + struct qstr name; + int error, entries = 0, i; + + TRACE(""); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + while (1) { + error = zfs_proc_readdir_zfsd(&dc, args); + if (error) + break; + + if (!decode_dir_list(dc, &list)) { + error = -EPROTO; + break; + } + + for (i = 0; i < list.n; i++) { + if (!decode_dir_entry(dc, &entry)) { + error = -EPROTO; + break; + } + + TRACE("entry: ino=%u, cookie=%d, '%s'", entry.ino, entry.cookie, entry.name.str); + + name.name = entry.name.str; + name.len = entry.name.len; + + error = filldir(dirent, name.name, name.len, file->f_pos, entry.ino, DT_UNKNOWN); + if (error) + break; + + /* Store the cookie to be able to continue list dir. */ + *COOKIE(file->private_data) = entry.cookie; + file->f_pos++; + entries++; + } + if (error) + break; + + if (!finish_decoding(dc)) { + error = -EPROTO; + break; + } + + if (list.eof) { + file->f_pos = -1; + break; + } + + args->cookie = entry.cookie; + } + + dc_put(dc); + + TRACE("%d", entries ? entries : error); + + return entries ? entries : error; + } + + int zfsd_read(char __user *buf, read_args *args) + { + DC *dc; + uint32_t nbytes; + int error; + + TRACE("reading %u bytes", args->count); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_read_zfsd(&dc, args); + if (!error) { + if (!decode_uint32_t(dc, &nbytes) + || (nbytes > args->count)) + error = -EPROTO; + else { + dc->cur_length += nbytes; + if (!finish_decoding(dc)) + error = -EPROTO; + else { + if (copy_to_user(buf, dc->cur_pos, nbytes)) + error = -EFAULT; + else + error = nbytes; + } + } + } + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_write(write_args *args) + { + DC *dc; + write_res res; + int error; + + TRACE("writting %u bytes", args->data.len); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_write_zfsd(&dc, args); + if (!error) { + if (!decode_write_res(dc, &res) + || (res.written > args->data.len) + || !finish_decoding(dc)) + error = -EPROTO; + else + error = res.written; + } else if (dc->cur_pos == NULL) + error = -EFAULT; + + dc_put(dc); + + TRACE("%d", error); + + return error; + } + + int zfsd_readpage(char *buf, read_args *args) + { + DC *dc; + uint32_t nbytes; + int error; + + TRACE("reading %u bytes", args->count); + + dc = dc_get(); + if (!dc) + return -ENOMEM; + + error = zfs_proc_read_zfsd(&dc, args); + if (!error) { + if (!decode_uint32_t(dc, &nbytes) + || (nbytes > args->count)) + error = -EPROTO; + else { + dc->cur_length += nbytes; + if (!finish_decoding(dc)) + error = -EPROTO; + else { + memcpy(buf, dc->cur_pos, nbytes); + error = nbytes; + } + } + } + + dc_put(dc); + + TRACE("%d", error); + + return error; + } Index: fs/zfs/zfsd_call.h =================================================================== RCS file: fs/zfs/zfsd_call.h diff -N fs/zfs/zfsd_call.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- fs/zfs/zfsd_call.h 20 May 2004 14:20:08 -0000 1.9 *************** *** 0 **** --- 1,54 ---- + /* + Functions to call ZFSd. + Copyright (C) 2004 Martin Zlomek + + This file is part of ZFS. + + ZFS 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, or (at your option) + any later version. + + ZFS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License along with + ZFS; see the file COPYING. If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA; + or download it from http://www.gnu.org/licenses/gpl.html + */ + + #ifndef _ZFSD_CALL_H + #define _ZFSD_CALL_H + + #include + #include + + #include "zfs.h" + #include "zfs_prot.h" + + + extern int send_request(struct request *req); + extern int zfsd_root(zfs_fh *fh); + extern int zfsd_getattr(fattr *attr, zfs_fh *fh); + extern int zfsd_setattr(fattr *attr, sattr_args *args); + extern int zfsd_create(create_res *res, create_args *args); + extern int zfsd_lookup(dir_op_res *res, dir_op_args *args); + extern int zfsd_link(link_args *args); + extern int zfsd_unlink(dir_op_args *args); + extern int zfsd_symlink(dir_op_res *res, symlink_args *args); + extern int zfsd_mkdir(dir_op_res *res, mkdir_args *args); + extern int zfsd_rmdir(dir_op_args *args); + extern int zfsd_mknod(dir_op_res *res, mknod_args *args); + extern int zfsd_rename(rename_args *args); + extern int zfsd_readlink(read_link_res *res, zfs_fh *fh); + extern int zfsd_open(zfs_cap *cap, open_args *args); + extern int zfsd_close(zfs_cap *cap); + extern int zfsd_readdir(read_dir_args *args, struct file *file, void *dirent, filldir_t filldir); + extern int zfsd_read(char __user *buf, read_args *args); + extern int zfsd_write(write_args *args); + extern int zfsd_readpage(char *buf, read_args *args); + + #endif