diff options
Diffstat (limited to 'fs/ncpfs/dir.c')
-rw-r--r-- | fs/ncpfs/dir.c | 217 |
1 files changed, 158 insertions, 59 deletions
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 005431485..8424b2ec7 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -7,6 +7,8 @@ * */ +#include <linux/config.h> + #include <linux/sched.h> #include <linux/errno.h> #include <linux/stat.h> @@ -105,10 +107,10 @@ ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos) /* * Dentry operations routines */ -static int ncp_lookup_validate(struct dentry *); -static void ncp_delete_dentry(struct dentry *); +static int ncp_lookup_validate(struct dentry *); static int ncp_hash_dentry(struct dentry *, struct qstr *); static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); +static void ncp_delete_dentry(struct dentry *); static struct dentry_operations ncp_dentry_operations = { @@ -125,19 +127,23 @@ static struct dentry_operations ncp_dentry_operations = */ #define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c)) - +/* + * Note: leave the hash unchanged if the directory + * is case-sensitive. + */ static int ncp_hash_dentry(struct dentry *dentry, struct qstr *this) { - unsigned long hash; - int i; - - hash = init_name_hash(); - for (i=0; i<this->len ; i++) - hash = partial_name_hash(tolower(this->name[i]),hash); - this->hash = end_name_hash(hash); - - return 0; + unsigned long hash; + int i; + + if (!ncp_case_sensitive(dentry->d_inode)) { + hash = init_name_hash(); + for (i=0; i<this->len ; i++) + hash = partial_name_hash(tolower(this->name[i]),hash); + this->hash = end_name_hash(hash); + } + return 0; } static int @@ -202,45 +208,17 @@ dentry->d_parent->d_name.name, dentry->d_name.name); */ ino_t ncp_invent_inos(unsigned long n) { - static ino_t ino = 1; + static ino_t ino = 2; if (ino + 2*n < ino) { /* wrap around */ - ino += n; + ino = 2; } ino += n; return ino; } -/* - * Check whether a dentry already exists for the given name, - * and return the inode number if it has an inode. This is - * needed to keep getcwd() working. - */ -static ino_t -find_inode_number(struct dentry *dir, struct qstr *name) -{ - unsigned long hash; - int i; - struct dentry * dentry; - ino_t ino = 0; - - hash = init_name_hash(); - for (i=0; i<name->len ; i++) - hash = partial_name_hash(tolower(name->name[i]),hash); - name->hash = end_name_hash(hash); - - dentry = d_lookup(dir, name); - if (dentry) - { - if (dentry->d_inode) - ino = dentry->d_inode->i_ino; - dput(dentry); - } - return ino; -} - static inline int ncp_single_volume(struct ncp_server *server) { @@ -271,6 +249,98 @@ static inline void ncp_unlock_dircache(void) * This is the callback when the dcache has a lookup hit. */ + +#ifdef CONFIG_NCPFS_STRONG +/* try to delete a readonly file (NW R bit set) */ + +static int +ncp_force_unlink(struct inode *dir, struct dentry* dentry) +{ + int res=0x9c,res2; + struct iattr ia; + + /* remove the Read-Only flag on the NW server */ + + memset(&ia,0,sizeof(struct iattr)); + ia.ia_mode = dentry->d_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(dentry, &ia); + if (res2) + { + goto leave_me; + } + + /* now try again the delete operation */ + + res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry); + + if (res) /* delete failed, set R bit again */ + { + memset(&ia,0,sizeof(struct iattr)); + ia.ia_mode = dentry->d_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(dentry, &ia); + if (res2) + { + goto leave_me; + } + } +leave_me: + return(res); +} +#endif /* CONFIG_NCPFS_STRONG */ + +#ifdef CONFIG_NCPFS_STRONG +static int +ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name, + struct inode *new_dir, struct dentry* new_dentry, char *_new_name) +{ + int res=0x90,res2; + struct iattr ia; + + /* remove the Read-Only flag on the NW server */ + + memset(&ia,0,sizeof(struct iattr)); + ia.ia_mode = old_dentry->d_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(old_dentry, &ia); + if (res2) + { + goto leave_me; + } + + /* now try again the rename operation */ + res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), + old_dir, _old_name, + new_dir, _new_name); + + memset(&ia,0,sizeof(struct iattr)); + ia.ia_mode = old_dentry->d_inode->i_mode; + ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */ + ia.ia_valid = ATTR_MODE; + + /* FIXME: uses only inode info, no dentry info... so it is safe to call */ + /* it now with old dentry. If we use name (in future), we have to move */ + /* it after dentry_move in caller */ + res2=ncp_notify_change(old_dentry, &ia); + 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_lookup_validate(struct dentry * dentry) { @@ -338,13 +408,15 @@ dentry->d_parent->d_name.name, __name, res); * If we didn't find it, or if it has a different dirEntNum to * what we remember, it's not valid any more. */ - if (!res) + if (!res) { if (finfo.nw_info.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) val=1; #ifdef NCPFS_PARANOIA else printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n"); #endif + ncp_update_inode2(dentry->d_inode, &finfo.nw_info); + } if (!val) ncp_invalid_dir_cache(dir); finished: @@ -641,6 +713,8 @@ int ncp_conn_logged_in(struct ncp_server *server) int result; if (ncp_single_volume(server)) { + struct dentry* dent; + result = -ENOENT; str_upper(server->m.mounted_vol); if (ncp_lookup_volume(server, server->m.mounted_vol, @@ -651,6 +725,19 @@ printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol); goto out; } str_lower(server->root.finfo.i.entryName); + dent = server->root_dentry; + if (dent) { + struct inode* ino = dent->d_inode; + if (ino) { + NCP_FINFO(ino)->volNumber = server->root.finfo.i.volNumber; + NCP_FINFO(ino)->dirEntNum = server->root.finfo.i.dirEntNum; + NCP_FINFO(ino)->DosDirNum = server->root.finfo.i.DosDirNum; + } else { + DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry->d_inode == NULL!\n"); + } + } else { + DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry == NULL!\n"); + } } result = 0; @@ -838,6 +925,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode); finfo.nw_info.access = O_RDWR; error = ncp_instantiate(dir, dentry, &finfo); } else { + if (result == 0x87) error = -ENAMETOOLONG; DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n", dentry->d_parent->d_name.name, dentry->d_name.name); } @@ -928,8 +1016,7 @@ out: static int ncp_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - int error, result; - __u8 _name[dentry->d_name.len + 1]; + int error; DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -953,21 +1040,23 @@ printk(KERN_DEBUG "ncp_unlink: closing file\n"); ncp_make_closed(inode); } - strncpy(_name, dentry->d_name.name, dentry->d_name.len); - _name[dentry->d_name.len] = '\0'; - if (!ncp_preserve_case(dir)) - { - str_upper(_name); + error = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry); +#ifdef CONFIG_NCPFS_STRONG + if (error == 0x9C && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) { /* R/O */ + error = ncp_force_unlink(dir, dentry); } - error = -EACCES; - result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name); - if (!result) { +#endif + if (!error) { DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); ncp_invalid_dir_cache(dir); d_delete(dentry); - error = 0; + } else if (error == 0xFF) { + error = -ENOENT; + } else { + error = -EACCES; } + out: return error; } @@ -977,7 +1066,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, { int old_len = old_dentry->d_name.len; int new_len = new_dentry->d_name.len; - int error, result; + int error; char _old_name[old_dentry->d_name.len + 1]; char _new_name[new_dentry->d_name.len + 1]; @@ -1010,18 +1099,28 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, str_upper(_new_name); } - error = -EACCES; - result = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), + error = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), old_dir, _old_name, new_dir, _new_name); - if (result == 0) +#ifdef CONFIG_NCPFS_STRONG + if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */ + error = ncp_force_rename(old_dir, old_dentry, _old_name, new_dir, new_dentry, _new_name); + } +#endif + if (error == 0) { DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", old_dentry->d_name.name,new_dentry->d_name.name); ncp_invalid_dir_cache(old_dir); ncp_invalid_dir_cache(new_dir); d_move(old_dentry,new_dentry); - error = 0; + } else { + if (error == 0x9E) + error = -ENAMETOOLONG; + else if (error == 0xFF) + error = -ENOENT; + else + error = -EACCES; } out: return error; |