NetBSD-5.0.2/dist/iscsi/src/osdfs.c

Compare this file to the similar file:
Show the results in this format:

/*
 * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or
 * using the software you agree to this license. If you do not agree to this license, do not download, install,
 * copy or use the software. 
 *
 * Intel License Agreement 
 *
 * Copyright (c) 2002, Intel Corporation
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met: 
 *
 * -Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *  following disclaimer. 
 *
 * -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *  following disclaimer in the documentation and/or other materials provided with the distribution. 
 *
 * -The name of Intel Corporation may not be used to endorse or promote products derived from this software
 *  without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * Object-Based Storage Devices (OSD) Filesystem for Linux
 */


#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <asm/uaccess.h>
#include <endian.h>
#include <linux/blkdev.h>
#include <scsi.h>
#include "osd.h"
#include "osd_ops.h"
#include "iscsiutil.h"
#include "util.c"


/*
 * Contants
 */


#define OSDFS_MAGIC  0xabcdef01
#define MAX_INODES   32768
#define MAX_NAME_LEN 32


/*
 * Types
 */


typedef struct osdfs_link_t {
  char name[MAX_NAME_LEN];
  struct osdfs_link_t* next;
} osdfs_link_t;

typedef struct osdfs_inode_t {
  osdfs_link_t  *link;
} osdfs_inode_t;

typedef struct osdfs_metadata_t {
  uint64_t ObjectID;
  int      used;
} osdfs_metadata_t;


/*
 * Prototypes
 */

static int osdfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);


/*
 * Globals
 */


static struct super_operations osdfs_ops;
static struct address_space_operations osdfs_aops;
static struct file_operations osdfs_dir_operations;
static struct file_operations osdfs_file_operations;
static struct inode_operations osdfs_dir_inode_operations;
static uint32_t root_gid;
static uint64_t root_uid;
static iscsi_mutex_t g_mutex;


/*
 * SCSI transport function for OSD
 */


int osd_exec_via_scsi(void *dev, osd_args_t *args, OSD_OPS_MEM *m) {
  Scsi_Request *SRpnt;
  Scsi_Device *SDpnt;
  unsigned char cdb[256];
  kdev_t kdev = *((kdev_t *) dev);
  void *ptr = NULL;
  int len = 0;

  if (m->send_sg||m->recv_sg) {
    iscsi_trace_error("scatter/gather not yet implemented!\n");
    return -1;
  }

  SDpnt = blk_dev[MAJOR(kdev)].queue(kdev)->queuedata;
  SRpnt = scsi_allocate_request(SDpnt); 
  SRpnt->sr_cmd_len = CONFIG_OSD_CDB_LEN;
  SRpnt->sr_sense_buffer[0] = 0;
  SRpnt->sr_sense_buffer[2] = 0;
  switch(args->service_action) {
    case OSD_WRITE:
    case OSD_SET_ATTR:
      len = m->send_len;
      ptr = m->send_data;
      SRpnt->sr_data_direction = SCSI_DATA_WRITE;
      break;
    case OSD_CREATE:
    case OSD_CREATE_GROUP:
    case OSD_READ:
    case OSD_GET_ATTR:
      len = m->recv_len;
      ptr = m->recv_data;
      SRpnt->sr_data_direction = SCSI_DATA_READ;
      break;
    case OSD_REMOVE:
    case OSD_REMOVE_GROUP:
      SRpnt->sr_data_direction = 0;
      break;
    default:
      iscsi_trace_error("unsupported OSD service action 0x%x\n", args->service_action);
      return -1;
  }
  OSD_ENCAP_CDB(args, cdb);

  /*  Exec SCSI command */

  scsi_wait_req(SRpnt, cdb, ptr, len, 5*HZ, 5);
  if (SRpnt->sr_result!=0) {
    iscsi_trace_error("SCSI command failed (result %u)\n", SRpnt->sr_result);
    scsi_release_request(SRpnt);
    SRpnt = NULL;
    return -1;
  } 
  scsi_release_request(SRpnt);
  SRpnt = NULL;

  return 0;
}

/* 
 * Internal OSDFS functions
 */


/* Directory operations */

