/*
|
/*
|
* 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;
|
}
|
}
|
|
|