OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [smbfs/] [dir.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*
/*
 *  dir.c
 *  dir.c
 *
 *
 *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
 *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
 *
 *
 */
 */
 
 
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/smb_fs.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smbno.h>
#include <asm/segment.h>
#include <asm/segment.h>
#include <asm/semaphore.h>
#include <asm/semaphore.h>
#include <linux/errno.h>
#include <linux/errno.h>
 
 
static int
static int
 smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);
 smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);
 
 
static int
static int
 smb_readdir(struct inode *inode, struct file *filp,
 smb_readdir(struct inode *inode, struct file *filp,
             void *dirent, filldir_t filldir);
             void *dirent, filldir_t filldir);
 
 
static struct smb_inode_info *
static struct smb_inode_info *
 smb_find_dir_inode(struct inode *parent, const char *name, int len);
 smb_find_dir_inode(struct inode *parent, const char *name, int len);
 
 
static int
static int
 smb_lookup(struct inode *dir, const char *__name,
 smb_lookup(struct inode *dir, const char *__name,
            int len, struct inode **result);
            int len, struct inode **result);
 
 
static int
static int
 smb_create(struct inode *dir, const char *name, int len, int mode,
 smb_create(struct inode *dir, const char *name, int len, int mode,
            struct inode **result);
            struct inode **result);
 
 
static int
static int
 smb_mkdir(struct inode *dir, const char *name, int len, int mode);
 smb_mkdir(struct inode *dir, const char *name, int len, int mode);
 
 
static int
static int
 smb_rmdir(struct inode *dir, const char *name, int len);
 smb_rmdir(struct inode *dir, const char *name, int len);
 
 
static int
static int
 smb_unlink(struct inode *dir, const char *name, int len);
 smb_unlink(struct inode *dir, const char *name, int len);
 
 
static int
static int
 smb_rename(struct inode *old_dir, const char *old_name, int old_len,
 smb_rename(struct inode *old_dir, const char *old_name, int old_len,
            struct inode *new_dir, const char *new_name, int new_len,
            struct inode *new_dir, const char *new_name, int new_len,
            int must_be_dir);
            int must_be_dir);
 
 
