SearchFS
可以通过 searchfs
实现高效的 APFS/HFS+ 文件系统文件名搜索.
#include <sys/attr.h>
#include <unistd.h>
int searchfs(const char * path,
struct fssearchblock * searchBlock,
unsigned long * numMatches,
unsigned long scriptCode,
unsigned long options,
struct searchstate * state);
说明:
- 此系统调用会搜索一个指定的卷(已挂载的文件系统), 该卷通过
path
参数指定. - 搜索条件通过
searchBlock
/scriptCode
/options
指定. numMatches
用于返回匹配的文件个数.(返回的结果个数最多可以等于searchBlock
中指定的maxmatches
).- 在
searchBlock
携带的 buffer 中, 会带回这些文件的元数据信息. searchState
参数用于 "断点续查", 从哪里中断的, 可以再从此处开始搜索.- 此调用只会返回当前执行者有权限访问的文件(即对目录有执行权限
x
的用户). - 一般而言, 传入卷根目录会搜索整个卷, 但文件路径中的所有前置目录必须符合上面 6 的需求(对目录有 x 权限).
scriptCode
目前未使用, 需要固定传入0x08000103
.options
参数是一个位集合, 用于控制searchfs
系统调用的行为.- 并非所有的卷都支持
searchfs
, 使用前, 需要通过getattrlist
调用, 获取到ATTR_VOL_CAPABILITIES
后, 测试VOL_CAP_INT_SEARCHFS
flag, 若成功才表示支持. 在不支持的卷上调用, 会直接返回ENOTSUP
.
细节参数说明
fssearchblock 类型的 searchBlock
参数
fssearchblock
类型定义为如下, 在调用前, 需要将其中成员按需填全:
struct fssearchblock {
struct attrlist * returnattrs;
void * returnbuffer;
size_t returnbuffersize;
unsigned long maxmatches;
struct timeval timelimit;
void * searchparams1;
size_t sizeofsearchparams1;
void * searchparams2;
size_t sizeofsearchparams2;
struct attrlist searchattrs;
};
其中:
returnattrs
: 指定需要返回的搜索结果文件元数据.(注意不能请求卷相关的元数据)returnbuffer
: 被搜到的文件对应的文件元数据会被放入这个缓冲区.returnbuffersize
: 文件元数据缓冲区的字节(Byte)大小.maxmatches
: 指定最多返回多少个匹配结果.timelimit
: 指定searchfs
调用的最大运行时间(避免资源耗尽).searchparams1
: 指定搜索条件的"下限".sizeofsearchparams1
: 指定搜索条件下限缓冲区的字节大小.searchparams2
: 指定搜索条件的"上限".sizeofsearchparams2
: 指定搜索条件上限缓冲区的字节大小.searchattrs
: 指定文件属性搜索条件.
options
参数
它是一个位集合, 可包含如下:
SRCHFS_START
: 设置后,searchfs
会忽略searchstate
, 从而开始一次新的搜索.SRCHFS_MATCHPARTIALNAMES
:searchfs
会将ATTR_CMN_NAME
指定的字符串对应的子串匹配成功也认为是满足搜索条件.SRCHFS_MATCHDIRS
: 搜索可以匹配目录名.SRCHFS_MATCHFILES
: 搜索可以匹配文件名.SRCHFS_SKIPLINKS
: 搜索只匹配硬链接, 忽略软链接.(此选项需要 root 权限, 且不能和ATTR_CMN_NAME
或ATTR_CMN_PAROBJID
一起使用.)SRCHFS_SKIPINVISIBLE
: 忽略隐藏文件(即拥有ATTR_CMN_FNDRINFO
属性的第 9 Byte 的第 6 bit 为 1 的文件)SRCHFS_SKIPPACKAGES
: 忽略 package 内部的文件(比较有用).SRCHFS_SKIPINAPPROPRIATE
: 忽略特定目录下的文件, 目前这样的目录只有一个/System
SRCHFS_NEGATEPARAMS
: 返回所有不匹配搜索条件的文件, 即可以按 "非" 进行搜索.
state
参数
为一个透明指针, 代表当前搜索状态. 因 searchfs
可能在返回时只返回了部分结果, 其结束状态会通过 state 带回, 若要获取完整结果, 则需要继续调用, 在调用时传入上次的 state.
首次调用或需要重新开始 searchfs
调用时, 在 options
参数中设置 SRCHFS_START
即可.
关于错误和流程处理
错误有如下情况:
- 当搜索到和
maxmatches
指定数量相同的结果后, 就会返回EAGAIN
. - 当搜索结果缓冲区大小不足时, 会返回
ENOBUFS
, 此时应分配更大的缓冲区并重试. 当返回ENOBUFS
时,numMatches
的值会是 0. - 当达到
timelimit
后会超时, 此时返回EAGAIN
. - 当尝试指定不同的 catelog 继续上次搜索时(未指定
SRCHFS_START
且传入了state
), 会返回EBUSY
, 此时需要重新开始搜索. - 当返回
EAGAIN
且numMatches
大于 0 时, 表示当前是 "返回部分结果" 状态, 此时应:- 将结果进行处理(目的是腾出缓冲区)
- 继续进行查询操作(传入
state
且不要设置SRCHFS_START
)
关于搜索条件指定
如下五个参数共同确定搜索条件: searchparams1
, sizeofsearchparams1
, searchparams2
, sizeofsearchparams2
, searchattrs
.
- 对于固定的搜索条件, 比如文件名, 会使用
searchparams1
中的而忽略searchparams2
中的. - 对于类似数值条件, 比如创建日期, 则
searchparams1
中的为下限,searchparams2
中的为上限. - 对于结构体条件, 特别是
ATTR_CMN_FNDRINFO
, 会先使用searchparams2
中的进行 mask 后, 再和searchparams1
中的进行字节对比, 若相等, 则表明匹配.
关于返回值
- 搜索结束, 会返回
0
. - 否则返回
-1
并设置errno
, 此时应根据errno
进行对应处理.
关于支持的文件属性
searchfs
当前并非支持任意文件属性搜索, 可以支持的属性有如下:
ATTR_CMN_NAME
ATTR_CMN_OBJID
ATTR_CMN_PAROBJID
ATTR_CMN_CRTIME
ATTR_CMN_MODTIME
ATTR_CMN_CHGTIME
ATTR_CMN_ACCTIME
ATTR_CMN_BKUPTIME
ATTR_CMN_FNDRINFO
ATTR_CMN_BKUPTIME
ATTR_CMN_OWNERID
ATTR_CMN_GRPID
ATTR_CMN_ACCESSMASK
ATTR_DIR_ENTRYCOUNT
ATTR_FILE_DATALENGTH
ATTR_FILE_DATAALLOCSIZE
ATTR_FILE_RSRCLENGTH
ATTR_FILE_RSRCALLOCSIZE
示例
#include <assert.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <sys/attr.h>
#include <sys/errno.h>
#include <unistd.h>
typedef struct attrlist attrlist_t;
typedef struct fssearchblock fssearchblock_t;
typedef struct searchstate searchstate_t;
struct SearchAttrBuf {
unsigned long length;
char finderInfo[32];
};
typedef struct SearchAttrBuf SearchAttrBuf;
struct ResultAttrBuf {
unsigned long length;
attrreference_t name;
fsobj_id_t parObjID;
};
typedef struct ResultAttrBuf ResultAttrBuf;
enum {
kMatchesPerCall = 16
};
static int SearchFSDemo(
const char *volPath,
const char *type,
const char *creator
)
{
int err;
fssearchblock_t searchBlock;
SearchAttrBuf lower;
SearchAttrBuf upper;
static const unsigned char kAllOnes[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
unsigned long matchCount;
unsigned long matchIndex;
unsigned long options;
searchstate_t state;
ResultAttrBuf * thisEntry;
attrlist_t returnAttrList;
char resultAttrBuf[ kMatchesPerCall
* (sizeof(ResultAttrBuf) + 64)];
// resultAttrBuf is big enough for kMatchesPerCall entries,
// assuming that the average name length is less than 64.
assert(strlen(type) == 4);
assert(strlen(creator) == 4);
memset(&searchBlock, 0, sizeof(searchBlock));
searchBlock.searchattrs.bitmapcount = ATTR_BIT_MAP_COUNT;
searchBlock.searchattrs.commonattr = ATTR_CMN_FNDRINFO;
memset(&lower, 0, sizeof(lower));
memset(&upper, 0, sizeof(upper));
lower.length = sizeof(lower);
upper.length = sizeof(upper);
memcpy(&lower.finderInfo[0], type, 4);
memcpy(&lower.finderInfo[4], creator, 4);
memcpy(&upper.finderInfo[0], kAllOnes, 4);
memcpy(&upper.finderInfo[4], kAllOnes, 4);
searchBlock.searchparams1 = &lower;
searchBlock.sizeofsearchparams1 = sizeof(lower);
searchBlock.searchparams2 = &upper;
searchBlock.sizeofsearchparams2 = sizeof(lower);
searchBlock.timelimit.tv_sec = 0;
searchBlock.timelimit.tv_usec = 100 * 1000;
searchBlock.maxmatches = kMatchesPerCall;
memset(&returnAttrList, 0, sizeof(returnAttrList));
returnAttrList.bitmapcount = ATTR_BIT_MAP_COUNT;
returnAttrList.commonattr = ATTR_CMN_NAME | ATTR_CMN_PAROBJID;
searchBlock.returnattrs = &returnAttrList;
searchBlock.returnbuffer = resultAttrBuf;
searchBlock.returnbuffersize = sizeof(resultAttrBuf);
options = SRCHFS_START | SRCHFS_MATCHFILES;
do {
err = searchfs(
volPath,
&searchBlock,
&matchCount,
0x08000103,
options,
&state
);
if (err != 0) {
err = errno;
}
if ( (err == 0) || (err == EAGAIN) ) {
thisEntry = (ResultAttrBuf *) resultAttrBuf;
for (matchIndex = 0; matchIndex < matchCount; matchIndex++) {
printf("%08x ", thisEntry->parObjID.fid_objno);
printf(
"%s\n",
((char *) &thisEntry->name)
+ thisEntry->name.attr_dataoffset
);
// Advance to the next entry.
((char *) thisEntry) += thisEntry->length;
}
}
options &= ~SRCHFS_START;
} while (err == EAGAIN);
return err;
}