static int entries_get(kdev_t dev, uint64_t uid, char **entries, uint32_t *num, uint64_t *size) {
  struct inode inode;
  uint16_t len;

  if (osd_get_one_attr((void *)&dev, root_gid, uid, 0x30000000, 0x0, sizeof(struct inode), &osd_exec_via_scsi, &len, (void *) &inode)!=0) {
    iscsi_trace_error("osd_get_one_attr() failed\n");
    return -1;
  }
  *num = 0; 
  if ((*size=inode.i_size)) {
    char *ptr, *ptr2;
    int n = 0;

    if ((*entries=vmalloc(*size+1))==NULL) {
      iscsi_trace_error("vmalloc() failed\n");
      return -1;
    }
    if (osd_read((void *)&dev, root_gid, uid, 0, *size, *entries, 0, &osd_exec_via_scsi)!=0) {
      iscsi_trace_error("osd_read() failed\n");
      vfree(*entries);
      return -1;
    }
    (*entries)[*size] = 0x0;
    ptr = *entries;
    do {
      n++;
      if ((ptr2=strchr(ptr, '\n'))!=NULL) {
        n++;
        if ((ptr2 = strchr(ptr2+1, '\n'))==NULL) {
          iscsi_trace_error("directory 0x%llx corrupted (line %i)\n", uid, n);
          return -1;
        }
        (*num)++;
      } else {
        iscsi_trace_error("directory 0x%llx corrupted (line %i)\n", uid, n);
        return -1;
      }
      ptr = ptr2+1;
    } while (*ptr);
  }

  return 0;
}

static int entry_add(kdev_t dev, ino_t dir_ino, ino_t entry_ino, 
                     const char *name, uint64_t *new_size) {
  char entry[MAX_NAME_LEN+16];
  uint64_t uid = dir_ino;
  struct inode inode;
  uint16_t len;

  /*  Get size of directory */

  if (osd_get_one_attr((void *)&dev, root_gid, uid, 0x30000000, 0x0, sizeof(struct inode), &osd_exec_via_scsi, &len, (void *) &inode)!=0) {
    iscsi_trace_error("osd_get_one_attr() failed\n");
    return -1;
  }

  /*  Write entry at end */
  
  sprintf(entry, "%s\n", name);
  sprintf(entry+strlen(entry), "%li\n", entry_ino);
  if (osd_write((void *)&dev, root_gid, uid, inode.i_size, strlen(entry), entry, 0, &osd_exec_via_scsi)!=0) {
    iscsi_trace_error("osd_write() failed\n");
    return -1;
  }
  *new_size += strlen(entry);

  return 0;
}

static int entry_del(kdev_t dev, ino_t dir_ino, ino_t ino, const char *name, uint64_t *new_size) {
  char *entries;
  uint32_t num_entries;
  uint64_t size;
  uint64_t dir_uid = (unsigned) dir_ino;

  /*  Read */

  if (entries_get(dev, dir_ino, &entries, &num_entries, &size)!=0) {
    iscsi_trace_error("entries_get() failed\n");
    return -1;
  }
  entries[size] = 0x0;

  iscsi_trace(TRACE_OSDFS, "dir_ino 0x%llx has %u entries\n", dir_uid, num_entries);
  if (num_entries) {
    char *ptr = entries;
    char *tmp = NULL;
    char *nl;
    int n = 0;

    do {
      n++;
      if ((nl=strchr(ptr, '\n'))==NULL) {
        iscsi_trace_error("directory 0x%llx corrupted (line %i)\n", dir_uid, n);
        return -1;
      }
      *nl = 0x0;
      if (!strcmp(ptr, name)) {
        tmp = ptr;
      } 
      *nl = '\n';
      n++;
      if ((ptr=strchr(nl+1, '\n'))==NULL) {
        iscsi_trace_error("directory 0x%llx corrupted (line %i)\n", dir_uid, n);
        return -1;
      }
      ptr++;
    } while (!tmp && *ptr);

    if (!tmp) {
      iscsi_trace_error("entry \"%s\" not found in dir 0x%llx\n", name, dir_uid);
      return -1;
    }
    if (entries+size-ptr) {
      iscsi_trace(TRACE_OSDFS, "writing remaining %u directory bytes at offset %u\n", 
            entries+size-ptr, tmp-entries);
      if (osd_write((void *)&dev, root_gid, dir_uid, tmp-entries, entries+size-ptr, ptr, 0, &osd_exec_via_scsi)!=0) {
        iscsi_trace_error("osd_write() failed\n");
        return -1;
      }
    }
    *new_size = size-(ptr-tmp); 
    vfree(entries);
  } else {
    iscsi_trace_error("dir 0x%llx has no entries\n", dir_uid);
    return -1;
  }

  return 0;
}

static int entry_num(kdev_t dev, ino_t ino) {
  char *entries; 
  uint32_t num_entries;
  uint64_t size;

  if (entries_get(dev, ino, &entries, &num_entries, &size)!=0) {
    iscsi_trace_error("entries_get() failed\n");
    return -1;
  }
  iscsi_trace(TRACE_OSDFS, "ino %li has %i entries\n", ino, num_entries);
  if (num_entries) vfree(entries);
  return num_entries;
}

/* Inode operations */

static void osdfs_set_ops(struct inode *inode) {
  switch (inode->i_mode & S_IFMT) {
    case S_IFREG:
      inode->i_fop = &osdfs_file_operations;
      break;
    case S_IFDIR:
      inode->i_op = &osdfs_dir_inode_operations;
      inode->i_fop = &osdfs_dir_operations;
      break;
    case S_IFLNK:
      inode->i_op = &page_symlink_inode_operations;
      break;
    default:
      iscsi_trace_error("UNKNOWN MODE\n");
  }
  inode->i_mapping->a_ops = &osdfs_aops;
}

static struct inode *osdfs_get_inode(struct super_block *sb, int mode, int dev, const char *name, 
                              uint64_t ObjectID) {
  struct inode *inode;
  ino_t ino = ObjectID;

  iscsi_trace(TRACE_OSDFS, "osdfs_get_inode(\"%s\", mode %i (%s))\n", name, mode,
        S_ISDIR(mode)?"DIR":(S_ISREG(mode)?"REG":"LNK"));

  /*  iget() gets a free VFS inode and subsequently call  */
  /*  osdfds_read_inode() to fill the inode structure. */

  if ((inode=iget(sb, ino))==NULL) {
    iscsi_trace_error("iget() failed\n");
    return NULL;
  }

  return inode;
}


/*
 * Super Operations
 */


static void osdfs_read_inode(struct inode *inode) {
  ino_t ino = inode->i_ino;
  kdev_t dev = inode->i_sb->s_dev;
  uint64_t uid = ino;
  unsigned char *attr;
  uint16_t len;

  iscsi_trace(TRACE_OSDFS, "osdfs_read_inode(ino 0x%x, major %i, minor %i)\n",
        (unsigned) ino, MAJOR(dev), MINOR(dev));

  /*  Get object attributes for rest of inode */

  if ((attr=iscsi_malloc_atomic(sizeof(struct inode)))==NULL) {
    iscsi_trace_error("iscsi_malloc_atomic() failed\n");
  }
  if (osd_get_one_attr((void *)&dev, root_gid, uid, 0x30000000, 0x0, sizeof(struct inode), &osd_exec_via_scsi, &len, attr)!=0) {
    iscsi_trace_error("osd_get_one_attr() failed\n");
    return;
  }

  inode->i_size   = ((struct inode *)(attr))->i_size;
  inode->i_mode   = ((struct inode *)(attr))->i_mode;
  inode->i_nlink  = ((struct inode *)(attr))->i_nlink;
  inode->i_gid    = ((struct inode *)(attr))->i_gid;
  inode->i_uid    = ((struct inode *)(attr))->i_uid;
  inode->i_ctime  = ((struct inode *)(attr))->i_ctime;
  inode->i_atime  = ((struct inode *)(attr))->i_atime;
  inode->i_mtime  = ((struct inode *)(attr))->i_mtime;

  iscsi_free_atomic(attr);

  osdfs_set_ops(inode);
}

void osdfs_dirty_inode(struct inode *inode) {
  iscsi_trace(TRACE_OSDFS, "osdfs_dirty_inode(ino 0x%x)\n", (unsigned) inode->i_ino);
}

void osdfs_write_inode(struct inode *inode, int sync) {
  ino_t ino = inode->i_ino;
  kdev_t dev = inode->i_sb->s_dev;
  uint64_t uid = ino;

  iscsi_trace(TRACE_OSDFS, "osdfs_write_inode(0x%llx)\n", uid);

  if (osd_set_one_attr((void *)&dev, root_gid, uid, 0x30000000, 0x1, sizeof(struct inode), (void *) inode, &osd_exec_via_scsi)!=0) {
    iscsi_trace_error("osd_set_one_attr() failed\n");
  }
  inode->i_state &= ~I_DIRTY;
}

void osdfs_put_inode(struct inode *inode) {
  iscsi_trace(TRACE_OSDFS, "osdfs_put_inode(0x%x)\n", (unsigned) inode->i_ino);
}

void osdfs_delete_inode(struct inode *inode) {
  iscsi_trace(TRACE_OSDFS, "osdfs_delete_inode(%lu)\n", inode->i_ino);
  clear_inode(inode);
}

void osdfs_put_super(struct super_block *sb) {
  iscsi_trace_error("osdfs_put_super() not implemented\n");
}

void osdfs_write_super(struct super_block *sb) {
  iscsi_trace_error("osdfs_write_super() not implemented\n");
}

void osdfs_write_super_lockfs(struct super_block *sb) {
  iscsi_trace_error("osdfs_write_super_lockfs() not implemented\n");
}

void osdfs_unlockfs(struct super_block *sb) {
  iscsi_trace_error("osdfs_unlockfs() not implemented\n");
}

int osdfs_statfs(struct super_block *sb, struct statfs *buff) {
  iscsi_trace(TRACE_OSDFS, "statfs()\n");
  buff->f_type    = OSDFS_MAGIC;
  buff->f_bsize   = PAGE_CACHE_SIZE;
  buff->f_blocks  = 256;
  buff->f_bfree   = 128;
  buff->f_bavail  = 64;
  buff->f_files   = 0;
  buff->f_ffree   = 0;
  buff->f_namelen = MAX_NAME_LEN;

  return 0;
}

int osdfs_remount_fs(struct super_block *sb, int *i, char *c) {
  iscsi_trace_error("osdfs_remount_fs() not implemented\n");

  return -1;
}

void osdfs_clear_inode(struct inode *inode) {
  iscsi_trace(TRACE_OSDFS, "osdfs_clear_inode(ino %lu)\n", inode->i_ino);
}

void osdfs_umount_begin(struct super_block *sb) {
  iscsi_trace_error("osdfs_unmount_begin() not implemented\n");
}

static struct super_operations osdfs_ops = {
  read_inode: osdfs_read_inode,
  dirty_inode: osdfs_dirty_inode,
  write_inode: osdfs_write_inode,
  put_inode: osdfs_put_inode,
  delete_inode: osdfs_delete_inode,
  put_super: osdfs_put_super,
  write_super: osdfs_write_super,
  write_super_lockfs: osdfs_write_super_lockfs,
  unlockfs: osdfs_unlockfs,
  statfs: osdfs_statfs,
  remount_fs: osdfs_remount_fs,
  clear_inode: osdfs_clear_inode,
  umount_begin: osdfs_umount_begin
};


/*
 * Inode operations for directories
 */


static int osdfs_create(struct inode *dir, struct dentry *dentry, int mode) {

  iscsi_trace(TRACE_OSDFS, "osdfs_create(\"%s\")\n", dentry->d_name.name);
  if (osdfs_mknod(dir, dentry, mode | S_IFREG, 0)!=0) {
    iscsi_trace_error("osdfs_mknod() failed\n");
    return -1;
  } 
  iscsi_trace(TRACE_OSDFS, "file \"%s\" is inode 0x%x\n", dentry->d_name.name, (unsigned) dentry->d_inode->i_ino);

  return 0;
}

static struct dentry * osdfs_lookup(struct inode *dir, struct dentry *dentry) {
  const char *name = dentry->d_name.name;
  struct inode *inode = NULL;
  ino_t ino;
  kdev_t dev = dir->i_sb->s_dev;
  uint64_t uid = dir->i_ino;
  char *entries;
  uint32_t num_entries;
  uint64_t size;

  iscsi_trace(TRACE_OSDFS, "osdfs_lookup(\"%s\" in dir ino %lu)\n", name, dir->i_ino);

  /*  Get directory entries */

  ISCSI_LOCK(&g_mutex, return NULL);
  if (entries_get(dev, uid, &entries, &num_entries, &size)!=0) {
    iscsi_trace_error("entries_get() failed\n");
    ISCSI_UNLOCK(&g_mutex, return NULL);
    return NULL;
  }
  ISCSI_UNLOCK(&g_mutex, return NULL);
  iscsi_trace(TRACE_OSDFS, "ino %li has %i entries\n", dir->i_ino, num_entries);

  /*  Search for this entry */

  if (num_entries) {
    char *ptr = entries;
    char *ptr2;

    do {
      if ((ptr2=strchr(ptr, '\n'))!=NULL) {
        *ptr2 = 0x0;
        ptr2 = strchr(ptr2+1, '\n');
        if (!strcmp(ptr, name)) {
          sscanf(ptr+strlen(ptr)+1, "%li", &ino);
          iscsi_trace(TRACE_OSDFS, "found \"%s\" at ino %li\n", name, ino);
          if ((inode=iget(dir->i_sb, ino))==NULL) {
            iscsi_trace_error("iget() failed\n");
            return NULL;
          }
        } 
      }
    } while (ptr2&&(ptr=ptr2+1));
    vfree(entries);
  } 
  if (!inode) {
    iscsi_trace(TRACE_OSDFS, "\"%s\" not found\n", name);
  }
  d_add(dentry, inode);

  return NULL;
}

static int osdfs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry) {
  struct inode *inode = old_dentry->d_inode;
  kdev_t dev = dir->i_sb->s_dev;
  ino_t dir_ino = dir->i_ino;
  ino_t ino = inode->i_ino;
  const char *name = dentry->d_name.name;

  if (S_ISDIR(inode->i_mode)) return -EPERM;
  iscsi_trace(TRACE_OSDFS, "osdfs_link(%lu, \"%s\")\n", ino, name);
  ISCSI_LOCK(&g_mutex, return -1);
  if (entry_add(dev, dir_ino, ino, name, &dir->i_size)!=0) {
    iscsi_trace_error("entry_add() failed\n");
    return -1;
  }
  inode->i_nlink++; 
  atomic_inc(&inode->i_count); 
  osdfs_write_inode(inode, 0);
  osdfs_write_inode(dir, 0);
  d_instantiate(dentry, inode);
  ISCSI_UNLOCK(&g_mutex, return -1);

  return 0; 
}

