📄 dir.c
字号:
/* * dir.c * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * Modified 1998, 1999 Wolfram Pienkoss for NLS * Modified 1999 Wolfram Pienkoss for directory caching * */#include <linux/config.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/stat.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/mm.h>#include <asm/uaccess.h>#include <asm/byteorder.h>#include <linux/locks.h>#include <linux/smp_lock.h>#include <linux/ncp_fs.h>#include "ncplib_kernel.h"static void ncp_read_volume_list(struct file *, void *, filldir_t, struct ncp_cache_control *);static void ncp_do_readdir(struct file *, void *, filldir_t, struct ncp_cache_control *);static int ncp_readdir(struct file *, void *, filldir_t);static int ncp_create(struct inode *, struct dentry *, int);static struct dentry *ncp_lookup(struct inode *, struct dentry *);static int ncp_unlink(struct inode *, struct dentry *);static int ncp_mkdir(struct inode *, struct dentry *, int);static int ncp_rmdir(struct inode *, struct dentry *);static int ncp_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);#ifdef CONFIG_NCPFS_EXTRASextern int ncp_symlink(struct inode *, struct dentry *, const char *);#endif struct file_operations ncp_dir_operations ={ read: generic_read_dir, readdir: ncp_readdir, ioctl: ncp_ioctl,};struct inode_operations ncp_dir_inode_operations ={ create: ncp_create, lookup: ncp_lookup, unlink: ncp_unlink,#ifdef CONFIG_NCPFS_EXTRAS symlink: ncp_symlink,#endif mkdir: ncp_mkdir, rmdir: ncp_rmdir, rename: ncp_rename, setattr: ncp_notify_change,};/* * Dentry operations routines */static int ncp_lookup_validate(struct dentry *, int);static int ncp_hash_dentry(struct dentry *, struct qstr *);static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);static int ncp_delete_dentry(struct dentry *);struct dentry_operations ncp_dentry_operations ={ d_revalidate: ncp_lookup_validate, d_hash: ncp_hash_dentry, d_compare: ncp_compare_dentry, d_delete: ncp_delete_dentry,};/* * Note: leave the hash unchanged if the directory * is case-sensitive. */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->d_inode)) { hash = init_name_hash(); for (i=0; i<this->len ; i++) hash = partial_name_hash(ncp_tolower(t, this->name[i]), hash); this->hash = end_name_hash(hash); } return 0;}static intncp_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)) return strncmp(a->name, b->name, a->len); return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len);}/* * This is the callback from dput() when d_count is going to 0. * We use this to unhash dentries with bad inodes. * Closing files can be safely postponed until iput() - it's done there anyway. */static intncp_delete_dentry(struct dentry * dentry){ struct inode *inode = dentry->d_inode; if (inode) { if (is_bad_inode(inode)) return 1; } else { /* N.B. Unhash negative dentries? */ } return 0;}static inline intncp_single_volume(struct ncp_server *server){ return (server->m.mounted_vol[0] != '\0');}static inline int ncp_is_server_root(struct inode *inode){ return (!ncp_single_volume(NCP_SERVER(inode)) && inode == inode->i_sb->s_root->d_inode);}/* * 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 intncp_force_unlink(struct inode *dir, struct dentry* dentry){ int res=0x9c,res2; struct nw_modify_dos_info info; __u32 old_nwattr; struct inode *inode; memset(&info, 0, sizeof(info)); /* remove the Read-Only flag on the NW server */ inode = dentry->d_inode; old_nwattr = NCP_FINFO(inode)->nwattr; info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT); res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); 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 */ { info.attributes = old_nwattr; res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); if (res2) goto leave_me; }leave_me: return(res);}#endif /* CONFIG_NCPFS_STRONG */#ifdef CONFIG_NCPFS_STRONGstatic intncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name, struct inode *new_dir, struct dentry* new_dentry, char *_new_name){ struct nw_modify_dos_info info; int res=0x90,res2; struct inode *old_inode = old_dentry->d_inode; __u32 old_nwattr = NCP_FINFO(old_inode)->nwattr; __u32 new_nwattr = 0; /* shut compiler warning */ int old_nwattr_changed = 0; int new_nwattr_changed = 0; memset(&info, 0, sizeof(info)); /* remove the Read-Only flag on the NW server */ info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); if (!res2) old_nwattr_changed = 1; if (new_dentry && new_dentry->d_inode) { new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr; info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); if (!res2) new_nwattr_changed = 1; } /* now try again the rename operation */ /* but only if something really happened */ if (new_nwattr_changed || old_nwattr_changed) { res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), old_dir, _old_name, new_dir, _new_name); } if (res) goto leave_me; /* file was successfully renamed, so: do not set attributes on old file - it no longer exists copy attributes from old file to new */ new_nwattr_changed = old_nwattr_changed; new_nwattr = old_nwattr; old_nwattr_changed = 0; leave_me:; if (old_nwattr_changed) { info.attributes = old_nwattr; res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); /* ignore errors */ } if (new_nwattr_changed) { info.attributes = new_nwattr; res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); /* ignore errors */ } return(res);}#endif /* CONFIG_NCPFS_STRONG */static int__ncp_lookup_validate(struct dentry * dentry, int flags){ struct ncp_server *server; struct inode *dir = dentry->d_parent->d_inode; struct ncp_entry_info finfo; int res, val = 0, len = dentry->d_name.len + 1; __u8 __name[len]; if (!dentry->d_inode || !dir) goto finished; server = NCP_SERVER(dir); if (!ncp_conn_valid(server)) goto finished; /* * Inspired by smbfs: * The default validation is based on dentry age: * We set the max age at mount time. (But each * successful server lookup renews the timestamp.) */ val = NCP_TEST_AGE(server, dentry); if (val) goto finished; DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n", dentry->d_parent->d_name.name, dentry->d_name.name, NCP_GET_AGE(dentry)); if (ncp_is_server_root(dir)) { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, len-1, 1); if (!res) res = ncp_lookup_volume(server, __name, &(finfo.i)); } else { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, len-1, !ncp_preserve_case(dir)); if (!res) res = ncp_obtain_info(server, dir, __name, &(finfo.i)); } DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n", 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 (finfo.i.dirEntNum == NCP_FINFO(dentry->d_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); }finished: DDPRINTK("ncp_lookup_validate: result=%d\n", val); return val;}static intncp_lookup_validate(struct dentry * dentry, int flags){ int res; lock_kernel(); res = __ncp_lookup_validate(dentry, flags); unlock_kernel(); return res;}static struct dentry *ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos){ struct dentry *dent = dentry; struct list_head *next; if (d_validate(dent, parent)) { if (dent->d_name.len <= NCP_MAXPATHLEN && (unsigned long)dent->d_fsdata == fpos) { if (!dent->d_inode) { dput(dent); dent = NULL; } return dent; } dput(dent); } /* If a pointer is invalid, we search the dentry. */ spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { dent = list_entry(next, struct dentry, d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); else dent = NULL; spin_unlock(&dcache_lock); goto out; } next = next->next; } spin_unlock(&dcache_lock); return NULL;out: return dent;}static time_t ncp_obtain_mtime(struct dentry *dentry){ struct inode *inode = dentry->d_inode; struct ncp_server *server = NCP_SERVER(inode); struct nw_info_struct i; if (!ncp_conn_valid(server) || ncp_is_server_root(inode)) return 0; if (ncp_obtain_info(server, inode, NULL, &i)) return 0; return ncp_date_dos2unix(le16_to_cpu(i.modifyTime), le16_to_cpu(i.modifyDate));}static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; struct page *page = NULL; struct ncp_server *server = NCP_SERVER(inode); union ncp_dir_cache *cache = NULL; struct ncp_cache_control ctl; int result, mtime_valid = 0; time_t mtime = 0; ctl.page = NULL; ctl.cache = NULL; DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos); result = -EIO; if (!ncp_conn_valid(server)) goto out; result = 0; if (filp->f_pos == 0) { if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) goto out; filp->f_pos = 1; } if (filp->f_pos == 1) { if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino, DT_DIR)) goto out; filp->f_pos = 2; } page = grab_cache_page(&inode->i_data, 0); if (!page) goto read_really; ctl.cache = cache = kmap(page); ctl.head = cache->head; if (!Page_Uptodate(page) || !ctl.head.eof) goto init_cache; if (filp->f_pos == 2) { if (jiffies - ctl.head.time >= NCP_MAX_AGE(server)) goto init_cache; mtime = ncp_obtain_mtime(dentry); mtime_valid = 1; if ((!mtime) || (mtime != ctl.head.mtime)) goto init_cache; } if (filp->f_pos > ctl.head.end) goto finished; ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2); ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE; ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE; for (;;) { if (ctl.ofs != 0) { ctl.page = find_lock_page(&inode->i_data, ctl.ofs); if (!ctl.page) goto invalid_cache; ctl.cache = kmap(ctl.page); if (!Page_Uptodate(ctl.page)) goto invalid_cache; } while (ctl.idx < NCP_DIRCACHE_SIZE) { struct dentry *dent; int res; dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx], dentry, filp->f_pos); if (!dent) goto invalid_cache; res = filldir(dirent, dent->d_name.name, dent->d_name.len, filp->f_pos, dent->d_inode->i_ino, DT_UNKNOWN); dput(dent); if (res) goto finished; filp->f_pos += 1; ctl.idx += 1; if (filp->f_pos > ctl.head.end) goto finished; } if (ctl.page) { kunmap(ctl.page); SetPageUptodate(ctl.page); UnlockPage(ctl.page); page_cache_release(ctl.page); ctl.page = NULL; } ctl.idx = 0; ctl.ofs += 1; }invalid_cache: if (ctl.page) { kunmap(ctl.page); UnlockPage(ctl.page); page_cache_release(ctl.page); ctl.page = NULL; } ctl.cache = cache;init_cache: ncp_invalidate_dircache_entries(dentry); if (!mtime_valid) { mtime = ncp_obtain_mtime(dentry); mtime_valid = 1; } ctl.head.mtime = mtime; ctl.head.time = jiffies; ctl.head.eof = 0; ctl.fpos = 2; ctl.ofs = 0; ctl.idx = NCP_DIRCACHE_START; ctl.filled = 0; ctl.valid = 1;read_really: if (ncp_is_server_root(inode)) { ncp_read_volume_list(filp, dirent, filldir, &ctl); } else { ncp_do_readdir(filp, dirent, filldir, &ctl); } ctl.head.end = ctl.fpos - 1; ctl.head.eof = ctl.valid;finished: if (page) { cache->head = ctl.head; kunmap(page); SetPageUptodate(page); UnlockPage(page); page_cache_release(page); } if (ctl.page) { kunmap(ctl.page); SetPageUptodate(ctl.page); UnlockPage(ctl.page); page_cache_release(ctl.page); }out: return result;}static intncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct ncp_cache_control *ctrl, struct ncp_entry_info *entry){ struct dentry *newdent, *dentry = filp->f_dentry; struct inode *newino, *inode = dentry->d_inode; struct ncp_cache_control ctl = *ctrl; struct qstr qname; int valid = 0; int hashed = 0; ino_t ino = 0; __u8 __name[256]; qname.len = 256; if (ncp_vol2io(NCP_SERVER(inode), __name, &qname.len, entry->i.entryName, entry->i.nameLen, !ncp_preserve_entry_case(inode, entry->i.NSCreator))) return 1; /* I'm not sure */ qname.name = __name; qname.hash = full_name_hash(qname.name, qname.len); if (dentry->d_op && dentry->d_op->d_hash) if (dentry->d_op->d_hash(dentry, &qname) != 0) goto end_advance; newdent = d_lookup(dentry, &qname); if (!newdent) { newdent = d_alloc(dentry, &qname); if (!newdent) goto end_advance; } else { hashed = 1; memcpy((char *) newdent->d_name.name, qname.name, newdent->d_name.len); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -