最近调研了ksmbd,打算贡献社区补丁,用calc-func-lines.sh
脚本发现所有的文件系统的函数中排名第二长度(901行, 3714-2810)的是smb2_open()
(排名第一的是ntfs3的1470行的log_replay()
,但不熟悉就暂时不瞎参与了),就想着先把这个函数给尝试重构了,也先通过这个函数入手深入了解ksmbd。当然除了这个函数也会尝试做一些其他的重构。
重构技巧可以参考Jason Yan yanaijie@huawei.com的ext4重构补丁集some refactor of __ext4_fill_super()
。
函数参数是结构体请参考nfs4_run_open_task()
。
[PATCH] ksmbd: remove duplicate SMB2 Oplock levels definitions
smb2_open()
重构重构补丁还未完成,但发了一些这个函数的bugfix补丁,请查看[PATCH v2 00/12] smb: fix some bugs, move duplicate definitions to common header file, and some small cleanups
,以及2023年时发过的这个函数的一个bugfix补丁624b445544f ksmbd: fix possible refcount leak in smb2_open()
。
先整理一下函数流程。smb2_open()
框架流程:
ksmbd_override_fsids
ksmbd_vfs_getattrgoto reconnected_fp;
ksmbd_override_fsids// 之前是 goto err_out2
ksmbd_vfs_kern_path_lockedgoto err_out;
smb2_creat
ksmbd_vfs_kern_path_unlockgoto err_out1;
reconnected_fp:// 已经重连
err_out:
ksmbd_vfs_kern_path_unlock
err_out1:
ksmbd_revert_fsids
err_out2:// 最后的错误处理
更具体的代码流程:
kthread
worker_thread
process_scheduled_works
process_one_work
handle_ksmbd_work
__handle_ksmbd_work
__process_request
smb2_open
smb2_open
WORK_BUFFERS// 获取smb2_create_req和smb2_create_rsp
__wbuf// 获取要打开的文件名
smb2_get_name // stream name是什么鬼,后面再看TODO
parse_stream_name // 检查是否禁止访问的文件,veto翻译为禁止或否决
ksmbd_share_veto_filename // 处理durable handle
server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE
parse_durable_handle_context
ksmbd_lookup_durable_fd
__ksmbd_lookup_fd
ksmbd_fp_get// 增加引用计数
atomic_inc_not_zero(&fp->refcount)
smb2_check_durable_oplock
ksmbd_reopen_durable_fd
__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
ksmbd_override_fsids
ksmbd_put_durable_fd
__ksmbd_close_fd// 减少引用计数
atomic_dec_and_test(&fp->refcount)
ksmbd_fd_put// 减少引用计数
atomic_dec_and_test(&fp->refcount)
__put_fd_final
__ksmbd_close_fd
ksmbd_remove_durable_fd
__ksmbd_remove_durable_fd
idr_remove(global_ft.idr, fp->persistent_id)
__ksmbd_remove_fd
atomic_dec(&work->conn->stats.open_files_count)// 扮演,模仿
req->ImpersonationLevel // 判断选项是否有效
req->CreateOptions // Disposition 性情,气质,脾性
req->CreateDisposition // 访问权限检查
req->DesiredAccess // 属性
req->FileAttributes // 4次调用,non-durable handle
smb2_find_context_vals // 这个还没看懂TODO
ksmbd_override_fsids // 获取当前文件和父目录的path
ksmbd_vfs_kern_path_locked if (stream_name) { // 处理stream name
// 报错是个文件夹
CreateOptions & FILE_NON_DIRECTORY_FILE_LE && S_ISDIR // 报错不是文件夹
CreateOptions & FILE_DIRECTORY_FILE_LE && !S_ISDIR // 报错已存在
file_present && CreateDisposition == FILE_CREATE_LE // 访问权限处理
smb_map_generic_desired_access // 检查访问权限
smb_check_perm_dacl // 请求最大访问权限
ksmbd_vfs_query_maximal_access // 生成open flag
smb2_create_open_flags // 文件不存在
// 创建
smb2_creat // Extended Attributes
smb2_set_ea // 文件存在且未请求最大访问权限,处理访问权限
// 分别检查文件和父目录的访问权限
inode_permission // 此时文件肯定存在了
// 获取inode状态
ksmbd_query_inode_status
dentry_open
vfs_open
do_dentry_open// 执行到具体的后端文件系统
ext2_file_open // 处理Create Action Flags
file_info // 将 SMB IO 缓存选项转换为 Linux 选项
ksmbd_vfs_set_fadvise // ksmbd_file, Volatile-ID
ksmbd_open_fd
__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
atomic_inc(&work->conn->stats.open_files_count)// Persistent-ID
ksmbd_open_durable_fd // 开始,如果创建新文件,则设置默认的 Windows 和 POSIX ACL
// 继承
ksmbd_vfs_inherit_posix_acl // TODO
smb_inherit_dacl // security descriptor
smb2_create_sd_buffer
ksmbd_vfs_set_init_posix_acl// 获取cf_acls和cf_dacls
ksmbd_acls_fattr // 将权限位从模式转换为等效的 CIFS ACL
build_sec_desc // 设置security descriptor扩展属性
ksmbd_vfs_set_sd_xattr // 结束,如果创建新文件,则设置默认的 Windows 和 POSIX ACL
// stream name扩展属性
smb2_set_stream_name_xattr // 在 daccess、saccess、attrib_only 和 stream 初始化后,能够通过 ksmbd_inode.m_fp_list 搜索到 fp
list_add(&fp->node, &fp->f_ci->m_fp_list);// 在 oplock 断裂前,检查之前的 fp 中是否有删除待处理
ksmbd_inode_pending_delete // 断开批处理/独占 oplock 和二级 oplock
smb_break_all_oplock // 检查shared mode
ksmbd_smb_check_shared_mode // 使用parent key比较parent lease。如果没有具有相同parent lease,发送lease断裂通知
smb_send_parent_lease_break_noti // 在文件打开时处理 Oplock/Lease 请求
smb_grant_oplock // 关闭时自动删除
ksmbd_fd_set_delete_on_close
smb2_create_truncate// 在打开请求中查找特定的上下文信息
smb2_find_context_vals // 发送 Level 2 Oplock 或 Read Lease 断裂命令
smb_break_all_levII_oplock
vfs_fallocate
ksmbd_vfs_getattr// 如果请求了租约,则发送租约上下文响应 opinfo && opinfo->is_lease
fs/smb/common/smb2status.h
fs/smb/client/smb2status.h
fs/smb/server/smbstatus.h
e2f34481b24d cifsd: add server-side procedures for SMB3
的fs/cifsd/smbstatus.h
和最新的fs/smb/server/smbstatus.h
只有以下不同:
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
- * fs/server/smb2status.h
+ * fs/cifs/smb2status.h
*
* SMB2 Status code (network error) definitions
* Definitions are from MS-ERREF
在vim下,fs/smb/server/smbstatus.h
先做两组替换:%s/\t\\\n\t/ /g
和:%s/ \\\n\t/ /g
,然后再和fs/smb/client/smb2status.h
对比,有以下不同:
# 这俩是client端的
+#define STATUS_SERVER_UNAVAILABLE cpu_to_le32(0xC0000466)
+#define STATUS_FILE_NOT_AVAILABLE cpu_to_le32(0xC0000467)
# 这俩是server端的
-#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000)
-#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1)
fs/smb/common/smbacl.h
执行以下替换:
find fs/smb/client -type f -exec sed -i 's/struct cifs_ntsd/struct smb_ntsd/g' {} +
find fs/smb/client -type f -exec sed -i 's/struct cifs_sid/struct smb_sid/g' {} +
find fs/smb/client -type f -exec sed -i 's/struct cifs_acl/struct smb_acl/g' {} +
find fs/smb/client -type f -exec sed -i 's/struct cifs_ace/struct smb_ace/g' {} +
再把重复的宏定义移动到公共头文件。
smb2_compound_op()
重构这个函数有12个参数,649行(827-177),必须重构了他。
/*
* 注意: 如果传递了 cfile,这里会释放对它的引用。所以请确保在从此函数返回后不要再次使用 cfile。
* 如果传递了 @out_iov 和 @out_buftype,请确保它们都足够大(>= 3)以容纳所有复合响应。调用方也负责使用 free_rsp_buf() 来释放它们。
*/
smb2_compound_op
smb2_lock()
重构这个函数也挺长挺复杂,有353行(7535-7181)。