static int osdfs_unlink(struct inode * dir, struct dentry *dentry) {
  kdev_t dev = dir->i_sb->s_dev;
  struct inode *inode = dentry->d_inode;
  ino_t dir_ino = dir->i_ino;
  ino_t ino = dentry->d_inode->i_ino;
  const char *name = dentry->d_name.name;

  iscsi_trace(TRACE_OSDFS, "osdfs_unlink(\"%s\", ino 0x%x)\n", name, (unsigned) ino);
  ISCSI_LOCK(&g_mutex, return -1);
  switch (inode->i_mode & S_IFMT) {
    case S_IFREG:
    case S_IFLNK:
      break;
    case S_IFDIR:
      if (entry_num(dev, ino)) {
        iscsi_trace_error("directory 0x%x still has %i entries\n", 
                    (unsigned) ino, entry_num(dev, ino));
        ISCSI_UNLOCK(&g_mutex, return -1);
        return -ENOTEMPTY;
      }
  }
  if (entry_del(dev, dir_ino, ino, name, &(dir->i_size))!=0) {
    iscsi_trace_error("entry_del() failed\n");
    ISCSI_UNLOCK(&g_mutex, return -1);
    return -1;
  }
  osdfs_write_inode(dir, 0);
  if (--inode->i_nlink) {
    iscsi_trace(TRACE_OSDFS, "ino 0x%x still has %i links\n", (unsigned) ino, inode->i_nlink);
    osdfs_write_inode(inode, 0);
  } else {
    iscsi_trace(TRACE_OSDFS, "ino 0x%x link count reached 0, removing object\n", (unsigned) ino);
    if (osd_remove((void *)&dev, root_gid, ino, &osd_exec_via_scsi)!=0) {
      iscsi_trace_error("osd_remove() failed\n");
      return -1;
    }
  }
  ISCSI_UNLOCK(&g_mutex, return -1);

  return 0;
}

static int osdfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname) {
  struct inode *inode;

  iscsi_trace(TRACE_OSDFS, "osdfs_symlink(\"%s\"->\"%s\")\n", dentry->d_name.name, symname);
  if (osdfs_mknod(dir, dentry,  S_IRWXUGO | S_IFLNK, 0)!=0) {
    iscsi_trace_error("osdfs_mknod() failed\n");
    return -1;
  } 
  inode = dentry->d_inode;
  if (block_symlink(inode, symname, strlen(symname)+1)!=0) {
    iscsi_trace_error("block_symlink() failed\n");
    return -1;
  }
  iscsi_trace(TRACE_OSDFS, "symbolic link \"%s\" is inode %lu\n", dentry->d_name.name, inode->i_ino);

  return 0; 
}

static int osdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) {

  iscsi_trace(TRACE_OSDFS, "osdfs_mkdir(\"%s\")\n", dentry->d_name.name);
  if (osdfs_mknod(dir, dentry, mode | S_IFDIR, 0)!=0) {
    iscsi_trace_error("osdfs_mkdir() failed\n");
  } 

  return 0;
}

static int osdfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev_in) {
  struct inode *inode = NULL;
  uint64_t uid;
  struct inode attr;
  kdev_t dev = dir->i_sb->s_dev;
  const char *name = dentry->d_name.name;

  iscsi_trace(TRACE_OSDFS, "osdfs_mknod(\"%s\")\n", dentry->d_name.name);

  /*  Create object */

  if (osd_create((void *)&dev, root_gid, &osd_exec_via_scsi, &uid)!=0) {
    iscsi_trace_error("osd_create() failed\n");
    return -1;
  }

  /*  Initialize object attributes */

  memset(&attr, 0, sizeof(struct inode));
  attr.i_mode = mode;
  attr.i_uid = current->fsuid;
  attr.i_gid = current->fsgid;
  attr.i_ctime = CURRENT_TIME;
  attr.i_atime = CURRENT_TIME;
  attr.i_mtime = CURRENT_TIME;
  attr.i_nlink = 1;
  if (osd_set_one_attr((void *)&dir->i_sb->s_dev, root_gid, uid, 0x30000000, 0x1, sizeof(struct inode), 
                        &attr, &osd_exec_via_scsi)!=0) {
    iscsi_trace_error("osd_set_one_attr() failed\n");
    return -1;
  }

  /*  Assign to an inode */

  if ((inode = osdfs_get_inode(dir->i_sb, mode, dev, name, uid))==NULL) {
    iscsi_trace_error("osdfs_get_inode() failed\n");
    return -ENOSPC;
  }
  d_instantiate(dentry, inode);

  /*  Add entry to parent directory */

  if (inode->i_ino != 1) {
    ISCSI_LOCK(&g_mutex, return -1);
    if (entry_add(dev, dir->i_ino, inode->i_ino, name, &dir->i_size)!=0) {
      iscsi_trace_error("entry_add() failed\n");
      return -1;
    }
    osdfs_write_inode(dir, 0);
    ISCSI_UNLOCK(&g_mutex, return -1);
  }

  return 0;
}

static int osdfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) {
  kdev_t dev = old_dir->i_sb->s_dev;
  ino_t old_dir_ino =  old_dir->i_ino;
  ino_t new_dir_ino = new_dir->i_ino;
  ino_t old_ino = old_dentry->d_inode->i_ino;
  ino_t new_ino = new_dentry->d_inode?new_dentry->d_inode->i_ino:old_ino;
  const char *old_name = old_dentry->d_name.name;
  const char *new_name = new_dentry->d_name.name;

  iscsi_trace(TRACE_OSDFS, "old_dir = 0x%p (ino 0x%x)\n", old_dir, (unsigned) old_dir_ino);
  iscsi_trace(TRACE_OSDFS, "new_dir = 0x%p (ino 0x%x)\n", new_dir, (unsigned) new_dir_ino);
  iscsi_trace(TRACE_OSDFS, "old_dentry = 0x%p (ino 0x%x)\n", old_dentry, (unsigned) old_ino);
  iscsi_trace(TRACE_OSDFS, "new_dentry = 0x%p (ino 0x%x)\n", new_dentry, (unsigned) new_ino);

  /*
   * If we return -1, the VFS will implement a rename with a combination 
   * of osdfs_unlink() and osdfs_create(). 
   */

  /*  Delete entry from old directory */

  ISCSI_LOCK(&g_mutex, return -1);
  if (entry_del(dev, old_dir_ino, old_ino, old_name, &old_dir->i_size)!=0) {
    iscsi_trace_error("error deleting old entry \"%s\"\n", old_name);
    ISCSI_UNLOCK(&g_mutex, return -1);
    return -1;
  }
  osdfs_write_inode(old_dir, 0);
  ISCSI_UNLOCK(&g_mutex, return -1);

  /*  Unlink entry from new directory */

  if (new_dentry->d_inode) {
    iscsi_trace(TRACE_OSDFS, "unlinking existing file\n");
    if (osdfs_unlink(new_dir, new_dentry)!=0) {
      iscsi_trace_error("osdfs_unlink() failed\n");
      return -1;
    }
  }

  /*  Add entry to new directory (might be the same dir) */

  ISCSI_LOCK(&g_mutex, return -1);
  if (entry_add(dev, new_dir_ino, new_ino, new_name, &new_dir->i_size)!=0) {
    iscsi_trace_error("error adding new entry \"%s\"\n", new_name);
    ISCSI_UNLOCK(&g_mutex, return -1);
    return -1;
  }
  osdfs_write_inode(new_dir, 0);
  ISCSI_UNLOCK(&g_mutex, return -1);

  return 0;
}

static struct inode_operations osdfs_dir_inode_operations = {
	create:		osdfs_create,
	lookup:		osdfs_lookup,
	link:		osdfs_link,
	unlink:		osdfs_unlink,
	symlink:	osdfs_symlink,
	mkdir:		osdfs_mkdir,
	rmdir:		osdfs_unlink,
	mknod:		osdfs_mknod,
	rename:		osdfs_rename,
};


/*
 * File operations (regular files)
 */


static int osdfs_sync_file(struct file * file, struct dentry *dentry, int datasync) {
  iscsi_trace_error("osdfs_syncfile() not implemented\n");
  return -1;
}

static struct file_operations osdfs_file_operations = {
	read:		generic_file_read,
	write:		generic_file_write,
	mmap:		generic_file_mmap,
	fsync:		osdfs_sync_file,
};


/*
 * File operations (directories)
 */


