diff options
Diffstat (limited to 'fs/ncpfs/dir.c')
-rw-r--r-- | fs/ncpfs/dir.c | 221 |
1 files changed, 129 insertions, 92 deletions
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 9578cbe0cd5..aac8832e919 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -95,6 +95,34 @@ const struct dentry_operations ncp_root_dentry_operations = }; +#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber]) + +static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator) +{ +#ifdef CONFIG_NCPFS_SMALLDOS + int ns = ncp_namespace(i); + + if ((ns == NW_NS_DOS) +#ifdef CONFIG_NCPFS_OS2_NS + || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS)) +#endif /* CONFIG_NCPFS_OS2_NS */ + ) + return 0; +#endif /* CONFIG_NCPFS_SMALLDOS */ + return 1; +} + +#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS) + +static inline int ncp_case_sensitive(struct dentry *dentry) +{ +#ifdef CONFIG_NCPFS_NFS_NS + return ncp_namespace(dentry->d_inode) == NW_NS_NFS; +#else + return 0; +#endif /* CONFIG_NCPFS_NFS_NS */ +} + /* * Note: leave the hash unchanged if the directory * is case-sensitive. @@ -102,13 +130,12 @@ const struct dentry_operations ncp_root_dentry_operations = static int ncp_hash_dentry(struct dentry *dentry, struct qstr *this) { - struct nls_table *t; - unsigned long hash; - int i; - - t = NCP_IO_TABLE(dentry); + if (!ncp_case_sensitive(dentry)) { + struct nls_table *t; + unsigned long hash; + int i; - if (!ncp_case_sensitive(dentry->d_inode)) { + t = NCP_IO_TABLE(dentry); hash = init_name_hash(); for (i=0; i<this->len ; i++) hash = partial_name_hash(ncp_tolower(t, this->name[i]), @@ -124,7 +151,7 @@ ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) if (a->len != b->len) return 1; - if (ncp_case_sensitive(dentry->d_inode)) + if (ncp_case_sensitive(dentry)) return strncmp(a->name, b->name, a->len); return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len); @@ -266,7 +293,7 @@ leave_me:; static int -__ncp_lookup_validate(struct dentry *dentry) +ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd) { struct ncp_server *server; struct dentry *parent; @@ -283,9 +310,6 @@ __ncp_lookup_validate(struct dentry *dentry) server = NCP_SERVER(dir); - if (!ncp_conn_valid(server)) - goto finished; - /* * Inspired by smbfs: * The default validation is based on dentry age: @@ -304,8 +328,11 @@ __ncp_lookup_validate(struct dentry *dentry) if (ncp_is_server_root(dir)) { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, 1); - if (!res) + if (!res) { res = ncp_lookup_volume(server, __name, &(finfo.i)); + if (!res) + ncp_update_known_namespace(server, finfo.i.volNumber, NULL); + } } else { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, !ncp_preserve_case(dir)); @@ -320,13 +347,17 @@ __ncp_lookup_validate(struct dentry *dentry) * what we remember, it's not valid any more. */ if (!res) { - if (finfo.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) { + struct inode *inode = dentry->d_inode; + + mutex_lock(&inode->i_mutex); + if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) { ncp_new_dentry(dentry); val=1; } else DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n"); - ncp_update_inode2(dentry->d_inode, &finfo); + ncp_update_inode2(inode, &finfo); + mutex_unlock(&inode->i_mutex); } finished: @@ -335,16 +366,6 @@ finished: return val; } -static int -ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd) -{ - int res; - lock_kernel(); - res = __ncp_lookup_validate(dentry); - unlock_kernel(); - return res; -} - static struct dentry * ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) { @@ -411,8 +432,6 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) int result, mtime_valid = 0; time_t mtime = 0; - lock_kernel(); - ctl.page = NULL; ctl.cache = NULL; @@ -421,6 +440,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) (int) filp->f_pos); result = -EIO; + /* Do not generate '.' and '..' when server is dead. */ if (!ncp_conn_valid(server)) goto out; @@ -532,6 +552,12 @@ read_really: ctl.head.end = ctl.fpos - 1; ctl.head.eof = ctl.valid; finished: + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } if (page) { cache->head = ctl.head; kunmap(page); @@ -539,23 +565,17 @@ finished: unlock_page(page); page_cache_release(page); } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } out: - unlock_kernel(); return result; } static int ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, - struct ncp_cache_control *ctrl, struct ncp_entry_info *entry) + struct ncp_cache_control *ctrl, struct ncp_entry_info *entry, + int inval_childs) { struct dentry *newdent, *dentry = filp->f_path.dentry; - struct inode *newino, *inode = dentry->d_inode; + struct inode *dir = dentry->d_inode; struct ncp_cache_control ctl = *ctrl; struct qstr qname; int valid = 0; @@ -564,9 +584,9 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, __u8 __name[NCP_MAXPATHLEN + 1]; qname.len = sizeof(__name); - if (ncp_vol2io(NCP_SERVER(inode), __name, &qname.len, + if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len, entry->i.entryName, entry->i.nameLen, - !ncp_preserve_entry_case(inode, entry->i.NSCreator))) + !ncp_preserve_entry_case(dir, entry->i.NSCreator))) return 1; /* I'm not sure */ qname.name = __name; @@ -584,22 +604,64 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, goto end_advance; } else { hashed = 1; - memcpy((char *) newdent->d_name.name, qname.name, - newdent->d_name.len); + + /* If case sensitivity changed for this volume, all entries below this one + should be thrown away. This entry itself is not affected, as its case + sensitivity is controlled by its own parent. */ + if (inval_childs) + shrink_dcache_parent(newdent); + + /* + * It is not as dangerous as it looks. NetWare's OS2 namespace is + * case preserving yet case insensitive. So we update dentry's name + * as received from server. We found dentry via d_lookup with our + * hash, so we know that hash does not change, and so replacing name + * should be reasonably safe. + */ + if (qname.len == newdent->d_name.len && + memcmp(newdent->d_name.name, qname.name, newdent->d_name.len)) { + struct inode *inode = newdent->d_inode; + + /* + * Inside ncpfs all uses of d_name are either for debugging, + * or on functions which acquire inode mutex (mknod, creat, + * lookup). So grab i_mutex here, to be sure. d_path + * uses dcache_lock when generating path, so we should too. + * And finally d_compare is protected by dentry's d_lock, so + * here we go. + */ + if (inode) + mutex_lock(&inode->i_mutex); + spin_lock(&dcache_lock); + spin_lock(&newdent->d_lock); + memcpy((char *) newdent->d_name.name, qname.name, + newdent->d_name.len); + spin_unlock(&newdent->d_lock); + spin_unlock(&dcache_lock); + if (inode) + mutex_unlock(&inode->i_mutex); + } } if (!newdent->d_inode) { + struct inode *inode; + entry->opened = 0; - entry->ino = iunique(inode->i_sb, 2); - newino = ncp_iget(inode->i_sb, entry); - if (newino) { + entry->ino = iunique(dir->i_sb, 2); + inode = ncp_iget(dir->i_sb, entry); + if (inode) { newdent->d_op = &ncp_dentry_operations; - d_instantiate(newdent, newino); + d_instantiate(newdent, inode); if (!hashed) d_rehash(newdent); } - } else - ncp_update_inode2(newdent->d_inode, entry); + } else { + struct inode *inode = newdent->d_inode; + + mutex_lock(&inode->i_mutex); + ncp_update_inode2(inode, entry); + mutex_unlock(&inode->i_mutex); + } if (newdent->d_inode) { ino = newdent->d_inode->i_ino; @@ -617,7 +679,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, ctl.cache = NULL; ctl.idx -= NCP_DIRCACHE_SIZE; ctl.ofs += 1; - ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); + ctl.page = grab_cache_page(&dir->i_data, ctl.ofs); if (ctl.page) ctl.cache = kmap(ctl.page); } @@ -633,7 +695,7 @@ end_advance: if (!ino) ino = find_inode_number(dentry, &qname); if (!ino) - ino = iunique(inode->i_sb, 2); + ino = iunique(dir->i_sb, 2); ctl.filled = filldir(dirent, qname.name, qname.len, filp->f_pos, ino, DT_UNKNOWN); if (!ctl.filled) @@ -660,6 +722,7 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir, (unsigned long) filp->f_pos); for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) { + int inval_dentry; if (ncp_get_volume_info_with_number(server, i, &info) != 0) return; @@ -675,8 +738,9 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir, info.volume_name); continue; } + inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL); entry.volume = entry.i.volNumber; - if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) + if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry)) return; } } @@ -739,7 +803,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir, rpl += onerpl; rpls -= onerpl; entry.volume = entry.i.volNumber; - if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) + if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0)) break; } } while (more); @@ -775,17 +839,19 @@ int ncp_conn_logged_in(struct super_block *sb) if (dent) { struct inode* ino = dent->d_inode; if (ino) { + ncp_update_known_namespace(server, volNumber, NULL); NCP_FINFO(ino)->volNumber = volNumber; NCP_FINFO(ino)->dirEntNum = dirEntNum; NCP_FINFO(ino)->DosDirNum = DosDirNum; + result = 0; } else { DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n"); } } else { DPRINTK("ncpfs: sb->s_root == NULL!\n"); } - } - result = 0; + } else + result = 0; out: return result; @@ -799,7 +865,6 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc int error, res, len; __u8 __name[NCP_MAXPATHLEN + 1]; - lock_kernel(); error = -EIO; if (!ncp_conn_valid(server)) goto finished; @@ -813,6 +878,8 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc dentry->d_name.len, 1); if (!res) res = ncp_lookup_volume(server, __name, &(finfo.i)); + if (!res) + ncp_update_known_namespace(server, finfo.i.volNumber, NULL); } else { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, !ncp_preserve_case(dir)); @@ -846,7 +913,6 @@ add_entry: finished: PPRINTK("ncp_lookup: result=%d\n", error); - unlock_kernel(); return ERR_PTR(error); } @@ -887,11 +953,6 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n", dentry->d_parent->d_name.name, dentry->d_name.name, mode); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, dentry); len = sizeof(__name); error = ncp_io2vol(server, __name, &len, dentry->d_name.name, @@ -917,6 +978,8 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, if (result) { if (result == 0x87) error = -ENAMETOOLONG; + else if (result < 0) + error = result; DPRINTK("ncp_create: %s/%s failed\n", dentry->d_parent->d_name.name, dentry->d_name.name); goto out; @@ -935,7 +998,6 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, error = ncp_instantiate(dir, dentry, &finfo); out: - unlock_kernel(); return error; } @@ -955,11 +1017,6 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) DPRINTK("ncp_mkdir: making %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, dentry); len = sizeof(__name); error = ncp_io2vol(server, __name, &len, dentry->d_name.name, @@ -967,12 +1024,11 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (error) goto out; - error = -EACCES; - if (ncp_open_create_file_or_subdir(server, dir, __name, + error = ncp_open_create_file_or_subdir(server, dir, __name, OC_MODE_CREATE, aDIR, cpu_to_le16(0xffff), - &finfo) == 0) - { + &finfo); + if (error == 0) { if (ncp_is_nfs_extras(server, finfo.volume)) { mode |= S_IFDIR; finfo.i.nfs.mode = mode; @@ -983,9 +1039,10 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto out; } error = ncp_instantiate(dir, dentry, &finfo); + } else if (error > 0) { + error = -EACCES; } out: - unlock_kernel(); return error; } @@ -998,11 +1055,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) DPRINTK("ncp_rmdir: removing %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - error = -EBUSY; if (!d_unhashed(dentry)) goto out; @@ -1036,11 +1088,10 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) error = -ENOENT; break; default: - error = -EACCES; + error = result < 0 ? result : -EACCES; break; } out: - unlock_kernel(); return error; } @@ -1050,15 +1101,10 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) struct ncp_server *server; int error; - lock_kernel(); server = NCP_SERVER(dir); DPRINTK("ncp_unlink: unlinking %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - if (!ncp_conn_valid(server)) - goto out; - /* * Check whether to close the file ... */ @@ -1097,12 +1143,9 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) error = -ENOENT; break; default: - error = -EACCES; + error = error < 0 ? error : -EACCES; break; } - -out: - unlock_kernel(); return error; } @@ -1118,11 +1161,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, old_dentry); ncp_age_dentry(server, new_dentry); @@ -1161,11 +1199,10 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, error = -ENOENT; break; default: - error = -EACCES; + error = error < 0 ? error : -EACCES; break; } out: - unlock_kernel(); return error; } |