一、Samba VFS Module模块介绍
回收站是windows系统的一个重要功能,可以有效的避免用户误删除文件。在使用samba作为服务端的cifs共享文件系统时,默认是不具备回收站功能的。 Samba VFS 提供了一种机制,允许开发者扩展 Samba 的功能。例如:
- 将 NTFS ACL 转换为 NFSv4 ACL,以便存储在支持它们的文件系统中。
- 支持供应商在其文件系统中实现的 Linux 文件系统不支持的特性。
- 实现诸如交替数据流之类的特性。
- 通过将它们存储在 XATTRs 中并正确处理语义来实现完整的 NT ACL 支持(请参见 source 3/module/vfs_acl_xattr.c 和 source 3/module/vfs_acl_common.c)。
- 可以通过共享内存接口或用户空间库访问用户态文件系统。
二、Samba VFS Module基本流程
Samba VFS是一个共享库或模块,该模块实现 Samba VFS 接口提供的部分或全部功能。VFS Modules可以堆叠 ,并且有一个默认的VFS (source3/module/vfs_default.c) ,它提供默认的Samba对文件的处理。VFS Modules的调用流程如下所示:
- SMB请求进入 Samba (步骤1或11) ,Samba 调用VFS Module的接口。该调用是通过源代码中类似于SMB_VFS_XXX 的宏来检索文件元数据的(例如 SMB_VFS_STAT)。
- VFS Layer实现所请求函数的第一个VFS模块中的入口点。在上图中,Req1调用vfs_mod_1中的函数接口(步骤2),Req2调用vfs_mod_2中的函数接口(步骤12)。
- 如果被调用的函数是在其他模块实现的,那么它将调用 VFS_SMB_NEXT_XXX调用下一个模块的该函数。上图中该函数最终位于缺省的VFS模块vfs_default.c 中。
- default VFS 模块中的入口点通常调用系统函数,例如 sys_stat (步骤4)。
- 系统模块通过系统调用调用内核函数,例如 stat 系统调用(步骤5)。
- 系统调用返回到系统模块(步骤6) 。
- 系统函数向vfs_default.c返回结果(步骤7) 。
- vfs_default.c向vfs_mod_1模块返回结果(步骤8) 。
- vfs_mod_1模块向主 Samba 返回结果(步骤9) ,
- 主 Samba模块向客户端返回结果(步骤10)。
三、Samba Recycle功能
cifs客户端在跟服务端建立连接的时候,会调用vfs相关的初始化函数 :
make_connection_smb2->make_connection_snum->smb_vfs_init->vfs_init_default->vfs_init_custom
bool smbd_vfs_init(connection_struct *conn)
{
const char **vfs_objects;
unsigned int i = 0;
int j = 0;
/* Normal share - initialise with disk access functions */
vfs_init_default(conn);
/* No need to load vfs modules for printer connections */
if (conn->printer) {
return True;
}
vfs_objects = lp_vfs_objects(SNUM(conn));
/* Override VFS functions if 'vfs object' was not specified*/
if (!vfs_objects || !vfs_objects[0])
return True;
for (i=0; vfs_objects[i] ;) {
i++;
}
for (j=i-1; j >= 0; j--) {
if (!vfs_init_custom(conn, vfs_objects[j])) {
DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_objects[j]));
return False;
}
}
return True;
}
Samba回收站功能由recycle.so模块提供,代码位于vfs_recycle.c中。该文件中定义了回收站相关的操作函数:
static struct vfs_fn_pointers vfs_recycle_fns = { .unlink_fn = recycle_unlink };
由代码可知,samba定义了一个recycle_unlink函数。该函数注册给samba vfs layer模块:
NTSTATUS vfs_recycle_init(TALLOC_CTX *ctx)
{
NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "recycle",
&vfs_recycle_fns); if (!NT_STATUS_IS_OK(ret))
return ret; vfs_recycle_debug_level = debug_add_class("recycle");
if (vfs_recycle_debug_level == -1) {
vfs_recycle_debug_level = DBGC_VFS;
DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
} else {
DEBUG(10, ("vfs_recycle: Debug class number of 'recycle': %d\n", vfs_recycle_debug_level));
} return ret;
}
根据第二章节可知,samba在处理删除文件的req请求时,会根据SMB_VFS_UNLINK宏调用到具体模块中的unlink接口,这里会先调用vfs_recycle.so中的unlink接口。检查回收站目录是否已存在,如果不存在则新创建一个,回收站的名字根据smb.conf中的配置命名,为隐藏文件类型:
repository = talloc_sub_full(NULL, lp_servicename(talloc_tos(), SNUM(conn)),
conn->session_info->unix_info->unix_name,
conn->connectpath,
conn->session_info->unix_token->gid,
conn->session_info->unix_info->sanitized_username,
conn->session_info->info->domain_name,
recycle_repository(handle));
ALLOC_CHECK(repository, done);
获取被删除的文件的大小,判断是否超出回收站的可用空间大小,如果超出了则直接删除,否则走回收站删除流程:
file_size = recycle_get_file_size(handle, smb_fname);
if(fsize == 0) {
DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name));
rc = SMB_VFS_NEXT_UNLINK(handle,file_name);
goto done;
}
maxsize = recycle_maxsize(handle);
if(maxsize > 0 && file_size > maxsize) {
DEBUG(3, ("recycle: File %s exceeds maximum recycle size, "
"purging... \n", smb_fname_str_dbg(smb_fname)));
rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
goto done;
}
minsize = recycle_minsize(handle);
if(minsize > 0 && file_size < minsize) {
DEBUG(3, ("recycle: File %s lowers minimum recycle size, "
"purging... \n", smb_fname_str_dbg(smb_fname)));
rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
goto done;
}
如果被删除的文件已经在回收站了,则按正常的删除流程删除文件。这也就是从回收站删除文件的流程:
while (recycle_file_exist(handle, smb_fname_final)) {
SAFE_FREE(final_name);
if (asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base) == -1) {
ALLOC_CHECK(final_name, done);
}
TALLOC_FREE(smb_fname_final->base_name);
smb_fname_final->base_name = talloc_strdup(smb_fname_final,
final_name);
if (smb_fname_final->base_name == NULL) {
rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
goto done;
}
}
如果文件没有在回收站中,则走回收站删除流程:
if (recycle_touch(handle) == True || recycle_touch_mtime(handle))
recycle_do_touch(handle, smb_fname_final,
recycle_touch_mtime(handle));
四、samba回收站功能配置
如果要启用samba回收站功能,需要在/etc/samba/smb.conf文件中对共享目录参数做如下配置:
[share_dir]
comment = Home Directories
path = /home/test_dir
read only = no
writable = yes
available = yes
vfs object = recycle
veto files = /*.mp3/*.mp4/*.rmvb/*.wma/*.exe/*.bat/*.dll/*.avi/*.rar
recycle:repository=.deleted
recycle:keeptree=Yes
recycle:versions=Yes
recycle:maxsize=0
recycle:exclude=*.tmp|*.bak
配置项说明:
vfs object:启动samba的回收站功能,载入recycle.so模块
veto files:禁止上传的文件类型
recycle:repository:设置回收站目录,通常设置成隐藏目录类型,用户可以查看该目录内容
recycle:keeptree:删除文件时,是否保存原有文件夹层级结构
recycle:versions:删除文件时,遇到同名文件是否启用版本号功能,将文件标记为“Copy #x of”的形式
recycle:maxsize:回收站的最大空间是多少,单位是字节,0表示不限制
recycle:exclude:设置哪些文件类型被删除之后不放入回收站
五、使用技巧
1.回收站文件夹的用户和用户组权限设置要和共享文件夹的设置是一样的, 如果不一致被删除的文件是无法保存到回收站文件夹
2.如果maxsize设置为0, 那么回收站的文件需要管理员手工去删除. 当然, 可以将删除文件的任务交给计划任务去完成。在linux客户端使用时,可以用crontab设置定时任务,比如设置每天8:30 删除回收站内大于30天的文件: vim /etc/crontab 30 8 * * * root find //home/leixudong/.deleted -mtime +30 -exec rm -rf {} ;
3.回收站是要占存储空间的 4.如果是空目录被删除,貌似不会进回收站。暂时不知道原理。
六、测试验证
1.创建带回收站的cifs共享目录,使用windows客户端访问改共享目录,并开启显示隐藏文件。此时没有deleted目录:
2.删除文件和目录,提示是否要永久删除:
3.确定删除后,samba自动创建回收站目录,客户端需要刷新才能看到:
4.进入回收站,删除的文件和目录保持之前的结构,可以随时拷贝出来:
5.删除回收站目录,数据全部丢失,不可恢复。下一次再删除文件的时候自动创建回收站目录: