📄 dcache.c
字号:
check_lock(); memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN); old_name = target->d_name.name; new_name = dentry->d_name.name; if (old_name == target->d_iname) old_name = dentry->d_iname; if (new_name == dentry->d_iname) new_name = target->d_iname; target->d_name.name = new_name; dentry->d_name.name = old_name;}/* * We cannibalize "target" when moving dentry on top of it, * because it's going to be thrown away anyway. We could be more * polite about it, though. * * This forceful removal will result in ugly /proc output if * somebody holds a file open that got deleted due to a rename. * We could be nicer about the deleted file, and let it show * up under the name it got deleted rather than the name that * deleted it. * * Careful with the hash switch. The hash switch depends on * the fact that any list-entry can be a head of the list. * Think about it. */ /** * d_move - move a dentry * @dentry: entry to move * @target: new dentry * * Update the dcache to reflect the move of a file name. Negative * dcache entries should not be moved in this way. */ void d_move(struct dentry * dentry, struct dentry * target){ check_lock(); if (!dentry->d_inode) printk(KERN_WARNING "VFS: moving negative dcache entry\n"); spin_lock(&dcache_lock); /* Move the dentry to the target hash queue */ list_del(&dentry->d_hash); list_add(&dentry->d_hash, &target->d_hash); /* Unhash the target: dput() will then get rid of it */ list_del_init(&target->d_hash); list_del(&dentry->d_child); list_del(&target->d_child); /* Switch the parents and the names.. */ switch_names(dentry, target); do_switch(dentry->d_parent, target->d_parent); do_switch(dentry->d_name.len, target->d_name.len); do_switch(dentry->d_name.hash, target->d_name.hash); /* And add them back to the (new) parent lists */ list_add(&target->d_child, &target->d_parent->d_subdirs); list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); spin_unlock(&dcache_lock);}/** * d_path - return the path of a dentry * @dentry: dentry to report * @vfsmnt: vfsmnt to which the dentry belongs * @root: root dentry * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length * * Convert a dentry into an ASCII path name. If the entry has been deleted * the string " (deleted)" is appended. Note that this is ambiguous. Returns * the buffer. * * "buflen" should be %PAGE_SIZE or more. Caller holds the dcache_lock. */char * __d_path(struct dentry *dentry, struct vfsmount *vfsmnt, struct dentry *root, struct vfsmount *rootmnt, char *buffer, int buflen){ char * end = buffer+buflen; char * retval; int namelen; *--end = '\0'; buflen--; if (!IS_ROOT(dentry) && list_empty(&dentry->d_hash)) { buflen -= 10; end -= 10; memcpy(end, " (deleted)", 10); } /* Get '/' right */ retval = end-1; *retval = '/'; for (;;) { struct dentry * parent; if (dentry == root && vfsmnt == rootmnt) break; if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ if (vfsmnt->mnt_parent == vfsmnt) goto global_root; dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; continue; } parent = dentry->d_parent; namelen = dentry->d_name.len; buflen -= namelen + 1; if (buflen < 0) break; end -= namelen; memcpy(end, dentry->d_name.name, namelen); *--end = '/'; retval = end; dentry = parent; } return retval;global_root: namelen = dentry->d_name.len; buflen -= namelen; if (buflen >= 0) { retval -= namelen-1; /* hit the slash */ memcpy(retval, dentry->d_name.name, namelen); } return retval;}/* * NOTE! The user-level library version returns a * character pointer. The kernel system call just * returns the length of the buffer filled (which * includes the ending '\0' character), or a negative * error value. So libc would do something like * * char *getcwd(char * buf, size_t size) * { * int retval; * * retval = sys_getcwd(buf, size); * if (retval >= 0) * return buf; * errno = -retval; * return NULL; * } */asmlinkage long sys_getcwd(char *buf, unsigned long size){ int error; struct vfsmount *pwdmnt, *rootmnt; struct dentry *pwd, *root; char *page = (char *) __get_free_page(GFP_USER); if (!page) return -ENOMEM; read_lock(¤t->fs->lock); pwdmnt = mntget(current->fs->pwdmnt); pwd = dget(current->fs->pwd); rootmnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); read_unlock(¤t->fs->lock); error = -ENOENT; /* Has the current directory has been unlinked? */ spin_lock(&dcache_lock); if (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) { unsigned long len; char * cwd; cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE); spin_unlock(&dcache_lock); error = -ERANGE; len = PAGE_SIZE + page - cwd; if (len <= size) { error = len; if (copy_to_user(buf, cwd, len)) error = -EFAULT; } } else spin_unlock(&dcache_lock); dput(pwd); mntput(pwdmnt); dput(root); mntput(rootmnt); free_page((unsigned long) page); return error;}/* * Test whether new_dentry is a subdirectory of old_dentry. * * Trivially implemented using the dcache structure *//** * is_subdir - is new dentry a subdirectory of old_dentry * @new_dentry: new dentry * @old_dentry: old dentry * * Returns 1 if new_dentry is a subdirectory of the parent (at any depth). * Returns 0 otherwise. */ int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry){ int result; result = 0; for (;;) { if (new_dentry != old_dentry) { struct dentry * parent = new_dentry->d_parent; if (parent == new_dentry) break; new_dentry = parent; continue; } result = 1; break; } return result;}void d_genocide(struct dentry *root){ struct dentry *this_parent = root; struct list_head *next; spin_lock(&dcache_lock);repeat: next = this_parent->d_subdirs.next;resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next; if (d_unhashed(dentry)||!dentry->d_inode) continue; if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; goto repeat; } atomic_dec(&dentry->d_count); } if (this_parent != root) { next = this_parent->d_child.next; atomic_dec(&this_parent->d_count); this_parent = this_parent->d_parent; goto resume; } spin_unlock(&dcache_lock);}/** * find_inode_number - check for dentry with name * @dir: directory to check * @name: Name to find. * * Check whether a dentry already exists for the given name, * and return the inode number if it has an inode. Otherwise * 0 is returned. * * This routine is used to post-process directory listings for * filesystems using synthetic inode numbers, and is necessary * to keep getcwd() working. */ ino_t find_inode_number(struct dentry *dir, struct qstr *name){ struct dentry * dentry; ino_t ino = 0; /* * Check for a fs-specific hash function. Note that we must * calculate the standard hash first, as the d_op->d_hash() * routine may choose to leave the hash value unchanged. */ name->hash = full_name_hash(name->name, name->len); if (dir->d_op && dir->d_op->d_hash) { if (dir->d_op->d_hash(dir, name) != 0) goto out; } dentry = d_lookup(dir, name); if (dentry) { if (dentry->d_inode) ino = dentry->d_inode->i_ino; dput(dentry); }out: return ino;}static void __init dcache_init(unsigned long mempages){ struct list_head *d; unsigned long order; unsigned int nr_hash; int i; /* * A constructor could be added for stable state like the lists, * but it is probably not worth it because of the cache nature * of the dcache. * If fragmentation is too bad then the SLAB_HWCACHE_ALIGN * flag could be removed here, to hint to the allocator that * it should not try to get multiple page regions. */ dentry_cache = kmem_cache_create("dentry_cache", sizeof(struct dentry), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!dentry_cache) panic("Cannot create dentry cache");#if PAGE_SHIFT < 13 mempages >>= (13 - PAGE_SHIFT);#endif mempages *= sizeof(struct list_head); for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++) ; do { unsigned long tmp; nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct list_head); d_hash_mask = (nr_hash - 1); tmp = nr_hash; d_hash_shift = 0; while ((tmp >>= 1UL) != 0UL) d_hash_shift++; dentry_hashtable = (struct list_head *) __get_free_pages(GFP_ATOMIC, order); } while (dentry_hashtable == NULL && --order >= 0); printk("Dentry-cache hash table entries: %d (order: %ld, %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order)); if (!dentry_hashtable) panic("Failed to allocate dcache hash table\n"); d = dentry_hashtable; i = nr_hash; do { INIT_LIST_HEAD(d); d++; i--; } while (i);}static void init_buffer_head(void * foo, kmem_cache_t * cachep, unsigned long flags){ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { struct buffer_head * bh = (struct buffer_head *) foo; memset(bh, 0, sizeof(*bh)); init_waitqueue_head(&bh->b_wait); }}/* SLAB cache for __getname() consumers */kmem_cache_t *names_cachep;/* SLAB cache for file structures */kmem_cache_t *filp_cachep;/* SLAB cache for dquot structures */kmem_cache_t *dquot_cachep;/* SLAB cache for buffer_head structures */kmem_cache_t *bh_cachep;EXPORT_SYMBOL(bh_cachep);extern void bdev_cache_init(void);extern void cdev_cache_init(void);void __init vfs_caches_init(unsigned long mempages){ bh_cachep = kmem_cache_create("buffer_head", sizeof(struct buffer_head), 0, SLAB_HWCACHE_ALIGN, init_buffer_head, NULL); if(!bh_cachep) panic("Cannot create buffer head SLAB cache"); names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!names_cachep) panic("Cannot create names SLAB cache"); filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!filp_cachep) panic("Cannot create filp SLAB cache");#if defined (CONFIG_QUOTA) dquot_cachep = kmem_cache_create("dquot", sizeof(struct dquot), sizeof(unsigned long) * 4, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!dquot_cachep) panic("Cannot create dquot SLAB cache");#endif dcache_init(mempages); inode_init(mempages); mnt_init(mempages); bdev_cache_init(); cdev_cache_init();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -