/*
* 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)
syntax highlighted by Code2HTML, v. 0.9.1