static int osdfs_readdir(struct file * filp, void * dirent, filldir_t filldir) {
  struct dentry *dentry = filp->f_dentry;
  const char *name;
  ino_t ino = dentry->d_inode->i_ino;
  kdev_t dev = dentry->d_inode->i_sb->s_dev;
  int offset = filp->f_pos;
  char *entries, *ptr, *ptr2;
  uint32_t num_entries;
  uint64_t size;
  uint64_t uid = ino;

  name = dentry->d_name.name;
  iscsi_trace(TRACE_OSDFS, "osdfs_readdir(\"%s\", ino 0x%x, offset %i)\n", 
        name, (unsigned) ino, offset);
  ISCSI_LOCK(&g_mutex, return -1);
  if (entries_get(dev, uid, &entries, &num_entries, &size)!=0) {
    iscsi_trace_error("entries_get() failed\n");
    ISCSI_UNLOCK(&g_mutex, return -1);
    return -1;
  }
  ISCSI_UNLOCK(&g_mutex, return -1);

  /*  Update the offset if our number of entries has changed since the last  */
  /*  call to osdfs_readdir().  filp->private_data stores the number of  */
  /*  entries this directory had on the last call. */

  if (offset) {
    if (((int)filp->private_data)>num_entries) {
      filp->f_pos = offset -= (((int)filp->private_data)-num_entries);
      filp->private_data = (void *) num_entries;
    }
  } else {
    filp->private_data = (void *) num_entries;
  }

  switch (offset) {

    case 0:

      iscsi_trace(TRACE_OSDFS, "adding \".\" (ino 0x%x)\n", (unsigned) ino);
      if (filldir(dirent, ".", 1, filp->f_pos++, ino, DT_DIR) < 0) {
        iscsi_trace_error("filldir() failed for \".\"??\n");
        vfree(entries);
        return -1;
      }
   
    case 1:

      iscsi_trace(TRACE_OSDFS, "adding \"..\" (ino 0x%x)\n", (unsigned) dentry->d_parent->d_inode->i_ino);
      if (filldir(dirent, "..", 2, filp->f_pos++, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
        iscsi_trace_error("filldir() failed for \"..\"??\n");
        vfree(entries);
        return -1;
      }

    default:

      if (!num_entries) return 0;
      ptr = entries;
      offset -= 2;
      do {
        if ((ptr2=strchr(ptr, '\n'))!=NULL) {
          *ptr2 = 0x0; 
          ptr2 = strchr(ptr2+1, '\n');
          if (offset>0) {
            offset--;
          } else {
            sscanf(ptr+strlen(ptr)+1, "%li", &ino);
            iscsi_trace(TRACE_OSDFS, "adding \"%s\" (ino 0x%x)\n", ptr, (unsigned) ino);
            if (filldir(dirent, ptr, strlen(ptr), filp->f_pos++, ino, DT_UNKNOWN) < 0) {
              vfree(entries);
              return 0;
            }
          }
        } 
      } while (ptr2&&(ptr=ptr2+1));
  }
  if (num_entries) vfree(entries);

  return 0;
}

static struct file_operations osdfs_dir_operations = {
	read:		generic_read_dir,
	readdir:        osdfs_readdir,
	fsync:		osdfs_sync_file,
};


/*
 * Address space operations
 */


static int osdfs_readpage(struct file *file, struct page * page) {
  uint64_t Offset = page->index<<PAGE_CACHE_SHIFT;
  uint64_t Length = 1<<PAGE_CACHE_SHIFT;
  struct inode *inode = page->mapping->host;
  kdev_t dev = inode->i_sb->s_dev;
  ino_t ino = inode->i_ino;
  uint64_t len;
  uint64_t uid = ino;
 
  iscsi_trace(TRACE_OSDFS, "osdfs_readpage(ino %lu, Offset %llu, Length %llu)\n", ino, Offset, Length);
  if (Offset+Length>inode->i_size) {
    len =  inode->i_size-Offset;
  } else {
    len = Length;
  }
  if (!Page_Uptodate(page)) {
    memset(kmap(page), 0, PAGE_CACHE_SIZE);
    if (osd_read((void *)&dev, root_gid, uid, Offset, len, page->virtual, 0, &osd_exec_via_scsi)!=0) {
      iscsi_trace_error("osd_read() failed\n");
      UnlockPage(page);
      return -1;;
    }
    kunmap(page);
    flush_dcache_page(page);
    SetPageUptodate(page);
  } else {
    iscsi_trace_error("The page IS up to date???\n");
  }
  UnlockPage(page);

  return 0;
}

static int osdfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) {
  iscsi_trace(TRACE_OSDFS, "osdfs_prepare_write(ino %lu, offset %u, to %u)\n", page->mapping->host->i_ino, offset, to);
  return 0;
}

static int osdfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) {
  uint64_t Offset = (page->index<<PAGE_CACHE_SHIFT)+offset;
  uint64_t Length = to-offset;
  struct inode *inode = page->mapping->host;
  kdev_t dev = inode->i_sb->s_dev;
  ino_t ino = inode->i_ino;
  uint64_t uid = ino;

  iscsi_trace(TRACE_OSDFS, "osdfs_commit_write(ino %lu, offset %u, to %u, Offset %llu, Length %llu)\n",
        ino, offset, to, Offset, Length);
  if (osd_write((void *)&dev, root_gid, uid, Offset, Length, page->virtual+offset, 0, &osd_exec_via_scsi)!=0) {
    iscsi_trace_error("osd_write() failed\n");
    return -1;
  }
  if (Offset+Length>inode->i_size) {
    inode->i_size = Offset+Length;  
  }
  osdfs_write_inode(inode, 0);

  return 0;
}

