Android用readdir()函数读取exfat格式U盘文件返回的d_type为空的问题

23 篇文章 3 订阅
19 篇文章 3 订阅

一、前言

Android4.4上面,用readdir()函数读取exfat格式的U盘文件后,在返回的dirent结构体中,有关读取文件的类型d_type值为空。

二、问题分析

这个有个可能是exfat文件在挂载时,相应的结构体没有填充导致。

三、解决方法

有两种解决方法,一种是用stat()函数,另一种从根源解决。

  1. 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()函数进行判断,这样一来既繁琐又耗费时间。

  1. 修改fuse.c文件
    修改fuse.c文件可以从根源上解决,目前GitHub[点击进入]上已经修改过,可以解决d_type为空的问题,需要修改的文件为lib/fuse.ccommit号为: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文件下即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值