static struct file_operations smb_dir_operations =
static struct file_operations smb_dir_operations =
{
{
        NULL,                   /* lseek - default */
        NULL,                   /* lseek - default */
        smb_dir_read,           /* read - bad */
        smb_dir_read,           /* read - bad */
        NULL,                   /* write - bad */
        NULL,                   /* write - bad */
        smb_readdir,            /* readdir */
        smb_readdir,            /* readdir */
        NULL,                   /* select - default */
        NULL,                   /* select - default */
        smb_ioctl,              /* ioctl - default */
        smb_ioctl,              /* ioctl - default */
        NULL,                   /* mmap */
        NULL,                   /* mmap */
        NULL,                   /* no special open code */
        NULL,                   /* no special open code */
        NULL,                   /* no special release code */
        NULL,                   /* no special release code */
        NULL                    /* fsync */
        NULL                    /* fsync */
};
};
 
 
struct inode_operations smb_dir_inode_operations =
struct inode_operations smb_dir_inode_operations =
{
{
        &smb_dir_operations,    /* default directory file ops */
        &smb_dir_operations,    /* default directory file ops */
        smb_create,             /* create */
        smb_create,             /* create */
        smb_lookup,             /* lookup */
        smb_lookup,             /* lookup */
        NULL,                   /* link */
        NULL,                   /* link */
        smb_unlink,             /* unlink */
        smb_unlink,             /* unlink */
        NULL,                   /* symlink */
        NULL,                   /* symlink */
        smb_mkdir,              /* mkdir */
        smb_mkdir,              /* mkdir */
        smb_rmdir,              /* rmdir */
        smb_rmdir,              /* rmdir */
        NULL,                   /* mknod */
        NULL,                   /* mknod */
        smb_rename,             /* rename */
        smb_rename,             /* rename */
        NULL,                   /* readlink */
        NULL,                   /* readlink */
        NULL,                   /* follow_link */
        NULL,                   /* follow_link */
        NULL,                   /* readpage */
        NULL,                   /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        NULL,                   /* truncate */
        NULL,                   /* permission */
        NULL,                   /* permission */
        NULL                    /* smap */
        NULL                    /* smap */
};
};
 
 
static int
static int
strncasecmp(const char *s1, const char *s2, int len)
strncasecmp(const char *s1, const char *s2, int len)
{
{
        int result = 0;
        int result = 0;
 
 
        for (; len > 0; len -= 1)
        for (; len > 0; len -= 1)
        {
        {
                char c1, c2;
                char c1, c2;
 
 
                c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1;
                c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1;
                c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2;
                c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2;
                s1 += 1;
                s1 += 1;
                s2 += 1;
                s2 += 1;
 
 
                if ((result = c1 - c2) != 0 || c1 == 0)
                if ((result = c1 - c2) != 0 || c1 == 0)
                {
                {
                        return result;
                        return result;
                }
                }
        }
        }
        return result;
        return result;
}
}
 
 
struct smb_inode_info *
struct smb_inode_info *
smb_find_inode(struct smb_server *server, ino_t ino)
smb_find_inode(struct smb_server *server, ino_t ino)
{
{
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *this = root;
        struct smb_inode_info *this = root;
 
 
        do
        do
        {
        {
                if (ino == smb_info_ino(this))
                if (ino == smb_info_ino(this))
                {
                {
                        return this;
                        return this;
                }
                }
                this = this->next;
                this = this->next;
        }
        }
        while (this != root);
        while (this != root);
 
 
        return NULL;
        return NULL;
}
}
 
 
static ino_t
static ino_t
smb_fresh_inodes(struct smb_server *server, int no)
smb_fresh_inodes(struct smb_server *server, int no)
{
{
        static ino_t seed = 1;
        static ino_t seed = 1;
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *this;
        struct smb_inode_info *this;
 
 
      retry:
      retry:
        if (seed + no <= no)
        if (seed + no <= no)
        {
        {
                /* avoid inode number of 0 at wrap-around */
                /* avoid inode number of 0 at wrap-around */
                seed += no;
                seed += no;
        }
        }
        this = root;
        this = root;
        do
        do
        {
        {
                /* We assume that ino_t is unsigned! */
                /* We assume that ino_t is unsigned! */
                if (this->finfo.f_ino - seed < no)
                if (this->finfo.f_ino - seed < no)
                {
                {
                        seed += no;
                        seed += no;
                        goto retry;
                        goto retry;
                }
                }
                this = this->next;
                this = this->next;
        }
        }
        while (this != root);
        while (this != root);
 
 
        seed += no;
        seed += no;
 
 
        return seed - no;
        return seed - no;
}
}
 
 
static int
static int
smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count)
smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count)
{
{
        return -EISDIR;
        return -EISDIR;
}
}
 
 
 
 
static unsigned long c_ino = 0;
static unsigned long c_ino = 0;
static kdev_t c_dev;
static kdev_t c_dev;
static int c_size;
static int c_size;
static int c_seen_eof;
static int c_seen_eof;
static int c_last_returned_index;
static int c_last_returned_index;
static struct smb_dirent *c_entry = NULL;
static struct smb_dirent *c_entry = NULL;
 
 
static struct smb_dirent *
static struct smb_dirent *
smb_search_in_cache(struct inode *dir, unsigned long f_pos)
smb_search_in_cache(struct inode *dir, unsigned long f_pos)
{
{
        int i;
        int i;
 
 
        if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
        if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
        {
        {
                return NULL;
                return NULL;
        }
        }
        for (i = 0; i < c_size; i++)
        for (i = 0; i < c_size; i++)
        {
        {
                if (f_pos == c_entry[i].f_pos)
                if (f_pos == c_entry[i].f_pos)
                {
                {
                        c_last_returned_index = i;
                        c_last_returned_index = i;
                        return &(c_entry[i]);
                        return &(c_entry[i]);
                }
                }
        }
        }
        return NULL;
        return NULL;
}
}
 
 
static int
static int
smb_refill_dir_cache(struct smb_server *server, struct inode *dir,
smb_refill_dir_cache(struct smb_server *server, struct inode *dir,
                     unsigned long f_pos)
                     unsigned long f_pos)
{
{
        int result;
        int result;
        static struct semaphore sem = MUTEX;
        static struct semaphore sem = MUTEX;
        int i;
        int i;
        ino_t ino;
        ino_t ino;
 
 
        do
        do
        {
        {
                down(&sem);
                down(&sem);
                result = smb_proc_readdir(server, dir, f_pos,
                result = smb_proc_readdir(server, dir, f_pos,
                                          SMB_READDIR_CACHE_SIZE, c_entry);
                                          SMB_READDIR_CACHE_SIZE, c_entry);
 
 
                if (result <= 0)
                if (result <= 0)
                {
                {
                        smb_invalid_dir_cache(dir->i_ino);
                        smb_invalid_dir_cache(dir->i_ino);
                        up(&sem);
                        up(&sem);
                        return result;
                        return result;
                }
                }
                c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
                c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
                c_dev = dir->i_dev;
                c_dev = dir->i_dev;
                c_ino = dir->i_ino;
                c_ino = dir->i_ino;
                c_size = result;
                c_size = result;
                c_last_returned_index = 0;
                c_last_returned_index = 0;
 
 
                ino = smb_fresh_inodes(server, c_size);
                ino = smb_fresh_inodes(server, c_size);
                for (i = 0; i < c_size; i++)
                for (i = 0; i < c_size; i++)
                {
                {
                        c_entry[i].f_ino = ino;
                        c_entry[i].f_ino = ino;
                        ino += 1;
                        ino += 1;
                }
                }
 
 
                up(&sem);
                up(&sem);
 
 
        }
        }
        while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
        while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
 
 
        return result;
        return result;
}
}
 
 
static int
static int
smb_readdir(struct inode *dir, struct file *filp,
smb_readdir(struct inode *dir, struct file *filp,
            void *dirent, filldir_t filldir)
            void *dirent, filldir_t filldir)
{
{
        int result, i = 0;
        int result, i = 0;
        struct smb_dirent *entry = NULL;
        struct smb_dirent *entry = NULL;
        struct smb_server *server = SMB_SERVER(dir);
        struct smb_server *server = SMB_SERVER(dir);
 
 
        DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
        DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
        DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
        DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
                 dir->i_ino, c_ino);
                 dir->i_ino, c_ino);
 
 
        if ((dir == NULL) || !S_ISDIR(dir->i_mode))
        if ((dir == NULL) || !S_ISDIR(dir->i_mode))
        {
        {
                printk("smb_readdir: dir is NULL or not a directory\n");
                printk("smb_readdir: dir is NULL or not a directory\n");
                return -EBADF;
                return -EBADF;
        }
        }
        if (c_entry == NULL)
        if (c_entry == NULL)
        {
        {
                i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
                i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
                c_entry = (struct smb_dirent *) smb_vmalloc(i);
                c_entry = (struct smb_dirent *) smb_vmalloc(i);
                if (c_entry == NULL)
                if (c_entry == NULL)
                {
                {
                        printk("smb_readdir: no MEMORY for cache\n");
                        printk("smb_readdir: no MEMORY for cache\n");
                        return -ENOMEM;
                        return -ENOMEM;
                }
                }
        }
        }
        if (filp->f_pos == 0)
        if (filp->f_pos == 0)
        {
        {
                c_ino = 0;
                c_ino = 0;
                c_dev = 0;
                c_dev = 0;
                c_seen_eof = 0;
                c_seen_eof = 0;
 
 
                if (filldir(dirent, ".", 1, filp->f_pos,
                if (filldir(dirent, ".", 1, filp->f_pos,
                            smb_info_ino(SMB_INOP(dir))) < 0)
                            smb_info_ino(SMB_INOP(dir))) < 0)
                {
                {
                        return 0;
                        return 0;
                }
                }
                filp->f_pos += 1;
                filp->f_pos += 1;
        }
        }
        if (filp->f_pos == 1)
        if (filp->f_pos == 1)
        {
        {
                if (filldir(dirent, "..", 2, filp->f_pos,
                if (filldir(dirent, "..", 2, filp->f_pos,
                            smb_info_ino(SMB_INOP(dir)->dir)) < 0)
                            smb_info_ino(SMB_INOP(dir)->dir)) < 0)
                {
                {
                        return 0;
                        return 0;
                }
                }
                filp->f_pos += 1;
                filp->f_pos += 1;
        }
        }
        entry = smb_search_in_cache(dir, filp->f_pos);
        entry = smb_search_in_cache(dir, filp->f_pos);
 
 
        if (entry == NULL)
        if (entry == NULL)
        {
        {
                if (c_seen_eof)
                if (c_seen_eof)
                {
                {
                        /* End of directory */
                        /* End of directory */
                        return 0;
                        return 0;
                }
                }
                result = smb_refill_dir_cache(server, dir, filp->f_pos);
                result = smb_refill_dir_cache(server, dir, filp->f_pos);
                if (result <= 0)
                if (result <= 0)
                {
                {
                        return result;
                        return result;
                }
                }
                entry = c_entry;
                entry = c_entry;
        }
        }
        while (entry < &(c_entry[c_size]))
        while (entry < &(c_entry[c_size]))
        {
        {
                /* We found it.  For getwd(), we have to return the
                /* We found it.  For getwd(), we have to return the
                   correct inode in d_ino if the inode is currently in
                   correct inode in d_ino if the inode is currently in
                   use. Otherwise the inode number does not
                   use. Otherwise the inode number does not
                   matter. (You can argue a lot about this..) */
                   matter. (You can argue a lot about this..) */
 
 
                struct smb_inode_info *ino_info
                struct smb_inode_info *ino_info
                = smb_find_dir_inode(dir, entry->name, entry->len);
                = smb_find_dir_inode(dir, entry->name, entry->len);
 
 
                ino_t ino = entry->f_ino;
                ino_t ino = entry->f_ino;
 
 
                if (ino_info != NULL)
                if (ino_info != NULL)
                {
                {
                        ino = smb_info_ino(ino_info);
                        ino = smb_info_ino(ino_info);
                }
                }
                DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
                DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
                DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
                DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
 
 
                if (filldir(dirent, entry->name, strlen(entry->name),
                if (filldir(dirent, entry->name, strlen(entry->name),
                            entry->f_pos, ino) < 0)
                            entry->f_pos, ino) < 0)
                {
                {
                        break;
                        break;
                }
                }
                if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
                if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
                    || (entry->f_pos != filp->f_pos))
                    || (entry->f_pos != filp->f_pos))
                {
                {
                        /* Someone has destroyed the cache while we slept
                        /* Someone has destroyed the cache while we slept
                           in filldir */
                           in filldir */
                        break;
                        break;
                }
                }
                filp->f_pos += 1;
                filp->f_pos += 1;
                entry += 1;
                entry += 1;
        }
        }
        return 0;
        return 0;
}
}
 
 
void
void
smb_init_dir_cache(void)
smb_init_dir_cache(void)
{
{
        c_ino = 0;
        c_ino = 0;
        c_dev = 0;
        c_dev = 0;
        c_entry = NULL;
        c_entry = NULL;
}
}
 
 
void
void
smb_invalid_dir_cache(unsigned long ino)
smb_invalid_dir_cache(unsigned long ino)
{
{
        /* TODO: check for dev as well */
        /* TODO: check for dev as well */
        if (ino == c_ino)
        if (ino == c_ino)
        {
        {
                c_ino = 0;
                c_ino = 0;
                c_seen_eof = 0;
                c_seen_eof = 0;
        }
        }
}
}
 
 
void
void
smb_free_dir_cache(void)
smb_free_dir_cache(void)
{
{
        if (c_entry != NULL)
        if (c_entry != NULL)
        {
        {
                smb_vfree(c_entry);
                smb_vfree(c_entry);
        }
        }
        c_entry = NULL;
        c_entry = NULL;
}
}
 
 
/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
   under dir. The caller must assure that it's not already there. We
   under dir. The caller must assure that it's not already there. We
   assume that path is allocated for us. */
   assume that path is allocated for us. */
 
 
