1138 lines
28 KiB
C
1138 lines
28 KiB
C
/*
|
|
* dir.c
|
|
*
|
|
* Copyright (C) 1995 by Volker Lendecke
|
|
*
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#ifdef MODULE
|
|
#include <linux/module.h>
|
|
#include <linux/version.h>
|
|
#endif
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/malloc.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/ncp_fs.h>
|
|
#include <asm/segment.h>
|
|
#include <linux/errno.h>
|
|
#include "ncplib.h"
|
|
|
|
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
|
|
#define ROUND_UP(x) (((x)+3) & ~3)
|
|
|
|
static int
|
|
ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count);
|
|
|
|
static int
|
|
ncp_readdir(struct inode *inode, struct file *filp,
|
|
void *dirent, filldir_t filldir);
|
|
static int
|
|
ncp_read_volume_list(struct ncp_server *server, int start_with,
|
|
int cache_size);
|
|
|
|
static int
|
|
ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
|
|
int cache_size, struct ncp_dirent *entry);
|
|
|
|
static int
|
|
get_pname(struct inode *dir, const char *name, int len,
|
|
char **res_path, int *res_len);
|
|
|
|
static int
|
|
get_pname_static(struct inode *dir, const char *name, int len,
|
|
char *path, int *res_len);
|
|
|
|
static struct inode *
|
|
ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo);
|
|
|
|
static void
|
|
put_pname(char *path);
|
|
|
|
static struct ncp_inode_info *
|
|
ncp_find_inode(struct ncp_server *server, const char *path);
|
|
|
|
static int
|
|
ncp_lookup(struct inode *dir, const char *__name,
|
|
int len, struct inode **result);
|
|
|
|
static int
|
|
ncp_create(struct inode *dir, const char *name, int len, int mode,
|
|
struct inode **result);
|
|
|
|
static int
|
|
ncp_mkdir(struct inode *dir, const char *name, int len, int mode);
|
|
|
|
static int
|
|
ncp_rmdir(struct inode *dir, const char *name, int len);
|
|
|
|
static int
|
|
ncp_unlink(struct inode *dir, const char *name, int len);
|
|
|
|
static int
|
|
ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
|
|
struct inode *new_dir, const char *new_name, int new_len);
|
|
|
|
static int
|
|
date_dos2unix(unsigned short time,unsigned short date);
|
|
|
|
/*
|
|
static void
|
|
date_unix2dos(int unix_date,unsigned short *time, unsigned short *date);
|
|
*/
|
|
|
|
static inline void
|
|
str_upper(char *name)
|
|
{
|
|
while (*name) {
|
|
if (*name >= 'a' && *name <= 'z')
|
|
*name -= ('a' - 'A');
|
|
name++;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
str_lower(char *name)
|
|
{
|
|
while (*name) {
|
|
if (*name >= 'A' && *name <= 'Z')
|
|
*name += ('a' - 'A');
|
|
name ++;
|
|
}
|
|
}
|
|
|
|
static struct file_operations ncp_dir_operations = {
|
|
NULL, /* lseek - default */
|
|
ncp_dir_read, /* read - bad */
|
|
NULL, /* write - bad */
|
|
ncp_readdir, /* readdir */
|
|
NULL, /* select - default */
|
|
ncp_ioctl, /* ioctl - default */
|
|
NULL, /* mmap */
|
|
NULL, /* no special open code */
|
|
NULL, /* no special release code */
|
|
NULL /* fsync */
|
|
};
|
|
|
|
struct inode_operations ncp_dir_inode_operations = {
|
|
&ncp_dir_operations, /* default directory file ops */
|
|
ncp_create, /* create */
|
|
ncp_lookup, /* lookup */
|
|
NULL, /* link */
|
|
ncp_unlink, /* unlink */
|
|
NULL, /* symlink */
|
|
ncp_mkdir, /* mkdir */
|
|
ncp_rmdir, /* rmdir */
|
|
NULL, /* mknod */
|
|
ncp_rename, /* rename */
|
|
NULL, /* readlink */
|
|
NULL, /* follow_link */
|
|
NULL, /* bmap */
|
|
NULL, /* truncate */
|
|
NULL, /* permission */
|
|
NULL /* smap */
|
|
};
|
|
|
|
|
|
static int
|
|
ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count)
|
|
{
|
|
return -EISDIR;
|
|
}
|
|
|
|
/* In ncpfs, we have unique inodes across all mounted filesystems, for
|
|
all inodes that are in memory. That's why it's enough to index the
|
|
directory cache by the inode number. */
|
|
|
|
static unsigned long c_ino = 0;
|
|
static int c_size;
|
|
static int c_seen_eof;
|
|
static int c_last_returned_index;
|
|
static struct ncp_dirent* c_entry = NULL;
|
|
|
|
static int
|
|
ncp_readdir(struct inode *inode, struct file *filp,
|
|
void *dirent, filldir_t filldir)
|
|
{
|
|
int result, i = 0;
|
|
int index = 0;
|
|
struct ncp_dirent *entry = NULL;
|
|
struct ncp_server *server = NCP_SERVER(inode);
|
|
|
|
DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos);
|
|
DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
|
|
inode->i_ino, c_ino);
|
|
|
|
if (!inode || !S_ISDIR(inode->i_mode)) {
|
|
printk("ncp_readdir: inode is NULL or not a directory\n");
|
|
return -EBADF;
|
|
}
|
|
|
|
if (c_entry == NULL)
|
|
{
|
|
i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE;
|
|
c_entry = (struct ncp_dirent *) ncp_kmalloc(i, GFP_KERNEL);
|
|
if (c_entry == NULL) {
|
|
printk("ncp_readdir: no MEMORY for cache\n");
|
|
return -ENOMEM;
|
|
}
|
|
for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) {
|
|
c_entry[i].path =
|
|
(char *) ncp_kmalloc(NCP_MAXNAMELEN + 1,
|
|
GFP_KERNEL);
|
|
if (c_entry[i].path == NULL) {
|
|
DPRINTK("ncp_readdir: could not alloc path\n");
|
|
while (--i>=0)
|
|
kfree(c_entry[i].path);
|
|
kfree(c_entry);
|
|
c_entry = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (filp->f_pos == 0) {
|
|
ncp_invalid_dir_cache(inode->i_ino);
|
|
}
|
|
|
|
if (inode->i_ino == c_ino) {
|
|
for (i = 0; i < c_size; i++) {
|
|
if (filp->f_pos == c_entry[i].f_pos) {
|
|
entry = &c_entry[i];
|
|
c_last_returned_index = i;
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if ((entry == NULL) && c_seen_eof)
|
|
return 0;
|
|
}
|
|
|
|
if (entry == NULL) {
|
|
DPRINTK("ncp_readdir: Not found in cache.\n");
|
|
|
|
if (inode->i_ino == (int)&(server->root)) {
|
|
|
|
result = ncp_read_volume_list(server, filp->f_pos,
|
|
NCP_READDIR_CACHE_SIZE);
|
|
DPRINTK("ncp_read_volume_list returned %d\n", result);
|
|
|
|
} else {
|
|
|
|
result = ncp_do_readdir(server, inode, filp->f_pos,
|
|
NCP_READDIR_CACHE_SIZE,
|
|
c_entry);
|
|
DPRINTK("ncp_readdir returned %d\n", result);
|
|
}
|
|
|
|
|
|
if (result < 0) {
|
|
c_ino = 0;
|
|
return result;
|
|
}
|
|
|
|
if (result > 0) {
|
|
c_seen_eof = (result < NCP_READDIR_CACHE_SIZE);
|
|
c_ino = inode->i_ino;
|
|
c_size = result;
|
|
entry = c_entry;
|
|
c_last_returned_index = 0;
|
|
index = 0;
|
|
|
|
for (i = 0; i < c_size; i++) {
|
|
str_lower(c_entry[i].path);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entry == NULL) {
|
|
/* Nothing found, even from a ncp call */
|
|
return 0;
|
|
}
|
|
|
|
while (index < c_size) {
|
|
|
|
/* We found it. For getwd(), we have to return the
|
|
correct inode in d_ino if the inode is currently in
|
|
use. Otherwise the inode number does not
|
|
matter. (You can argue a lot about this..) */
|
|
|
|
int path_len;
|
|
int len;
|
|
struct ncp_inode_info *ino_info;
|
|
char complete_path[NCP_MAXPATHLEN];
|
|
|
|
len = strlen(entry->path);
|
|
if ((result = get_pname_static(inode, entry->path, len,
|
|
complete_path,
|
|
&path_len)) < 0)
|
|
return result;
|
|
|
|
ino_info = ncp_find_inode(server, complete_path);
|
|
|
|
/* Some programs seem to be confused about a zero
|
|
inode number, so we set it to one. Thanks to
|
|
Gordon Chaffee for this one. */
|
|
if (ino_info == NULL) {
|
|
ino_info = (struct ncp_inode_info *) 1;
|
|
}
|
|
|
|
DDPRINTK("ncp_readdir: entry->path = %s\n", entry->path);
|
|
DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
|
|
|
|
if (filldir(dirent, entry->path, len,
|
|
entry->f_pos, (ino_t)ino_info) < 0) {
|
|
break;
|
|
}
|
|
|
|
if ( (inode->i_ino != c_ino)
|
|
|| (entry->f_pos != filp->f_pos)) {
|
|
/* Someone has destroyed the cache while we slept
|
|
in filldir */
|
|
break;
|
|
}
|
|
filp->f_pos += 1;
|
|
index += 1;
|
|
entry += 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size)
|
|
{
|
|
struct ncp_dirent *entry = c_entry;
|
|
|
|
int total_count = 0;
|
|
int i;
|
|
|
|
void fill_one(char *name)
|
|
{
|
|
if (total_count < fpos) {
|
|
DPRINTK("ncp_do_readdir: skipped file: %s\n", name);
|
|
} else if (total_count >= fpos + cache_size) {
|
|
return;
|
|
} else {
|
|
DPRINTK("ncp_do_readdir: found file: %s\n", name);
|
|
|
|
entry->attr = aDIR;
|
|
entry->mtime = 0;
|
|
entry->ctime = 0;
|
|
entry->atime = 0;
|
|
entry->size = 1024;
|
|
entry->f_pos = total_count;
|
|
strcpy(entry->path, name);
|
|
entry += 1;
|
|
}
|
|
total_count += 1;
|
|
}
|
|
|
|
for (i=0; i<NCP_NUMBER_OF_VOLUMES; i++) {
|
|
|
|
struct ncp_volume_info info;
|
|
|
|
if (ncp_get_volume_info_with_number(server, i, &info) != 0) {
|
|
return total_count;
|
|
}
|
|
|
|
if (strlen(info.volume_name) > 0) {
|
|
fill_one(info.volume_name);
|
|
}
|
|
}
|
|
fill_one(".");
|
|
fill_one("..");
|
|
return (total_count - fpos);
|
|
}
|
|
|
|
static int
|
|
ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
|
|
int cache_size, struct ncp_dirent *entry)
|
|
{
|
|
struct ncp_filesearch_info fsinfo;
|
|
struct ncp_file_info finfo;
|
|
|
|
int total_count = 0;
|
|
|
|
void fill_one(int attr, char *name,
|
|
int upd_time, int upd_date,
|
|
int cr_date, int ac_date, int length)
|
|
{
|
|
if (total_count < fpos) {
|
|
DPRINTK("ncp_do_readdir: skipped file: %s\n", name);
|
|
} else if (total_count >= fpos + cache_size) {
|
|
return;
|
|
} else {
|
|
DPRINTK("ncp_do_readdir: found file: %s\n", name);
|
|
|
|
entry->attr = attr;
|
|
if ((attr & aDIR) == 0) {
|
|
entry->mtime = date_dos2unix(upd_time,
|
|
upd_date);
|
|
entry->ctime = date_dos2unix(0, cr_date);
|
|
entry->atime = date_dos2unix(0, ac_date);
|
|
entry->size = length;
|
|
} else {
|
|
/* Sorry, I do not know how to get the
|
|
* values for directories :-(. Mail
|
|
* lendecke@namu01.gwdg.de if you
|
|
* know more. */
|
|
entry->mtime = 0;
|
|
entry->ctime = 0;
|
|
entry->atime = 0;
|
|
entry->size = 1024;
|
|
}
|
|
entry->f_pos = total_count;
|
|
strcpy(entry->path, name);
|
|
entry += 1;
|
|
}
|
|
total_count += 1;
|
|
}
|
|
|
|
void doit(int attr) {
|
|
|
|
if (ncp_file_search_init(server, 0, NCP_FINFO(dir)->path,
|
|
&fsinfo) != 0) {
|
|
DPRINTK("could not fs init\n");
|
|
return;
|
|
}
|
|
|
|
while (ncp_file_search_continue(server, &fsinfo, attr, "*",
|
|
&finfo) == 0) {
|
|
|
|
fill_one(attr, finfo.file_name,
|
|
finfo.update_time, finfo.update_date,
|
|
finfo.creation_date, finfo.access_date,
|
|
finfo.file_length);
|
|
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
fill_one(aDIR, ".", 0, 0, 0, 0, 0);
|
|
fill_one(aDIR, "..", 0, 0, 0, 0, 0);
|
|
|
|
doit(0);
|
|
doit(aDIR);
|
|
|
|
return (total_count - fpos);
|
|
}
|
|
|
|
void
|
|
ncp_init_dir_cache(void)
|
|
{
|
|
c_ino = 0;
|
|
c_entry = NULL;
|
|
}
|
|
|
|
void
|
|
ncp_invalid_dir_cache(unsigned long ino)
|
|
{
|
|
if (ino == c_ino) {
|
|
c_ino = 0;
|
|
c_seen_eof = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
ncp_free_dir_cache(void)
|
|
{
|
|
int i;
|
|
|
|
DPRINTK("ncp_free_dir_cache: enter\n");
|
|
|
|
if (c_entry == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) {
|
|
ncp_kfree_s(c_entry[i].path, NAME_MAX + 1);
|
|
}
|
|
|
|
ncp_kfree_s(c_entry,
|
|
sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE);
|
|
c_entry = NULL;
|
|
|
|
DPRINTK("ncp_free_dir_cache: exit\n");
|
|
}
|
|
|
|
|
|
/* get_pname_static: it expects the res_path to be a preallocated
|
|
string of len NCP_MAXPATHLEN. */
|
|
static int
|
|
get_pname_static(struct inode *dir, const char *name, int len,
|
|
char *path, int *res_len)
|
|
{
|
|
char *parentname = NCP_INOP(dir)->finfo.path;
|
|
int parentlen = NCP_INOP(dir)->finfo.len;
|
|
|
|
#if 1
|
|
if (parentlen != strlen(parentname)) {
|
|
printk("get_pname: parent->finfo.len = %d instead of %d\n",
|
|
parentlen, strlen(parentname));
|
|
parentlen = strlen(parentname);
|
|
}
|
|
|
|
#endif
|
|
DDPRINTK("get_pname_static: parentname = %s, len = %d\n",
|
|
parentname, parentlen);
|
|
|
|
if (len > NCP_MAXNAMELEN) {
|
|
return -ENAMETOOLONG;
|
|
}
|
|
|
|
/* Fast cheat for . */
|
|
if (len == 0 || (len == 1 && name[0] == '.')) {
|
|
|
|
memcpy(path, parentname, parentlen + 1);
|
|
*res_len = parentlen;
|
|
return 0;
|
|
}
|
|
|
|
/* Hmm, what about .. ? */
|
|
if (len == 2 && name[0] == '.' && name[1] == '.') {
|
|
|
|
char *pos = strrchr(parentname, '\\');
|
|
|
|
if ( (pos == NULL)
|
|
&& ( (parentname[parentlen-1] == ':')
|
|
|| (parentlen == 0))) {
|
|
|
|
/* We're at the top */
|
|
|
|
path[0] = '\0';
|
|
*res_len = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (pos == NULL) {
|
|
printk("ncp_make_name: Bad parent NCP-name: %s",
|
|
parentname);
|
|
return -ENODATA;
|
|
}
|
|
|
|
len = pos - parentname;
|
|
|
|
memcpy(path, parentname, len);
|
|
path[len] = '\0';
|
|
}
|
|
else if (parentlen == 0) {
|
|
memcpy(path, name, len);
|
|
path[len] = ':';
|
|
path[len+1] = '\0';
|
|
len = len+1;
|
|
}
|
|
else
|
|
{
|
|
if (len + parentlen + 2 > NCP_MAXPATHLEN)
|
|
return -ENAMETOOLONG;
|
|
|
|
memcpy(path, parentname, parentlen);
|
|
path[parentlen] = '\\';
|
|
memcpy(path + parentlen + 1, name, len);
|
|
path[parentlen + 1 + len] = '\0';
|
|
len = parentlen + len + 1;
|
|
}
|
|
|
|
*res_len = len;
|
|
|
|
DDPRINTK("get_pname: path = %s, *pathlen = %d\n",
|
|
path, *res_len);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_pname(struct inode *dir, const char *name, int len,
|
|
char **res_path, int *res_len)
|
|
{
|
|
char result[NCP_MAXPATHLEN];
|
|
int result_len;
|
|
int res;
|
|
|
|
if ((res = get_pname_static(dir,name,len,result,&result_len) != 0)) {
|
|
return res;
|
|
}
|
|
|
|
if ((*res_path = ncp_kmalloc(result_len+1, GFP_KERNEL)) == NULL) {
|
|
printk("get_pname: Out of memory while allocating name.");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
strcpy(*res_path, result);
|
|
*res_len = result_len;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
put_pname(char *path)
|
|
{
|
|
ncp_kfree_s(path, 0);
|
|
}
|
|
|
|
/* Insert a NEW ncp_inode_info into the inode tree of our filesystem,
|
|
under dir. The caller must assure that it's not already there. We
|
|
assume that path is allocated for us. */
|
|
|
|
static struct inode *
|
|
ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo)
|
|
{
|
|
struct inode *inode;
|
|
struct ncp_inode_info *new_inode_info;
|
|
struct ncp_inode_info *root;
|
|
|
|
if (!dir) {
|
|
printk("ncp_iget: dir is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!path) {
|
|
printk("ncp_iget: path is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!finfo) {
|
|
printk("ncp_iget: finfo is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info),
|
|
GFP_KERNEL);
|
|
|
|
if (new_inode_info == NULL) {
|
|
printk("ncp_iget: could not alloc mem for %s\n", path);
|
|
return NULL;
|
|
}
|
|
|
|
new_inode_info->state = INODE_LOOKED_UP;
|
|
new_inode_info->nused = 0;
|
|
new_inode_info->dir = NCP_INOP(dir);
|
|
|
|
new_inode_info->finfo = *finfo;
|
|
new_inode_info->finfo.opened = 0;
|
|
new_inode_info->finfo.path = path;
|
|
new_inode_info->finfo.len = strlen(path);
|
|
|
|
NCP_INOP(dir)->nused += 1;
|
|
|
|
/* We have to link the new inode_info into the doubly linked
|
|
list of inode_infos to make a complete linear search
|
|
possible. */
|
|
|
|
root = &(NCP_SERVER(dir)->root);
|
|
|
|
new_inode_info->prev = root;
|
|
new_inode_info->next = root->next;
|
|
root->next->prev = new_inode_info;
|
|
root->next = new_inode_info;
|
|
|
|
if (!(inode = iget(dir->i_sb, (int)new_inode_info))) {
|
|
printk("ncp_iget: iget failed!");
|
|
return NULL;
|
|
}
|
|
|
|
return inode;
|
|
}
|
|
|
|
void
|
|
ncp_free_inode_info(struct ncp_inode_info *i)
|
|
{
|
|
if (i == NULL) {
|
|
printk("ncp_free_inode: i == NULL\n");
|
|
return;
|
|
}
|
|
|
|
i->state = INODE_CACHED;
|
|
while ((i->nused == 0) && (i->state == INODE_CACHED)) {
|
|
struct ncp_inode_info *dir = i->dir;
|
|
|
|
i->next->prev = i->prev;
|
|
i->prev->next = i->next;
|
|
|
|
ncp_kfree_s(i->finfo.path, i->finfo.len+1);
|
|
ncp_kfree_s(i, sizeof(struct ncp_inode_info));
|
|
|
|
if (dir == NULL) return;
|
|
|
|
(dir->nused)--;
|
|
i = dir;
|
|
}
|
|
}
|
|
|
|
void
|
|
ncp_init_root(struct ncp_server *server)
|
|
{
|
|
struct ncp_inode_info *root = &(server->root);
|
|
|
|
DPRINTK("ncp_init_root: server %s\n", server->m.server_name);
|
|
|
|
root->finfo.opened = 0;
|
|
root->finfo.attr = aDIR;
|
|
root->finfo.size = 1024;
|
|
root->finfo.mtime = 0;
|
|
root->finfo.ctime = 0;
|
|
root->finfo.atime = 0;
|
|
|
|
server->root_path = '\0';
|
|
root->finfo.path = &(server->root_path);
|
|
root->finfo.len = 0;
|
|
|
|
root->state = INODE_LOOKED_UP;
|
|
root->nused = 1;
|
|
root->dir = NULL;
|
|
root->next = root->prev = root;
|
|
return;
|
|
}
|
|
|
|
void
|
|
ncp_free_all_inodes(struct ncp_server *server)
|
|
{
|
|
/* Here nothing should be to do. I do not know whether it's
|
|
better to leave some memory allocated or be stuck in an
|
|
endless loop */
|
|
#if 1
|
|
struct ncp_inode_info *root = &(server->root);
|
|
|
|
if (root->next != root) {
|
|
printk("ncp_free_all_inodes: INODES LEFT!!!\n");
|
|
}
|
|
|
|
while (root->next != root) {
|
|
printk("ncp_free_all_inodes: freeing inode\n");
|
|
ncp_free_inode_info(root->next);
|
|
/* In case we have an endless loop.. */
|
|
schedule();
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/* We will search the inode that belongs to this name, currently by a
|
|
complete linear search through the inodes belonging to this
|
|
filesystem. This has to be fixed. */
|
|
|
|
static struct ncp_inode_info *
|
|
ncp_find_inode(struct ncp_server *server, const char *path)
|
|
{
|
|
struct ncp_inode_info *result = &(server->root);
|
|
|
|
if (path == NULL)
|
|
return NULL;
|
|
|
|
do {
|
|
if (strcmp(result->finfo.path, path) == 0)
|
|
return result;
|
|
result = result->next;
|
|
|
|
} while (result != &(server->root));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
ncp_lookup(struct inode *dir, const char *__name, int len,
|
|
struct inode **result)
|
|
{
|
|
char *name = NULL;
|
|
struct ncp_dirent finfo;
|
|
struct ncp_server *server;
|
|
struct ncp_inode_info *result_info;
|
|
int error;
|
|
int found_in_cache;
|
|
int path_len;
|
|
|
|
*result = NULL;
|
|
|
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
|
printk("ncp_lookup: inode is NULL or not a directory.\n");
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
|
|
DPRINTK("ncp_lookup: %s\n", __name);
|
|
|
|
server = NCP_SERVER(dir);
|
|
|
|
/* Fast cheat for . */
|
|
if (len == 0 || (len == 1 && __name[0] == '.')) {
|
|
*result = dir;
|
|
return 0;
|
|
}
|
|
|
|
/* Now we will have to build up an NCP filename. */
|
|
if ((error = get_pname(dir, __name, len, &name, &path_len)) < 0) {
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
result_info = ncp_find_inode(NCP_SERVER(dir), name);
|
|
|
|
if (result_info != 0) {
|
|
|
|
if (result_info->state == INODE_CACHED)
|
|
result_info->state = INODE_LOOKED_UP;
|
|
|
|
put_pname(name);
|
|
|
|
/* Here we convert the inode_info address into an
|
|
inode number */
|
|
|
|
*result = iget(dir->i_sb, (int)result_info);
|
|
iput(dir);
|
|
|
|
if (*result == NULL) {
|
|
return -EACCES;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Ok, now we have made our name. We have to build a new
|
|
ncp_inode_info struct and insert it into the tree, if it is
|
|
a name that exists on the server */
|
|
|
|
/* If the file is in the dir cache, we do not have to ask the
|
|
server. */
|
|
|
|
found_in_cache = 0;
|
|
|
|
if (dir->i_ino == c_ino) {
|
|
int first = c_last_returned_index;
|
|
int i;
|
|
|
|
i = first;
|
|
do {
|
|
DDPRINTK("ncp_lookup: trying index: %d, name: %s\n",
|
|
i, c_entry[i].path);
|
|
if (strcmp(c_entry[i].path, __name) == 0) {
|
|
DPRINTK("ncp_lookup: found in cache!\n");
|
|
finfo = c_entry[i];
|
|
finfo.path = NULL; /* It's not ours! */
|
|
found_in_cache = 1;
|
|
break;
|
|
}
|
|
i = (i + 1) % c_size;
|
|
DDPRINTK("ncp_lookup: index %d, name %s failed\n",
|
|
i, c_entry[i].path);
|
|
} while (i != first);
|
|
}
|
|
|
|
if (found_in_cache == 0) {
|
|
|
|
char this_name[len+1];
|
|
|
|
memcpy(this_name, __name, len);
|
|
this_name[len] = 0;
|
|
|
|
if (dir->i_ino == (int)&(server->root)) {
|
|
|
|
/* We want to look up a volume. We only want
|
|
to know whether it exists, nothing more. */
|
|
int vol_no;
|
|
|
|
DPRINTK("ncp_lookup: looking up volume %s\n",
|
|
this_name);
|
|
|
|
if (ncp_get_volume_number(server,this_name,
|
|
&vol_no)!=0) {
|
|
put_pname(name);
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
|
|
finfo.attr = aDIR;
|
|
finfo.mtime = 0;
|
|
finfo.ctime = 0;
|
|
finfo.atime = 0;
|
|
finfo.size = 1024;
|
|
|
|
} else {
|
|
struct ncp_file_info ninfo;
|
|
if (ncp_get_finfo(server, 0, NCP_FINFO(dir)->path,
|
|
this_name, &ninfo) != 0) {
|
|
error = -ENOENT;
|
|
}
|
|
|
|
finfo.attr = ninfo.file_attributes;
|
|
finfo.mtime = date_dos2unix(ninfo.update_time,
|
|
ninfo.update_date);
|
|
finfo.ctime = date_dos2unix(0, ninfo.creation_date);
|
|
finfo.atime = date_dos2unix(0, ninfo.access_date);
|
|
finfo.size = (ninfo.file_attributes & aDIR) != 0
|
|
? 1024 : ninfo.file_length;
|
|
}
|
|
|
|
if (error < 0) {
|
|
put_pname(name);
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
if (!(*result = ncp_iget(dir, name, &finfo))) {
|
|
put_pname(name);
|
|
iput(dir);
|
|
return -EACCES;
|
|
}
|
|
|
|
DDPRINTK("ncp_lookup: %s => %lu\n", name, (unsigned long)result_info);
|
|
iput(dir);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ncp_create(struct inode *dir, const char *name, int len, int mode,
|
|
struct inode **result)
|
|
{
|
|
int error;
|
|
char *path = NULL;
|
|
struct ncp_dirent entry;
|
|
struct ncp_file_info finfo;
|
|
struct ncp_dirent *ino_finfo;
|
|
|
|
*result = NULL;
|
|
|
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
|
printk("ncp_create: inode is NULL or not a directory\n");
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Now we will have to build up an NCP filename. */
|
|
if ((error = get_pname(dir, name, len, &path, &len)) < 0) {
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
if (ncp_create_newfile(NCP_SERVER(dir), 0, path, 0, &finfo) != 0) {
|
|
put_pname(path);
|
|
iput(dir);
|
|
return -EACCES;
|
|
}
|
|
|
|
entry.attr = finfo.file_attributes;
|
|
entry.mtime = date_dos2unix(finfo.update_time,
|
|
finfo.update_date);
|
|
entry.ctime = date_dos2unix(0, finfo.creation_date);
|
|
entry.atime = date_dos2unix(0, finfo.access_date);
|
|
entry.size = 0;
|
|
|
|
ncp_invalid_dir_cache(dir->i_ino);
|
|
|
|
if (!(*result = ncp_iget(dir, path, &entry)) < 0) {
|
|
ncp_close_file(NCP_SERVER(dir), finfo.file_id);
|
|
put_pname(path);
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
ino_finfo = &(NCP_INOP(*result)->finfo);
|
|
ino_finfo->opened = 1;
|
|
ino_finfo->access = O_RDWR;
|
|
memcpy(&(ino_finfo->file_id), finfo.file_id, NCP_FILE_ID_LEN);
|
|
|
|
iput(dir);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ncp_mkdir(struct inode *dir, const char *name, int len, int mode)
|
|
{
|
|
int error;
|
|
char path[NCP_MAXPATHLEN];
|
|
|
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
|
printk("ncp_mkdir: inode is NULL or not a directory\n");
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Now we will have to build up an NCP filename. */
|
|
if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
if ((error = ncp_create_directory(NCP_SERVER(dir), 0, path,
|
|
0xff)) == 0) {
|
|
ncp_invalid_dir_cache(dir->i_ino);
|
|
}
|
|
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
ncp_rmdir(struct inode *dir, const char *name, int len)
|
|
{
|
|
int error;
|
|
char path[NCP_MAXPATHLEN];
|
|
|
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
|
printk("ncp_rmdir: inode is NULL or not a directory\n");
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
if (ncp_find_inode(NCP_SERVER(dir), path) != NULL) {
|
|
error = -EBUSY;
|
|
} else {
|
|
if ((error = ncp_delete_directory(NCP_SERVER(dir),
|
|
0, path)) == 0) {
|
|
ncp_invalid_dir_cache(dir->i_ino);
|
|
}
|
|
}
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
ncp_unlink(struct inode *dir, const char *name, int len)
|
|
{
|
|
int error;
|
|
char path[NCP_MAXPATHLEN];
|
|
|
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
|
printk("ncp_unlink: inode is NULL or not a directory\n");
|
|
iput(dir);
|
|
return -ENOENT;
|
|
}
|
|
if ((error = get_pname_static(dir, name, len, path, &len)) < 0) {
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
if (ncp_find_inode(NCP_SERVER(dir), path) != NULL) {
|
|
error = -EBUSY;
|
|
} else {
|
|
if ((error = ncp_erase_file(NCP_SERVER(dir), 0, path,
|
|
0)) == 0)
|
|
ncp_invalid_dir_cache(dir->i_ino);
|
|
}
|
|
|
|
iput(dir);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
|
|
struct inode *new_dir, const char *new_name, int new_len)
|
|
{
|
|
int res;
|
|
char old_path[NCP_MAXPATHLEN], new_path[NCP_MAXPATHLEN];
|
|
|
|
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
|
|
printk("ncp_rename: old inode is NULL or not a directory\n");
|
|
res = -ENOENT;
|
|
goto finished;
|
|
}
|
|
|
|
if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
|
|
printk("ncp_rename: new inode is NULL or not a directory\n");
|
|
res = -ENOENT;
|
|
goto finished;
|
|
}
|
|
|
|
res = get_pname_static(old_dir, old_name, old_len, old_path, &old_len);
|
|
if (res < 0) {
|
|
goto finished;
|
|
}
|
|
|
|
res = get_pname_static(new_dir, new_name, new_len, new_path, &new_len);
|
|
if (res < 0) {
|
|
goto finished;
|
|
}
|
|
|
|
if ( (ncp_find_inode(NCP_SERVER(old_dir), old_path) != NULL)
|
|
|| (ncp_find_inode(NCP_SERVER(new_dir), new_path) != NULL)) {
|
|
res = -EBUSY;
|
|
goto finished;
|
|
}
|
|
|
|
res = ncp_rename_file(NCP_SERVER(old_dir), 0, old_path, 0,0, new_path);
|
|
|
|
if (res == 0) {
|
|
ncp_invalid_dir_cache(old_dir->i_ino);
|
|
ncp_invalid_dir_cache(new_dir->i_ino);
|
|
}
|
|
|
|
finished:
|
|
iput(old_dir);
|
|
iput(new_dir);
|
|
return res;
|
|
}
|
|
|
|
/* The following routines are taken directly from msdos-fs */
|
|
|
|
/* Linear day numbers of the respective 1sts in non-leap years. */
|
|
|
|
static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
|
|
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
|
|
|
|
|
|
extern struct timezone sys_tz;
|
|
|
|
/*
|
|
static int
|
|
utc2local(int time)
|
|
{
|
|
return time - sys_tz.tz_minuteswest*60;
|
|
}
|
|
*/
|
|
|
|
static int
|
|
local2utc(int time)
|
|
{
|
|
return time + sys_tz.tz_minuteswest*60;
|
|
}
|
|
|
|
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
|
|
|
|
static int
|
|
date_dos2unix(unsigned short time,unsigned short date)
|
|
{
|
|
int month,year,secs;
|
|
|
|
month = ((date >> 5) & 15)-1;
|
|
year = date >> 9;
|
|
secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
|
|
((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
|
|
month < 2 ? 1 : 0)+3653);
|
|
/* days since 1.1.70 plus 80's leap day */
|
|
return local2utc(secs);
|
|
}
|
|
|
|
|
|
/* Convert linear UNIX date to a MS-DOS time/date pair. */
|
|
#if 0
|
|
static void
|
|
date_unix2dos(int unix_date,unsigned short *time, unsigned short *date)
|
|
{
|
|
int day,year,nl_day,month;
|
|
|
|
unix_date = utc2local(unix_date);
|
|
*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
|
|
(((unix_date/3600) % 24) << 11);
|
|
day = unix_date/86400-3652;
|
|
year = day/365;
|
|
if ((year+3)/4+365*year > day) year--;
|
|
day -= (year+3)/4+365*year;
|
|
if (day == 59 && !(year & 3)) {
|
|
nl_day = day;
|
|
month = 2;
|
|
}
|
|
else {
|
|
nl_day = (year & 3) || day <= 59 ? day : day-1;
|
|
for (month = 0; month < 12; month++)
|
|
if (day_n[month] > nl_day) break;
|
|
}
|
|
*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
|
|
}
|
|
#endif
|