Skip to main content

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);

说明:

  1. 此系统调用会搜索一个指定的卷(已挂载的文件系统), 该卷通过 path 参数指定.
  2. 搜索条件通过 searchBlock/scriptCode/options 指定.
  3. numMatches 用于返回匹配的文件个数.(返回的结果个数最多可以等于 searchBlock 中指定的 maxmatches).
  4. searchBlock 携带的 buffer 中, 会带回这些文件的元数据信息.
  5. searchState 参数用于 "断点续查", 从哪里中断的, 可以再从此处开始搜索.
  6. 此调用只会返回当前执行者有权限访问的文件(即对目录有执行权限 x 的用户).
  7. 一般而言, 传入卷根目录会搜索整个卷, 但文件路径中的所有前置目录必须符合上面 6 的需求(对目录有 x 权限).
  8. scriptCode 目前未使用, 需要固定传入 0x08000103.
  9. options 参数是一个位集合, 用于控制 searchfs 系统调用的行为.
  10. 并非所有的卷都支持 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_NAMEATTR_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 即可.

关于错误和流程处理

错误有如下情况:

  1. 当搜索到和 maxmatches 指定数量相同的结果后, 就会返回 EAGAIN.
  2. 当搜索结果缓冲区大小不足时, 会返回 ENOBUFS, 此时应分配更大的缓冲区并重试. 当返回 ENOBUFS 时, numMatches 的值会是 0.
  3. 当达到 timelimit 后会超时, 此时返回 EAGAIN.
  4. 当尝试指定不同的 catelog 继续上次搜索时(未指定 SRCHFS_START 且传入了 state), 会返回 EBUSY, 此时需要重新开始搜索.
  5. 当返回 EAGAINnumMatches 大于 0 时, 表示当前是 "返回部分结果" 状态, 此时应:
    1. 将结果进行处理(目的是腾出缓冲区)
    2. 继续进行查询操作(传入 state 且不要设置 SRCHFS_START)

关于搜索条件指定

如下五个参数共同确定搜索条件: searchparams1, sizeofsearchparams1, searchparams2, sizeofsearchparams2, searchattrs.

  1. 对于固定的搜索条件, 比如文件名, 会使用 searchparams1 中的而忽略 searchparams2 中的.
  2. 对于类似数值条件, 比如创建日期, 则 searchparams1 中的为下限, searchparams2 中的为上限.
  3. 对于结构体条件, 特别是 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;
}