一、前言
Android4.4
上面,用readdir()
函数读取exfat
格式的U
盘文件后,在返回的dirent
结构体中,有关读取文件的类型d_type
值为空。
二、问题分析
这个有个可能是exfat
文件在挂载时,相应的结构体没有填充导致。
三、解决方法
有两种解决方法,一种是用stat()函数,另一种从根源解决。
stat()
函数
具体方式参照/frameworks/av/media/libmedia/MediaScanner.cpp
中对位置文件类型处理方式进行处理。代码如下:
MediaScanResult MediaScanner::doProcessDirectoryEntry(
char *path, int pathRemaining, MediaScannerClient &client, bool noMedia,
struct dirent* entry, char* fileSpot) {
struct stat statbuf;
const char* name = entry->d_name;
// ignore "." and ".."
if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
return MEDIA_SCAN_RESULT_SKIPPED;
}
int nameLength = strlen(name);
if (nameLength + 1 > pathRemaining) {
// path too long!
return MEDIA_SCAN_RESULT_SKIPPED;
}
strcpy(fileSpot, name);
/* 用stat()函数区分文件类型 */
int type = entry->d_type;
if (type == DT_UNKNOWN) {
// If the type is unknown, stat() the file instead.
// This is sometimes necessary when accessing NFS mounted filesystems, but
// could be needed in other cases well.
if (stat(path, &statbuf) == 0) {
if (S_ISREG(statbuf.st_mode)) {
type = DT_REG;
} else if (S_ISDIR(statbuf.st_mode)) {
type = DT_DIR;
}
} else {
ALOGD("stat() failed for %s: %s", path, strerror(errno) );
}
}
if (type == DT_DIR) {
bool childNoMedia = noMedia;
// set noMedia flag on directories with a name that starts with '.'
// for example, the Mac ".Trashes" directory
if (name[0] == '.')
childNoMedia = true;
// report the directory to the client
if (stat(path, &statbuf) == 0) {
status_t status = client.scanFile(path, statbuf.st_mtime, 0,
true /*isDirectory*/, childNoMedia);
if (status) {
return MEDIA_SCAN_RESULT_ERROR;
}
}
// and now process its contents
strcat(fileSpot, "/");
MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1,
client, childNoMedia);
if (result == MEDIA_SCAN_RESULT_ERROR) {
return MEDIA_SCAN_RESULT_ERROR;
}
} else if (type == DT_REG) {
stat(path, &statbuf);
status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size,
false /*isDirectory*/, noMedia);
if (status) {
return MEDIA_SCAN_RESULT_ERROR;
}
}
return MEDIA_SCAN_RESULT_OK;
}
用stat()
函数的缺点是每次调用readdir()
后,当d_type
为空时,需要调用stat()
函数进行判断,这样一来既繁琐又耗费时间。
- 修改
fuse.c
文件
修改fuse.c文件可以从根源上解决,目前GitHub[点击进入]上已经修改过,可以解决d_type
为空的问题,需要修改的文件为lib/fuse.c
,commit
号为:bdd2d4110fbc6d2059eb699efad2cca4a7eacccb
,代码如下:
static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
off_t off, enum fuse_fill_dir_flags flags)
{
struct fuse_dh *dh = (struct fuse_dh *) dh_;
struct fuse_entry_param e = {
/* ino=0 tells the kernel to ignore readdirplus stat info */
.ino = 0,
};
struct fuse *f = dh->fuse;
int res;
if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
dh->error = -EIO;
return 1;
}
if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
e.attr = *statp;
if (!is_dot_or_dotdot(name)) {
res = do_lookup(f, dh->nodeid, name, &e);
if (res) {
dh->error = res;
return 1;
}
}
} else {
e.attr.st_ino = FUSE_UNKNOWN_INO;
/* 处理d_type为空的情况 -- start */
if (statp) {
e.attr.st_mode = statp->st_mode;
if (f->conf.use_ino)
e.attr.st_ino = statp->st_ino;
}
/* 处理d_type为空的情况 -- end */
if (!f->conf.use_ino && f->conf.readdir_ino) {
e.attr.st_ino = (ino_t)
lookup_nodeid(f, dh->nodeid, name);
}
}
if (off) {
size_t newlen;
if (dh->filled) {
dh->error = -EIO;
return 1;
}
if (dh->first) {
dh->error = -EIO;
return 1;
}
if (extend_contents(dh, dh->needlen) == -1)
return 1;
newlen = dh->len +
fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
dh->needlen - dh->len, name,
&e, off);
if (newlen > dh->needlen)
return 1;
dh->len = newlen;
} else {
dh->filled = 1;
if (fuse_add_direntry_to_dh(dh, name, &e.attr) == -1)
return 1;
}
return 0;
}
修改完后局部编译模块,将生成的bin
文件放置Android
系统的/system/bin
文件下即可。