在 Java 里写下一行 channel.close(),我们通常不会去想它背后发生了什么。但这行代码要穿过 Java 状态机、JNI 边界、glibc 的系统调用包装、Linux 内核的 fdtable 管理、struct file 引用计数、Socket 抽象层和 TCP 协议栈,最终才让一个连接进入四次挥手。本文以一组横跨 Java、JNI、glibc 与 Linux 内核的源码为线索,把这条链路一环一环串起来,并重点澄清两个容易混淆的问题:file->f_op->release 到底是不是 inet_release?struct file 的引用计数又是在哪里被初始化的?
一、Java 层:关闭不是"立刻关掉"
Java NIO 的 SocketChannelImpl 内部维护一个状态机(ST_OPENED → ST_CLOSING → ST_CLOSED),关闭动作被拆成了两步:先把状态置为 ST_CLOSING,再尝试真正关闭。真正的关闭条件是"没有任何 I/O 正在进行,且没有注册到 Selector 上":
java
/**
* Closes the socket if there are no I/O operations in progress and the
* channel is not registered with a Selector.
*/
private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
// 三个条件同时满足才真正关闭:
// 1. 没有读者线程正在使用该 fd
// 2. 没有写者线程正在使用该 fd
// 3. 没有注册在 Selector 上
// 否则直接返回 false,由最后一个退出的 I/O 操作负责收尾
if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
state = ST_CLOSED;
nd.close(fd); // 调用 NativeDispatcher 关闭文件描述符
return true;
} else {
return false;
}
}
这种设计避免了"一个线程还在 read() 另一个线程就把 fd 关了"的经典问题。nd 是 NativeDispatcher,对 Socket 来说通常是 SocketDispatcher(继承自 FileDispatcherImpl)。注意 FileDispatcherImpl.close0 的注释里写着 "Shared with SocketDispatcher and DatagramDispatcher but NOT used by FileDispatcherImpl"------这说明这个 close0 是给 Socket 和 Datagram 用的实现,普通文件的关闭走的是另一条路径。
二、JNI 与 glibc:进入内核前的最后一站
JNI 实现只做两件事:把 Java 的 FileDescriptor 对象转成整数 fd,然后调用 libc 的 close():
c
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo)
{
jint fd = fdval(env, fdo); // 从 FileDescriptor 对象中取出 int fd
closeFileDescriptor(env, fd);
}
static void closeFileDescriptor(JNIEnv *env, int fd) {
if (fd != -1) { // fd 已被关闭则直接跳过,防止重复 close
int result = close(fd); // 调用 glibc 的 close
if (result < 0)
JNU_ThrowIOExceptionWithLastError(env, "Close failed");
}
}
glibc 里的 close 是一个弱别名,最终落到 __close:
c
/* Close the file descriptor FD. */
int
__close (int fd)
{
return SYSCALL_CANCEL (close, fd); // 发起 close 系统调用
}
libc_hidden_def (__close)
strong_alias (__close, __libc_close)
weak_alias (__close, close) // 用户程序里的 close 就是它
SYSCALL_CANCEL 负责在发起系统调用前处理信号取消点等细节。到这一步,执行流正式从用户态切换到内核态。
三、内核 close:先摘 fd,再放 file
内核侧的 close 系统调用实现如下,注意那段 "Careful here!" 的注释:
c
/*
* Careful here! We test whether the file pointer is NULL before
* releasing the fd. This ensures that one clone task can't release
* an fd while another clone is opening it.
*/
SYSCALL_DEFINE1(close, unsigned int, fd)
{
int retval;
struct file *file;
// 1. 从当前进程的 fdtable 中摘下 struct file,并清空该 fd 槽位
file = file_close_fd(fd);
if (!file)
return -EBADF; // fd 无效
// 2. 刷新文件(对 socket 而言通常无实际动作,但保留 VFS 语义)
retval = filp_flush(file, current->files);
/*
* We're returning to user space. Don't bother
* with any delayed fput() cases.
*/
// 3. 同步减少引用计数,若归零则立即执行 __fput
__fput_sync(file);
/* can't restart close syscall because file table entry was cleared */
// 4. 系统调用不可重启,把可重启错误码统一转成 -EINTR
if (unlikely(retval == -ERESTARTSYS ||
retval == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK))
retval = -EINTR;
return retval;
}
3.1 file_close_fd:为什么先摘 fd 再放 file
c
struct file *file_close_fd(unsigned int fd)
{
struct files_struct *files = current->files;
struct file *file;
spin_lock(&files->file_lock);
file = file_close_fd_locked(files, fd);
spin_unlock(&files->file_lock);
return file;
}
struct file *file_close_fd_locked(struct files_struct *files, unsigned fd)
{
struct fdtable *fdt = files_fdtable(files);
struct file *file;
lockdep_assert_held(&files->file_lock);
if (fd >= fdt->max_fds)
return NULL;
fd = array_index_nospec(fd, fdt->max_fds); // 防御 Spectre 的索引消毒
file = fdt->fd[fd];
if (file) {
rcu_assign_pointer(fdt->fd[fd], NULL); // ★ 先清空 fd 槽位
__put_unused_fd(files, fd); // ★ 再把 fd 号标记为可复用
}
return file;
}
close 的顺序是"先清 fd 槽位,再释放 file ",这一点对应注释里的 "one clone task can't release an fd while another clone is opening it"。clone 出来的线程共享 files_struct,如果先释放 file 再清 fd 槽位,另一个线程可能在这段窗口里通过 open() 复用同一个 fd 号,造成 fd 号与 struct file 对象生命周期错位。先摘槽位,就把 fd 号的生命周期与 struct file 的生命周期解耦了。
__put_unused_fd 还做了一件小事:把 files->next_fd 回退到较小的值,让下一次 open 优先复用低编号 fd。
c
static void __put_unused_fd(struct files_struct *files, unsigned int fd)
{
struct fdtable *fdt = files_fdtable(files);
__clear_open_fd(fd, fdt); // 清除 open_fds / full_fds_bits 位图
if (fd < files->next_fd)
files->next_fd = fd; // 下次 open 优先复用这个较小的 fd 号
}
3.2 __fput_sync:为什么不走延迟路径
c
/*
* synchronous analog of fput(); for kernel threads that might be needed
* in some umount() ... Use only if you really need it ...
*/
void __fput_sync(struct file *file)
{
if (atomic_long_dec_and_test(&file->f_count)) // ★ 引用计数减一,归零才真正释放
__fput(file);
}
普通的 fput() 会把 __fput 推迟到 task_work 里执行(所谓"延迟 fput"),而 close 系统调用即将返回用户态,没必要绕这一圈,于是直接用 __fput_sync。注意 atomic_long_dec_and_test 的语义:只有当 f_count 减到 0 时才返回 true。这意味着如果这个 file 还有别的引用(例如 dup() 出来的另一个 fd,或者 epoll 内部持有的引用),这里就不会触发 __fput,真正的释放留给最后一个持有者。
四、__fput:VFS 层清理与 release 调用
c
/* the real guts of fput() - releasing the last reference to file */
static void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
struct inode *inode = file->f_inode;
fmode_t mode = file->f_mode;
if (unlikely(!(file->f_mode & FMODE_OPENED)))
goto out; // 未真正完成 open 的 file 直接释放
might_sleep();
fsnotify_close(file);
/*
* The function eventpoll_release() should be the first called
* in the file cleanup chain.
*/
eventpoll_release(file); // ★ 必须最先调用:从 epoll 中摘除引用
locks_remove_file(file); // 清理文件锁
ima_file_free(file); // IMA 完整性度量清理
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0); // 取消异步通知
}
// ★ 核心:调用文件操作集的 release
if (file->f_op->release)
file->f_op->release(inode, file);
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL &&
!(mode & FMODE_PATH))) {
cdev_put(inode->i_cdev);
}
fops_put(file->f_op);
put_pid(file->f_owner.pid);
put_file_access(file);
dput(dentry); // 放掉 dentry 引用
if (unlikely(mode & FMODE_NEED_UNMOUNT))
dissolve_on_fput(mnt);
mntput(mnt); // 放掉 vfsmount 引用
out:
file_free(file); // 释放 struct file 本身
}
__fput 是整个旅程的枢纽:在它之前,大家操作的都是"fd 编号"和"struct file 指针";在它之后,真正属于该文件类型的释放逻辑才开始执行。eventpoll_release 被要求最先调用,是因为如果 file 还被 epoll 引用着,release 回调里触发的任何操作都可能踩到已经失效的 epoll 数据结构。
五、socket_file_ops:f_op->release 究竟是谁
__fput 里那行 file->f_op->release(inode, file),对 Socket 文件描述符来说,调用的是谁?用户提供的源码给出了决定性证据:
c
static const struct file_operations socket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read_iter = sock_read_iter,
.write_iter = sock_write_iter,
.poll = sock_poll,
.unlocked_ioctl = sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_sock_ioctl,
#endif
.uring_cmd = io_uring_cmd_sock,
.mmap = sock_mmap,
.release = sock_close, // ★ 就是它!
.fasync = sock_fasync,
.splice_write = splice_to_socket,
.splice_read = sock_splice_read,
.splice_eof = sock_splice_eof,
.show_fdinfo = sock_show_fdinfo,
};
当 socket 被创建时,alloc_file_pseudo → alloc_file 会把 socket_file_ops 赋给 file->f_op。因此,__fput 里调的 release 是 sock_close ,而不是 inet_release。
这里要澄清一个常见的误解。很多人以为 file->private_data 存着 struct socket,所以 sock_close 会走 file->private_data。实际上:
-
file->private_data确实指向struct socket,但它主要服务于sock_poll、sock_ioctl、sock_mmap、sock_fasync这些需要快速拿到 socket 的操作; -
sock_close走的是SOCKET_I(inode),即从inode反推出struct socket(struct socket内嵌在socket_alloc中,SOCKET_I用container_of零成本转换)。选择inode路径是因为__sock_release需要inode_lock来保护 socket 的释放过程。
六、Socket 抽象层与协议层:inet_release 终于登场
sock_close 调用 __sock_release,后者在持有 inode_lock 的情况下调用 sock->ops->release(sock)。sock->ops 是 struct proto_ops,对 INET 流式套接字来说是 inet_stream_ops,其 .release 正是 inet_release:
c
int inet_release(struct socket *sock)
{
struct sock *sk = sock->sk;
if (sk) {
long timeout;
if (!sk->sk_kern_sock)
BPF_CGROUP_RUN_PROG_INET_SOCK_RELEASE(sk); // cgroup BPF 释放钩子
/* Applications forget to leave groups before exiting */
ip_mc_drop_socket(sk); // 清理多播组成员关系
/*
* If linger is set, we don't return until the close is complete.
* ...
* If the close is due to the process exiting, we never linger.
*/
timeout = 0;
if (sock_flag(sk, SOCK_LINGER) &&
!(current->flags & PF_EXITING)) // 进程正在退出时不 linger
timeout = sk->sk_lingertime;
sk->sk_prot->close(sk, timeout); // ★ 进入协议层,如 tcp_close
sock->sk = NULL;
}
return 0;
}
EXPORT_SYMBOL(inet_release);
到这里,TCP 状态机才真正开始执行四次挥手。sk->sk_prot->close 对 TCP 来说是 tcp_close,它负责把连接从 ESTABLISHED 推进到 FIN_WAIT_1,发出 FIN 包,并处理 SO_LINGER 语义。
七、引用计数从哪里来:alloc_file_pseudo 到底做了什么
前面反复出现 f_count 的递减,那么 f_count 是在哪里被初始化的?很多人看到 alloc_file_pseudo 里有一串 mntget、d_instantiate、ihold,就以为其中某一行在给 f_count 加一。其实不是。
c
struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
const char *name, int flags,
const struct file_operations *fops)
{
struct qstr this = QSTR_INIT(name, strlen(name));
struct path path;
struct file *file;
path.dentry = d_alloc_pseudo(mnt->mnt_sb, &this); // 分配匿名 dentry
if (!path.dentry)
return ERR_PTR(-ENOMEM);
path.mnt = mntget(mnt); // ★ 增加的是 vfsmount 的 mnt_count
d_instantiate(path.dentry, inode); // 把 inode 挂到 dentry 上(不增加 i_count)
file = alloc_file(&path, flags, fops); // ★ f_count 在内部被初始化为 1
if (IS_ERR(file)) {
ihold(inode); // ★ 失败路径补偿 inode 引用
path_put(&path); // 释放 dentry + vfsmount
}
return file;
}
mntget 增加的是 vfsmount->mnt_count,与 file->f_count 无关;ihold 只在失败路径上补 inode 引用;d_instantiate 不增加 inode 引用。那 f_count 在哪?
c
static struct file *alloc_file(const struct path *path, int flags,
const struct file_operations *fop)
{
struct file *file;
// ★ 新 file 对象在这里被分配,f_count 在内部被初始化为 1
file = alloc_empty_file(flags, current_cred());
if (IS_ERR(file))
return file;
file->f_path = *path;
file->f_inode = path->dentry->d_inode;
file->f_mapping = path->dentry->d_inode->i_mapping;
// ... 省略若干字段填充 ...
file->f_mode |= FMODE_OPENED;
file->f_op = fop; // ★ 绑定 socket_file_ops
// ★ 注意:这是 inode 的"读计数",不是 file 的 f_count
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(path->dentry->d_inode);
return file;
}
真正的初始化发生在 fs/file_table.c 的 __alloc_file() 里:
c
static struct file *__alloc_file(int flags, const struct cred *cred)
{
struct file *f;
int error;
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL); // 从 slab 分配,清零
if (unlikely(!f))
return ERR_PTR(-ENOMEM);
f->f_cred = get_cred(cred); // 增加 cred 引用
error = security_file_alloc(f);
if (unlikely(error)) {
put_cred(f->f_cred);
kmem_cache_free(filp_cachep, f);
return ERR_PTR(error);
}
// ★★★ 就是这一行:新对象的引用计数被初始化为 1
atomic_long_set(&f->f_count, 1);
spin_lock_init(&f->f_lock);
// ...
return f;
}
f_count 是"新分配对象自带一个引用"被初始化为 1 的,而不是通过 get_file() 增加出来的。 这也解释了 __fput_sync 里那句 atomic_long_dec_and_test 配对的是什么------正是 __alloc_file 里这个初值 1。close 放掉的,就是 open/alloc 时新对象自带的那一个引用。
需要额外说明的是,"对已有对象增加引用"的函数是 get_file(),它定义在 include/linux/fs.h:
c
/**
* get_file - 增加文件的引用计数
*/
static inline struct file *get_file(struct file *f)
{
atomic_long_inc(&f->f_count); // 原子增加引用计数
return f;
}
alloc_file_pseudo 之所以不调用它,是因为对象刚刚从 slab 里分配出来、尚未对外发布,直接 set(..., 1) 更清晰,也避免了对"0 引用对象"调用 get_file() 的争议(这正是内核社区讨论 get_file_rcu 的原因)。
把整个引用计数生命周期归纳一下:
| 操作 | 语义 | 源码位置 |
|---|---|---|
atomic_long_set(&f->f_count, 1) |
新分配对象,持有者天然拥有 1 个引用 | fs/file_table.c 的 __alloc_file() |
get_file(f) / get_file_rcu(f) |
对已有对象额外获取一个引用 | include/linux/fs.h |
fput(f) / __fput_sync(f) |
放掉一个引用,归零时执行 __fput() |
fs/file_table.c |
八、完整调用链
把前面的内容串起来,一次 Java SocketChannel.close() 的完整调用链如下:
text
Java 层
SocketChannelImpl.close() / tryClose()
└─ nd.close(fd)
JNI 层
└─ FileDispatcherImpl.close0(FileDescriptor)
└─ Java_sun_nio_ch_FileDispatcherImpl_close0
└─ closeFileDescriptor → close(fd)
glibc 层
└─ __close → SYSCALL_CANCEL(close, fd)
内核 VFS 层
└─ SYSCALL_DEFINE1(close, fd)
├─ file_close_fd(fd) // 先摘 fd 槽位
├─ filp_flush(file, ...)
└─ __fput_sync(file) // f_count 减一
└─ __fput(file) // 归零才进入
└─ file->f_op->release(inode, file)
Socket VFS 层(file_operations)
└─ sock_close(inode, file)
└─ __sock_release(SOCKET_I(inode), inode)
Socket 协议抽象层(proto_ops)
└─ inet_release(sock)
协议实现层(proto)
└─ tcp_close(sk, timeout)
与之对称的"获取"路径是:
text
socket() 系统调用
└─ sock_create → __sock_create
└─ sock_alloc_file(sock, ...)
└─ alloc_file_pseudo(inode, mnt, ..., &socket_file_ops)
├─ mntget(mnt) // mnt_count++
├─ d_instantiate(dentry, inode)
└─ alloc_file(&path, flags, &socket_file_ops)
└─ alloc_empty_file → __alloc_file
└─ atomic_long_set(&f->f_count, 1) // ★ f_count 初值
九、几个容易混淆的点
-
file->f_op->release≠inet_release。前者是 VFS 层的socket_file_ops.release,即sock_close;后者是struct proto_ops.release,由__sock_release调用。两者之间隔着sock_close→__sock_release两层。 -
file->private_data确实是struct socket,但sock_close不用它,而是通过SOCKET_I(inode)获取。private_data服务于poll/ioctl/mmap/fasync等操作。 -
close先摘 fd 再放 file 。这是为了在共享files_struct的多线程(clone)场景下避免 fd 号被复用导致的竞态,注释里的 "Careful here!" 说的正是这件事。 -
__fput_sync与fput的区别 。前者同步执行__fput,适合close这种即将返回用户态、不需要延迟处理的场景;后者通过 task_work 延迟执行,避免在原子上下文中做可能睡眠的清理。 -
alloc_file_pseudo里没有"增加f_count"的语句 。mntget加的是 mount 计数,ihold是失败路径补 inode 引用,i_readcount_inc是 inode 读计数。f_count的初值 1 来自__alloc_file()里的atomic_long_set(&f->f_count, 1)。 -
FMODE_OPENED检查 。__fput开头判断file->f_mode & FMODE_OPENED,只有真正完成 open 的 file 才走完整释放流程,否则直接file_free。 -
eventpoll_release必须最先调用 。如果 fd 还被 epoll 引用着,必须先摘除,否则后续release回调可能踩到已经失效的 epoll 结构。
十、结语
一行 SocketChannel.close(),背后是 Java 状态机、JNI 边界、glibc 系统调用包装、VFS fdtable 操作、struct file 引用计数、socket_file_ops、proto_ops、proto 四层抽象依次接力。理解这条链路,不仅能回答"f_op->release 到底是哪个函数""f_count 在哪里被初始化"这类具体问题,更能帮助我们在排查 fd 泄漏、epoll 悬挂、TCP 连接未正常挥手、SO_LINGER 行为异常等问题时,准确判断问题发生在哪一层。
下次在 Java 里写下 channel.close() 时,不妨想一想:这一行代码,已经替你走过了从用户态到内核协议栈的整段旅程。
#源码
cpp
/**
* Closes the socket if there are no I/O operations in progress and the
* channel is not registered with a Selector.
*/
private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
state = ST_CLOSED;
nd.close(fd);
return true;
} else {
return false;
}
}
void close(FileDescriptor fd) throws IOException {
FileDispatcherImpl.close0(fd);
}
// Shared with SocketDispatcher and DatagramDispatcher but
// NOT used by FileDispatcherImpl
static native void close0(FileDescriptor fd) throws IOException;
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo)
{
jint fd = fdval(env, fdo);
closeFileDescriptor(env, fd);
}
static void closeFileDescriptor(JNIEnv *env, int fd) {
if (fd != -1) {
int result = close(fd);
if (result < 0)
JNU_ThrowIOExceptionWithLastError(env, "Close failed");
}
}
JNIEXPORT void JNICALL
Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo)
{
jint fd = fdval(env, fdo);
closeFileDescriptor(env, fd);
}
// instance method close0 for FileDescriptor
JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_close0(JNIEnv *env, jobject this) {
fileDescriptorClose(env, this);
}
/* Close the file descriptor FD. */
int
__close (int fd)
{
return SYSCALL_CANCEL (close, fd);
}
libc_hidden_def (__close)
strong_alias (__close, __libc_close)
weak_alias (__close, close)
/*
* Careful here! We test whether the file pointer is NULL before
* releasing the fd. This ensures that one clone task can't release
* an fd while another clone is opening it.
*/
SYSCALL_DEFINE1(close, unsigned int, fd)
{
int retval;
struct file *file;
file = file_close_fd(fd);
if (!file)
return -EBADF;
retval = filp_flush(file, current->files);
/*
* We're returning to user space. Don't bother
* with any delayed fput() cases.
*/
__fput_sync(file);
/* can't restart close syscall because file table entry was cleared */
if (unlikely(retval == -ERESTARTSYS ||
retval == -ERESTARTNOINTR ||
retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK))
retval = -EINTR;
return retval;
}
/*
* synchronous analog of fput(); for kernel threads that might be needed
* in some umount() (and thus can't use flush_delayed_fput() without
* risking deadlocks), need to wait for completion of __fput() and know
* for this specific struct file it won't involve anything that would
* need them. Use only if you really need it - at the very least,
* don't blindly convert fput() by kernel thread to that.
*/
void __fput_sync(struct file *file)
{
if (atomic_long_dec_and_test(&file->f_count))
__fput(file);
}
/**
* file_close_fd - return file associated with fd
* @fd: file descriptor to retrieve file for
*
* Doesn't take a separate reference count.
*
* Returns: The file associated with @fd (NULL if @fd is not open)
*/
struct file *file_close_fd(unsigned int fd)
{
struct files_struct *files = current->files;
struct file *file;
spin_lock(&files->file_lock);
file = file_close_fd_locked(files, fd);
spin_unlock(&files->file_lock);
return file;
}
/**
* file_close_fd_locked - return file associated with fd
* @files: file struct to retrieve file from
* @fd: file descriptor to retrieve file for
*
* Doesn't take a separate reference count.
*
* Context: files_lock must be held.
*
* Returns: The file associated with @fd (NULL if @fd is not open)
*/
struct file *file_close_fd_locked(struct files_struct *files, unsigned fd)
{
struct fdtable *fdt = files_fdtable(files);
struct file *file;
lockdep_assert_held(&files->file_lock);
if (fd >= fdt->max_fds)
return NULL;
fd = array_index_nospec(fd, fdt->max_fds);
file = fdt->fd[fd];
if (file) {
rcu_assign_pointer(fdt->fd[fd], NULL);
__put_unused_fd(files, fd);
}
return file;
}
static void __put_unused_fd(struct files_struct *files, unsigned int fd)
{
struct fdtable *fdt = files_fdtable(files);
__clear_open_fd(fd, fdt);
if (fd < files->next_fd)
files->next_fd = fd;
}
static inline void __clear_open_fd(unsigned int fd, struct fdtable *fdt)
{
__clear_bit(fd, fdt->open_fds);
__clear_bit(fd / BITS_PER_LONG, fdt->full_fds_bits);
}
/* the real guts of fput() - releasing the last reference to file
*/
static void __fput(struct file *file)
{
struct dentry *dentry = file->f_path.dentry;
struct vfsmount *mnt = file->f_path.mnt;
struct inode *inode = file->f_inode;
fmode_t mode = file->f_mode;
if (unlikely(!(file->f_mode & FMODE_OPENED)))
goto out;
might_sleep();
fsnotify_close(file);
/*
* The function eventpoll_release() should be the first called
* in the file cleanup chain.
*/
eventpoll_release(file);
locks_remove_file(file);
ima_file_free(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
}
if (file->f_op->release)
file->f_op->release(inode, file);
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL &&
!(mode & FMODE_PATH))) {
cdev_put(inode->i_cdev);
}
fops_put(file->f_op);
put_pid(file->f_owner.pid);
put_file_access(file);
dput(dentry);
if (unlikely(mode & FMODE_NEED_UNMOUNT))
dissolve_on_fput(mnt);
mntput(mnt);
out:
file_free(file);
}
/*
* The peer socket should always be NULL (or else). When we call this
* function we are destroying the object and from then on nobody
* should refer to it.
*/
int inet_release(struct socket *sock)
{
struct sock *sk = sock->sk;
if (sk) {
long timeout;
if (!sk->sk_kern_sock)
BPF_CGROUP_RUN_PROG_INET_SOCK_RELEASE(sk);
/* Applications forget to leave groups before exiting */
ip_mc_drop_socket(sk);
/* If linger is set, we don't return until the close
* is complete. Otherwise we return immediately. The
* actually closing is done the same either way.
*
* If the close is due to the process exiting, we never
* linger..
*/
timeout = 0;
if (sock_flag(sk, SOCK_LINGER) &&
!(current->flags & PF_EXITING))
timeout = sk->sk_lingertime;
sk->sk_prot->close(sk, timeout);
sock->sk = NULL;
}
return 0;
}
EXPORT_SYMBOL(inet_release);
/*
* Socket files have a set of 'special' operations as well as the generic file ones. These don't appear
* in the operation structures but are done directly via the socketcall() multiplexor.
*/
static const struct file_operations socket_file_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read_iter = sock_read_iter,
.write_iter = sock_write_iter,
.poll = sock_poll,
.unlocked_ioctl = sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_sock_ioctl,
#endif
.uring_cmd = io_uring_cmd_sock,
.mmap = sock_mmap,
.release = sock_close,
.fasync = sock_fasync,
.splice_write = splice_to_socket,
.splice_read = sock_splice_read,
.splice_eof = sock_splice_eof,
.show_fdinfo = sock_show_fdinfo,
};
struct file *alloc_file_pseudo(struct inode *inode, struct vfsmount *mnt,
const char *name, int flags,
const struct file_operations *fops)
{
struct qstr this = QSTR_INIT(name, strlen(name));
struct path path;
struct file *file;
path.dentry = d_alloc_pseudo(mnt->mnt_sb, &this);
if (!path.dentry)
return ERR_PTR(-ENOMEM);
path.mnt = mntget(mnt);
d_instantiate(path.dentry, inode);
file = alloc_file(&path, flags, fops);
if (IS_ERR(file)) {
ihold(inode);
path_put(&path);
}
return file;
}
EXPORT_SYMBOL(alloc_file_pseudo);
/**
* alloc_file - allocate and initialize a 'struct file'
*
* @path: the (dentry, vfsmount) pair for the new file
* @flags: O_... flags with which the new file will be opened
* @fop: the 'struct file_operations' for the new file
*/
static struct file *alloc_file(const struct path *path, int flags,
const struct file_operations *fop)
{
struct file *file;
file = alloc_empty_file(flags, current_cred());
if (IS_ERR(file))
return file;
file->f_path = *path;
file->f_inode = path->dentry->d_inode;
file->f_mapping = path->dentry->d_inode->i_mapping;
file->f_wb_err = filemap_sample_wb_err(file->f_mapping);
file->f_sb_err = file_sample_sb_err(file);
if (fop->llseek)
file->f_mode |= FMODE_LSEEK;
if ((file->f_mode & FMODE_READ) &&
likely(fop->read || fop->read_iter))
file->f_mode |= FMODE_CAN_READ;
if ((file->f_mode & FMODE_WRITE) &&
likely(fop->write || fop->write_iter))
file->f_mode |= FMODE_CAN_WRITE;
file->f_iocb_flags = iocb_flags(file);
file->f_mode |= FMODE_OPENED;
file->f_op = fop;
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(path->dentry->d_inode);
return file;
}