static struct inode *
static struct inode *
smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
{
{
        struct inode *inode;
        struct inode *inode;
        struct smb_inode_info *root;
        struct smb_inode_info *root;
 
 
        if ((dir == NULL) || (new_inode_info == NULL))
        if ((dir == NULL) || (new_inode_info == NULL))
        {
        {
                printk("smb_iget: parameter is NULL\n");
                printk("smb_iget: parameter is NULL\n");
                return NULL;
                return NULL;
        }
        }
        new_inode_info->state = SMB_INODE_LOOKED_UP;
        new_inode_info->state = SMB_INODE_LOOKED_UP;
        new_inode_info->nused = 0;
        new_inode_info->nused = 0;
        new_inode_info->dir = SMB_INOP(dir);
        new_inode_info->dir = SMB_INOP(dir);
 
 
        SMB_INOP(dir)->nused += 1;
        SMB_INOP(dir)->nused += 1;
 
 
        /*
        /*
         * We have to link the new inode_info into the doubly linked
         * We have to link the new inode_info into the doubly linked
         * list of inode_infos to make a complete linear search possible.
         * list of inode_infos to make a complete linear search possible.
         */
         */
        root = &(SMB_SERVER(dir)->root);
        root = &(SMB_SERVER(dir)->root);
 
 
        new_inode_info->prev = root;
        new_inode_info->prev = root;
        new_inode_info->next = root->next;
        new_inode_info->next = root->next;
        root->next->prev = new_inode_info;
        root->next->prev = new_inode_info;
        root->next = new_inode_info;
        root->next = new_inode_info;
 
 
        if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
        if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
        {
        {
                printk("smb_iget: iget failed!");
                printk("smb_iget: iget failed!");
                /*
                /*
                 * If we blocked in iget(), another task may have referenced
                 * If we blocked in iget(), another task may have referenced
                 * the info structure ... clean up with smb_free_inode_info.
                 * the info structure ... clean up with smb_free_inode_info.
                 */
                 */
                smb_free_inode_info(new_inode_info);
                smb_free_inode_info(new_inode_info);
                return NULL;
                return NULL;
        }
        }
 
 
        return inode;
        return inode;
}
}
 
 
void
void
smb_free_inode_info(struct smb_inode_info *i)
smb_free_inode_info(struct smb_inode_info *i)
{
{
        if (i == NULL)
        if (i == NULL)
        {
        {
                printk("smb_free_inode: i == NULL\n");
                printk("smb_free_inode: i == NULL\n");
                return;
                return;
        }
        }
        i->state = SMB_INODE_CACHED;
        i->state = SMB_INODE_CACHED;
        while ((i->nused == 0) && (i->state == SMB_INODE_CACHED))
        while ((i->nused == 0) && (i->state == SMB_INODE_CACHED))
        {
        {
                struct smb_inode_info *dir = i->dir;
                struct smb_inode_info *dir = i->dir;
 
 
                i->next->prev = i->prev;
                i->next->prev = i->prev;
                i->prev->next = i->next;
                i->prev->next = i->next;
 
 
                smb_kfree_s(i, sizeof(struct smb_inode_info));
                smb_kfree_s(i, sizeof(struct smb_inode_info));
 
 
                if (dir == NULL)
                if (dir == NULL)
                {
                {
                        return;
                        return;
                }
                }
                dir->nused -= 1;
                dir->nused -= 1;
                i = dir;
                i = dir;
        }
        }
}
}
 
 
void
void
smb_init_root(struct smb_server *server)
smb_init_root(struct smb_server *server)
{
{
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *root = &(server->root);
 
 
        root->state = SMB_INODE_LOOKED_UP;
        root->state = SMB_INODE_LOOKED_UP;
        root->nused = 1;
        root->nused = 1;
        root->dir = NULL;
        root->dir = NULL;
        root->next = root->prev = root;
        root->next = root->prev = root;
 
 
        return;
        return;
}
}
 
 
void
void
smb_free_all_inodes(struct smb_server *server)
smb_free_all_inodes(struct smb_server *server)
{
{
        /* Here nothing should be to do. I do not know whether it's
        /* Here nothing should be to do. I do not know whether it's
           better to leave some memory allocated or be stuck in an
           better to leave some memory allocated or be stuck in an
           endless loop */
           endless loop */
#if 1
#if 1
        struct smb_inode_info *root = &(server->root);
        struct smb_inode_info *root = &(server->root);
 
 
        if (root->next != root)
        if (root->next != root)
        {
        {
                printk("smb_free_all_inodes: INODES LEFT!!!\n");
                printk("smb_free_all_inodes: INODES LEFT!!!\n");
        }
        }
        while (root->next != root)
        while (root->next != root)
        {
        {
                printk("smb_free_all_inodes: freeing inode\n");
                printk("smb_free_all_inodes: freeing inode\n");
                smb_free_inode_info(root->next);
                smb_free_inode_info(root->next);
                /* In case we have an endless loop.. */
                /* In case we have an endless loop.. */
                schedule();
                schedule();
        }
        }
#endif
#endif
 
 
        return;
        return;
}
}
 
 
/* This has to be called when a connection has gone down, so that all
/* This has to be called when a connection has gone down, so that all
   file-handles we got from the server are invalid */
   file-handles we got from the server are invalid */
void
void
smb_invalidate_all_inodes(struct smb_server *server)
smb_invalidate_all_inodes(struct smb_server *server)
{
{
        struct smb_inode_info *ino = &(server->root);
        struct smb_inode_info *ino = &(server->root);
 
 
        do
        do
        {
        {
                ino->finfo.opened = 0;
                ino->finfo.opened = 0;
                ino = ino->next;
                ino = ino->next;
        }
        }
        while (ino != &(server->root));
        while (ino != &(server->root));
 
 
        return;
        return;
}
}
 
 
static int
static int
compare_filename(const struct smb_server *server,
compare_filename(const struct smb_server *server,
                 const char *s1, int len, struct smb_dirent *entry)
                 const char *s1, int len, struct smb_dirent *entry)
{
{
        if (len != entry->len)
        if (len != entry->len)
        {
        {
#if 0
#if 0
                /* Check whether the entry is about to be removed */
                /* Check whether the entry is about to be removed */
                if (!entry->len)
                if (!entry->len)
                        printk("SMBFS: dead entry %s\n", entry->name);
                        printk("SMBFS: dead entry %s\n", entry->name);
#endif
#endif
                return 1;
                return 1;
        }
        }
        if (server->case_handling == CASE_DEFAULT)
        if (server->case_handling == CASE_DEFAULT)
        {
        {
                return strncasecmp(s1, entry->name, len);
                return strncasecmp(s1, entry->name, len);
        }
        }
        return strncmp(s1, entry->name, len);
        return strncmp(s1, entry->name, len);
}
}
 
 
/*
/*
 * Search for the smb_inode_info that belongs to this name,
 * Search for the smb_inode_info that belongs to this name,
 * currently by a complete linear search through the inodes
 * currently by a complete linear search through the inodes
 * belonging to this filesystem.
 * belonging to this filesystem.
 *
 *
 * Note that this returns files as well as directories.
 * Note that this returns files as well as directories.
 */
 */
