男生跟男生做口视频网站/2022年可以打开的网址
打开文件流程
本文基本Linux5.15
当应用层通过open api打开一个文件,内核中究竟如何处理? 本身用来描述内核中对应open 系统调用的处理流程。
数据结构
fdtable
一个进程可以打开很多文件, 内核用fdtable来管理这些文件。
include/linux/fdtable.h
struct fdtable {unsigned int max_fds;struct file __rcu **fd; /* current fd array */unsigned long *close_on_exec;unsigned long *open_fds;unsigned long *full_fds_bits;struct rcu_head rcu;
};
fd: 文件描述符数组
open_fds: 为方便查找数组中的空闲项, 为该数组建立的位图
close_on_exec: 在打开的文件中, 有些文件时用于执行目的, 在执行完成之后应该自动关闭
files_struct
对于大多数进程, 打开文件的数量是有限的,一种优化的设计方式是为每个进程内置分配少量数目的文件描述符指针数组, 但进程需要更多的指针时, 再动态扩展。 为此, 进程并不直接使用fdtable, 而是使用files_struct结构体, 作为task_struct的一个域
/** Open file table structure*/
struct files_struct {/** read mostly part*/atomic_t count;bool resize_in_progress;wait_queue_head_t resize_wait;struct fdtable __rcu *fdt;struct fdtable fdtab;/** written part on a separate cache line in SMP*/spinlock_t file_lock ____cacheline_aligned_in_smp;unsigned int next_fd;unsigned long close_on_exec_init[1];unsigned long open_fds_init[1];unsigned long full_fds_bits_init[1];struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
fdt指向进程实际使用的fdtable。 对于大多数进程来说, 打开文件的梳理并不会很多, 这时候无需另外分配空间, 直接指向内嵌的结构, 即fdtab域。
file
每个打开的文件都会对应一个file结构体, 进程通过它对文件进行操作。
include/linux/fs.h
struct file {union {struct llist_node fu_llist;struct rcu_head fu_rcuhead;} f_u;struct path f_path;struct inode *f_inode; /* cached value */const struct file_operations *f_op;/** Protects f_ep_links, f_flags.* Must not be taken from IRQ context.*/spinlock_t f_lock;enum rw_hint f_write_hint;atomic_long_t f_count;unsigned int f_flags;fmode_t f_mode;struct mutex f_pos_lock;loff_t f_pos;struct fown_struct f_owner;const struct cred *f_cred;struct file_ra_state f_ra;u64 f_version;
#ifdef CONFIG_SECURITYvoid *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;struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */struct address_space *f_mapping;errseq_t f_wb_err;errseq_t f_sb_err; /* for syncfs */
} __randomize_layout
f_path: 文件路径
f_op: 指向文件操作表, read/write等操作都会调用这里的回调
f_mapping: 指向文件地址空间描述符
f_pos: 当前文件的偏移值
处理流程
open系统调用
整体系统调用栈如下:
#3 0xffffffff81218174 in do_filp_open (dfd=dfd@entry=-100, pathname=pathname@entry=0xffff888004950000, op=op@entry=0xffffc90000173ee4) at fs/namei.c:3396
#4 0xffffffff81203cfd in do_sys_openat2 (dfd=-100, filename=<optimized out>, how=how@entry=0xffffc90000173f20) at fs/open.c:1168
#5 0xffffffff81205135 in do_sys_open (dfd=<optimized out>, filename=<optimized out>, flags=<optimized out>, mode=<optimized out>) at fs/open.c:1184
#6 0xffffffff819bf903 in do_syscall_64 (nr=<optimized out>, regs=0xffffc90000173f58) at arch/x86/entry/common.c:46
#7 0xffffffff81a0007c in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:120
open 系统调用的入口函数定义如下:
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{struct open_how how = build_open_how(flags, mode);return do_sys_openat2(dfd, filename, &how);
}SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{if (force_o_largefile())flags |= O_LARGEFILE;return do_sys_open(AT_FDCWD, filename, flags, mode);
}
do_sys_open 函数调用了do_sys_openat2, 其处理流程如下:
static long do_sys_openat2(int dfd, const char __user *filename,struct open_how *how)
{struct open_flags op;int fd = build_open_flags(how, &op);struct filename *tmp;if (fd)return fd;tmp = getname(filename);if (IS_ERR(tmp))return PTR_ERR(tmp);fd = get_unused_fd_flags(how->flags); /* 1 */if (fd >= 0) {struct file *f = do_filp_open(dfd, tmp, &op); /* 2 */if (IS_ERR(f)) {put_unused_fd(fd);fd = PTR_ERR(f);} else {fsnotify_open(f);fd_install(fd, f); /* 3 */}}putname(tmp);return fd;
}
(1) 获取一个空闲的fd
(2) 执行真正的open 流程, 是后面需要分析的重点
(3) open 成功后, 将fd 链接到当前进程的task_struct 结构体中
fd_install 的处理流程如下:
fs/file.c
void fd_install(unsigned int fd, struct file *file)
{__fd_install(current->files, fd, file);
}void __fd_install(struct files_struct *files, unsigned int fd,struct file *file)
{struct fdtable *fdt;rcu_read_lock_sched();if (unlikely(files->resize_in_progress)) {rcu_read_unlock_sched();spin_lock(&files->file_lock);fdt = files_fdtable(files);BUG_ON(fdt->fd[fd] != NULL);rcu_assign_pointer(fdt->fd[fd], file);spin_unlock(&files->file_lock);return;}/* coupled with smp_wmb() in expand_fdtable() */smp_rmb();fdt = rcu_dereference_sched(files->fdt); /* 1 */BUG_ON(fdt->fd[fd] != NULL); rcu_assign_pointer(fdt->fd[fd], file); /* 2 */rcu_read_unlock_sched();
}
(1) 找到进程对应的fdt table
(2) 将file结构体赋值到对应的fdt中
do_file_open 函数的处理如下, 主要调用了path_openat 函数去执行真正的open 流程:
fs/namei.cdo_sys_open->do_sys_openat2->do_filp_openstruct file *do_filp_open(int dfd, struct filename *pathname,const struct open_flags *op)
{struct nameidata nd;int flags = op->lookup_flags;struct file *filp;set_nameidata(&nd, dfd, pathname);filp = path_openat(&nd, op, flags | LOOKUP_RCU);if (unlikely(filp == ERR_PTR(-ECHILD)))filp = path_openat(&nd, op, flags);if (unlikely(filp == ERR_PTR(-ESTALE)))filp = path_openat(&nd, op, flags | LOOKUP_REVAL);restore_nameidata();return filp;
}
path_openat: 执行open的核心流程
fs/namei.cdo_sys_open->do_sys_openat2->do_filp_open->path_openatstatic struct file *path_openat(struct nameidata *nd,const struct open_flags *op, unsigned flags)
{struct file *file;int error;file = alloc_empty_file(op->open_flag, current_cred()); /* 1 */if (IS_ERR(file))return file;if (unlikely(file->f_flags & __O_TMPFILE)) {error = do_tmpfile(nd, flags, op, file);} else if (unlikely(file->f_flags & O_PATH)) {error = do_o_path(nd, flags, file);} else {const char *s = path_init(nd, flags);while (!(error = link_path_walk(s, nd)) && /* 2 */(s = open_last_lookups(nd, file, op)) != NULL) /* 3 */;if (!error)error = do_open(nd, file, op); /* 4 */terminate_walk(nd);}if (likely(!error)) {if (likely(file->f_mode & FMODE_OPENED))return file;WARN_ON(1);error = -EINVAL;}fput(file);if (error == -EOPENSTALE) {if (flags & LOOKUP_RCU)error = -ECHILD;elseerror = -ESTALE;}return ERR_PTR(error);
}
(1) 申请 file 结构体, 并做初始化
(2) 找到路径的最后一个分量
(3) 对于最后一个分量进行处理, 这里面会去查找文件是否存在,如果不存在则看条件创建
(4) 执行open的最后步骤, 例如调用open 回调
下面分别针对上述的2,3,4 步做详细说明
link_path_walk
link_path_walk的内部实现有点复杂, 大致逻辑是,反复调用walk_component函数, 直到找到路径的最后一个分量。
open_last_lookups
open_lask_lookups 调用lookup_open函数执行 lookup and maybe create 操作
fs/namei.c
do_sys_open->do_sys_openat2->do_filp_open->path_openat->open_last_lookups->lookup_openstatic struct dentry *lookup_open(struct nameidata *nd, struct file *file,const struct open_flags *op,bool got_write)
{struct dentry *dir = nd->path.dentry;struct inode *dir_inode = dir->d_inode;int open_flag = op->open_flag;struct dentry *dentry;int error, create_error = 0;umode_t mode = op->mode;DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);if (unlikely(IS_DEADDIR(dir_inode)))return ERR_PTR(-ENOENT);file->f_mode &= ~FMODE_CREATED;dentry = d_lookup(dir, &nd->last); /* 1 */for (;;) {if (!dentry) {dentry = d_alloc_parallel(dir, &nd->last, &wq);if (IS_ERR(dentry))return dentry;}if (d_in_lookup(dentry))break;error = d_revalidate(dentry, nd->flags);if (likely(error > 0))break;if (error)goto out_dput;d_invalidate(dentry);dput(dentry);dentry = NULL;}if (dentry->d_inode) {/* Cached positive dentry: will open in f_op->open */return dentry;}/** Checking write permission is tricky, bacuse we don't know if we are* going to actually need it: O_CREAT opens should work as long as the* file exists. But checking existence breaks atomicity. The trick is* to check access and if not granted clear O_CREAT from the flags.** Another problem is returing the "right" error value (e.g. for an* O_EXCL open we want to return EEXIST not EROFS).*/if (unlikely(!got_write))open_flag &= ~O_TRUNC;if (open_flag & O_CREAT) {if (open_flag & O_EXCL)open_flag &= ~O_TRUNC;if (!IS_POSIXACL(dir->d_inode))mode &= ~current_umask();if (likely(got_write))create_error = may_o_create(&nd->path, dentry, mode);elsecreate_error = -EROFS;}if (create_error)open_flag &= ~O_CREAT;if (dir_inode->i_op->atomic_open) {dentry = atomic_open(nd, dentry, file, open_flag, mode);if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT))dentry = ERR_PTR(create_error);return dentry;}if (d_in_lookup(dentry)) {struct dentry *res = dir_inode->i_op->lookup(dir_inode, dentry,nd->flags); /* 2 */d_lookup_done(dentry);if (unlikely(res)) {if (IS_ERR(res)) {error = PTR_ERR(res);goto out_dput;}dput(dentry);dentry = res;}}/* Negative dentry, just create the file */if (!dentry->d_inode && (open_flag & O_CREAT)) {file->f_mode |= FMODE_CREATED;audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);if (!dir_inode->i_op->create) {error = -EACCES;goto out_dput;}error = dir_inode->i_op->create(dir_inode, dentry, mode,open_flag & O_EXCL); /* 3 */if (error)goto out_dput;}if (unlikely(create_error) && !dentry->d_inode) {error = create_error;goto out_dput;}return dentry;out_dput:dput(dentry);return ERR_PTR(error);
}
(1) 从缓存中查找dentry
(2) 如果没有找到, 调用文件系统的lookup 方法进行查找
(3) 如果没有找到且O_CREAT, 调用文件系统的create方法进行创建
do_open
在找到对应的文件后,do_open对其进行最后的收尾工作。
fs/namei.cdo_sys_open->do_sys_openat2->do_filp_open->path_openat->do_openstatic int do_open(struct nameidata *nd,struct file *file, const struct open_flags *op)
{int open_flag = op->open_flag;bool do_truncate;int acc_mode;int error;if (!(file->f_mode & (FMODE_OPENED | FMODE_CREATED))) {error = complete_walk(nd);if (error)return error;}if (!(file->f_mode & FMODE_CREATED))audit_inode(nd->name, nd->path.dentry, 0);if (open_flag & O_CREAT) {if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED))return -EEXIST;if (d_is_dir(nd->path.dentry))return -EISDIR;error = may_create_in_sticky(nd->dir_mode, nd->dir_uid,d_backing_inode(nd->path.dentry));if (unlikely(error))return error;}if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry))return -ENOTDIR;do_truncate = false;acc_mode = op->acc_mode;if (file->f_mode & FMODE_CREATED) {/* Don't check for write permission, don't truncate */open_flag &= ~O_TRUNC;acc_mode = 0;} else if (d_is_reg(nd->path.dentry) && open_flag & O_TRUNC) {error = mnt_want_write(nd->path.mnt);if (error)return error;do_truncate = true;}error = may_open(&nd->path, acc_mode, open_flag); /* 1 */if (!error && !(file->f_mode & FMODE_OPENED)) error = vfs_open(&nd->path, file); /* 2 */if (!error)error = ima_file_check(file, op->acc_mode);if (!error && do_truncate)error = handle_truncate(file);if (unlikely(error > 0)) {WARN_ON(1);error = -EINVAL;}if (do_truncate)mnt_drop_write(nd->path.mnt);return error;
}
(1) map_open 里面会做一些权限检查, 比如检测文件系统是否是readonly
(2) 调用vfs_open执行最后的open 流程
fs/open.cdo_sys_open->do_sys_openat2->do_filp_open->path_openat->do_open->vfs_openint vfs_open(const struct path *path, struct file *file)
{file->f_path = *path;return do_dentry_open(file, d_backing_inode(path->dentry), NULL);
}static int do_dentry_open(struct file *f,struct inode *inode,int (*open)(struct inode *, struct file *))
{static const struct file_operations empty_fops = {};int error;path_get(&f->f_path);f->f_inode = inode;f->f_mapping = inode->i_mapping;f->f_wb_err = filemap_sample_wb_err(f->f_mapping);f->f_sb_err = file_sample_sb_err(f); /* 1 */if (unlikely(f->f_flags & O_PATH)) {f->f_mode = FMODE_PATH | FMODE_OPENED;f->f_op = &empty_fops;return 0;}if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {error = get_write_access(inode);if (unlikely(error))goto cleanup_file;error = __mnt_want_write(f->f_path.mnt);if (unlikely(error)) {put_write_access(inode);goto cleanup_file;}f->f_mode |= FMODE_WRITER;}/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))f->f_mode |= FMODE_ATOMIC_POS;f->f_op = fops_get(inode->i_fop); /* 2 */if (WARN_ON(!f->f_op)) {error = -ENODEV;goto cleanup_all;}error = security_file_open(f);if (error)goto cleanup_all;error = break_lease(locks_inode(f), f->f_flags);if (error)goto cleanup_all;/* normally all 3 are set; ->open() can clear them if needed */f->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;if (!open)open = f->f_op->open;if (open) {error = open(inode, f); /* 3 */if (error)goto cleanup_all;}f->f_mode |= FMODE_OPENED;if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)i_readcount_inc(inode);if ((f->f_mode & FMODE_READ) &&likely(f->f_op->read || f->f_op->read_iter))f->f_mode |= FMODE_CAN_READ;if ((f->f_mode & FMODE_WRITE) &&likely(f->f_op->write || f->f_op->write_iter))f->f_mode |= FMODE_CAN_WRITE;f->f_write_hint = WRITE_LIFE_NOT_SET;f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);/* NB: we're sure to have correct a_ops only after f_op->open */if (f->f_flags & O_DIRECT) {if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)return -EINVAL;}/** XXX: Huge page cache doesn't support writing yet. Drop all page* cache for this file before processing writes.*/if ((f->f_mode & FMODE_WRITE) && filemap_nr_thps(inode->i_mapping))truncate_pagecache(inode, 0);return 0;cleanup_all:if (WARN_ON_ONCE(error > 0))error = -EINVAL;fops_put(f->f_op);if (f->f_mode & FMODE_WRITER) {put_write_access(inode);__mnt_drop_write(f->f_path.mnt);}
cleanup_file:path_put(&f->f_path);f->f_path.mnt = NULL;f->f_path.dentry = NULL;f->f_inode = NULL;return error;
}
(1) (2) 设置file结构体的一些成员
(3) 找到open 回调, 并执行
exfat 相关回调
下面以exfat 文件系统为例, 介绍一下open 流程中相关回调的具体实现。
open 流程中,涉及到三个具体回调: lookup, create, open
其中lookup 和 create 位于inode_operations, open
struct inode_operations {****struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);int (*create) (struct inode *,struct dentry *, umode_t, bool);****
}struct file_operations {struct module *owner;*****int (*open) (struct inode *, struct file *);*****
}
其中, exfat 没有实现open函数, 只实现了create和lookup 函数。
exfat_lookup
static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,unsigned int flags)
{struct super_block *sb = dir->i_sb;struct inode *inode;struct dentry *alias;struct exfat_dir_entry info;int err;loff_t i_pos;mode_t i_mode;mutex_lock(&EXFAT_SB(sb)->s_lock);err = exfat_find(dir, &dentry->d_name, &info); /* 1 */if (err) {if (err == -ENOENT) {inode = NULL;goto out;}goto unlock;}i_pos = exfat_make_i_pos(&info); inode = exfat_build_inode(sb, &info, i_pos); /* 2 */err = PTR_ERR_OR_ZERO(inode);if (err)goto unlock;i_mode = inode->i_mode;alias = d_find_alias(inode);/** Checking "alias->d_parent == dentry->d_parent" to make sure* FS is not corrupted (especially double linked dir).*/if (alias && alias->d_parent == dentry->d_parent &&!exfat_d_anon_disconn(alias)) {/** Unhashed alias is able to exist because of revalidate()* called by lookup_fast. You can easily make this status* by calling create and lookup concurrently* In such case, we reuse an alias instead of new dentry*/if (d_unhashed(alias)) {WARN_ON(alias->d_name.hash_len !=dentry->d_name.hash_len);exfat_info(sb, "rehashed a dentry(%p) in read lookup",alias);d_drop(dentry);d_rehash(alias);} else if (!S_ISDIR(i_mode)) {/** This inode has non anonymous-DCACHE_DISCONNECTED* dentry. This means, the user did ->lookup() by an* another name (longname vs 8.3 alias of it) in past.** Switch to new one for reason of locality if possible.*/d_move(alias, dentry);}iput(inode);mutex_unlock(&EXFAT_SB(sb)->s_lock);return alias;}dput(alias);
out:mutex_unlock(&EXFAT_SB(sb)->s_lock);if (!inode)exfat_d_version_set(dentry, inode_query_iversion(dir));return d_splice_alias(inode, dentry); /* 3 */
unlock:mutex_unlock(&EXFAT_SB(sb)->s_lock);return ERR_PTR(err);
}
lookup 函数的入参定义为:
dir: 父目录对应的inode
dentry: 所需要找的文件对应的dentry
返回值为所找文件对应的dentry。
在调用这个函数之前, 已经为子节点分配了dentry, 并将它关联到父目录的dentry, 但是它还没有被关联到inode。
这个函数应该在父目录中找到文件, 并分配inode, 关联到对用的dentry上。
(1) 根据name, 在父目录中,找对对应的entry
(2) 建立对应的inode
(3) 建立inode 和dentry之间的联系
其中, exfat_find的流程如下:
/* lookup a file */
static int exfat_find(struct inode *dir, struct qstr *qname,struct exfat_dir_entry *info)
{int ret, dentry, num_entries, count;struct exfat_chain cdir;struct exfat_uni_name uni_name;struct super_block *sb = dir->i_sb;struct exfat_sb_info *sbi = EXFAT_SB(sb);struct exfat_inode_info *ei = EXFAT_I(dir);struct exfat_dentry *ep, *ep2;struct exfat_entry_set_cache *es;if (qname->len == 0)return -ENOENT;/* check the validity of directory name in the given pathname */ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name);if (ret)return ret;num_entries = exfat_calc_num_entries(&uni_name);if (num_entries < 0)return num_entries;/* check the validation of hint_stat and initialize it if required */if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {ei->hint_stat.clu = cdir.dir;ei->hint_stat.eidx = 0;ei->version = (inode_peek_iversion_raw(dir) & 0xffffffff);ei->hint_femp.eidx = EXFAT_HINT_NONE;}/* search the file name for directories */dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name,num_entries, TYPE_ALL); /* 1 */if (dentry < 0)return dentry; /* -error value */info->dir = cdir;info->entry = dentry;info->num_subdirs = 0;es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);if (!es)return -EIO;ep = exfat_get_dentry_cached(es, 0);ep2 = exfat_get_dentry_cached(es, 1);info->type = exfat_get_entry_type(ep); /* 2 */info->attr = le16_to_cpu(ep->dentry.file.attr);info->size = le64_to_cpu(ep2->dentry.stream.valid_size);if ((info->type == TYPE_FILE) && (info->size == 0)) {info->flags = ALLOC_NO_FAT_CHAIN;info->start_clu = EXFAT_EOF_CLUSTER;} else {info->flags = ep2->dentry.stream.flags;info->start_clu =le32_to_cpu(ep2->dentry.stream.start_clu);}exfat_get_entry_time(sbi, &info->crtime,ep->dentry.file.create_tz,ep->dentry.file.create_time,ep->dentry.file.create_date,ep->dentry.file.create_time_cs);exfat_get_entry_time(sbi, &info->mtime,ep->dentry.file.modify_tz,ep->dentry.file.modify_time,ep->dentry.file.modify_date,ep->dentry.file.modify_time_cs);exfat_get_entry_time(sbi, &info->atime,ep->dentry.file.access_tz,ep->dentry.file.access_time,ep->dentry.file.access_date,0);exfat_free_dentry_set(es, false);if (ei->start_clu == EXFAT_FREE_CLUSTER) {exfat_fs_error(sb,"non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)",i_size_read(dir), ei->dir.dir, ei->entry);return -EIO;}if (info->type == TYPE_DIR) {exfat_chain_set(&cdir, info->start_clu,EXFAT_B_TO_CLU(info->size, sbi), info->flags);count = exfat_count_dir_entries(sb, &cdir);if (count < 0)return -EIO;info->num_subdirs = count + EXFAT_MIN_SUBDIR;}return 0;
}
从逻辑上讲, 这个函数要做的事情,应该是遍历这个父目录的cluster, 根据name找到匹配的那个entry。
(1) 根据name找到对应的entry
(2) entry中记录的相关信息记录到struct exfat_dir_entry *info 这个结构体中
exfat_create
static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,bool excl)
{struct super_block *sb = dir->i_sb;struct inode *inode;struct exfat_chain cdir;struct exfat_dir_entry info;loff_t i_pos;int err;mutex_lock(&EXFAT_SB(sb)->s_lock);exfat_set_volume_dirty(sb);err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,&info); /* 1 */exfat_clear_volume_dirty(sb);if (err)goto unlock;inode_inc_iversion(dir);dir->i_ctime = dir->i_mtime = current_time(dir);if (IS_DIRSYNC(dir))exfat_sync_inode(dir);elsemark_inode_dirty(dir);i_pos = exfat_make_i_pos(&info);inode = exfat_build_inode(sb, &info, i_pos); /* 2 */err = PTR_ERR_OR_ZERO(inode);if (err)goto unlock;inode_inc_iversion(inode);inode->i_mtime = inode->i_atime = inode->i_ctime =EXFAT_I(inode)->i_crtime = current_time(inode);exfat_truncate_atime(&inode->i_atime);/* timestamp is already written, so mark_inode_dirty() is unneeded. */d_instantiate(dentry, inode); /* 3 */
unlock:mutex_unlock(&EXFAT_SB(sb)->s_lock);return err;
}
exfat_create用于在目录下创建文件。 第一个参数为目录对应的inode, 第二个参数为需要创建的文件对应的dentry。
这个函数应该做的是, 在文件系统中创建一个新的文件, 并建立inode, 关联到对应的dentry上。
(1) 在目录中添加一个entry
(2) 建立inode
(3) 管理inode和denty
其中exfat_add_entry的主要流程如下:
static int exfat_add_entry(struct inode *inode, const char *path,struct exfat_chain *p_dir, unsigned int type,struct exfat_dir_entry *info)
{int ret, dentry, num_entries;struct super_block *sb = inode->i_sb;struct exfat_sb_info *sbi = EXFAT_SB(sb);struct exfat_uni_name uniname;struct exfat_chain clu;int clu_size = 0;unsigned int start_clu = EXFAT_FREE_CLUSTER;ret = exfat_resolve_path(inode, path, p_dir, &uniname);if (ret)goto out;num_entries = exfat_calc_num_entries(&uniname);if (num_entries < 0) {ret = num_entries;goto out;}/* exfat_find_empty_entry must be called before alloc_cluster() */dentry = exfat_find_empty_entry(inode, p_dir, num_entries); /* 1 */if (dentry < 0) {ret = dentry; /* -EIO or -ENOSPC */goto out;}if (type == TYPE_DIR) {ret = exfat_alloc_new_dir(inode, &clu);if (ret)goto out;start_clu = clu.dir;clu_size = sbi->cluster_size;}/* update the directory entry *//* fill the dos name directory entry information of the created file.* the first cluster is not determined yet. (0)*/ret = exfat_init_dir_entry(inode, p_dir, dentry, type,start_clu, clu_size); /* 2 */if (ret)goto out;ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname);if (ret)goto out;info->dir = *p_dir;info->entry = dentry;info->flags = ALLOC_NO_FAT_CHAIN;info->type = type;if (type == TYPE_FILE) {info->attr = ATTR_ARCHIVE;info->start_clu = EXFAT_EOF_CLUSTER;info->size = 0;info->num_subdirs = 0;} else {info->attr = ATTR_SUBDIR;info->start_clu = start_clu;info->size = clu_size;info->num_subdirs = EXFAT_MIN_SUBDIR;}memset(&info->crtime, 0, sizeof(info->crtime));memset(&info->mtime, 0, sizeof(info->mtime));memset(&info->atime, 0, sizeof(info->atime));
out:return ret;
}
(1) 在目录中找到一个空闲的entry。这个里面会去遍历整个目录的cluster, 直到找到没有使用的entry
(2) 根据找到的entry, 初始化相关的元数据
相关文章:

linux内核open文件流程
打开文件流程 本文基本Linux5.15 当应用层通过open api打开一个文件,内核中究竟如何处理? 本身用来描述内核中对应open 系统调用的处理流程。 数据结构 fdtable 一个进程可以打开很多文件, 内核用fdtable来管理这些文件。 include/linu…...

遗传算法讲解
遗传算法(Genetic Algorithm,GA) 是模拟生物在自然环境中的遗传和进化的过程而形成的自适应全局优化搜索算法。它借用了生物遗传学的观点,通过自然选择、遗传和变异等作用机制,实现各个个体适应性的提高。 基因型 (G…...

PostgreSQL修炼之道之高可用性方案设计(十六)
20 高可用性方案设计(一) 在一个生产系统中,通常都需要用高可用方案来保证系统的不间断运行。本章将详细介绍如何实现PostgreSQL数据库的高可用方案。 20.1 高可用架构基础 通常数据库的高可用方案都是让多个数据库服务器协同工作࿰…...

Bybit面经
缘起 V2EX有广告内推,看描述还挺不错 贴主5 年半工作经验,有两年大厂工作经历,20 年 11 月来到新加坡分公司开始工作 后来是猎头Jeff找的我 0318 主面 主要一个面试官是后端开发金融背景 某条金融线的负责人;其余是交叉面试。面…...

GORM---创建
目录 模型定义使用Create创建记录一次性创建多条数据批量插入数据时开启事务默认值问题 模型定义 定义一个PersonInfo结构体。 type PersonInfo struct {Id uint64 gorm:"column:id;primary_key;NOT NULL" json:"id"UserName string gorm:"co…...

高级查询 — 分组汇总
关于分组汇总 1.概述 将查询结果按某一列或者多列的值分组。 group by子句 分组后聚合函数将作用于每一个组,即每一组都有一个函数值。 语法 select 字段列表 from 表名 where 筛选条件 group by 分组的字段;select 字段列表 from 表名 group by 分组的字段 hav…...

【多线程】阻塞队列
1. 认识阻塞队列和消息队列 阻塞队列也是一个队列,也是一个特殊的队列,也遵守先进先出的原则,但是带有特殊的功能。 如果阻塞队列为空,执行出队列操作,就会阻塞等待,阻塞到另一个线程往阻塞队列中添加元素(…...

python2升级python3
查看当前版本 [roottest-01 node-v18.16.0]# python -V Python 2.7.5 安装依赖 [roottest-01 node-v18.16.0]# yum install -y gcc gcc-c zlib zlib-devel readline-devel 已加载插件:fastestmirror, langpacks Loading mirror speeds from cached hostfile * base…...

Apache Hudi初探(八)(与spark的结合)--非bulk_insert模式
背景 之前讨论的都是’hoodie.datasource.write.operation’:bulk_insert’的前提下,在这种模式下,是没有json文件的已形成如下的文件: /dt1/.hoodie_partition_metadata /dt1/2ffe3579-6ddb-4c5f-bf03-5c1b5dfce0a0-0_0-41263-0_202305282…...

Java之旅(九)
Java 循环语句 Java 中的循环语句包括 for、while 和 do-while,它们都可以用于实现循环结构。 for 语句用于循环执行一段代码块,直到给定的条件表达式的布尔值为 false。 for 语句的一般格式如下: for (initialization; condition; update…...

6年测试经验之谈,为什么要做自动化测试?
一、自动化测试 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。 个人认为,只要能服务于测试工作,能够帮助我们提升工作效率的,不管是所谓的自动化工具,还是简单的SQL 脚本、批处理脚本,还是自己编写…...

二分法的边界条件 2517. 礼盒的最大甜蜜度
2517. 礼盒的最大甜蜜度 给你一个正整数数组 price ,其中 price[i] 表示第 i 类糖果的价格,另给你一个正整数 k 。 商店组合 k 类 不同 糖果打包成礼盒出售。礼盒的 甜蜜度 是礼盒中任意两种糖果 价格 绝对差的最小值。 返回礼盒的 最大 甜蜜度。 记录一…...

java设计模式(十六)命令模式
目录 定义模式结构角色职责代码实现适用场景优缺点 定义 命令模式(Command Pattern) 又叫动作模式或事务模式。指的是将一个请求封装成一个对象,使发出请求的责任和执行请求的责任分割开,然后可以使用不同的请求把客户端参数化&a…...

[运维] iptables限制指定ip访问指定端口和只允许指定ip访问指定端口
iptables限制指定ip访问指定端口 要使用iptables限制特定IP地址访问特定端口,您可以使用以下命令: iptables -A INPUT -p tcp -s <IP地址> --dport <端口号> -j DROP请将 <IP地址> 替换为要限制的IP地址,将 <端口号&g…...

JS学习笔记(3. 流程控制)
1. 分歧 1.1 if条件 if (条件) {...} // 为真则执行,单条语句可省略大括号 if (条件) {...} else {...}// 为真则执行if,否则执行else if (条件1) {...} else if (条件2) {...} else {...} // 条件1为真则,条件2为真则,否则执…...

遥感云大数据在灾害、水体与湿地领域典型案例及GPT模型教程
详情点击链接:遥感云大数据在灾害、水体与湿地领域典型案例及GPT模型教程 一:平台及基础开发平台 GEE平台及典型应用案例; GEE开发环境及常用数据资源; ChatGPT、文心一言等GPT模型 JavaScript基础; GEE遥感云重…...

什么是文件描述符以及重定向的本质和软硬链接(Linux)
目录 1 什么是文件?什么是文件操作?认识系统接口open 什么是文件描述符认识Linux底层进程如何打开的文件映射关系重定向的本质理解软硬链接扩展问题 1 什么是文件?什么是文件操作? 文件 文件内容 文件属性(文件属性…...

LVM逻辑卷元数据丢失恢复案例 —— 筑梦之路
Lvm常见的故障主要是pv出现异常,有以下几种情况 一个是pv所在的磁盘发生了lvm的元数据损坏一个是系统无法识别到pv所在的磁盘一个是系统异常,断电等导致重启后盘符发生变化,也就是系统识别的磁盘uuid发生变化,但是wwid还是可以对应…...

Java技术规范概览
Java技术规范 目录概述需求: 设计思路实现思路分析1.Java JSR的部分2.JSR-000373.JSR-0000394.JSR-000337 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a bet…...

【OpenMMLab AI实战营第二期】二十分钟入门OpenMMLab笔记
OpenMMlab 主页:openmmlab.com 开源地址:https://github.com/open-mmlab 学习视频地址:https://www.bilibili.com/video/BV1js4y1i72P/ 概述 开源成为人工智能行业发展引擎 时间轴 theano:2007 Caffe:2013 Ten…...

docker-compose单机容器集群编排
docker-compose dockerfile模板文件可以定义一个独立的应用容器,如果需要多个容器就需要服务编排。服务编排有很多技术方案 docker-compose开源的项目实现对容器集群的快速编排 docker-compose将所管理的容器分为三层,分别为工程,服务&#…...

CentOS7 安装Gitlab
1、安装依赖 sudo yum install -y curl openssh-server ca-certificates tzdata perl libsemanage-devel 2、安装邮件服务工具 sudo yum install -y postfix 3、配置GitLab 软件源镜像 curl -fsSL https://packages.gitlab.cn/repository/raw/scripts/setup.sh | /bin/bash …...

Mysql InnoDB的Buffer Pool
Buffer Pool 在MySQL服务器启动的时候就向操作系统申请了⼀⽚连续的内存,他们给这⽚内存起了个名,叫做Buffer Pool(中⽂名 是缓冲池)。 默认情况下Buffer Pool只有128M⼤⼩,最⼩值为5M,通过修改配置文件设…...

SMTP简单邮件传输协议(C/C++ 发送电子邮件)
SMTP是用于通过Internet发送电子邮件的协议。电子邮件客户端(如Microsoft Outlook或macOS Mail应用程序)使用SMTP连接到邮件服务器并发送电子邮件。邮件服务器还使用SMTP将邮件从一个邮件服务器交换到另一个。它不用于从服务器下载电子邮件;相…...

uploads靶场通关(1-11关)
Pass-01(JS校验) 看题目我们准备好我们的php脚本文件,命名为1.php 上传该php文件,发现上传失败 方法一:将浏览器的JavaScript禁用 然后就能上传了 方法二: 查看源码,发现只能上传以下形式的文…...

6.1黄金探底回升是否到顶,今日多空如何布局
近期有哪些消息面影响黄金走势?今日黄金多空该如何研判? 黄金消息面解析:周三(5月31日)黄金期货价格攀升,美国国债收益率下降推动金价升至一周最高收盘位。美市尾盘,现货黄金收报1962.42美元/盎司,上升3…...

自定义ViewGroup实现流式布局
目录 1、View的绘制流程 2、自定义ViewGroup构造函数的作用 3、onMeasure 方法 3.1、View的度量方式 3.2、onMeasure方法参数的介绍 3.3、自定义ViewGroup onMeasure 方法的实现 4、onLayout方法 5、onDraw方法 6、自定义View的生命周期 7、自定义流式布局的实现 扩展ÿ…...

Git版本控制
目录 版本控制 概念 为什么需要版本控制? 常见的版本控制工具 Git 1、安装 2、了解基本的Linux命令 3、配置git 用户名和邮箱 4、git 工作模式 5、git 项目管理 6、git 分支 托管平台 远程仓库 Gitee 关联多个远程库 Git服务器 Git GUI 版本控制 概…...

若依之权限处理
若依之权限处理 若依前后端不分离版本使用的是shiro进行权限控制,本文主要是对shiro在若依中的使用进行分析。 RBAC权限模型 RBAC是指基于角色的访问控制。其基本思想是,对系统的各种权限不是直接授予具体的用户,而是在用户集合与权限集合…...

华为OD机试真题 Java 实现【矩阵最大值】【2023 B卷 100分】,附详细解题思路
一、题目描述 给定一个仅包含0和1的N*N的二维矩阵,请计算二维矩阵的最大值。 计算规则如下: 1、每行元素按下标顺序组成一个二进制数(下标越大越排在低位),二进制数的值就是该行的值。矩阵各行值之和为矩阵的值。 2、允许通过向左或向右整体循环移动每行元素来改变各元…...