linux 应用层可以使用opendir, readdir, closedir接口进行文件夹遍历.
API接口位于 linux/fs/readdir.c
before 3.11
linux driver层, 则可以使用file_operation中的readdir(3.11以前)接口进行读取.
typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
struct file_operations {
int (*readdir) (struct file *, void *, filldir_t);
};
示例如下:
以下是查找指定目录下的.txt或.ini文件
static int tmp_filldir(void *buf, const char *name, int namelen,
loff_t offset, u64 ino, unsigned d_type)
{
unsigned long d_ino;
d_ino = ino;
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
return -EOVERFLOW;
}
if (namelen > 0) {
memcpy(buf, name, namelen);
return 0;
}
return -ENOENT;
}
static int linux_get_file_path(const char *dirpath, char *full_path)
{
struct file *pfile;
mm_segment_t old_fs;
char read_buf[100] = {0};
int ret;
int err;
pfile = filp_open(dirpath, O_RDONLY, 0);
if (IS_ERR(pfile)) {
pr_err("Open %s failed!\n", dirpath);
return -EINVAL;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
err = -ENOENT;
if (pfile->f_op->readdir) {
while (true) {
ret = pfile->f_op->readdir(pfile, read_buf, tmp_filldir);
if (ret || (read_buf[0] == '\0')) {
break;
}
if (strstr(read_buf, ".txt") || strstr(read_buf, ".TXT") ||
strstr(read_buf, ".ini") || strstr(read_buf, ".ini")) {
// for too long filename
*(extstr+4) = '\0';
snprintf(full_path, PATH_MAX, "%s/%s", dirpath, read_buf);
pr_err("find file: %s\n", read_buf);
err = 0;
break;
}
memset(read_buf, 0, 100);
}
}
filp_close(pfile, NULL);
set_fs(old_fs);
return err;
}
After 3.11
3.11以后由iterate接口代替了readdir
static char g_buffer[1024] = {0};
static int linux_filldir(void *unused, const char *name, int namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
pr_info("(%d)%s", namelen, name);
if (namelen > 0) {
memcpy(g_buffer, name, namelen);
return 0;
}
return -ENOENT;
}
static int linux_get_file_path(const char *dirpath, char **full_path)
{
struct file *pfile;
mm_segment_t old_fs;
char *read_buf = NULL;
int ret;
int err;
char *extstr;
struct dir_context ctx = {.actor = linux_filldir};
pfile = filp_open(dirpath, O_RDONLY, 0);
if (IS_ERR(pfile)) {
pr_err("Open %s failed!\n", dirpath);
return -EINVAL;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
err = -ENOENT;
if (pfile->f_op->iterate) {
while (true) {
ret = pfile->f_op->iterate(pfile, &ctx);
if (ret || (g_buffer[0] == '\0')) {
break;
}
if ((extstr = strstr(g_buffer, ".txt")) ||
(extstr = strstr(g_buffer, ".TXT")) ||
(extstr = strstr(g_buffer, ".ini")) ||
(extstr = strstr(g_buffer, ".INI"))) {
// for long filename
*(extstr+4) = '\0';
snprintf(*full_path, PATH_MAX, "%s/%s", dirpath, read_buf);
pr_err("find file: %s\n", g_buffer);
err = 0;
break;
}
memset(g_buffer, 0, PATH_MAX);
}
}
err_out:
filp_close(pfile, NULL);
set_fs(old_fs);
return err;
}
调用示例:
#define MY_DIR "/sdcard/my/dir"
char *filepath = NULL;
filepath = kzalloc(PATH_MAX, GFP_KERNEL);
if (!filepath) {
pr_err("allocate mem for porch failed!\n");
return;
}
ret = linux_get_file_path(MY_DIR, filepath);
if (ret) {
pr_err("file not found, use default porch.txt!\n");
snprintf(filepath, PATH_MAX, "%s/%s", MY_DIR, "porch.txt");
}