diff --git a/REUSE.toml b/REUSE.toml index 4aff8dd0..fd34f430 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -45,30 +45,12 @@ precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "CC0-1.0" -[[annotations]] -path = "rpm/**" -precedence = "aggregate" -SPDX-FileCopyrightText = "None" -SPDX-License-Identifier = "CC0-1.0" - [[annotations]] path = ["debian/**", "examples/**/debian/**"] precedence = "aggregate" SPDX-FileCopyrightText = "None" SPDX-License-Identifier = "CC0-1.0" -[[annotations]] -path = "archlinux/**" -precedence = "aggregate" -SPDX-FileCopyrightText = "None" -SPDX-License-Identifier = "CC0-1.0" - -[[annotations]] -path = ["3rdparty/fsearch/thread_pool.c", "3rdparty/fsearch/thread_pool.h"] -precedence = "aggregate" -SPDX-FileCopyrightText = "None" -SPDX-License-Identifier = "CC0-1.0" - [[annotations]] path = "examples/**/**.ts" precedence = "aggregate" diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD deleted file mode 100644 index 4986a267..00000000 --- a/archlinux/PKGBUILD +++ /dev/null @@ -1,49 +0,0 @@ -# Maintainer: DingYuan Zhang -pkgbase=deepin-anything-git -pkgname=(deepin-anything-git deepin-anything-dkms-git) -pkgver=6.0.7 -_extramodules=extramodules-ARCH -pkgrel=1 -sourcename=deepin-anything -sourcedir="$sourcename"-"$pkgver" -sourcetars=("$sourcename"-"$pkgver".tar.gz) -pkgdesc="Deepin Anything file search library" -arch=('x86_64' 'aarch64') -url="https://github.com/linuxdeepin/deepin-anything" -license=('GPL3') -groups=('deepin-git') -makedepends=('git' 'libudisks2-qt5-dev' 'libmount-dev' 'libpcre3-dev' 'libnl-genl-3-dev') -source=("${sourcetars[@]}::${url}/archive/refs/tags/${pkgver}.tar.gz" - deepin-anything-server.sysusers) -sha512sums=('SKIP' - 'SKIP') - -prepare() { - cd $sourcedir -} - -build() { - cd $sourcedir - make VERSION=$pkgver -} - -package_deepin-anything-dkms-git() { - depends=('dkms') - provides=('DEEPIN-ANYTHING-MODULE' 'deepin-anything-dkms') - conflicts=('DEEPIN-ANYTHING-MODULE' 'deepin-anything-dkms') - cd $sourcedir - install -dm 755 "$pkgdir"/usr/src - cp -r src/kernelmod "$pkgdir"/usr/src/$sourcedir - install -m644 debian/deepin-anything-dkms.dkms "$pkgdir"/usr/src/$sourcedir/dkms.conf -} - -package_deepin-anything-git() { - depends=('DEEPIN-ANYTHING-MODULE' 'libudisks2-qt5-dev' 'libmount-dev' 'libpcre3-dev' 'libnl-genl-3-dev') - provides=('deepin-anything') - conflicts=('deepin-anything') - cd $sourcedir - make VERSION=$pkgver DESTDIR="$pkgdir" install - rm -r "$pkgdir"/usr/src - mv "$pkgdir"/etc/dbus-1/system.d "$pkgdir"/usr/share/dbus-1/system.d - install -Dm644 ../deepin-anything-server.sysusers "$pkgdir/usr/lib/sysusers.d/deepin-anything-server.conf" -} diff --git a/archlinux/deepin-anything-server.sysusers b/archlinux/deepin-anything-server.sysusers deleted file mode 100644 index 57d62cda..00000000 --- a/archlinux/deepin-anything-server.sysusers +++ /dev/null @@ -1,2 +0,0 @@ -u deepin-anything - "deepin-anything User" -g deepin-anything - diff --git a/docs/app_developer.md b/docs/app_developer.md deleted file mode 100644 index 40487291..00000000 --- a/docs/app_developer.md +++ /dev/null @@ -1,127 +0,0 @@ -# 建立基础索引并搜索 - -```C -#include "fs_buf.h" -#include "walkdir.h" - -... - -/* -创建基础索引对象,创建后索引没有实际数据。 - -INITIAL_BUFSIZE是应用程序指定的fsbuf内部缓存区初始大小,单位为字节。 -此缓存区用来存储文件系统索引,所以它和文件系统中文件与目录数量的多少成正比。作为一个参考值,一个有38.7万个文件与目录的文件系统实际占用了约7 MB内存的缓存区,此参数至少应为1 MB + strlen(root_path),最大为1 GB,如果在使用过程中发现缓存区不够,程序将自动扩展缓存区空间。 - -root_path是文件系统的根目录,例如,你只对自己的家目录感兴趣,就可以仅索引/home/deepin目录。 - -new_fs_buf返回NULL表示失败(参数错误、内部分配内存错误或者是初始化同步锁错误),返回非空值表示成功。 - */ -fs_buf* fsbuf = new_fs_buf(INITIAL_BUFSIZE, root_path); - -/* -建立基础索引。 - -创建了fsbuf后,即可以调用build_fstree建立索引。 - -build_fstree的第一个参数为通过new_fs_buf创建的fs_buf的指针。 - -第二个参数则是一个0或者1的参数,表示是否按照分区建立索引。若此参数为0,则表示在构建索引时将仅在fsbuf的root_path所在的分区上建立索引,而不会对其它分区的文件系统建立索引。若此参数为1,则表示在构建索引时将对fsbuf的root_path下的所有的目录与文件建立索引,而不管其位于哪个分区。由于分区的加载与卸载将导致大量的文件加入与删除,因此我们推荐此参数设置为0,即每个分区都建立自己的基础索引,而不要将所有分区的基础索引合并在一起。 - -第三个参数为进度控制函数,是一个函数指针,其原型为int (uint32_t file_count, uint32_t dir_count, const char* cur_dir, void *param)。其中file_count为当前已经扫描过的文件数,dir_count为当前已经扫描过的目录数,cur_dir为当前正准备开始扫描的目录全路径,param为用户自定义的参数指针,可以传入自定义的参数。此函数将在进入每个目录开始扫描之前被调用,如果此函数的返回值为非零值,则表示需要取消整个创建索引的过程,而且build_fstree函数将返回1,否则build_fstree函数将返回0。一旦创建索引的过程被取消,则必须重新创建一个fs_buf对象再重新调用build_fstree扫描。通过此函数,也可以控制build_fstree函数的暂停,例如在此函数里进行等待,则整个build_fstree都将等待。由于此函数在每个目录都会被调用,因此此函数的处理应该非常快速,例如不要在每次调用的时候函数都打印一个日志,那就会导致创建索引慢数十倍。如果对构建索引的进度不感兴趣,也不想中途停止构建索引,可以将此参数设置为空。 - -第四个参数即为传给用户定义的进度控制函数的参数指针,显然若第三个参数为空,则第四个参数无意义。 - -建立基础索引将忽略/proc、/sys、/dev与/run目录。 - -由于build_fstree将对文件系统进行遍历,因此此过程会有一定耗时,其时间将试文件系统的情况而定。作为一个参考值,一个有38.7万个文件与目录的文件系统遍历耗时约36秒。 -*/ -build_fstree(fsbuf, 0, NULL, NULL); - -/* -保存基础索引。 - -save_fs_buf返回0表示成功,不然表示失败。 -*/ -char* full_path = "/home/deepin/myfsbuf.lft"; -save_fs_buf(fsbuf, full_path); - -/* -搜索。 - -使用search_files函数进行搜索,并将获得的值传入get_path_by_name_off参数获得完整的路径,如果需要知道是文件还是目录,可以调用is_file函数判断。此外,search_files函数还支持分页(通过第二个参数)。 - -参见cli/src/console_test.c中的search_by_fsbuf函数,下面是一个简化版。 -*/ -uint32_t name_offs[MAX_RESULTS], end_off = get_tail(fsbuf); -uint32_t count = MAX_RESULTS, start_off = first_name(fsbuf); -search_files(fsbuf, &start_off, end_off, query, name_offs, &count); - -char path[PATH_MAX]; -for (uint32_t i = 0; i < count; i++) { - char *p = get_path_by_name_off(fsbuf, name_offs[i], path, sizeof(path)); - printf("\t%'u: %c %s\n", i+1, is_file(fsbuf, name_offs[i]) ? 'F' : 'D', p); -} -uint32_t total = count; -while(count == MAX_RESULTS) { - search_files(fsbuf, &start_off, end_off, query, name_offs, &count); - total += count; -} -return total; -``` - -如果希望在给定目录中搜索,可以使用两种方法。 - -第一种是先调用`get_path_range`函数,此函数原型为`void get_path_range(fs_buf* fsbuf, char* path, uint32_t *path_off, uint32_t *start_off, uint32_t *end_off)`,然后再将得到的start\_off与end\_off设置为`search_files`中的start\_off参数的初始值与end\_off的参数值。 - -第二种方法是仍然仅使用`search_files`函数,但是在此函数调用得到路径后,再使用C语言的`strstr`函数判断是否是相应的目录里的路径即可。由于`search_files`给出的结果是按照路径排序的,因此,一旦发现原来有目录下的路径,但是现在没有了,即可结束对`search_files`的继续调用了。 - -```C -/* -载入基础索引。 - -使用load_fs_buf函数即可载入基础索引。一般来说,在最开始build_fstree建立好基础索引后,以后每次都只需要load_fs_buf,然后监听文件系统的变更即可。 - -此函数返回0表示成功,返回非零表示失败。 -*/ -fs_buf* fsbuf = NULL; -load_fs_buf(&fsbuf, "/home/deepin/myfsbuf.lft"); - -``` - -# 建立二级索引并搜索 - -TODO。请先参见`new_allmem_index`、`add_index`、`save_allmem_index`、`load_fs_index`、`get_index_keyword`等函数在`cli/src/main.c`与`cli/src/console_test.c`里面的使用。 - -值得注意的是`load_fs_index`有两种策略,每种策略的二级索引载入花费的时间、程序占用的内存以及搜索的快慢是不一样的。 - -此外,基础索引现在是支持文件系统变更修改的,但是二级索引还没有变更修改的功能,所以如果使用二级索引,暂时只能支持离线搜索。 - -# 文件系统更新 - -anything开发了一个内核模块用来监听文件系统的文件创建、文件删除与文件改名事件,并将其保存下来,等待上层用户态应用程序的提取。 - -在使用此功能之前,需要载入内核模块,需要通过root用户进入`kernelmod`目录使用`insmod vfs_monitor.ko`载入内核模块,卸载内核模块使用`rmmod vfs_monitor`即可。 - -由于文件系统更新完全是异步的,与程序逻辑无关,因此用户态应用程序一般的做法是开发一个守护进程或守护线程(daemon),对给定的接口进行循环查询得到变更信息,并调用相应的函数将变更信息传递给基础索引,让其更新其内部的数据。 - -此daemon的一般流程如下(可参考`cli/src/monitor_vfs.c`的相关代码): - -1. daemon启动时需要得到一个fs\_buf的指针,要么是创建时外界构建好(即成功调用`build_fstree`后)传给它的,要么是它自行创建并调用`build_fstree`构建好的 -2. daemon应调用`ioctl`函数访问`/proc/vfs_changes`文件,这个文件是内核模块创建的接口文件,用来给上层用户态应用程序访问的。`ioctl`传入的第一个参数应是打开`/proc/vfs_changes`文件的文件描述符,第二个参数应是`VC_IOCTL_READDATA`(参见`kernelmod/vfs_change_uapi.h`文件),第三个参数应是一个`ioctl_rd_args`类型的指针,此结构体中的`count`成员应该初始化为`data`成员指向的内存块的大小(单位为字节) -3. `ioctl`返回零则表示调用成功,此时第三个参数将会被修改,其中的`count`变量表示的是一共有多少条文件系统的变更,`data`则保存了具体的变更。`data`中数据的格式是第一个字节为变更的类型,其值参见`kernelmod/vfs_change_consts.h`文件,接着是一个C风格的字符串,表示变更的文件或目录名,以空字符结尾,如果是变更是一个改名变更,则接下来还会是一个C风格的字符串,表示改名后的文件或目录名,以空字符结尾。接下来又是一个字节的变更类型,依次类推。 -4. daemon在得到文件变更信息后,应该分别调用`rename_path`、`remove_path`与`insert_path`函数处理文件(目录)的改名、文件(目录)的删除以及文件(目录)的添加,以修改原fs\_buf的内部文件系统结构 -5. 在所有的文件变更都修改完毕后,可以调用`save_fs_buf`保存基础索引数据。如果此fs\_buf是与外部搜索使用的fs\_buf是同一个对象,则不需要额外的同步处理,基础索引会自行在内部处理多线程同步的问题,但是对于非共享对象来说(例如daemon是一个单独的守护进程),则需要使用额外的手段通知被搜索的基础索引更新数据,如果数据量不大(38.7万个文件与目录仅需要7 MB,载入仅需要不到10毫秒),建议调用`load_fs_buf`直接重新载入基础索引数据即可 - -在实现上,文件系统更新同步当然可以采用多种方式,例如daemon也可以将这些文件更新保存到一个文件里,等待搜索程序将这些变更及时应用到正在被使用的基础索引对象上。 - -此外,在载入了内核模块(`insmod vfs_monitor.ko`)之后,用户还可以通过`cat /proc/vfs_changes`来直观地看到文件系统的变更情况,但是这些变更在`ioctl`被成功调用之后就会被删除,而且一旦保存这些变更的内存超过了1 MB,最老的文件系统变更就将被删除。 - -# 其它 - -出于安全方面的考虑,我们可能不希望让用户看到他/她不能看到的文件/目录,这一点索引系统并不负责,而且为了能获得所有的文件系统数据,索引系统要求在建立索引的时候使用root用户的权限,或者至少能有相应的能力(capabilities,一般是DAC相关的能力)能得到所有目录与文件的文件名以及它们的从属关系。 - -因此,调用方应该自行处理权限相关的问题,例如通过`search_files`得到的某个路径是否应该被当前搜索的用户看到。注意,这个问题不是简单地使用`access`函数就可以搞定的,因为不能读取并不表示不能看到。 - -此外,也许用户会想要忽略调用某些目录下的文件,例如`/var/log`、`/tmp`、`.git`、`.svn`或者干脆是所有`.`开头目录与文件,同样索引系统也不会提供这种设置,这些配置管理需要由调用者负责,即根据用户在应用程序里的配置由应用程序自行对`search_files`得到的结果进行过滤。 - -对于外置存储,建议不要对其进行自动索引,因为对于IO速度较慢或者文件较多的外置存储设备,索引时间会比较长,干扰设备的使用。最好让用户手工明确对外置存储设备的索引,将索引数据保存在本机上,以提供离线搜索功能,同时也可以避免外设的只读权限问题。而且由于外置存储设备有可能被离线修改,因此,其索引数据无法保证与实际的文件系统数据同步,最好每次都全部重新检索。 diff --git a/docs/design_considerations.md b/docs/design_considerations.md deleted file mode 100644 index 24f6e61a..00000000 --- a/docs/design_considerations.md +++ /dev/null @@ -1,380 +0,0 @@ -# 文件搜索 - -## 基础索引存储结构 - -在Linux下,最常用的文件搜索软件是`find`,它会递归遍历每个目录,针对每个目录与文件按照用户给出的参数过滤出符合条件的目录与文件并打印出来。 - -在命令行模式下,`find`使用很广,功能强大,不仅可以根据文件名查找文件,还可以根据文件类型、权限、修改时间、大小,甚至与其它软件配合对文件进行过滤。但是在图形界面下,用户最常用的仍是根据文件名对文件进行查找,这正好是anything的用武之地。 - -使用`find`搜索时,如果已经搜索过一遍,则对应的文件与目录信息将会被缓存在内存中,可以大大加速搜索。当然,root用户可以通过运行`sysctl -w vm.drop_caches=3`来清除这些缓存。但是,即使使用了缓存,在仅使用文件名搜索的前提下,`find`的搜索速度仍比anything至少慢两个数量级。 - -anything之所以这么快速主要是因为: - -* 相比于`find`,它不依赖于额外的系统调用与函数调用,减少了大量的系统调用开销与内存复制开销 -* 它使用的文件系统存储结构的设计针对性极强,内容非常紧凑,使用也很高效 - -`find`搜索会通过系统调用遍历每个目录的内容,读取其中的文件列表,再根据文件列表中的inode号找到对应的inode信息,接着读取inode类型为目录中的的文件内容……如此递归查找。可以看到这个查找过程经过多重递归,会不断打开目录(需要系统调用与内存复制),而且文件名在其中与inode信息夹杂在一起,会严重减缓纯粹基于文件名的查找。 - -anything的不同之处在于在其内部仅保存了文件(目录)名以及必要的归属关系,以便进行双向的查找。所有的数据都存放在用户态空间,没有额外的系统调用开销与内存复制开销。此外,考虑到数据访问的最大可能性,相邻数据应是最有可能被一起访问的数据,因此可以最大限度利用处理器的高速缓存,使得软件的运行性能更好。 - -在经典设计中,文件系统应该使用数据结构中的树来存储。树中的每个节点有一个字符串作为名称,有N个指针指向其N个子节点,还有一个指针指向其父节点以支持从上至下以及从下至上的双向树遍历。这种结构的优点是修改很方便,不管是删除、添加还是重命名一个节点都很快。但是在遍历读取的时候却因为需要不停的指针跳转,会导致处理器的高速缓存频频失效,从而使得访问速度降低,而又因为各个节点都是分散的,无法体现出相邻节点的访问相关性,所以也难以进行有效的内存访问优化。 - -下面是上述树设计数据结构内存消耗的分析。简单起见,先假设文件系统是一棵深度为D、分叉为N的完全树,这样,在整棵树中将一共有叶节点N^(D-1)个,非叶节点N^0 + N^1 + N^2 + …… + N^(D-2) = (N^(D-1) - 1)/(N - 1)个。对于每个叶节点而言,忽略其文件名,其它部分的内存仅包括一个指向父节点的指针。对于每个非叶节点而言,忽略其文件名,其它部分的内存包含一个指向父节点的指针(我们假设根节点也有一个父节点指针,但是其值为空),以及N个指向子节点的指针,一共的指针数是P = N^(D-1) + (N+1) x (N^(D-1) - 1)/(N-1) = (2 x N^D - N - 1)/(N-1)个,对于64位系统而言,需要的内存是8 x P个字节。 - -在anything内部存储结构设计的早期阶段,使用树来保存文件系统结构也曾被考虑过,但是顾及高速缓存失效与指针过大的问题,显然在此处使用线性存储设计更好,下面就是anything来保存文件系统数据的内部存储结构设计: - -``` -+--------------------+ <-- buf-head -| MAGIC (4 B) | -+--------------------+ -| size (4 B) | -+--------------------+ -| root_path (cstr) | -+--------------------+ <-- first_name -| filename (cstr) | -+--------------------+ -| tag (1 B or 4 B) | -+--------------------+ -| filename (cstr) | -+--------------------+ -| tag (1 B or 4 B) | -+--------------------+ -| ... ... ... | -+--------------------+ -| 0 (1 B) | -+--------------------+ -| TAG (4 B) | -+--------------------+ <-- dir1 end -| filename (cstr) | -+--------------------+ -| tag (1 B or 4 B) | -+--------------------+ -| ... ... ... | -+--------------------+ -| 0 (1 B) | -+--------------------+ -| TAG (4 B) | -+--------------------+ <-- last-dir end and buf-tail -``` - -在上图中可以看到,anything内部文件系统存储的头部是4字节的MAGIC和4字节的缓存区大小,之所以使用4个字节,而不是64位系统上的8个字节是因为对于1000万个文件与目录的文件系统而言,使用上述结构仅需250MB内存,不需要8字节的长度与偏移量,从而可以节约一半的指针长度。在8个字节之后是一个C风格的字符串,对应的是这个文件系统树的根目录名,例如`/`或者`/home/deepin/`,这个根目录要求`/`开头,`/`结尾,整个字符串以空字符结尾。 - -在上述头部数据之后即为正式的文件树结构,在图中以`first_name`为标记,每个文件或者目录名都是一个C风格的、以空字符结尾的字符串。在每个字符串之后是一个标签(tag),对于文件来说,此标签占据一个字节(1 B),标识这是一个文件。对于目录而言,此标签占据四个字节(4 B),标识这是一个目录,而且记录了这个目录中第一个文件的偏移量,按照之前的分析,这个偏移量仅需要4字节就够了,通过目录的这个4字节标签,就可以向下遍历了。如此反复,直到此目录结束,将是一个单独的值为0的字节,其后又是一个四字节的标签(TAG),此标签除了标识目录结束之外,还记录了本级目录的父节点的偏移量,如此,就可以通过这个标签支持向上遍历了。此外,当本级目录结束之后,将开始下级目录的存储,这样即可以使得一课目录树的数据是紧紧相邻的,在读取时非常高效,在删除时也非常方便。 - -上述结构不断重复,直到整个文件树结束。 - -由上述结构可以看出,相邻的节点,即同级目录下的文件,都存储在一起,这样有利于内存访问优化。而且将8字节指针优化为4字节偏移量,也节省了存储空间,减少了无效数据的存储与无效内存的访问,此外,线性存储也便于数据文件的存盘与读盘。 - -下面是上述存储结构的内存消耗分析(除了文件名本身以外),对应的文件系统为深度为D、分叉为N的完全树。对于每个叶节点,需要耗费1个字节存储tag,对于每个非叶节点,需要消耗4个字节存储子节点的偏移量,还有4个字节用来存储其所有子节点指向其本身的偏移量,则一共需要N^(D-1) + 8 x (N^(D-1) - 1)/(N-1) = (N^D + 7 x N^(D-1) - 8)/(N-1)个字节,对比之前树状结构需要耗费的8 x P = 8 x (2 x N^D - N - 1)/(N-1)个字节,易知在最好情况下是其内存的1/16,在最差情况下是其内存的1/3。 - -当使用此结构进行搜索时,不需要再通过树状搜索,而可以使用线性搜索,从前至后进行字符串匹配,并跳过数据量较小的标签即可,这样也方便进行搜索分页。 - -当搜索到某个名称符合要求时,即可使用目录结尾的标签定位到父目录的偏移量,继而构成完整的路径,返回给调用者。 - -在这里也可以发现,其实对于搜索而言,程序并不需要保存子节点的偏移量,只需要父节点的偏移量即可,这样还可以把额外的内存再节省约一半,但是这会导致文件系统更改(文件与目录删除、添加、重命名)时变更速度较慢。若仅需使用离线搜索,即不考虑文件系统改动的问题,则内存消耗确实还可通过使用上述方法进一步减少。 - -此外,由于我们需要至少一位来标识文件或者目录,因此最大的偏移量不可能是2^32,即最多能存储2^31或2G内存的数据,为了可扩展性考虑,在内部其实保留了两位数据以进行文件标识,因此,最多可以使用1G内存作为内部存储。根据之前的测试估算,大约能保存4000万个文件或目录,在眼前看来是足够的。 - -下面首先做理论对比,继而进行实际测试验证。测试环境仍包含38.7万个文件(目录),其中有大约4万个目录。 - -如果使用经典的目录遍历方法,即使用`opendir`/`readdir`/`closedir`函数遍历目录,则需要调用大约4万次`opendir`与`closedir`,38.7万次`readdir`,这些都是系统调用,而其中`readdir`将读取一个`struct dirent`大小的数据,即大约274个字节的数据,即使这些数据都在缓存中,也只是节省了从磁盘到内存的数据读取,但是仍然无法节省从内核态到用户态的数据复制开销。再接着,需要对这38.7万个数据进行38.7万次`strstr`调用。这就是`find`的大致开销,其中完全忽略了磁盘访问。 - -如果使用树进行数据存储与搜索,在遍历过程中需要持续生成节点,在搜索过程中需要递归遍历(递归函数调用)各个节点,然后对每个节点调用`strstr`函数,即需要有4万次递归函数调用,以及38.7万次`strstr`函数调用。当然,在此期间高速缓存也会频频失效。在搜索过程中,由于不再需要系统调用,以及额外的从内核态到用户态的内存复制(这个开销相对较小),这种搜索方法应该会至少比`find`快一个数量级。 - -使用anything的内部数据结构,则只需要从前向后,反复z在线性存储空间中调用`strstr`即可,即大约需要38.7万次`strstr`调用。没有系统调用开销,没有内存复制开销,而且由于内存紧凑,又是单向内存顺序访问,因此内存访问进入高速缓存浪费极少,非常自然,使用也很流畅,比目录树状递归跳转显然要高效很多。 - -为了进一步确定我们猜测是否正确,我们可以开发三个程序,第一个程序模仿`find`的做法,通过`opendir`/`readdir`/`closedir`函数遍历目录,同时调用`strstr`进行搜索。第二个程序遍历目录的时候建立树状结构后,再基于内存树存储进行搜索。第三个程序遍历目录的时候构建基础索引完成后,再基于基础索引进行搜索。 - -这三个程序的关键源码如下所示。 - -```C -void walkdir(const char* path, const char* query, int* count) -{ - DIR* dir = opendir(path); - if (0 == dir) - return; - - struct dirent* de = 0; - while ((de = readdir(dir)) != 0) { - if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - - // DT_REG: regular file/hardlinks, DT_LNK: softlinks - if (de->d_type != DT_DIR && de->d_type != DT_REG && de->d_type != DT_LNK) - continue; - - if (strstr(de->d_name, query) != 0) { - if (*count < 10) - printf("%d: %s/%s\n", 1 + *count, path, de->d_name); - *count = *count + 1; - } - - if (de->d_type == DT_DIR) { - char fullpath[PATH_MAX]; - sprintf(fullpath, "%s/%s", path, de->d_name); - walkdir(fullpath, query, count); - } - } - - closedir(dir); -} -``` - -```C -typedef struct __fs_tree_item__ { - char* name; - int kids_count; - struct __fs_tree_item__* parent; - struct __fs_tree_item__** kids; -} fs_tree_item; - -void walkdir(const char* path, fs_tree_item* fti) -{ - DIR* dir = opendir(path); - if (0 == dir) - return; - - struct dirent* de = 0; - while ((de = readdir(dir)) != 0) { - if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - - // DT_REG: regular file/hardlinks, DT_LNK: softlinks - if (de->d_type != DT_DIR && de->d_type != DT_REG && de->d_type != DT_LNK) - continue; - - fs_tree_item* kid = calloc(1, sizeof(fs_tree_item)); - if (kid == 0) { - printf("kid malloc error, path: %s, name: %s, count: %d\n", path, de->d_name, fti->kids_count); - continue; - } - - kid->name = strdup(de->d_name); - if (kid->name == 0) { - free(kid); - printf("kid strdup error, path: %s, name: %s, count: %d\n", path, de->d_name, fti->kids_count); - continue; - } - kid->parent = fti; - - fs_tree_item** kids = realloc(fti->kids, (fti->kids_count+1) * sizeof(void*)); - if (kids == 0) { - free(kid->name); - free(kid); - printf("kids realloc error, path: %s, name: %s, count: %d\n", path, de->d_name, fti->kids_count); - continue; - } - - fti->kids = kids; - fti->kids[fti->kids_count] = kid; - fti->kids_count++; - - if (de->d_type == DT_DIR) { - char fullpath[PATH_MAX]; - sprintf(fullpath, "%s/%s", path, de->d_name); - walkdir(fullpath, kid); - } - } - - closedir(dir); -} - -void search_tree(const char* path, fs_tree_item* root, const char* query, int* count) -{ - if (strstr(root->name, query) != 0) { - if (*count < 10) - printf("%d: %s/%s\n", 1+*count, path, root->name); - *count = *count + 1; - } - - char fullpath[PATH_MAX]; - sprintf(fullpath, "%s/%s", path, root->name); - for (int i = 0; i < root->kids_count; i++) - search_tree(fullpath, root->kids[i], query, count); -} -``` - -```C -static int walkdir(const char* name, fs_buf* fsbuf, uint32_t parent_off) -{ - DIR* dir = opendir(name); - if (0 == dir) - return EMPTY_DIR; - - uint32_t start = get_tail(fsbuf); - - struct dirent* de = 0; - while ((de = readdir(dir)) != 0) { - if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - - // DT_REG: regular file/hardlinks, DT_LNK: softlinks - if (de->d_type != DT_DIR && de->d_type != DT_REG && de->d_type != DT_LNK) - continue; - - append_new_name(fsbuf, de->d_name, de->d_type == DT_DIR); - } - closedir(dir); - - // empty folder - if (start == get_tail(fsbuf)) - return EMPTY_DIR; - - // set parent offset - uint32_t end = get_tail(fsbuf); - append_parent(fsbuf, parent_off); - - // loop through siblings - uint32_t off = start; - while (off < end) { - if (is_file(fsbuf, off)) { - off = next_name(fsbuf, off); - continue; - } - - // set kid offset - set_kids_off(fsbuf, off, get_tail(fsbuf)); - char path[PATH_MAX]; - snprintf(path, sizeof(path)-1, name[strlen(name)-1] == '/' ? "%s%s" : "%s/%s", name, get_name(fsbuf, off)); - if (walkdir(path, fsbuf, off) == EMPTY_DIR) - set_kids_off(fsbuf, off, 0); - off = next_name(fsbuf, off); - } - return NONEMPTY_DIR; -} - -#define MAX_RESULTS 10 - -static uint32_t search_by_fsbuf(fs_buf* fsbuf, const char* query) -{ - uint32_t name_offs[MAX_RESULTS], end_off = get_tail(fsbuf); - uint32_t count = MAX_RESULTS, start_off = first_name(fsbuf); - search_files(fsbuf, &start_off, end_off, query, name_offs, &count); - - char path[PATH_MAX]; - for (uint32_t i = 0; i < count; i++) { - char *p = get_path_by_name_off(fsbuf, name_offs[i], path, sizeof(path)); - printf("%'u: %c %s\n", i+1, is_file(fsbuf, name_offs[i]) ? 'F' : 'D', p); - } - uint32_t total = count; - while(count == MAX_RESULTS) { - search_files(fsbuf, &start_off, end_off, query, name_offs, &count); - total += count; - } - return total; -} -``` - -在程序里单独在搜索前后调用`gettimeofday`获取时间戳,进行差分比较,在有缓存的情况下,三种方法搜索的耗时分别为: - -* 边递归遍历目录边匹配(类find):10.1秒 -* 递归遍历目录后用树存储目录结构再搜索:77毫秒 -* 递归遍历目录后用基础索引存储目录结构再搜索:8毫秒 - -如果使用`perf`、`strace`与`google perftools`进行性能剖析,可以看到在第一个程序里主要的程序耗时(88%)都花在了`__opendir`的调用上。第二三个程序在搜索阶段运行没有与内核交互,而且时间过短,采样过少,因此没有有意义的数据产生,但是可以看到使用基础索引比使用树的方法快了接近十倍。 - -## 二级索引增强 - -上述基础索引的设计已经大大提高了基于文件名的搜索速度(相对于`find`而言),但这个搜索仍然需要从前至后遍历每个文件名进行搜索。从表面上看,因为需要对每个文件名进行检查,以得知其是否匹配搜索串,貌似很难更快了。其实,我们还可以做得更快,那就是使用倒排索引(inverted index)。 - -倒排索引的原理是对目标字符串进行切割,得到其所有的子字符串,然后将这些子字符串存放到对应的索引数据结构中,当用户输入对应的子字符串时能立刻找到相应的原始字符串。 - -例如原始字符串为china,则可以得到c、h、i、n、a、ch、hi、in、na、chi、hin、ina、chin、hina与china一共5+4+3+2+1=15个子字符串,假设china这个字符串的偏移量(或者指针)是100,则可以得到{"c" -> 100}, {"h" -> 100}, ……, {"china" -> 100}等25个索引。当用户输入hi时,即可以立刻得到{"hi" -> 100}这个索引,然后将100返回给调用者,由调用者通过这个偏移量或者指针得到"china"这个字符串。 - -在简单的倒排索引设计中,一般使用哈希链表或者Trie树来实现,anything使用的是哈希链表。当用户输入某个查询字符串(如hi)时,anything将首先对字符串进行一次哈希运算,得到哈希数组中的下标值,然后根据下标值即可得到对应的索引数组,在索引数组里顺序查找即可得到对应的索引(即hi),从而可以获取到所有的包含hi字符串的原始字符串的偏移量,将其返回给调用者。 - -关于倒排索引、哈希链表和Trie树的资料,网上和相关的书籍已经非常丰富了,在这里不再赘述。 - -下面是对二级索引内存消耗的分析,以及哈希链表设计问题的讨论。 - -对于长度为L的串,其生成的索引中,长度为1的子串有L个,长度为2的子串有L-1个,……,长度为L的子串有1个,则一共有L + (L-1) + …… + 1 = L x (L+1) / 2个子串,消耗的存储为1xL + 2x(L-1) + 3x(L-2) + ... + Lx1 = Lx(L+1)x(L+2)/6,如果考虑到字符串指针(在64位系统上为8字节)与字符串结尾的0字符,则额外需要Lx(L+1)/2x(8+1)个字节,合计为Lx(L+1)x(L+29)/6个字节。当然,其中字符串是可以被重用的,但是不一定能被重用,因为越长的串重复可能性越低。而且另一方面来说,这里的串是包括了中文字符的串,转成utf8后索引还会更长,此处的分析也忽略了这一点。下面是几个典型的数据: - -* 串长5时,占用170字节 -* 串长10时,占用715字节 -* 串长20时,占用3,430字节 -* 串长30时,占用9,145字节 -* 串长40时,占用18,860字节 -* 串长50时,占用33,575字节 -* 串长60时,占用54,290字节 -* 串长70时,占用82,005字节 -* 串长80时,占用117,720字节 - -可以发现一个串长80的文件名产生的空间占用就等效于164个串长为10的文件名,或者692个串长为5的文件名,虽然80仅是10的8倍,以及5的16倍。此外,真实的文件名还是有可能挺长的,比如`.git`目录下的文件名,比如debian的patch文件名等。举个例子,在测试环境下,38.7万个文件(目录)中一共有12个长度超过80个字符的文件(目录)名,有1888个长度为67个字符的文件(目录)名,以及4013个长度为70个字符的文件(目录)名。 - -对于38.7万个文件与目录遍历的实测表明,其索引内存占用将超过2G,这完全是不可接受的。 - -因此,二级索引需要进行优化。 - -anything使用的优化是对索引的最大长度加以限制。这是因为在实际使用中,用户真正输入的字符串不会很长,而且在索引串长到一定值之后,得到的原始字符串已经非常少,完全可以使用原始字符串进行二次匹配了。这样优化后,首先,索引数可以减少,其次,每个索引的长度也会减少,再加上使用的是4字节的偏移量,而不是8字节的指针,因此可以进一步减少内存消耗。下面是根据上述算法得到的内存消耗结果计算: - -* 串长5时,占用110字节 -* 串长10时,占用452字节 -* 串长20时,占用1,212字节 -* 串长30时,占用1,972字节 -* 串长40时,占用2,732字节 -* 串长50时,占用3,492字节 -* 串长60时,占用4,252字节 -* 串长70时,占用5,012字节 -* 串长80时,占用5,772字节 - -可以发现,内存消耗有了很大的减少,在对上述含有38.7万个文件与目录的遍历表明,其索引有大约六百万个,索引占用内存约230M,程序占据内存不超过500M(因为动态内存分配需要大量的额外内存空间),基本可以接受。 - -下面再来看哈希链表的设计。 - -在基础索引中,如果是38.7万文件与目录,则一共需要进行38.7万次`strstr`的调用以完成一次查找。在二级索引中,如果没有哈希链表,则需要对所有生成的索引进行从前至后的`strcmp`调用,一共有约六百万个索引,则需要进行六百万次`strcmp`调用,如果考虑到我们还对索引进行了最大长度限制,还需要进行一定数量的`strstr`调用以进行二次确认。这样二级索引就完全成了一个笑话了,不仅耗费内存多,还会导致搜索慢,简直一无是处。 - -如果使用了哈希链表,可以将具有相等哈希值的索引存放到一个数组中,然后将这些数组再存放到一个更大的哈希数组中,使得这六百万个索引尽量均匀地分布开。这样每次查找的时候就可以大大减少`strcmp`的次数了。在这里,anything采用了质数模的方法作为哈希函数,程序里使用的质数模是一个梅森数,即131071,如果此哈希函数能将索引值完全均匀分布的话,大约每个哈希数上将会有6000/131 ≈ 46个索引,当然,在实际中哈希函数不可能绝对均匀,假设其较差的值为5000个,那也可以将`strcmp`的次数减少1200倍,是一个极大的性能提高了。在较差的情况下,假设有100个文件满足要求,则只需要进行一次哈希计算,5000个`strcmp`函数调用和100多个`strstr`函数调用即可找到满足条件的文件名了。对比基础索引搜索需要的38.7万个`strstr`调用,确实提高了数十倍的性能。 - -在实测中,如果将二级索引完全存放在内存里,其搜索速度一般是使用基础索引搜索速度的20倍以上。如果将二级索引存放在磁盘上,其内存占用将减少到近乎零,在大部分情况下仍然能够取得很好的搜索速度,会比基础索引有明显的速度加快,但是在原始字符串较多的情况下,因为需要从索引文件中读取较大量的数据,就会显得比基础索引搜索还要慢了,这个是典型的时间空间复杂度互换的问题。 - -当然,二级索引的问题在于其占用内存实在太大,对于38.7万个文件与目录的文件数来说,基础索引约占用7 MB内存,而二级索引则需要占用约230 MB内存。其次,当发生文件系统变更的时候,基础索引的变更比较容易实现,但是二级索引的更新就相当繁复了,需要遍历所有的索引,找到对应的原始字符串偏移量进行修改。此外,二级索引的文件保存与载入也较为麻烦,因为需要将一整个哈希链表序列化与反序列化,而且当文件系统变更时,一旦变更了索引,则需要进行数百兆甚至上G数据的重新序列化,也会增大磁盘压力。 - -因此,除非是在对文件名搜索速度非常关键的场所或者离线搜索的场景下,不然我们使用基础索引进行文件名搜索即可满足要求了。 - -# 文件系统更新处理 - -* 内核模块,procfs,poll vs. blocking vs. aio,list vs. circ-buffer,spinlock vs. semaphore vs. rcu vs. rwlock vs. atomic -* fsbuf -* 倒排索引(add-list/remove-list) - -# 其它考虑 - -* composite\_str -* scanf/printf -* fs-tag与composite\_str大小端的问题 -* 不支持链接 -* 拼音搜索 -* 倒排索引文件的保存与读取 -* 在C里面支持多态与封装,以及不同载入策略的重构 - -# 文件系统监控 - -在上面可以看到搜索提速最重要的原因是省去了系统调用,但是文件系统是会不停变化的,因此anything需要对文件系统进行监控,在变化的时候及时对内部的基础索引进行修改才能保证搜索的正确性与实时性。 - -everything对于文件系统变更的追踪相对简单一些,因为它直接依赖于Windows下NTFS文件系统的日志。但是在Linux下,首先文件系统繁多,例如RHEL 7/CentOS 7采用的缺省文件系统是XFS,但是Debian与RHEL 6/CentOS 6使用的缺省文件系统却是ext4,而且这两者的文件系统日志都没有NTFS全,除此之外还有btrfs等一系列的其它文件系统。其次是在Linux下,管理员或者用户对于文件系统的选择较多,并且可以深入调整文件系统的参数,例如去掉日志,因此仅依赖于文件系统日志检查文件系统的变更是不太可靠的。 - -另外,由于Linux本身没有全局的文件创建、删除与重命名的用户态接口(比较接近的inotify无法递归监控目录),因此要解决此问题,只能使用独立的模块监听文件的创建、删除与重命名,并通知相应的用户态程序更新文件系统数据与索引数据。 - -在Linux下,监控整个文件系统的变化可以采用下列方法: - -1. 使用preload库,监控glibc对应函数的参数与返回值,包括open、fopen、creat、mkdir、rmdir、rename等。这种方式的优点是不依赖于内核,但是它会对所有依赖于glibc库的程序都造成影响,影响面大,工作量也不小(函数较多,参数处理较多),而且具有较大的安全隐患,此外,对于静态编译libc库的程序,以及不依赖于libc库的程序(虽然这些程序很少)无法监控 -2. 使用审计系统,加入对相应系统调用(open、creat、mkdir等)的审计,通过审计日志检查系统调用的结果,根据结果更新文件系统数据。其优点是不依赖于内核,系统调用数量较少,但是缺点是需要依赖于审计系统,而且事后处理日志的方式会缺少关键的场景信息,例如之前的文件或者目录是否存在 -3. 编写内核模块,通过`/proc/kallsyms`找到对应的内核函数,替换相应的函数为自己编写的加入了监控代码的函数。优点是基本不依赖内核的特性(除了kallsyms外),缺点是自行替换内核函数指针需要考虑SMP、抢占、调用路径等多方面的问题,容易造成内核的不稳定 -4. 编写内核模块,使用内核热补丁技术(livepatch)替换内核函数。与上述技术路线类似,不过不用自己考虑替换内核函数指针的问题了,缺点是需要重新实现或者调用原始函数,以保证功能的正确,而且热补丁技术对内核特性要求较高,龙芯申威内核现在并没有实现相应的功能,此外,如果相应的函数由于出现了问题需要使用热补丁修复,那会带来一些维护上的问题,虽然可能性较小 -5. 编写内核模块,使用内核tracepoint特性对系统调用入口与出口进行事件跟踪,并记录处理结果,缺点是会对所有的系统调用进行跟踪,因此需要自己过滤,而且系统调用路径较长,可能会导致较多的资源占用 -6. 编写内核模块,通过kprobes对内核函数进行挂钩,在函数入口和出口处进行参数与返回值进行检查,当发现满足要求的文件事件时将事件信息记录下来,其缺点是对比tracepoint来说,kprobes从理论上来说依赖的函数变化的可能性更大一些,当内核升级时可能会带来维护的问题 - -anything选择了最后一种解决方法,虽然它要求内核支持kprobes特性,但这是一个较容易实现的特性,而且anything要监控的内核函数也相对稳定。 - -具体到需要跟踪的内核函数,主要就是VFS的文件系统变更函数,包括`vfs_create`等,为了确保只有当函数成功返回时才进行记录,anything需要使用kretprobes进行函数跟踪。 - -需要注意的是,使用kretprobes有一个与架构相关的问题,那就是要在函数入口处得到函数各参数的值,而在这里,kretprobes没有给我们提供很多便利,需要我们根据内核的寄存器结构体(pt\_regs)来根据架构的函数调用规约获取相关的参数值。例如对于x86来说,其64位系统的函数调用规约是从第一个参数开始分别使用rdi、rsi、rdx、rcx、r8与r9寄存器保存参数,从第七个参数开始使用栈来保存剩余的参数。这些代码都被封装到`kernelmod/arg_extractor.c`里了,因此,当需要扩展架构支持时,主要在这里进行修改即可。此外,在实际使用中,我们现在仅用到了前四个参数,因此只要处理n等于1到4的情况就可以了。 - -内核模块对外提供了`/proc/vfs_changes`文件以向用户态的应用程序提供文件系统更新信息的访问接口,当应用程序通过此文件获取到文件系统更新信息后,即可更新对应的基础索引数据了。由于基础索引的数据结构设计使得一个目录树被保存在连续的一段内存里,因此对文件系统的更改都非常方便,最大的开销就是`memmove`而已,这个变更速度在x86上,对于10M的内存,只需要耗费毫秒级的时间。 - -# 后续工作 - -* 在文件系统更新时,快速更新倒排索引数据,最终更新倒排索引文件,并保证响应性 -* 针对其它文件系统进行测试与适配,包括XFS、NTFS、FAT32、iso9960(cdfs),其中XFS是RHEL 7/CentOS 7的缺省文件系统、NTFS/FAT32是Windows分区,而且是很多U盘采用的文件系统,iso9960是光盘常用的文件系统,ext系列与btrfs初步测试可以支持,其它的例如NFS、overlayfs、samba、sshfs等暂时先不考虑额外支持 -* ARM、申威的支持,主要是内核kprobes的支持与性能调优/对比 -* 继续性能优化与代码重构 diff --git a/docs/end_user_tester.md b/docs/end_user_tester.md deleted file mode 100644 index c1f7e730..00000000 --- a/docs/end_user_tester.md +++ /dev/null @@ -1,43 +0,0 @@ -# 测试内核模块 - -步骤: - -1. 切换到root,并进入`kernelmod`目录 -2. 执行`insmod vfs_monitor.ko`命令加载内核模块 -3. 执行`cat /proc/vfs_changes`查看整个文件系统的文件变更 -4. 执行`rmmod vfs_monitor`卸载内核模块 - -# 无文件系统更新的测试 - -为了避免干扰,我们先不加载内核模块来单独测试用户态程序。 - -测试用户态程序的步骤: - -1. 切换到root,并进入`cli`目录 (使用root是为了遍历所有目录与文件) -2. 执行`LD_LIBRARY_PATH=../library/bin/release/ ./bin/release/anything_cli scan`命令,对全盘文件系统进行扫描,视文件系统中文件数量的多少、硬盘规格以及处理器架构等多方面因素影响,这一步可能会花费数分钟到数十分钟不等 -3. 扫描结束之后会保存扫描数据,并将显示统计数据,包括总文件数、不同长度文件名的文件数、内存占用等,然后进入命令提示符($) -4. 在命令提示符处,你可以输入任何字符串进行搜索,例如输入hellfire在所有文件里搜索文件/目录名包含hellfire的,最多显示100个搜索结果,但是搜索总结果数与搜索时间统计都会显示在搜索结果的最后。在x86上,在百万个文件中搜索的延迟一般不超过20毫秒,在申威1621上,在百万个文件中搜索的延迟一般不超过300毫秒 -5. 在运行过程中使用`cat /proc/#anything_pid/status | grep Vm`观察程序的占用内存,对于百万个文件来说应该不会超过25M,其中`#anything_pid`是anything程序的pid,可以通过`pidof anything_cli`来获得 -6. 可以输入Ctrl+D退出程序 - -生成了文件系统数据后可以反复使用此数据进行搜索(即离线搜索,不再依赖于原始文件系统了),测试步骤如下: - -1. 执行`LD_LIBRARY_PATH=../library/bin/release/ ./bin/release/anything_cli load`命令载入扫描数据,载入速度会非常快(毫秒量级),而且也会显示相应的统计数据 -2. 在命令提示符中输入任何字符串进行搜索,延迟不变 -3. 在运行过程中观察程序内存占用情况,应该仍然较小 -4. 可以输入Ctrl+D退出程序 - -除此之外,程序还支持倒排索引,生成索引步骤如下: - -1. 切换到root,并进入`cli`目录 (使用root是为了遍历所有目录与文件) -2. 执行`LD_LIBRARY_PATH=../library/bin/release/ ./bin/release/anything_cli scan -i`命令,对全盘文件系统进行扫描并生成倒排索引,这一步会比不带`-i`参数的时候多花约一倍的时间(申威龙芯更慢一些) -3. 扫描结束之后会保存索引数据,并将显示统计数据,包括总文件数、不同长度文件名的文件数、内存占用、索引数等,然后进入命令提示符($) -4. 在命令提示符处,除了之前的搜索方式,还可以使用s/hellfire针对倒排索引搜索,一般会比不带s/的搜索(原搜索)快十倍以上,它也是最多显示100个搜索结果,搜索总结果数与搜索时间统计都会显示在搜索结果的最后 -5. 在运行过程中观察程序的占用内存,对于百万个文件来说内存占用大约会在1.2G左右,内存占用大幅提高 -6. 可以输入Ctrl+D退出程序 - -生成了倒排索引数据后也可以反复使用此数据进行搜索,测试步骤同上,内存消耗与不带倒排索引的载入搜索类似,但若搜索结果较多,则可能搜索较原来的搜索方式反而会较慢。 - -# 详细参数与测试命令 - -# 集成测试 \ No newline at end of file diff --git a/docs/lib_developer.md b/docs/lib_developer.md deleted file mode 100644 index e69de29b..00000000 diff --git a/rpm/deepin-anything.spec b/rpm/deepin-anything.spec deleted file mode 100755 index 98e0b874..00000000 --- a/rpm/deepin-anything.spec +++ /dev/null @@ -1,91 +0,0 @@ -%bcond_with check -%global _unpackaged_files_terminate_build 0 -%global debug_package %{nil} -%global common_description %{expand: Something like everything, but nothing is really like anything...} -%global dname dkms -%global lname libs -%global sname server - -Name: deepin-anything -Version: 6.0.7 -Release: 1 -Summary: Something like everything, but nothing is really like anything... -License: GPLv3 -URL: https://github.com/linuxdeepin/deepin-anything -Source0: %{url}/archive/refs/tags/%{version}.tar.gz - - -BuildRequires: qt5-qtbase-devel -BuildRequires: udisks2-qt5 -BuildRequires: udisks2-qt5-devel -BuildRequires: libmount -BuildRequires: libmount-devel -BuildRequires: pcre-devel libnl3-devel -BuildRequires: libpolkit-qt5-1-devel - - -%description -%{common_description} - -%package -n %{name}-%{dname} -Summary: %{summary} -%description -n %{name}-%{dname} - - -%package -n %{name}-%{lname} -Summary: %{summary} -%description -n %{name}-%{lname} - -%package -n %{name}-%{sname} -Summary: %{summary} -%description -n %{name}-%{sname} - -%package -n %{name}-devel -Summary: Development package for %name - -%description devel -This package provides header files and libraries for %name. - -%prep -%setup -sed -i 's|lib/$(DEB_HOST_MULTIARCH)|lib64/|g' src/Makefile - -%build -export PATH=$PATH:%{_libdir}/qt5/bin -%{__make} - -%install -%make_install -mkdir -p %{?buildroot}/usr/lib/modules-load.d/ - -%files -n %{name}-%{dname} -%exclude /usr/lib/modules-load.d/anything.conf -%{_usrsrc}/deepin-anything-0.0/* - -%files -n %{name}-%{lname} -%{_libdir}/libanything.so.* - -%files -n %{name}-%{sname} -%{_libdir}/libdeepin-anything-server-lib.so.* -%{_datadir}/dbus-1/interfaces/com.deepin.anything.xml -%{_sysconfdir}/dbus-1/system.d/com.deepin.anything.conf -%{_datadir}/polkit-1/actions/com.deepin.anything.policy - -%files devel -%{_libdir}/libanything.so -%{_libdir}/libdeepin-anything-server-lib.so -%dir %{_includedir}/%{name}*/ -%dir %{_includedir}/%name/index/ -%{_includedir}/%{name}*/*.h -%{_includedir}/%name/index/*.h -%{_libdir}/pkgconfig/deepin-anything-server-lib.pc - -%changelog -* Mon Apr 24 2023 uoser - 6.0.7-1 -- new struct and remove tool&monitor service. - -* Wed Mar 17 2021 uoser - 5.0.1-2 -- add devel - -* Wed Feb 07 2018 TagBuilder - 5.0.1-1 -- Project init.