URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [ncpfs/] [dir.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * dir.c * * Copyright (C) 1995, 1996 by Volker Lendecke * */ #include <linux/config.h> #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 <linux/locks.h> #include "ncplib_kernel.h" struct ncp_dirent { struct nw_info_struct i; struct nw_search_sequence s; /* given back for i */ unsigned long f_pos; }; 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 struct inode * ncp_iget(struct inode *dir, struct nw_file_info *finfo); static struct ncp_inode_info * ncp_find_dir_inode(struct inode *dir, const char *name); 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, int must_be_dir); 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 inline int ncp_namespace(struct inode *i) { struct ncp_server *server = NCP_SERVER(i); struct nw_info_struct *info = NCP_ISTRUCT(i); return server->name_space[info->volNumber]; } static inline int ncp_preserve_case(struct inode *i) { return #ifdef CONFIG_NCPFS_OS2_NS (ncp_namespace(i) == NW_NS_OS2) || #endif /* CONFIG_NCPFS_OS2_NS */ #ifdef CONFIG_NCPFS_NFS_NS (ncp_namespace(i) == NW_NS_NFS) || #endif /* CONFIG_NCPFS_NFS_NS */ 0; } 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 */ 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 */ }; /* Here we encapsulate the inode number handling that depends upon the * mount mode: When we mount a complete server, the memory address of * the ncp_inode_info is used as the inode number. When only a single * volume is mounted, then the dirEntNum is used as the inode * number. As this is unique for the complete volume, this should * enable the NFS exportability of a ncpfs-mounted volume. */ static inline int ncp_single_volume(struct ncp_server *server) { return (server->m.mounted_vol[0] != '\0'); } inline ino_t ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info) { return ncp_single_volume(server) ? (info->finfo.i.dirEntNum == server->root.finfo.i.dirEntNum)?0:info->finfo.i.dirEntNum: (ino_t)info; } static inline int ncp_is_server_root(struct inode *inode) { struct ncp_server *s = NCP_SERVER(inode); return ( (!ncp_single_volume(s)) && (inode->i_ino == ncp_info_ino(s, &(s->root)))); } struct ncp_inode_info * ncp_find_inode(struct inode *inode) { struct ncp_server *server = NCP_SERVER(inode); struct ncp_inode_info *root = &(server->root); struct ncp_inode_info *this = root; ino_t ino = inode->i_ino; do { if (ino == ncp_info_ino(server, this)) { return this; } this = this->next; } while (this != root); return NULL; } static int ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) { return -EISDIR; } static kdev_t c_dev = 0; 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 c_lock = 0; static struct wait_queue *c_wait = NULL; static inline void ncp_lock_dircache(void) { while (c_lock) sleep_on(&c_wait); c_lock = 1; } static inline void ncp_unlock_dircache(void) { c_lock = 0; wake_up(&c_wait); } static int ncp_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir) { int result = 0; int i = 0; int index = 0; struct ncp_dirent *entry = NULL; struct ncp_server *server = NCP_SERVER(inode); struct ncp_inode_info *dir = NCP_INOP(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 (!ncp_conn_valid(server)) { return -EIO; } ncp_lock_dircache(); if (c_entry == NULL) { i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; c_entry = (struct ncp_dirent *) vmalloc(i); if (c_entry == NULL) { printk("ncp_readdir: no MEMORY for cache\n"); result = -ENOMEM; goto finished; } } if (filp->f_pos == 0) { ncp_invalid_dir_cache(inode); if (filldir(dirent,".",1, filp->f_pos, ncp_info_ino(server, dir)) < 0) { goto finished; } filp->f_pos += 1; } if (filp->f_pos == 1) { if (filldir(dirent,"..",2, filp->f_pos, ncp_info_ino(server, dir->dir)) < 0) { goto finished; } filp->f_pos += 1; } if ((inode->i_dev == c_dev) && (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) { goto finished; } } if (entry == NULL) { int entries; DDPRINTK("ncp_readdir: Not found in cache.\n"); if (ncp_is_server_root(inode)) { entries = ncp_read_volume_list(server, filp->f_pos, NCP_READDIR_CACHE_SIZE); DPRINTK("ncp_read_volume_list returned %d\n", entries); } else { entries = ncp_do_readdir(server, inode, filp->f_pos, NCP_READDIR_CACHE_SIZE, c_entry); DPRINTK("ncp_readdir returned %d\n", entries); } if (entries < 0) { c_dev = 0; c_ino = 0; result = entries; goto finished; } if (entries > 0) { c_seen_eof = (entries < NCP_READDIR_CACHE_SIZE); c_dev = inode->i_dev; c_ino = inode->i_ino; c_size = entries; entry = c_entry; c_last_returned_index = 0; index = 0; if (!ncp_preserve_case(inode)) { for (i = 0; i < c_size; i++) { str_lower(c_entry[i].i.entryName); } } } } if (entry == NULL) { /* Nothing found, even from a ncp call */ goto finished; } while (index < c_size) { ino_t ino; if (ncp_single_volume(server)) { ino = (ino_t)(entry->i.dirEntNum); } else { /* 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..) */ struct ncp_inode_info *ino_info; ino_info = ncp_find_dir_inode(inode, entry->i.entryName); /* 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; } ino = (ino_t)(ino_info); } DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName); DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->i.entryName, entry->i.nameLen, entry->f_pos, ino) < 0) { break; } if ( (inode->i_dev != c_dev) || (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; } finished: ncp_unlock_dircache(); return result; } static int ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size) { struct ncp_dirent *entry = c_entry; int total_count = 2; int i; #if 1 if (fpos < 2) { printk("OOPS, we expect fpos >= 2"); fpos = 2; } #endif 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 - fpos); } if (strlen(info.volume_name) > 0) { if (total_count < fpos) { DPRINTK("ncp_read_volumes: skipped vol: %s\n", info.volume_name); } else if (total_count >= fpos + cache_size) { return (total_count - fpos); } else { DPRINTK("ncp_read_volumes: found vol: %s\n", info.volume_name); if (ncp_lookup_volume(server, info.volume_name, &(entry->i)) != 0) { DPRINTK("ncpfs: could not lookup vol " "%s\n", info.volume_name); continue; } entry->f_pos = total_count; entry += 1; } total_count += 1; } } 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) { static struct nw_search_sequence seq; static struct inode *last_dir; static int total_count; #if 1 if (fpos < 2) { printk("OOPS, we expect fpos >= 2"); fpos = 2; } #endif DPRINTK("ncp_do_readdir: fpos = %d\n", fpos); if (fpos == 2) { last_dir = NULL; total_count = 2; } if ((fpos != total_count) || (dir != last_dir)) { total_count = 2; last_dir = dir; DPRINTK("ncp_do_readdir: re-used seq for %s\n", NCP_ISTRUCT(dir)->entryName); if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq)!=0) { DPRINTK("ncp_init_search failed\n"); return total_count - fpos; } } while (total_count < fpos + cache_size) { if (ncp_search_for_file_or_subdir(server, &seq, &(entry->i)) != 0) { return total_count - fpos; } if (total_count < fpos) { DPRINTK("ncp_do_readdir: skipped file: %s\n", entry->i.entryName); } else { DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d", entry->i.entryName, fpos, total_count); entry->s = seq; entry->f_pos = total_count; entry += 1; } total_count += 1; } return (total_count - fpos); } void ncp_init_dir_cache(void) { c_dev = 0; c_ino = 0; c_entry = NULL; } void ncp_invalid_dir_cache(struct inode *ino) { if ((ino->i_dev == c_dev) && (ino->i_ino == c_ino)) { c_dev = 0; c_ino = 0; c_seen_eof = 0; } } void ncp_free_dir_cache(void) { DPRINTK("ncp_free_dir_cache: enter\n"); if (c_entry == NULL) { return; } vfree(c_entry); c_entry = NULL; DPRINTK("ncp_free_dir_cache: exit\n"); } static struct inode * ncp_iget(struct inode *dir, struct nw_file_info *finfo) { struct inode *inode; struct ncp_inode_info *new_inode_info; struct ncp_inode_info *root; if (dir == NULL) { printk("ncp_iget: dir is NULL\n"); return NULL; } if (finfo == NULL) { 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", finfo->i.entryName); return NULL; } new_inode_info->state = NCP_INODE_LOOKED_UP; new_inode_info->nused = 0; new_inode_info->dir = NCP_INOP(dir); new_inode_info->finfo = *finfo; 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, ncp_info_ino(NCP_SERVER(dir), 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 = NCP_INODE_CACHED; while ((i->nused == 0) && (i->state == NCP_INODE_CACHED)) { struct ncp_inode_info *dir = i->dir; i->next->prev = i->prev; i->prev->next = i->next; DDPRINTK("ncp_free_inode_info: freeing %s\n", i->finfo.i.entryName); ncp_kfree_s(i, sizeof(struct ncp_inode_info)); if (dir == i) return; (dir->nused)--; i = dir; } } void ncp_init_root(struct ncp_server *server) { struct ncp_inode_info *root = &(server->root); struct nw_info_struct *i = &(root->finfo.i); unsigned short dummy; DPRINTK("ncp_init_root: server %s\n", server->m.server_name); DPRINTK("ncp_init_root: i = %x\n", (int)i); root->finfo.opened = 0; i->attributes = aDIR; i->dataStreamSize = 1024; i->dirEntNum = i->DosDirNum = 0; i->volNumber = NCP_NUMBER_OF_VOLUMES+1; /* illegal volnum */ ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate)); ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate)); i->nameLen = 0; i->entryName[0] = '\0'; root->state = NCP_INODE_LOOKED_UP; root->nused = 1; root->dir = root; root->next = root->prev = root; return; } int ncp_conn_logged_in(struct ncp_server *server) { if (server->m.mounted_vol[0] == '\0') { return 0; } str_upper(server->m.mounted_vol); if (ncp_lookup_volume(server, server->m.mounted_vol, &(server->root.finfo.i)) != 0) { return -ENOENT; } str_lower(server->root.finfo.i.entryName); return 0; } 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_dir_inode(struct inode *dir, const char *name) { struct ncp_server *server = NCP_SERVER(dir); struct nw_info_struct *dir_info = NCP_ISTRUCT(dir); struct ncp_inode_info *result = &(server->root); if (name == NULL) { return NULL; } do { if ( (result->dir->finfo.i.dirEntNum == dir_info->dirEntNum) && (result->dir->finfo.i.volNumber == dir_info->volNumber) && (strcmp(result->finfo.i.entryName, name) == 0) /* The root dir is never looked up using this * routine. Without the following test a root * directory 'sys' in a volume named 'sys' could * never be looked up, because * server->root->dir==server->root. */ && (result != &(server->root))) { 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) { struct nw_file_info finfo; struct ncp_server *server; struct ncp_inode_info *result_info; int found_in_cache; int down_case = 0; char name[len+1]; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_lookup: inode is NULL or not a directory.\n"); iput(dir); return -ENOENT; } server = NCP_SERVER(dir); if (!ncp_conn_valid(server)) { iput(dir); return -EIO; } DPRINTK("ncp_lookup: %s, len %d\n", __name, len); /* Fast cheat for . */ if (len == 0 || (len == 1 && __name[0] == '.')) { *result = dir; return 0; } /* ..and for .. */ if (len == 2 && __name[0] == '.' && __name[1] == '.') { struct ncp_inode_info *parent = NCP_INOP(dir)->dir; if (parent->state == NCP_INODE_CACHED) { parent->state = NCP_INODE_LOOKED_UP; } *result = iget(dir->i_sb, ncp_info_ino(server, parent)); iput(dir); if (*result == 0) { return -EACCES; } else { return 0; } } memcpy(name, __name, len); name[len] = 0; lock_super(dir->i_sb); result_info = ncp_find_dir_inode(dir, name); if (result_info != 0) { if (result_info->state == NCP_INODE_CACHED) { result_info->state = NCP_INODE_LOOKED_UP; } /* Here we convert the inode_info address into an inode number */ *result = iget(dir->i_sb, ncp_info_ino(server, result_info)); unlock_super(dir->i_sb); iput(dir); if (*result == NULL) { return -EACCES; } return 0; } /* If the file is in the dir cache, we do not have to ask the server. */ found_in_cache = 0; ncp_lock_dircache(); if ((dir->i_dev == c_dev) && (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].i.entryName); if (strcmp(c_entry[i].i.entryName, name) == 0) { DPRINTK("ncp_lookup: found in cache!\n"); finfo.i = c_entry[i].i; found_in_cache = 1; break; } i = (i + 1) % c_size; } while (i != first); } ncp_unlock_dircache(); if (found_in_cache == 0) { int res; DDPRINTK("ncp_lookup: do_lookup on %s/%s\n", NCP_ISTRUCT(dir)->entryName, name); if (ncp_is_server_root(dir)) { str_upper(name); down_case = 1; res = ncp_lookup_volume(server, name, &(finfo.i)); } else { if (!ncp_preserve_case(dir)) { str_upper(name); down_case = 1; } res = ncp_obtain_info(server, NCP_ISTRUCT(dir)->volNumber, NCP_ISTRUCT(dir)->dirEntNum, name, &(finfo.i)); } if (res != 0) { unlock_super(dir->i_sb); iput(dir); return -ENOENT; } } finfo.opened = 0; if (down_case != 0) { str_lower(finfo.i.entryName); } if (!(*result = ncp_iget(dir, &finfo))) { unlock_super(dir->i_sb); iput(dir); return -EACCES; } unlock_super(dir->i_sb); iput(dir); return 0; } static int ncp_create(struct inode *dir, const char *name, int len, int mode, struct inode **result) { struct nw_file_info finfo; __u8 _name[len+1]; int error; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_create: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if (!ncp_conn_valid(NCP_SERVER(dir))) { iput(dir); return -EIO; } strncpy(_name, name, len); _name[len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } lock_super(dir->i_sb); if ((error = ncp_open_create_file_or_subdir(NCP_SERVER(dir), NCP_ISTRUCT(dir), _name, OC_MODE_CREATE|OC_MODE_OPEN| OC_MODE_REPLACE, 0, AR_READ|AR_WRITE, &finfo)) != 0) { unlock_super(dir->i_sb); iput(dir); if (error == 0x87) { return -ENAMETOOLONG; } return -EACCES; } ncp_invalid_dir_cache(dir); if (!ncp_preserve_case(dir)) { str_lower(finfo.i.entryName); } finfo.access = O_RDWR; if (!(*result = ncp_iget(dir, &finfo)) < 0) { ncp_close_file(NCP_SERVER(dir), finfo.file_handle); unlock_super(dir->i_sb); iput(dir); return -EINVAL; } unlock_super(dir->i_sb); iput(dir); return 0; } static int ncp_mkdir(struct inode *dir, const char *name, int len, int mode) { int error; struct nw_file_info new_dir; __u8 _name[len+1]; if ( (name[0] == '.') && ( (len == 1) || ( (len == 2) && (name[1] == '.')))) { iput(dir); return -EEXIST; } strncpy(_name, name, len); _name[len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_mkdir: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if (!ncp_conn_valid(NCP_SERVER(dir))) { iput(dir); return -EIO; } if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), NCP_ISTRUCT(dir), _name, OC_MODE_CREATE, aDIR, 0xffff, &new_dir) != 0) { error = -EACCES; } else { error = 0; ncp_invalid_dir_cache(dir); } iput(dir); return error; } static int ncp_rmdir(struct inode *dir, const char *name, int len) { int error; __u8 _name[len+1]; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_rmdir: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if (!ncp_conn_valid(NCP_SERVER(dir))) { iput(dir); return -EIO; } if (ncp_find_dir_inode(dir, name) != NULL) { error = -EBUSY; } else { strncpy(_name, name, len); _name[len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir), NCP_ISTRUCT(dir), _name)) == 0) { ncp_invalid_dir_cache(dir); } else { error = -EACCES; } } iput(dir); return error; } #ifdef CONFIG_NCPFS_STRONG /* try to delete a readonly file (NW R bit set) */ static int ncp_force_unlink(struct inode *dir,char *name,int len) { int res=0x9c,res2; struct inode *_inode; struct iattr ia; /* remove the Read-Only flag on the NW server */ dir->i_count++; res2=ncp_lookup(dir,name,len,&_inode); if (res2) { goto leave_me; /* abort operation */ } memset(&ia,0,sizeof(struct iattr)); ia.ia_mode = _inode->i_mode; ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222; /* set write bits */ ia.ia_valid = ATTR_MODE; res2=ncp_notify_change(_inode,&ia); if (res2) { iput(_inode); goto leave_me; } /* now try again the delete operation */ res2 = ncp_del_file_or_subdir(NCP_SERVER(dir),NCP_ISTRUCT(dir),name); res=res2; /* save status to use as return value */ res=res2; if (res2) /* delete failed, set R bit again */ { memset(&ia,0,sizeof(struct iattr)); ia.ia_mode = _inode->i_mode; ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222); /* clear write bits */ ia.ia_valid = ATTR_MODE; res2=ncp_notify_change(_inode,&ia); if (res2) { iput(_inode); goto leave_me; } } iput(_inode); leave_me: return(res); } #endif /* CONFIG_NCPFS_STRONG */ static int ncp_unlink(struct inode *dir, const char *name, int len) { int error; __u8 _name[len+1]; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_unlink: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if (!ncp_conn_valid(NCP_SERVER(dir))) { iput(dir); return -EIO; } if (ncp_find_dir_inode(dir, name) != NULL) { error = -EBUSY; } else { strncpy(_name, name, len); _name[len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } error = ncp_del_file_or_subdir(NCP_SERVER(dir), NCP_ISTRUCT(dir), _name); #ifdef CONFIG_NCPFS_STRONG if (error == 0x9c && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) /* readonly */ { error = ncp_force_unlink(dir,_name,len); /* special treatment */ } #endif /* CONFIG_NCPFS_STRONG */ if (error == 0) { ncp_invalid_dir_cache(dir); } else if (error == 0xFF) { error = -ENOENT; } else { error = -EACCES; } } iput(dir); return error; } #ifdef CONFIG_NCPFS_STRONG static int ncp_force_rename(struct inode *old_dir, const char *old_name, char *_old_name, int old_len, struct inode *new_dir, const char *new_name, char *_new_name, int new_len) { int res=0x90,res2; char _rename_old[old_len+1]; char _rename_new[new_len+1]; struct inode *_inode,*x_dir; struct iattr ia; strncpy(_rename_old,old_name,old_len); _rename_old[old_len] = 0; strncpy(_rename_new,new_name,new_len); _rename_new[new_len] = 0; /* remove the Read-Only flag on the NW server */ old_dir->i_count++; res2=ncp_lookup(old_dir,_rename_old,old_len,&_inode); if (res2) { goto leave_me; } memset(&ia,0,sizeof(struct iattr)); ia.ia_mode = _inode->i_mode; ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222; /* set write bits */ ia.ia_valid = ATTR_MODE; res2=ncp_notify_change(_inode,&ia); if (res2) { iput(_inode); goto leave_me; } /* now try again the rename operation */ res2 = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), NCP_ISTRUCT(old_dir), _old_name, NCP_ISTRUCT(new_dir), _new_name); res=res2; if (!res2) /* rename succeeded, get a new inode for the new file */ { x_dir=new_dir; new_dir->i_count++; iput(_inode); res2=ncp_lookup(new_dir,_rename_new,new_len,&_inode); if (res2) { goto leave_me; } } else { x_dir=old_dir; } memset(&ia,0,sizeof(struct iattr)); ia.ia_mode = _inode->i_mode; ia.ia_mode &= ~(NCP_SERVER(x_dir)->m.file_mode & 0222); /* clear write bits */ ia.ia_valid = ATTR_MODE; res2=ncp_notify_change(_inode,&ia); iput(_inode); if (res2) { printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2); goto leave_me; } leave_me: return(res); } #endif /* CONFIG_NCPFS_STRONG */ 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 must_be_dir) { int res; char _old_name[old_len+1]; char _new_name[new_len+1]; 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 (!ncp_conn_valid(NCP_SERVER(old_dir))) { res = -EIO; 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; } if ( (ncp_find_dir_inode(old_dir, old_name) != NULL) || (ncp_find_dir_inode(new_dir, new_name) != NULL)) { res = -EBUSY; goto finished; } strncpy(_old_name, old_name, old_len); _old_name[old_len] = '\0'; if (!ncp_preserve_case(old_dir)) { str_upper(_old_name); } strncpy(_new_name, new_name, new_len); _new_name[new_len] = '\0'; if (!ncp_preserve_case(new_dir)) { str_upper(_new_name); } res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), NCP_ISTRUCT(old_dir), _old_name, NCP_ISTRUCT(new_dir), _new_name); #ifdef CONFIG_NCPFS_STRONG if (res == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) /* file is readonly */ { res=ncp_force_rename(old_dir,old_name,_old_name,old_len,new_dir,new_name,_new_name,new_len); } #endif /* CONFIG_NCPFS_STRONG */ if (res == 0) { ncp_invalid_dir_cache(old_dir); ncp_invalid_dir_cache(new_dir); } else { if (res == 0x9E) res = -ENAMETOOLONG; else if (res == 0xFF) res = -ENOENT; else res = -EACCES; } 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 + sys_tz.tz_dsttime*3600; } static int local2utc(int time) { return time + sys_tz.tz_minuteswest*60 - sys_tz.tz_dsttime*3600; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ int ncp_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. */ void ncp_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); }