static struct address_space_operations osdfs_aops = {
	readpage:	osdfs_readpage,
	writepage:	NULL,
	prepare_write:	osdfs_prepare_write,
	commit_write:	osdfs_commit_write
};


/*
 * Superblock operations
 */


static struct super_block *osdfs_read_super(struct super_block *sb, void *data, int silent) {
  char opt[64];
  char *ptr, *ptr2;
  struct inode attr;
  struct inode *inode;

  iscsi_trace(TRACE_OSDFS, "osdfs_read_super(major %i minor %i)\n", MAJOR(sb->s_dev),  MINOR(sb->s_dev));

  root_gid = root_uid = 0;

  /* Parse options */

  ptr = (char *)data;
  while (ptr&&strlen(ptr)) {
    if ((ptr2=strchr(ptr, ','))) {
      strncpy(opt, ptr, ptr2-ptr);
      opt[ptr2-ptr] = 0x0;
      ptr = ptr2+1;
    } else {
      strcpy(opt, ptr);
      ptr = 0x0;
    }
    if (!strncmp(opt, "uid=", 3)) {
      if (sscanf(opt, "uid=0x%Lx", &root_uid)!=1) {
        iscsi_trace_error("malformed option \"%s\"\n", opt);
        return NULL;
      }
    } else if (!strncmp(opt, "gid=", 3)) {
      if (sscanf(opt, "gid=0x%x", &root_gid)!=1) {
        iscsi_trace_error("malformed option \"%s\"\n", opt);
        return NULL;
      }
    } else {
      iscsi_trace_error("unknown option \"%s\"\n", opt);
      return NULL;
    }
  }
 
  /*  Initialize superblock */

  sb->s_blocksize      = PAGE_CACHE_SIZE;
  sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
  sb->s_magic          = OSDFS_MAGIC;
  sb->s_op             = &osdfs_ops;

  if ((root_uid==0)||(root_gid==0)) {

    /*  Create group object for root directory */

    if (osd_create_group((void *)&sb->s_dev, &osd_exec_via_scsi, &root_gid)!=0) {
      iscsi_trace_error("osd_create_group() failed\n");
      return NULL;
    }
    printf("** ROOT DIRECTORY GROUP OBJECT IS 0x%x **\n", root_gid);

    /*  Create user object for root directory */

    if (osd_create((void *)&sb->s_dev, root_gid, &osd_exec_via_scsi, &root_uid)!=0) {
      iscsi_trace_error("osd_create() failed\n");
      return NULL;
    }
    printf("** ROOT DIRECTORY USER OBJECT IS 0x%llx **\n", root_uid);

    /*  Initialize Attributes */

    memset(&attr, 0, sizeof(struct inode));
    attr.i_mode = S_IFDIR | 0755;
    if (osd_set_one_attr((void *)&sb->s_dev, root_gid, root_uid, 0x30000000, 0x1, sizeof(struct inode), (void *) &attr, &osd_exec_via_scsi)!=0) {
      iscsi_trace_error("osd_set_one_attr() failed\n");
      return NULL;
    }
  } else {
    iscsi_trace(TRACE_OSDFS, "using root directory in 0x%x:0x%llx\n", root_gid, root_uid);
  } 

  /*  Create inode for root directory */
    
  if ((inode=osdfs_get_inode(sb, S_IFDIR | 0755, 0, "/", root_uid))==NULL) {
    iscsi_trace_error("osdfs_get_inode() failed\n");
    return NULL;
  }
  if ((sb->s_root=d_alloc_root(inode))==NULL) {
    iscsi_trace_error("d_alloc_root() failed\n");
    iput(inode);
    return NULL;
  }

  return sb;
}

static DECLARE_FSTYPE_DEV(osdfs_fs_type, "osdfs", osdfs_read_super);


/*
 * Module operations
 */


static int __init init_osdfs_fs(void) {
  iscsi_trace(TRACE_OSDFS, "init_osdfs_fs()\n");
  ISCSI_MUTEX_INIT(&g_mutex, return -1);
  return register_filesystem(&osdfs_fs_type);
}

static void __exit exit_osdfs_fs(void) {
  iscsi_trace(TRACE_OSDFS, "exit_osdfs_fs()\n");
  ISCSI_MUTEX_DESTROY(&g_mutex, printk("mutex_destroy() failed\n"));
  unregister_filesystem(&osdfs_fs_type);
}

module_init(init_osdfs_fs)
module_exit(exit_osdfs_fs)