HAMMER 12/many - add VOPs for symlinks, device, and fifo support.
authorMatthew Dillon <dillon@dragonflybsd.org>
Sun, 30 Dec 2007 00:47:22 +0000 (00:47 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sun, 30 Dec 2007 00:47:22 +0000 (00:47 +0000)
* Add some missing VOP bits.  Add a fixed attribute record type and store
  the symlink path as fixed attribute record #1.

* Adjust hammer_inode_data to add missing major and minor device fields.

* Add support for adding generic records and special case deletion of
  an object's entire set of records.

* Fix a minor bug that was causing the root cluster to be improperly kfree()'d.

sbin/newfs_hammer/newfs_hammer.c
sys/vfs/hammer/hammer.h
sys/vfs/hammer/hammer_btree.c
sys/vfs/hammer/hammer_cursor.c
sys/vfs/hammer/hammer_disk.h
sys/vfs/hammer/hammer_inode.c
sys/vfs/hammer/hammer_io.c
sys/vfs/hammer/hammer_object.c
sys/vfs/hammer/hammer_ondisk.c
sys/vfs/hammer/hammer_vfsops.c
sys/vfs/hammer/hammer_vnops.c

index ba06e04..a308eab 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.10 2007/12/14 08:05:37 dillon Exp $
+ * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.11 2007/12/30 00:47:20 dillon Exp $
  */
 
 #include "newfs_hammer.h"
@@ -671,7 +671,7 @@ format_root(struct cluster_info *cluster)
        idata->version = HAMMER_INODE_DATA_VERSION;
        idata->mode = 0755;
 
-       rec->base.base.obj_id = 1;
+       rec->base.base.obj_id = HAMMER_OBJID_ROOT;
        rec->base.base.key = 0;
        rec->base.base.create_tid = createtid();
        rec->base.base.delete_tid = 0;
index 5001e50..e077daf 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.14 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.15 2007/12/30 00:47:22 dillon Exp $
  */
 /*
  * This header file contains structures used internally by the HAMMERFS
@@ -406,6 +406,8 @@ struct hammer_sync_info {
 #if defined(_KERNEL)
 
 extern struct vop_ops hammer_vnode_vops;
+extern struct vop_ops hammer_spec_vops;
+extern struct vop_ops hammer_fifo_vops;
 extern struct hammer_alist_config Buf_alist_config;
 extern struct hammer_alist_config Vol_normal_alist_config;
 extern struct hammer_alist_config Vol_super_alist_config;
@@ -577,9 +579,13 @@ int  hammer_ip_add_directory(struct hammer_transaction *trans,
 int  hammer_ip_del_directory(struct hammer_transaction *trans,
                        hammer_cursor_t cursor, hammer_inode_t dip,
                        hammer_inode_t ip);
+int  hammer_ip_add_record(struct hammer_transaction *trans,
+                       hammer_record_t record);
 int  hammer_ip_delete_range(struct hammer_transaction *trans,
                        hammer_inode_t ip, int64_t ran_beg, int64_t ran_end,
                        struct hammer_cursor **spikep);
+int  hammer_ip_delete_range_all(struct hammer_transaction *trans,
+                       hammer_inode_t ip);
 int  hammer_ip_sync_data(struct hammer_transaction *trans,
                        hammer_inode_t ip, int64_t offset,
                        void *data, int bytes, struct hammer_cursor **spikep);
index ef8f428..1840c53 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_btree.c,v 1.11 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_btree.c,v 1.12 2007/12/30 00:47:22 dillon Exp $
  */
 
 /*
@@ -181,7 +181,6 @@ hammer_btree_iterate(hammer_cursor_t cursor)
                        elm = &node->elms[cursor->index];
                        r = hammer_btree_cmp(&cursor->key_end, &elm[0].base);
                        s = hammer_btree_cmp(&cursor->key_beg, &elm[1].base);
-                       kprintf("SCAN %d r/s %d/%d\n", cursor->index, r, s);
                        if (r < 0) {
                                error = ENOENT;
                                break;
index 192315b..a7edbb2 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_cursor.c,v 1.6 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_cursor.c,v 1.7 2007/12/30 00:47:22 dillon Exp $
  */
 
 /*
@@ -268,6 +268,8 @@ hammer_load_cursor_parent_local(hammer_cursor_t cursor)
                        break;
                }
        }
+       if (i == parent->ondisk->count)
+               panic("Bad B-Tree link: parent %p node %p\n", parent, node);
        KKASSERT(i != parent->ondisk->count);
        KKASSERT(parent->ondisk->elms[i].internal.rec_offset == 0);
        cursor->parent = parent;
@@ -319,7 +321,6 @@ hammer_load_cursor_parent_cluster(hammer_cursor_t cursor)
        parent = hammer_get_node(pcluster, ondisk->clu_btree_parent_offset,
                                 &error);
        hammer_rel_cluster(pcluster, 0);
-       kprintf("parent %p clu_no %d\n", parent, clu_no);
        if (error)
                return (error);
 
@@ -329,8 +330,6 @@ hammer_load_cursor_parent_cluster(hammer_cursor_t cursor)
        elm = NULL;
        for (i = 0; i < parent->ondisk->count; ++i) {
                elm = &parent->ondisk->elms[i];
-               if (elm->internal.subtree_type == HAMMER_BTREE_TYPE_CLUSTER)
-                       kprintf("SUBTEST CLU %d\n", elm->internal.subtree_clu_no);
                if (elm->internal.rec_offset != 0 &&
                    elm->internal.subtree_type == HAMMER_BTREE_TYPE_CLUSTER &&
                    elm->internal.subtree_clu_no == cursor->node->cluster->clu_no) {
@@ -529,6 +528,8 @@ hammer_cursor_down(hammer_cursor_t cursor)
                                       &error);
                if (error == 0) {
                        KKASSERT(elm->internal.subtree_type == node->ondisk->type);
+                       if(node->ondisk->parent != cursor->parent->node_offset)
+                               kprintf("node %p %d vs %d\n", node, node->ondisk->parent, cursor->parent->node_offset);
                        KKASSERT(node->ondisk->parent == cursor->parent->node_offset);
                }
        }
index 978d41a..0952cd6 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.12 2007/12/14 08:05:39 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.13 2007/12/30 00:47:22 dillon Exp $
  */
 
 #ifndef _SYS_UUID_H_
@@ -457,6 +457,9 @@ struct hammer_base_record {
 #define HAMMER_RECTYPE_DIRENTRY                0x11
 #define HAMMER_RECTYPE_DB              0x12
 #define HAMMER_RECTYPE_EXT             0x13    /* ext attributes */
+#define HAMMER_RECTYPE_FIX             0x14    /* fixed attribute */
+
+#define HAMMER_FIXKEY_SYMLINK          1
 
 #define HAMMER_OBJTYPE_UNKNOWN         0       /* (never exists on-disk) */
 #define HAMMER_OBJTYPE_DIRECTORY       1
@@ -620,10 +623,6 @@ typedef union hammer_fsbuf_ondisk *hammer_fsbuf_ondisk_t;
  * modifications to the contents of this structure will result in a record
  * replacement operation.
  *
- * state_sum allows a filesystem object to be validated to a degree by
- * generating a checksum of all of its pieces (in no particular order) and
- * checking it against this field.
- *
  * short_data_off allows a small amount of data to be embedded in the
  * hammer_inode_data structure.  HAMMER typically uses this to represent
  * up to 64 bytes of data, or to hold symlinks.  Remember that allocations
@@ -640,9 +639,8 @@ struct hammer_inode_data {
        u_int16_t version;      /* inode data version */
        u_int16_t mode;         /* basic unix permissions */
        u_int32_t uflags;       /* chflags */
-       u_int16_t short_data_off; /* degenerate data case */
-       u_int16_t short_data_len;
-       u_int32_t state_sum;
+       u_int32_t rmajor;       /* used by device nodes */
+       u_int32_t rminor;       /* used by device nodes */
        u_int64_t ctime;
        u_int64_t parent_obj_id;/* parent directory obj_id */
        uuid_t  uid;
@@ -652,6 +650,8 @@ struct hammer_inode_data {
 
 #define HAMMER_INODE_DATA_VERSION      1
 
+#define HAMMER_OBJID_ROOT              1
+
 /*
  * Rollup various structures embedded as record data
  */
index 341489a..27050ed 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_inode.c,v 1.12 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_inode.c,v 1.13 2007/12/30 00:47:22 dillon Exp $
  */
 
 #include "hammer.h"
@@ -135,6 +135,23 @@ hammer_get_vnode(struct hammer_inode *ip, int lktype, struct vnode **vpp)
                        ip->vp = vp;
                        vp->v_type = hammer_get_vnode_type(
                                            ip->ino_rec.base.base.obj_type);
+
+                       switch(ip->ino_rec.base.base.obj_type) {
+                       case HAMMER_OBJTYPE_CDEV:
+                       case HAMMER_OBJTYPE_BDEV:
+                               vp->v_ops = &ip->hmp->mp->mnt_vn_spec_ops;
+                               addaliasu(vp, ip->ino_data.rmajor,
+                                         ip->ino_data.rminor);
+                               break;
+                       case HAMMER_OBJTYPE_FIFO:
+                               vp->v_ops = &ip->hmp->mp->mnt_vn_fifo_ops;
+                               break;
+                       default:
+                               break;
+                       }
+                       if (ip->obj_id == HAMMER_OBJID_ROOT)
+                               vp->v_flag |= VROOT;
+
                        vp->v_data = (void *)ip;
                        /* vnode locked by getnewvnode() */
                        /* make related vnode dirty if inode dirty? */
@@ -288,6 +305,16 @@ hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
        ip->ino_data.ctime = trans->tid;
        ip->ino_data.parent_obj_id = (dip) ? dip->ino_rec.base.base.obj_id : 0;
 
+       switch(ip->ino_rec.base.base.obj_type) {
+       case HAMMER_OBJTYPE_CDEV:
+       case HAMMER_OBJTYPE_BDEV:
+               ip->ino_data.rmajor = vap->va_rmajor;
+               ip->ino_data.rminor = vap->va_rminor;
+               break;
+       default:
+               break;
+       }
+
        /*
         * Calculate default uid/gid and overwrite with information from
         * the vap.
@@ -524,9 +551,8 @@ hammer_sync_inode(hammer_inode_t ip, int waitfor, int handle_delete)
        if (ip->ino_rec.ino_nlinks == 0 && handle_delete) {
                if (ip->vp)
                        vtruncbuf(ip->vp, 0, HAMMER_BUFSIZE);
-               error = hammer_ip_delete_range(&trans, ip,
-                                              HAMMER_MIN_KEY, HAMMER_MAX_KEY,
-                                              NULL);
+               error = hammer_ip_delete_range_all(&trans, ip);
+               kprintf("delete_range_all error %d\n", error);
                KKASSERT(RB_EMPTY(&ip->rec_tree));
                ip->flags &= ~HAMMER_INODE_TID;
                ip->ino_rec.base.base.delete_tid = trans.tid;
index eaf7493..13e42e8 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_io.c,v 1.7 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_io.c,v 1.8 2007/12/30 00:47:22 dillon Exp $
  */
 /*
  * IO Primitives and buffer cache management
@@ -292,7 +292,6 @@ hammer_io_flush(struct hammer_io *io, struct hammer_sync_info *info)
                io->modified = 1;
        if (io->modified == 0)
                return;
-       kprintf("IO FLUSH BP %p TYPE %d REFS %d\n", bp, io->type, io->lock.refs);
        hammer_lock_ex(&io->lock);
 
        if ((bp = io->bp) != NULL && io->modified) {
@@ -317,8 +316,8 @@ hammer_io_flush(struct hammer_io *io, struct hammer_sync_info *info)
                         * also set B_LOCKED so we know something tried to
                         * flush it.
                         */
-                       kprintf("can't flush bp %p, %d refs - delaying\n",
-                               bp, io->lock.refs);
+                       kprintf("DELAYING IO FLUSH BP %p TYPE %d REFS %d\n",
+                               bp, io->type, io->lock.refs);
                        bp->b_flags |= B_LOCKED;
                        bqrelse(bp);
                }
@@ -359,8 +358,6 @@ hammer_io_complete(struct buf *bp)
 
        if (io->io.type == HAMMER_STRUCTURE_CLUSTER) {
                if (io->cluster.state == HAMMER_CLUSTER_ASYNC) {
-                       kprintf("cluster write complete flags %08x\n",
-                               io->cluster.ondisk->clu_flags);
                        io->cluster.state = HAMMER_CLUSTER_OPEN;
                        wakeup(&io->cluster);
                }
@@ -503,7 +500,6 @@ hammer_io_checkwrite(struct buf *bp)
                 * Cannot write out a cluster buffer if the cluster header
                 * I/O opening the cluster has not completed.
                 */
-               kprintf("hammer_io_checkwrite: w/ depend - delayed\n");
                bp->b_flags |= B_LOCKED;
                return(-1);
        } else if (iou->io.lock.refs) {
@@ -511,7 +507,6 @@ hammer_io_checkwrite(struct buf *bp)
                 * Cannot write out a bp if its associated buffer has active
                 * references.
                 */
-               kprintf("hammer_io_checkwrite: w/ refs - delayed\n");
                bp->b_flags |= B_LOCKED;
                return(-1);
        } else {
@@ -521,7 +516,6 @@ hammer_io_checkwrite(struct buf *bp)
                 */
                if (iou->io.type == HAMMER_STRUCTURE_CLUSTER)
                        hammer_close_cluster(&iou->cluster);
-               kprintf("hammer_io_checkwrite: ok\n");
                KKASSERT(iou->io.released);
                hammer_io_disassociate(iou);
                return(0);
index bb0bd65..e0db60a 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_object.c,v 1.10 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_object.c,v 1.11 2007/12/30 00:47:22 dillon Exp $
  */
 
 #include "hammer.h"
@@ -403,6 +403,47 @@ hammer_ip_del_directory(struct hammer_transaction *trans,
        return(error);
 }
 
+/*
+ * Add a record to an inode.
+ *
+ * The caller must allocate the record with hammer_alloc_mem_record(ip) and
+ * initialize the following additional fields:
+ *
+ * record->rec.entry.base.base.key
+ * record->rec.entry.base.base.rec_type
+ * record->rec.entry.base.base.data_len
+ * record->data                (a copy will be kmalloc'd if not embedded)
+ */
+int
+hammer_ip_add_record(struct hammer_transaction *trans, hammer_record_t record)
+{
+       hammer_inode_t ip = record->ip;
+       int error;
+       int bytes;
+       void *data;
+
+       record->rec.base.base.obj_id = ip->obj_id;
+       record->rec.base.base.create_tid = trans->tid;
+       record->rec.base.base.obj_type = ip->ino_rec.base.base.obj_type;
+       bytes = record->rec.base.data_len;
+
+       if (record->data) {
+               if ((char *)record->data < (char *)&record->rec ||
+                   (char *)record->data >= (char *)(&record->rec + 1)) {
+                       data = kmalloc(bytes, M_HAMMER, M_WAITOK);
+                       record->flags |= HAMMER_RECF_ALLOCDATA;
+                       bcopy(record->data, data, bytes);
+                       record->data = data;
+               } else {
+                       record->flags |= HAMMER_RECF_EMBEDDED_DATA;
+               }
+       }
+       hammer_modify_inode(trans, ip,
+                           HAMMER_INODE_RDIRTY | HAMMER_INODE_TID);
+       error = hammer_mem_add(trans, record);
+       return(error);
+}
+
 /*
  * Sync data from a buffer cache buffer (typically) to the filesystem.  This
  * is called via the strategy called from a cached data source.  This code
@@ -565,7 +606,7 @@ hammer_ip_sync_record(hammer_record_t record, struct hammer_cursor **spike)
                        rec->base.data_offset = ((char *)bdata -
                                                 (char *)&record->rec);
                        KKASSERT(rec->base.data_offset >= 0 && 
-                                rec->base.data_offset + rec->base.data_len <
+                                rec->base.data_offset + rec->base.data_len <=
                                  sizeof(*rec));
                        rec->base.data_offset += hammer_bclu_offset(cursor.record_buffer, rec);
                } else {
@@ -1007,7 +1048,6 @@ hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
        hammer_record_ondisk_t rec;
        hammer_base_elm_t base;
        int error;
-       int isregfile;
        int64_t off;
 
        hammer_init_cursor_ip(&cursor, ip);
@@ -1023,7 +1063,6 @@ hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
                cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
                cursor.key_end.rec_type = HAMMER_RECTYPE_DB;
                cursor.key_end.key = ran_end;
-               isregfile = 0;
        } else {
                /*
                 * The key in the B-Tree is (base+bytes), so the first possible
@@ -1040,7 +1079,6 @@ hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
                        cursor.key_end.key = 0x7FFFFFFFFFFFFFFFLL;
                else
                        cursor.key_end.key = ran_end + MAXPHYS + 1;
-               isregfile = 1;
        }
        cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
 
@@ -1119,6 +1157,58 @@ hammer_ip_delete_range(hammer_transaction_t trans, hammer_inode_t ip,
        return(error);
 }
 
+int
+hammer_ip_delete_range_all(hammer_transaction_t trans, hammer_inode_t ip)
+{
+       struct hammer_cursor cursor;
+       hammer_record_ondisk_t rec;
+       hammer_base_elm_t base;
+       int error;
+
+       hammer_init_cursor_ip(&cursor, ip);
+
+       cursor.key_beg.obj_id = ip->obj_id;
+       cursor.key_beg.create_tid = ip->obj_asof;
+       cursor.key_beg.delete_tid = 0;
+       cursor.key_beg.obj_type = 0;
+       cursor.key_beg.rec_type = 0;
+       cursor.key_beg.key = HAMMER_MIN_KEY;
+
+       cursor.key_end = cursor.key_beg;
+       cursor.key_end.rec_type = 0xFFFF;
+       cursor.key_end.key = HAMMER_MAX_KEY;
+
+       cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
+
+       error = hammer_ip_first(&cursor, ip);
+
+       /*
+        * Iterate through matching records and mark them as deleted.
+        */
+       while (error == 0) {
+               rec = cursor.record;
+               base = &rec->base.base;
+
+               KKASSERT(base->delete_tid == 0);
+
+               /*
+                * Mark the record and B-Tree entry as deleted.  This will
+                * also physically delete the B-Tree entry, record, and
+                * data if the retention policy dictates.  The function
+                * will set HAMMER_CURSOR_DELBTREE which hammer_ip_next()
+                * uses to perform a fixup.
+                */
+               error = hammer_ip_delete_record(&cursor, trans->tid);
+               if (error)
+                       break;
+               error = hammer_ip_next(&cursor);
+       }
+       hammer_done_cursor(&cursor);
+       if (error == ENOENT)
+               error = 0;
+       return(error);
+}
+
 /*
  * Delete the record at the current cursor
  */
index 6436f26..edcad37 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.11 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.12 2007/12/30 00:47:22 dillon Exp $
  */
 /*
  * Manage HAMMER's on-disk structures.  These routines are primarily
@@ -949,7 +949,8 @@ hammer_rel_cluster(hammer_cluster_t cluster, int flush)
                        /*
                         * Final cleanup
                         */
-                       if (cluster->io.bp == NULL &&
+                       if (cluster != cluster->volume->hmp->rootcl &&
+                           cluster->io.bp == NULL &&
                            cluster->io.lock.refs == 1 &&
                            RB_EMPTY(&cluster->rb_nods_root)) {
                                KKASSERT(RB_EMPTY(&cluster->rb_bufs_root));
index 75171fd..d11036f 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_vfsops.c,v 1.10 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_vfsops.c,v 1.11 2007/12/30 00:47:22 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -191,6 +191,8 @@ hammer_vfs_mount(struct mount *mp, char *mntpt, caddr_t data,
        mp->mnt_flag |= MNT_LOCAL;
 
        vfs_add_vnodeops(mp, &hammer_vnode_vops, &mp->mnt_vn_norm_ops);
+       vfs_add_vnodeops(mp, &hammer_spec_vops, &mp->mnt_vn_spec_ops);
+       vfs_add_vnodeops(mp, &hammer_fifo_vops, &mp->mnt_vn_fifo_ops);
 
        /*
         * The root volume's ondisk pointer is only valid if we hold a
index dec31cf..3eee9cd 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.10 2007/12/29 09:01:27 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.11 2007/12/30 00:47:22 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -44,6 +44,7 @@
 #include <sys/event.h>
 #include <sys/stat.h>
 #include <vm/vm_extern.h>
+#include <vfs/fifofs/fifo.h>
 #include "hammer.h"
 
 /*
@@ -76,6 +77,14 @@ static int hammer_vop_strategy(struct vop_strategy_args *);
 static int hammer_vop_nsymlink(struct vop_nsymlink_args *);
 static int hammer_vop_nwhiteout(struct vop_nwhiteout_args *);
 
+static int hammer_vop_fifoclose (struct vop_close_args *);
+static int hammer_vop_fiforead (struct vop_read_args *);
+static int hammer_vop_fifowrite (struct vop_write_args *);
+
+static int hammer_vop_specclose (struct vop_close_args *);
+static int hammer_vop_specread (struct vop_read_args *);
+static int hammer_vop_specwrite (struct vop_write_args *);
+
 struct vop_ops hammer_vnode_vops = {
        .vop_default =          vop_defaultop,
        .vop_fsync =            hammer_vop_fsync,
@@ -109,6 +118,32 @@ struct vop_ops hammer_vnode_vops = {
        .vop_nwhiteout =        hammer_vop_nwhiteout
 };
 
+struct vop_ops hammer_spec_vops = {
+       .vop_default =          spec_vnoperate,
+       .vop_fsync =            hammer_vop_fsync,
+       .vop_read =             hammer_vop_specread,
+       .vop_write =            hammer_vop_specwrite,
+       .vop_access =           hammer_vop_access,
+       .vop_close =            hammer_vop_specclose,
+       .vop_getattr =          hammer_vop_getattr,
+       .vop_inactive =         hammer_vop_inactive,
+       .vop_reclaim =          hammer_vop_reclaim,
+       .vop_setattr =          hammer_vop_setattr
+};
+
+struct vop_ops hammer_fifo_vops = {
+       .vop_default =          fifo_vnoperate,
+       .vop_fsync =            hammer_vop_fsync,
+       .vop_read =             hammer_vop_fiforead,
+       .vop_write =            hammer_vop_fifowrite,
+       .vop_access =           hammer_vop_access,
+       .vop_close =            hammer_vop_fifoclose,
+       .vop_getattr =          hammer_vop_getattr,
+       .vop_inactive =         hammer_vop_inactive,
+       .vop_reclaim =          hammer_vop_reclaim,
+       .vop_setattr =          hammer_vop_setattr
+};
+
 static int hammer_dounlink(struct nchandle *nch, struct vnode *dvp,
                           struct ucred *cred, int flags);
 static int hammer_vop_strategy_read(struct vop_strategy_args *ap);
@@ -389,7 +424,7 @@ hammer_vop_ncreate(struct vop_ncreate_args *ap)
 
        /*
         * Create a new filesystem object of the requested type.  The
-        * returned inode will be referenceds but not locked.
+        * returned inode will be referenced but not locked.
         */
 
        error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
@@ -469,6 +504,17 @@ hammer_vop_getattr(struct vop_getattr_args *ap)
        vap->va_fsid_uuid = ip->hmp->fsid;
        vap->va_vaflags = VA_UID_UUID_VALID | VA_GID_UUID_VALID |
                          VA_FSID_UUID_VALID;
+
+       switch (ip->ino_rec.base.base.obj_type) {
+       case HAMMER_OBJTYPE_CDEV:
+       case HAMMER_OBJTYPE_BDEV:
+               vap->va_rmajor = ip->ino_data.rmajor;
+               vap->va_rminor = ip->ino_data.rminor;
+               break;
+       default:
+               break;
+       }
+
        return(0);
 }
 
@@ -855,10 +901,13 @@ hammer_vop_readdir(struct vop_readdir_args *ap)
                base = &rec->base.base;
                saveoff = base->key;
 
+               if (base->obj_id != ip->obj_id)
+                       panic("readdir: bad record at %p", cursor.node);
+
                r = vop_write_dirent(
                             &error, uio, rec->entry.obj_id,
-                            rec->entry.base.data_len,
                             hammer_get_dtype(rec->entry.base.base.obj_type),
+                            rec->entry.base.data_len,
                             (void *)cursor.data);
                if (r)
                        break;
@@ -874,8 +923,6 @@ hammer_vop_readdir(struct vop_readdir_args *ap)
 
        if (ap->a_eofflag)
                *ap->a_eofflag = (error == ENOENT);
-       if (error == ENOENT)
-               error = 0;
        uio->uio_offset = saveoff;
        if (error && cookie_index == 0) {
                if (cookies) {
@@ -884,7 +931,8 @@ hammer_vop_readdir(struct vop_readdir_args *ap)
                        *ap->a_cookies = NULL;
                }
        } else {
-               error = 0;
+               if (error == ENOENT)
+                       error = 0;
                if (cookies) {
                        *ap->a_ncookies = cookie_index;
                        *ap->a_cookies = cookies;
@@ -900,7 +948,35 @@ static
 int
 hammer_vop_readlink(struct vop_readlink_args *ap)
 {
-       return EOPNOTSUPP;
+       struct hammer_cursor cursor;
+       struct hammer_inode *ip;
+       int error;
+
+       ip = VTOI(ap->a_vp);
+       hammer_init_cursor_ip(&cursor, ip);
+
+       /*
+        * Key range (begin and end inclusive) to scan.  Directory keys
+        * directly translate to a 64 bit 'seek' position.
+        */
+       cursor.key_beg.obj_id = ip->obj_id;
+       cursor.key_beg.create_tid = ip->obj_asof;
+       cursor.key_beg.delete_tid = 0;
+        cursor.key_beg.rec_type = HAMMER_RECTYPE_FIX;
+       cursor.key_beg.obj_type = 0;
+       cursor.key_beg.key = HAMMER_FIXKEY_SYMLINK;
+
+       error = hammer_ip_lookup(&cursor, ip);
+       if (error == 0) {
+               error = hammer_ip_resolve_data(&cursor);
+               if (error == 0) {
+                       error = uiomove((char *)cursor.data,
+                                       cursor.record->generic.base.data_len,
+                                       ap->a_uio);
+               }
+       }
+       hammer_done_cursor(&cursor);
+       return(error);
 }
 
 /*
@@ -1155,7 +1231,80 @@ static
 int
 hammer_vop_nsymlink(struct vop_nsymlink_args *ap)
 {
-       return EOPNOTSUPP;
+       struct hammer_transaction trans;
+       struct hammer_inode *dip;
+       struct hammer_inode *nip;
+       struct nchandle *nch;
+       hammer_record_t record;
+       int error;
+       int bytes;
+
+       ap->a_vap->va_type = VLNK;
+
+       nch = ap->a_nch;
+       dip = VTOI(ap->a_dvp);
+
+       /*
+        * Create a transaction to cover the operations we perform.
+        */
+       hammer_start_transaction(&trans, dip->hmp);
+
+       /*
+        * Create a new filesystem object of the requested type.  The
+        * returned inode will be referenced but not locked.
+        */
+
+       error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred, dip, &nip);
+       if (error) {
+               hammer_abort_transaction(&trans);
+               *ap->a_vpp = NULL;
+               return (error);
+       }
+
+       /*
+        * Add the new filesystem object to the directory.  This will also
+        * bump the inode's link count.
+        */
+       error = hammer_ip_add_directory(&trans, dip, nch->ncp, nip);
+
+       /*
+        * Add a record representing the symlink.  symlink stores the link
+        * as pure data, not a string, and is no \0 terminated.
+        */
+       if (error == 0) {
+               record = hammer_alloc_mem_record(nip);
+               bytes = strlen(ap->a_target);
+
+               record->rec.generic.base.base.key = HAMMER_FIXKEY_SYMLINK;
+               record->rec.generic.base.base.rec_type = HAMMER_RECTYPE_FIX;
+               record->rec.generic.base.data_len = bytes;
+               if (bytes <= sizeof(record->rec.generic.filler)) {
+                       record->data = (void *)record->rec.generic.filler;
+                       bcopy(ap->a_target, record->data, bytes);
+               } else {
+                       record->data = (void *)ap->a_target;
+                       /* will be reallocated by routine below */
+               }
+               error = hammer_ip_add_record(&trans, record);
+       }
+
+       /*
+        * Finish up.
+        */
+       if (error) {
+               hammer_rel_inode(nip, 0);
+               hammer_abort_transaction(&trans);
+               *ap->a_vpp = NULL;
+       } else {
+               hammer_commit_transaction(&trans);
+               error = hammer_get_vnode(nip, LK_EXCLUSIVE, ap->a_vpp);
+               hammer_rel_inode(nip, 0);
+               if (error == 0) {
+                       cache_setunresolved(ap->a_nch);
+                       cache_setvp(ap->a_nch, *ap->a_vpp);
+               }
+       }
+       return (error);
 }
 
 /*
@@ -1491,3 +1640,57 @@ hammer_dounlink(struct nchandle *nch, struct vnode *dvp, struct ucred *cred,
        return (error);
 }
 
+/************************************************************************
+ *                         FIFO AND SPECFS OPS                         *
+ ************************************************************************
+ *
+ */
+
+static int
+hammer_vop_fifoclose (struct vop_close_args *ap)
+{
+       /* XXX update itimes */
+       return (VOCALL(&fifo_vnode_vops, &ap->a_head));
+}
+
+static int
+hammer_vop_fiforead (struct vop_read_args *ap)
+{
+       int error;
+
+       error = VOCALL(&fifo_vnode_vops, &ap->a_head);
+       /* XXX update access time */
+       return (error);
+}
+
+static int
+hammer_vop_fifowrite (struct vop_write_args *ap)
+{
+       int error;
+
+       error = VOCALL(&fifo_vnode_vops, &ap->a_head);
+       /* XXX update access time */
+       return (error);
+}
+
+static int
+hammer_vop_specclose (struct vop_close_args *ap)
+{
+       /* XXX update itimes */
+       return (VOCALL(&spec_vnode_vops, &ap->a_head));
+}
+
+static int
+hammer_vop_specread (struct vop_read_args *ap)
+{
+       /* XXX update access time */
+       return (VOCALL(&spec_vnode_vops, &ap->a_head));
+}
+
+static int
+hammer_vop_specwrite (struct vop_write_args *ap)
+{
+       /* XXX update last change time */
+       return (VOCALL(&spec_vnode_vops, &ap->a_head));
+}
+