static struct smb_inode_info *
static struct smb_inode_info *
smb_find_dir_inode(struct inode *parent, const char *name, int len)
smb_find_dir_inode(struct inode *parent, const char *name, int len)
{
{
        struct smb_server *server = SMB_SERVER(parent);
        struct smb_server *server = SMB_SERVER(parent);
        struct smb_inode_info *dir = SMB_INOP(parent);
        struct smb_inode_info *dir = SMB_INOP(parent);
        struct smb_inode_info *result = &(server->root);
        struct smb_inode_info *result = &(server->root);
 
 
        if (name == NULL)
        if (name == NULL)
        {
        {
                return NULL;
                return NULL;
        }
        }
        if ((len == 1) && (name[0] == '.'))
        if ((len == 1) && (name[0] == '.'))
        {
        {
                return dir;
                return dir;
        }
        }
        if ((len == 2) && (name[0] == '.') && (name[1] == '.'))
        if ((len == 2) && (name[0] == '.') && (name[1] == '.'))
        {
        {
                return dir->dir;
                return dir->dir;
        }
        }
        do
        do
        {
        {
                if (result->dir == dir)
                if (result->dir == dir)
                {
                {
                        if (compare_filename(server, name, len,
                        if (compare_filename(server, name, len,
                                             &(result->finfo)) == 0)
                                             &(result->finfo)) == 0)
                        {
                        {
                                return result;
                                return result;
                        }
                        }
                }
                }
                result = result->next;
                result = result->next;
        }
        }
        while (result != &(server->root));
        while (result != &(server->root));
 
 
        return NULL;
        return NULL;
}
}
 
 
static int
static int
smb_lookup(struct inode *dir, const char *name, int len,
smb_lookup(struct inode *dir, const char *name, int len,
           struct inode **result)
           struct inode **result)
{
{
        struct smb_dirent finfo;
        struct smb_dirent finfo;
        struct smb_inode_info *result_info;
        struct smb_inode_info *result_info;
        int error;
        int error;
        int found_in_cache;
        int found_in_cache;
 
 
        struct smb_inode_info *new_inode_info = NULL;
        struct smb_inode_info *new_inode_info = NULL;
 
 
        *result = NULL;
        *result = NULL;
 
 
        if (!dir || !S_ISDIR(dir->i_mode))
        if (!dir || !S_ISDIR(dir->i_mode))
        {
        {
                printk("smb_lookup: inode is NULL or not a directory.\n");
                printk("smb_lookup: inode is NULL or not a directory.\n");
                iput(dir);
                iput(dir);
                return -ENOENT;
                return -ENOENT;
        }
        }
        DDPRINTK("smb_lookup: %s\n", name);
        DDPRINTK("smb_lookup: %s\n", name);
 
 
        /* Fast cheat for . */
        /* Fast cheat for . */
        if (len == 0 || (len == 1 && name[0] == '.'))
        if (len == 0 || (len == 1 && name[0] == '.'))
        {
        {
                *result = dir;
                *result = dir;
                return 0;
                return 0;
        }
        }
        /* ..and for .. */
        /* ..and for .. */
        if (len == 2 && name[0] == '.' && name[1] == '.')
        if (len == 2 && name[0] == '.' && name[1] == '.')
        {
        {
                struct smb_inode_info *parent = SMB_INOP(dir)->dir;
                struct smb_inode_info *parent = SMB_INOP(dir)->dir;
 
 
                if (parent->state == SMB_INODE_CACHED)
                if (parent->state == SMB_INODE_CACHED)
                {
                {
                        parent->state = SMB_INODE_LOOKED_UP;
                        parent->state = SMB_INODE_LOOKED_UP;
                }
                }
                *result = iget(dir->i_sb, smb_info_ino(parent));
                *result = iget(dir->i_sb, smb_info_ino(parent));
                iput(dir);
                iput(dir);
                if (*result == 0)
                if (*result == 0)
                {
                {
                        return -EACCES;
                        return -EACCES;
                }
                }
                return 0;
                return 0;
        }
        }
        result_info = smb_find_dir_inode(dir, name, len);
        result_info = smb_find_dir_inode(dir, name, len);
 
 
      in_tree:
      in_tree:
        if (result_info != NULL)
        if (result_info != NULL)
        {
        {
                if (result_info->state == SMB_INODE_CACHED)
                if (result_info->state == SMB_INODE_CACHED)
                {
                {
                        result_info->state = SMB_INODE_LOOKED_UP;
                        result_info->state = SMB_INODE_LOOKED_UP;
                }
                }
                *result = iget(dir->i_sb, smb_info_ino(result_info));
                *result = iget(dir->i_sb, smb_info_ino(result_info));
                iput(dir);
                iput(dir);
 
 
                if (new_inode_info != NULL)
                if (new_inode_info != NULL)
                {
                {
                        smb_kfree_s(new_inode_info,
                        smb_kfree_s(new_inode_info,
                                    sizeof(struct smb_inode_info));
                                    sizeof(struct smb_inode_info));
                }
                }
                if (*result == NULL)
                if (*result == NULL)
                {
                {
                        return -EACCES;
                        return -EACCES;
                }
                }
                return 0;
                return 0;
        }
        }
        /* If the file is in the dir cache, we do not have to ask the
        /* If the file is in the dir cache, we do not have to ask the
           server. */
           server. */
        found_in_cache = 0;
        found_in_cache = 0;
 
 
        if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0))
        if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0))
        {
        {
                int first = c_last_returned_index;
                int first = c_last_returned_index;
                int i;
                int i;
 
 
                i = first;
                i = first;
                do
                do
                {
                {
                        if (compare_filename(SMB_SERVER(dir), name, len,
                        if (compare_filename(SMB_SERVER(dir), name, len,
                                             &(c_entry[i])) == 0)
                                             &(c_entry[i])) == 0)
                        {
                        {
                                finfo = c_entry[i];
                                finfo = c_entry[i];
                                found_in_cache = 1;
                                found_in_cache = 1;
                                break;
                                break;
                        }
                        }
                        i = (i + 1) % c_size;
                        i = (i + 1) % c_size;
                }
                }
                while (i != first);
                while (i != first);
        }
        }
        if (found_in_cache == 0)
        if (found_in_cache == 0)
        {
        {
                DPRINTK("smb_lookup: not found in cache: %s\n", name);
                DPRINTK("smb_lookup: not found in cache: %s\n", name);
                if (len > SMB_MAXNAMELEN)
                if (len > SMB_MAXNAMELEN)
                {
                {
                        iput(dir);
                        iput(dir);
                        return -ENAMETOOLONG;
                        return -ENAMETOOLONG;
                }
                }
                error = smb_proc_getattr(dir, name, len, &finfo);
                error = smb_proc_getattr(dir, name, len, &finfo);
                if (error < 0)
                if (error < 0)
                {
                {
                        iput(dir);
                        iput(dir);
                        return error;
                        return error;
                }
                }
                finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
                finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
        }
        }
        new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
        new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
                                     GFP_KERNEL);
                                     GFP_KERNEL);
 
 
        /* Here somebody else might have inserted the inode */
        /* Here somebody else might have inserted the inode */
 
 
        result_info = smb_find_dir_inode(dir, name, len);
        result_info = smb_find_dir_inode(dir, name, len);
        if (result_info != NULL)
        if (result_info != NULL)
        {
        {
                goto in_tree;
                goto in_tree;
        }
        }
 
 
        if (new_inode_info == NULL)
        if (new_inode_info == NULL)
        {
        {
                iput(dir);
                iput(dir);
                return -ENOMEM;
                return -ENOMEM;
        }
        }
        new_inode_info->finfo = finfo;
        new_inode_info->finfo = finfo;
 
 
        DPRINTK("attr: %x\n", finfo.attr);
        DPRINTK("attr: %x\n", finfo.attr);
 
 
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        {
        {
                iput(dir);
                iput(dir);
                return -EACCES;
                return -EACCES;
        }
        }
        DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info);
        DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info);
        iput(dir);
        iput(dir);
        return 0;
        return 0;
}
}
 
 
static int
static int
smb_create(struct inode *dir, const char *name, int len, int mode,
smb_create(struct inode *dir, const char *name, int len, int mode,
           struct inode **result)
           struct inode **result)
{
{
        int error;
        int error;
        struct smb_dirent entry;
        struct smb_dirent entry;
        struct smb_inode_info *new_inode_info;
        struct smb_inode_info *new_inode_info;
 
 
        *result = NULL;
        *result = NULL;
 
 
        if (!dir || !S_ISDIR(dir->i_mode))
        if (!dir || !S_ISDIR(dir->i_mode))
        {
        {
                printk("smb_create: inode is NULL or not a directory\n");
                printk("smb_create: inode is NULL or not a directory\n");
                iput(dir);
                iput(dir);
                return -ENOENT;
                return -ENOENT;
        }
        }
        new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
        new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
                                     GFP_KERNEL);
                                     GFP_KERNEL);
        if (new_inode_info == NULL)
        if (new_inode_info == NULL)
        {
        {
                iput(dir);
                iput(dir);
                return -ENOMEM;
                return -ENOMEM;
        }
        }
        error = smb_proc_create(dir, name, len, 0, CURRENT_TIME);
        error = smb_proc_create(dir, name, len, 0, CURRENT_TIME);
        if (error < 0)
        if (error < 0)
        {
        {
                smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                iput(dir);
                iput(dir);
                return error;
                return error;
        }
        }
        smb_invalid_dir_cache(dir->i_ino);
        smb_invalid_dir_cache(dir->i_ino);
 
 
        if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0)
        if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0)
        {
        {
                smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
                iput(dir);
                iput(dir);
                return error;
                return error;
        }
        }
        entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
        entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
 
 
        new_inode_info->finfo = entry;
        new_inode_info->finfo = entry;
 
 
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        if ((*result = smb_iget(dir, new_inode_info)) == NULL)
        {
        {
                iput(dir);
                iput(dir);
                return error;
                return error;
        }
        }
        iput(dir);
        iput(dir);
        return 0;
        return 0;
}
}
 
 
static int
static int
smb_mkdir(struct inode *dir, const char *name, int len, int mode)
smb_mkdir(struct inode *dir, const char *name, int len, int mode)
{
{
        int error;
        int error;
 
 
        if (!dir || !S_ISDIR(dir->i_mode))
        if (!dir || !S_ISDIR(dir->i_mode))
        {
        {
                iput(dir);
                iput(dir);
                return -EINVAL;
                return -EINVAL;
        }
        }
        if ((error = smb_proc_mkdir(dir, name, len)) == 0)
        if ((error = smb_proc_mkdir(dir, name, len)) == 0)
        {
        {
                smb_invalid_dir_cache(dir->i_ino);
                smb_invalid_dir_cache(dir->i_ino);
        }
        }
        iput(dir);
        iput(dir);
        return error;
        return error;
}
}
 
 
static int
static int
smb_rmdir(struct inode *dir, const char *name, int len)
smb_rmdir(struct inode *dir, const char *name, int len)
{
{
        int error;
        int error;
 
 
        if (!dir || !S_ISDIR(dir->i_mode))
        if (!dir || !S_ISDIR(dir->i_mode))
        {
        {
                printk("smb_rmdir: inode is NULL or not a directory\n");
                printk("smb_rmdir: inode is NULL or not a directory\n");
                iput(dir);
                iput(dir);
                return -ENOENT;
                return -ENOENT;
        }
        }
        if (smb_find_dir_inode(dir, name, len) != NULL)
        if (smb_find_dir_inode(dir, name, len) != NULL)
        {
        {
                error = -EBUSY;
                error = -EBUSY;
        } else
        } else
        {
        {
                if ((error = smb_proc_rmdir(dir, name, len)) == 0)
                if ((error = smb_proc_rmdir(dir, name, len)) == 0)
                {
                {
                        smb_invalid_dir_cache(dir->i_ino);
                        smb_invalid_dir_cache(dir->i_ino);
                }
                }
        }
        }
        iput(dir);
        iput(dir);
        return error;
        return error;
}
}
 
 
static int
static int
smb_unlink(struct inode *dir, const char *name, int len)
smb_unlink(struct inode *dir, const char *name, int len)
{
{
        int error;
        int error;
 
 
        if (!dir || !S_ISDIR(dir->i_mode))
        if (!dir || !S_ISDIR(dir->i_mode))
        {
        {
                printk("smb_unlink: inode is NULL or not a directory\n");
                printk("smb_unlink: inode is NULL or not a directory\n");
                iput(dir);
                iput(dir);
                return -ENOENT;
                return -ENOENT;
        }
        }
        if (smb_find_dir_inode(dir, name, len) != NULL)
        if (smb_find_dir_inode(dir, name, len) != NULL)
        {
        {
                error = -EBUSY;
                error = -EBUSY;
        } else
        } else
        {
        {
                if ((error = smb_proc_unlink(dir, name, len)) == 0)
                if ((error = smb_proc_unlink(dir, name, len)) == 0)
                {
                {
                        smb_invalid_dir_cache(dir->i_ino);
                        smb_invalid_dir_cache(dir->i_ino);
                }
                }
        }
        }
        iput(dir);
        iput(dir);
        return error;
        return error;
}
}
 
 
static int
static int
smb_rename(struct inode *old_dir, const char *old_name, int old_len,
smb_rename(struct inode *old_dir, const char *old_name, int old_len,
           struct inode *new_dir, const char *new_name, int new_len,
           struct inode *new_dir, const char *new_name, int new_len,
           int must_be_dir)
           int must_be_dir)
{
{
        int res;
        int res;
 
 
        if (!old_dir || !S_ISDIR(old_dir->i_mode))
        if (!old_dir || !S_ISDIR(old_dir->i_mode))
        {
        {
                printk("smb_rename: old inode is NULL or not a directory\n");
                printk("smb_rename: old inode is NULL or not a directory\n");
                res = -ENOENT;
                res = -ENOENT;
                goto finished;
                goto finished;
        }
        }
        if (!new_dir || !S_ISDIR(new_dir->i_mode))
        if (!new_dir || !S_ISDIR(new_dir->i_mode))
        {
        {
                printk("smb_rename: new inode is NULL or not a directory\n");
                printk("smb_rename: new inode is NULL or not a directory\n");
                res = -ENOENT;
                res = -ENOENT;
                goto finished;
                goto finished;
        }
        }
        if ((smb_find_dir_inode(old_dir, old_name, old_len) != NULL)
        if ((smb_find_dir_inode(old_dir, old_name, old_len) != NULL)
            || (smb_find_dir_inode(new_dir, new_name, new_len) != NULL))
            || (smb_find_dir_inode(new_dir, new_name, new_len) != NULL))
        {
        {
                res = -EBUSY;
                res = -EBUSY;
                goto finished;
                goto finished;
        }
        }
        res = smb_proc_mv(old_dir, old_name, old_len,
        res = smb_proc_mv(old_dir, old_name, old_len,
                          new_dir, new_name, new_len);
                          new_dir, new_name, new_len);
 
 
        if (res == -EEXIST)
        if (res == -EEXIST)
        {
        {
                int res1 = smb_proc_unlink(old_dir, new_name, new_len);
                int res1 = smb_proc_unlink(old_dir, new_name, new_len);
 
 
                if (res1 == 0)
                if (res1 == 0)
                {
                {
                        res = smb_proc_mv(old_dir, old_name, old_len,
                        res = smb_proc_mv(old_dir, old_name, old_len,
                                          new_dir, new_name, new_len);
                                          new_dir, new_name, new_len);
                }
                }
        }
        }
        if (res == 0)
        if (res == 0)
        {
        {
                smb_invalid_dir_cache(old_dir->i_ino);
                smb_invalid_dir_cache(old_dir->i_ino);
                smb_invalid_dir_cache(new_dir->i_ino);
                smb_invalid_dir_cache(new_dir->i_ino);
        }
        }
      finished:
      finished:
        iput(old_dir);
        iput(old_dir);
        iput(new_dir);
        iput(new_dir);
        return res;
        return res;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.