ファイル名からファイル情報を見つける(namei)
このドキュメントのCopyleftはyfydzが所有しており、GPLを使用してリリースされ、コピー、転載、転載が可能です.転載時はドキュメントの完全性を維持し、ビジネス用途に使用することは厳禁です.
msn: [email protected]
ソース:http://yfydz.cublog.cn
msn: [email protected]
ソース:http://yfydz.cublog.cn
1.
inode Unix , inode, inode , inode 。 dentry , inode, , inode, namei。
Linux , 2.6.19.2。
fs/namei.c , fs/*/namei.c 。
2.
, namei , :
open , sys_open:
/* fs/open.c */
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
prevent_tail_call(ret);
return ret;
}
do_sys_open:
/* fs/open.c */
// dfd AT_FDCWD
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
//
// tmp cache ,
//
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
// , inode
fd = get_unused_fd();
if (fd >= 0) {
// ,
struct file *f = do_filp_open(dfd, tmp, flags, mode);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_dentry);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
//
static struct file *do_filp_open(int dfd, const char *filename, int flags,
int mode)
{
int namei_flags, error;
//
struct nameidata nd;
namei_flags = flags;
if ((namei_flags+1) & O_ACCMODE)
namei_flags++;
// nameidata, nd namei
error = open_namei(dfd, filename, namei_flags, mode, &nd);
if (!error)
// , nameidata file
return nameidata_to_filp(&nd, flags);
return ERR_PTR(error);
}
open_namei , inode , namei .
open_namei , getname, kmem_cache :
// ,
/* fs/namei.c */
char * getname(const char __user * filename)
{
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
/* include/linux/fs.h */
// __getname __putname , cache
// #define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL)
// #define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
// names cache, cache
// names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
// SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
tmp = __getname();
if (tmp) {
// cache
//
int retval = do_getname(filename, tmp);
// cache
result = tmp;
if (retval < 0) {
// , cache,
__putname(tmp);
result = ERR_PTR(retval);
}
}
// CONFIG_AUDITSYSCALL, audit_getname
//
audit_getname(result);
return result;
}
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
len = TASK_SIZE - (unsigned long) filename;
}
// cache
retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
if (retval < len)
return 0;
return -ENAMETOOLONG;
} else if (!retval)
retval = -ENOENT;
return retval;
}
3. namei
/* include/linux/namei.h */
struct nameidata {
//
struct dentry *dentry;
//
struct vfsmount *mnt;
//
struct qstr last;
unsigned int flags;
int last_type;
//
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + 1]; // 9
/* Intent data */
//
union {
//
struct open_intent open;
} intent;
};
struct open_intent {
//
int flags;
//
int create_mode;
//
struct file *file;
};
// , , dentry
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
/* include/linux/dcache.h */
// , cache
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
/* include/linux/fs.h */
//
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
// dentry
struct dentry *f_dentry;
//
struct vfsmount *f_vfsmnt;
//
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
4. namei
4.1 open_namei
/* fs/namei.c */
/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*
* Note that the low bits of "flag" aren't the same as in the open
* system call - they are 00 - no permissions needed
* 01 - read permission needed
* 10 - write permission needed
* 11 - read/write permissions needed
* which is a lot more logical, and also allows the "no perm" needed
* for symlinks (where the permissions are checked later).
* SMP-safe
*/
int open_namei(int dfd, const char *pathname, int flag,
int mode, struct nameidata *nd)
{
int acc_mode, error;
struct path path;
struct dentry *dir;
int count = 0;
// #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
//
acc_mode = ACC_MODE(flag);
/* O_TRUNC implies we need access checks for write permissions */
// , ,
if (flag & O_TRUNC)
acc_mode |= MAY_WRITE;
/* Allow the LSM permission hook to distinguish append
access from general write access. */
// ,
if (flag & O_APPEND)
acc_mode |= MAY_APPEND;
/*
* The simplest case - just a plain lookup.
*/
//
if (!(flag & O_CREAT)) {
// pathname dentry , nd
error = path_lookup_open(dfd, pathname, lookup_flags(flag),
nd, flag);
if (error)
return error;
goto ok;
}
/*
* Create - we need to know the parent.
*/
// dentry , nd
error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
if (error)
return error;
/*
* We have the parent and last component. First of all, check
* that we are not asked to creat(2) an obvious directory - that
* will not do.
*/
error = -EISDIR;
// nameidata last
if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
goto exit;
// dentry
dir = nd->dentry;
//
nd->flags &= ~LOOKUP_PARENT;
mutex_lock(&dir->d_inode->i_mutex);
// path , nd dentry
// dir path.dentry ?
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
do_last:
// path.entry
error = PTR_ERR(path.dentry);
if (IS_ERR(path.dentry)) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
// nd->intent.open.file ,
if (IS_ERR(nd->intent.open.file)) {
mutex_unlock(&dir->d_inode->i_mutex);
error = PTR_ERR(nd->intent.open.file);
goto exit_dput;
}
/* Negative dentry, just create the file */
if (!path.dentry->d_inode) {
// inode,
error = open_namei_create(nd, &path, flag, mode);
if (error)
goto exit;
return 0;
}
//
/*
* It already exists.
*/
mutex_unlock(&dir->d_inode->i_mutex);
audit_inode_update(path.dentry->d_inode);
error = -EEXIST;
// O_EXCL ,
if (flag & O_EXCL)
goto exit_dput;
if (__follow_mount(&path)) {
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
}
error = -ENOENT;
if (!path.dentry->d_inode)
goto exit_dput;
// dentry FS follow_link ,
// FS
if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
goto do_link;
// dentry mnt nameidata
path_to_nameidata(&path, nd);
error = -EISDIR;
// ,
if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
goto exit;
ok:
// nd dentry inode
error = may_open(nd, acc_mode, flag);
if (error)
goto exit;
return 0;
// , ,
exit_dput:
dput_path(&path, nd);
exit:
if (!IS_ERR(nd->intent.open.file))
release_open_intent(nd);
path_release(nd);
return error;
// , inode, ,
do_link:
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
/*
* This is subtle. Instead of calling do_follow_link() we do the
* thing by hands. The reason is that this way we have zero link_count
* and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
* After that we have the parent and last component, i.e.
* we are in the same situation as after the first path_walk().
* Well, almost - if the last component is normal we get its copy
* stored in nd->last.name and we will have to putname() it when we
* are done. Procfs-like symlinks just set LAST_BIND.
*/
// LOOKUP_PARENT
nd->flags |= LOOKUP_PARENT;
error = security_inode_follow_link(path.dentry, nd);
if (error)
goto exit_dput;
//
error = __do_follow_link(&path, nd);
if (error) {
/* Does someone understand code flow here? Or it is only
* me so stupid? Anathema to whoever designed this non-sense
* with "intent.open".
*/
release_open_intent(nd);
return error;
}
nd->flags &= ~LOOKUP_PARENT;
//
if (nd->last_type == LAST_BIND)
goto ok;
error = -EISDIR;
if (nd->last_type != LAST_NORM)
goto exit;
if (nd->last.name[nd->last.len]) {
__putname(nd->last.name);
goto exit;
}
error = -ELOOP;
// : 32
if (count++==32) {
__putname(nd->last.name);
goto exit;
}
dir = nd->dentry;
mutex_lock(&dir->d_inode->i_mutex);
// dentry
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
__putname(nd->last.name);
goto do_last;
}
4.2 path_lookup_open path_lookup_create
dentry , nameidata , create , :
/**
* path_lookup_open - lookup a file path with open intent
* @dfd: the directory to use as base, or AT_FDCWD
* @name: pointer to file name
* @lookup_flags: lookup intent flags
* @nd: pointer to nameidata
* @open_flags: open intent flags
*/
int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
struct nameidata *nd, int open_flags)
{
return __path_lookup_intent_open(dfd, name, lookup_flags, nd,
open_flags, 0);
}
/**
* path_lookup_create - lookup a file path with open + create intent
* @dfd: the directory to use as base, or AT_FDCWD
* @name: pointer to file name
* @lookup_flags: lookup intent flags
* @nd: pointer to nameidata
* @open_flags: open intent flags
* @create_mode: create intent flags
*/
static int path_lookup_create(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE,
nd, open_flags, create_mode);
}
__path_lookup_intent_open, ,create LOOKUP_CREATE create_mode:
static int __path_lookup_intent_open(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
//
struct file *filp = get_empty_filp();
int err;
// ,
if (filp == NULL)
return -ENFILE;
// nameidate ,
nd->intent.open.file = filp;
nd->intent.open.flags = open_flags;
nd->intent.open.create_mode = create_mode;
// , name
err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
// nd->intent.open.file err
if (IS_ERR(nd->intent.open.file)) {
//
if (err == 0) {
// do_path_lookup , path, err
err = PTR_ERR(nd->intent.open.file);
path_release(nd);
}
} else if (err != 0)
release_open_intent(nd);
return err;
}
//
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
static int fastcall do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
int retval = 0;
int fput_needed;
struct file *file;
//
struct fs_struct *fs = current->fs;
// last_type , "/"
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
// , get ,put
if (*name=='/') {
//
read_lock(&fs->lock);
if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
// root, chroot
// altrootmnt , vfsmount
nd->mnt = mntget(fs->altrootmnt);
nd->dentry = dget(fs->altroot);
read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
goto out; /* found in altroot */
read_lock(&fs->lock);
}
// rootmnt nd
nd->mnt = mntget(fs->rootmnt);
// dentry nd
nd->dentry = dget(fs->root);
read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) {
// sys_open ,
read_lock(&fs->lock);
// pwdmnt nd
nd->mnt = mntget(fs->pwdmnt);
// pwd nd
nd->dentry = dget(fs->pwd);
read_unlock(&fs->lock);
} else {
struct dentry *dentry;
// , fd
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
dentry = file->f_dentry;
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_fail;
//
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_fail;
// f_vfsmnt
nd->mnt = mntget(file->f_vfsmnt);
nd->dentry = dget(dentry);
//
fput_light(file, fput_needed);
}
//
current->total_link_count = 0;
// ,
retval = link_path_walk(name, nd);
out:
if (likely(retval == 0)) {
// ,
if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode);
}
out_fail:
return retval;
fput_fail:
fput_light(file, fput_needed);
goto out_fail;
}
do_path_lookup link_path_walk:
/*
* Wrapper to retry pathname resolution whenever the underlying
* file system returns an ESTALE.
*
* Retry the whole path once, forcing real lookup requests
* instead of relying on the dcache.
*/
int fastcall link_path_walk(const char *name, struct nameidata *nd)
{
//
struct nameidata save = *nd;
int result;
/* make sure the stuff we saved doesn't go away */
dget(save.dentry);
mntget(save.mnt);
result = __link_path_walk(name, nd);
if (result == -ESTALE) {
// ESTALE
// nameidate , LOOKUP_REVAL
*nd = save;
dget(nd->dentry);
mntget(nd->mnt);
nd->flags |= LOOKUP_REVAL;
result = __link_path_walk(name, nd);
}
dput(save.dentry);
mntput(save.mnt);
return result;
}
__link_path_walk:
/*
* Name resolution.
* This is the basic name resolution function, turning a pathname into
* the final dentry. We expect 'base' to be positive and a directory.
*
* Returns 0 and nd will have valid dentry and mnt on success.
* Returns error and drops reference to input namei data on failure.
*/
static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
{
struct path next;
struct inode *inode;
int err;
unsigned int lookup_flags = nd->flags;
// "/", "/"
while (*name=='/')
name++;
//
if (!*name)
goto return_reval;
// inode
inode = nd->dentry->d_inode;
if (nd->depth)
lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
/* At this point we know we have a real path component. */
for(;;) {
// , , '/'
unsigned long hash;
struct qstr this;
unsigned int c;
nd->flags |= LOOKUP_CONTINUE;
// , , / / , 0
err = exec_permission_lite(inode, nd);
if (err == -EAGAIN)
// EAGAIN inode ,
//
err = vfs_permission(nd, MAY_EXEC);
//
if (err)
break;
// quickstring
this.name = name;
// name
c = *(const unsigned char *)name;
// hash, '/'
hash = init_name_hash();
do {
name++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *)name;
} while (c && (c != '/'));
// ( )
this.len = name - (const char *) this.name;
// hash
this.hash = end_name_hash(hash);
/* remove trailing slashes? */
// c 0
if (!c)
goto last_component;
// '/'
while (*++name == '/');
// , '/'
if (!*name)
goto last_with_slashes;
/*
* "." and ".." are special - ".." especially so because it has
* to be able to know about the current root directory and
* parent relationships.
*/
// '.'
if (this.name[0] == '.') switch (this.len) {
default:
// '.'
break;
case 2:
// 2 ".",
if (this.name[1] != '.')
break;
// ".." , , nd nameidata , inode
follow_dotdot(nd);
inode = nd->dentry->d_inode;
/* fallthrough */
case 1:
// '.' , ,
continue;
}
/*
* See if the low-level filesystem might want
* to use its own hash..
*/
// FS HASH
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
/* This does the actual lookups.. */
// /
err = do_lookup(nd, &this, &next);
if (err)
break;
err = -ENOENT;
// inode inode
inode = next.dentry->d_inode;
// inode,
if (!inode)
goto out_dput;
err = -ENOTDIR;
if (!inode->i_op)
goto out_dput;
if (inode->i_op->follow_link) {
// ,
err = do_follow_link(&next, nd);
if (err)
goto return_err;
err = -ENOENT;
// inode inode
inode = nd->dentry->d_inode;
if (!inode)
break;
err = -ENOTDIR;
if (!inode->i_op)
break;
} else
// nd
path_to_nameidata(&next, nd);
err = -ENOTDIR;
if (!inode->i_op->lookup)
break;
//
continue;
/* here ends the main loop */
// ,
last_with_slashes:
// '/',
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
/* Clear LOOKUP_CONTINUE iff it was previously unset */
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
if (lookup_flags & LOOKUP_PARENT)
goto lookup_parent;
if (this.name[0] == '.') switch (this.len) {
default:
break;
case 2:
// "..",
if (this.name[1] != '.')
break;
// "..",
follow_dotdot(nd);
inode = nd->dentry->d_inode;
/* fallthrough */
case 1:
// ".",
goto return_reval;
}
//
// FS HASH
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
//
err = do_lookup(nd, &this, &next);
if (err)
break;
inode = next.dentry->d_inode;
if ((lookup_flags & LOOKUP_FOLLOW)
&& inode && inode->i_op && inode->i_op->follow_link) {
err = do_follow_link(&next, nd);
if (err)
goto return_err;
inode = nd->dentry->d_inode;
} else
// nameidata mnt, dentry
path_to_nameidata(&next, nd);
err = -ENOENT;
if (!inode)
break;
if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR;
if (!inode->i_op || !inode->i_op->lookup)
break;
}
goto return_base;
lookup_parent:
// quickstring this nd last
// LAST_NORM
nd->last = this;
nd->last_type = LAST_NORM;
if (this.name[0] != '.')
goto return_base;
if (this.len == 1)
nd->last_type = LAST_DOT;
else if (this.len == 2 && this.name[1] == '.')
nd->last_type = LAST_DOTDOT;
else
goto return_base;
return_reval:
//
/*
* We bypassed the ordinary revalidation routines.
* We may need to check the cached dentry for staleness.
*/
if (nd->dentry && nd->dentry->d_sb &&
(nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
err = -ESTALE;
/* Note: we do not d_invalidate() */
if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
break;
}
return_base:
return 0;
out_dput:
dput_path(&next, nd);
break;
}
//
path_release(nd);
return_err:
return err;
}
/*
* It's more convoluted than I'd like it to be, but... it's still fairly
* small and for now I'd prefer to have fast path as straight as possible.
* It _is_ time-critical.
*/
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path)
{
struct vfsmount *mnt = nd->mnt;
// dentry hash dentry nd->dentry, name dentry
struct dentry *dentry = __d_lookup(nd->dentry, name);
// dentry,
if (!dentry)
goto need_lookup;
// revalidate validate
if (dentry->d_op && dentry->d_op->d_revalidate)
goto need_revalidate;
done:
// , path : mnt dentry
path->mnt = mnt;
path->dentry = dentry;
__follow_mount(path);
return 0;
need_lookup:
// , read_lookup __d_lookup, fs
//
// real_lookup fs
dentry = real_lookup(nd->dentry, name, nd);
if (IS_ERR(dentry))
goto fail;
goto done;
need_revalidate:
// validate
dentry = do_revalidate(dentry, nd);
if (!dentry)
goto need_lookup;
if (IS_ERR(dentry))
goto fail;
goto done;
fail:
return PTR_ERR(dentry);
}
/*
* This is called when everything else fails, and we actually have
* to go to the low-level filesystem to find out what we should do..
*
* We get the directory semaphore, and after getting that we also
* make sure that nobody added the entry to the dcache in the meantime..
* SMP-safe
*/
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
struct dentry * result;
struct inode *dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
/*
* First re-do the cached lookup just in case it was created
* while we waited for the directory semaphore..
*
* FIXME! This could use version numbering or similar to
* avoid unnecessary cache lookups.
*
* The "dcache_lock" is purely to protect the RCU list walker
* from concurrent renames at this point (we mustn't get false
* negatives from the RCU list walk here, unlike the optimistic
* fast walk).
*
* so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
*/
// dentry
result = d_lookup(parent, name);
if (!result) {
// , dentry
struct dentry * dentry = d_alloc(parent, name);
result = ERR_PTR(-ENOMEM);
if (dentry) {
// inode ,
result = dir->i_op->lookup(dir, dentry, nd);
if (result)
// , dentry
dput(dentry);
else
// , dentry
result = dentry;
}
mutex_unlock(&dir->i_mutex);
return result;
}
/*
* Uhhuh! Nasty case: the cache was re-populated while
* we waited on the semaphore. Need to revalidate.
*/
// dentry , validate
mutex_unlock(&dir->i_mutex);
if (result->d_op && result->d_op->d_revalidate) {
result = do_revalidate(result, nd);
if (!result)
result = ERR_PTR(-ENOENT);
}
return result;
}
:
path_lookup_open path_lookup_create
| |
V V
__path_lookup_intent_open
|
V
do_path_lookup
|
V
link_path_walk
|
V
__link_path_walk
|
V
do_lookup
|
V
real_lookup
, , FS .
4.3 open_namei_create
static int open_namei_create(struct nameidata *nd, struct path *path,
int flag, int mode)
{
int error;
// nd dentry
struct dentry *dir = nd->dentry;
if (!IS_POSIXACL(dir->d_inode))
mode &= ~current->fs->umask;
error = vfs_create(dir->d_inode, path->dentry, mode, nd);
mutex_unlock(&dir->d_inode->i_mutex);
dput(nd->dentry);
nd->dentry = path->dentry;
if (error)
return error;
/* Don't check for write permission, don't truncate */
return may_open(nd, 0, flag & ~O_TRUNC);
}
4.4 path_to_nameidata
// nameidata
static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
{
//
dput(nd->dentry);
// ,
if (nd->mnt != path->mnt)
mntput(nd->mnt);
// nameidata
nd->mnt = path->mnt;
nd->dentry = path->dentry;
}
5.
, struct file , , dentry , , struct nameidata , 。