同ntfs一样,android默认的是不支持ext*格式的,可以自己把这些格式的支持加上去,但有一个问题,ext*不像fat那样挂载的时候支持uid、gid、fdmask等 参数,这会导致文件系统挂载上去后,发现还有一个问题,ext2,ext3,ext4的挂载上去后在应用中文件看不到,查了下原因是因为应用不能写u盘,说的具体点是应用的这个用户没有权限写u盘,我们看下fat挂载上去后的目录权限:
ls -l
drwxr-xr-x root system 1970-01-01 08:00 obb
drwxr-xr-x root system 1970-01-01 08:00 asec
drwx------ root root 1970-01-01 08:00 secure
d---rwxr-x system sdcard_rw 2012-03-29 17:11 sdcard
可以看到用户为systme,组为sdcard_rw但system的权限为0,组sdcard_rw的权限为7,可以读写,所以应用可以操作存储设备,这是因为在挂载fat格式的设备时mount的选项有uid=1000,gid=1015,fmask=702,dmask=702,1000在定义的是system,1015定义的是组sdcard_rw,fmask=702,dmask=702表示的是实际的权限与这个mask的~,所以:就有了上面的目录权限,只要是sdcard_rw组的用户都可以读写存储设备,但我在做其它格式的u盘挂载的时候,由于mount选项不支持,所以没有了这些选项的支持,导致挂载上去的目录是这样的:
ls -l
drwxr-xr-x root system 1970-01-01 08:00 obb
drwxr-xr-x root system 1970-01-01 08:00 asec
drwx------ root root 1970-01-01 08:00 secure
d rwxr-x --- root root 2012-03-29 17:11 sdcard
只有root用户才能写,所以应用进行写操作的时候会失败。开始用了一个很猥琐的方法,去解决这个问题,在mount上去后用chmod把sdcard这个目录的权限改为777 ,这样所有的用户对这个目录都有读写权限了,不过这好像感觉不太好,违背了android的权限管理,所以想办法搞成和fat挂载上去的权限一样,用了chown改变用户以及chgrp改变组,后面试了在mount的前面setgid改变组,不能使用setuid(1000),这样后没有权限mount了,这办法都试了,不过看了下效果还是不行,而且感觉做的很**,没办法,网上找了下,发现一个可以给内核打个补丁,使ext*打补丁能支持uid,gid选项,后面参照这个把mask的设置也加上去了,
ls -l
drwxr-xr-x root system 1970-01-01 08:00 obb
drwxr-xr-x root system 1970-01-01 08:00 asec
drwx------ root root 1970-01-01 08:00 secure
d---rwxr-x system sdcard_rw 2012-03-29 17:11 sdcard
# cd sdcard
cd sdcard
# ls -l
ls -l
d---rwxr-x system sdcard_rw 2012-03-29 17:04 lost+found
d---rwxr-x system sdcard_rw 2012-03-29 17:07 test
drwxr-xr-x root root 2012-03-29 17:11 LOST.DIR
细心的读者可能发现,虽说挂载上去的文件权限已经uid、gid对了,但是新建出来的文件还是不对,所以这里还要针对这个问题修改一下内核,如果不修改的话,会导致应用安装不上去。
先看一下mount的步骤:
这里不去介绍这个系统调用的过程了,主要看到最后调用 了ext2_fill_super取填充超级块结构,而这里super结构中的s_fs_info正是针对具体文件系统的一些信息,我们的修改也主要在此
首先在ext2_sb_info结构中增加如下成员:
uid_t s_uid; /* make all files appear to belong to this uid */
uid_t s_diskuid; /* write this uid to disk (if s_uid != 0) */
gid_t s_gid; /* make all files appear to belong to this gid */
gid_t s_diskgid; /* write this gid to disk (if s_gid != 0) */
unsigned short fs_fmask;
unsigned short fs_dmask;
这里的s_diskuid和 s_diskgid这里暂时还有用到,不过也加进去了
在ext2.h 增加下面两个函数,用来进行uid,gid,mode的相应修改:
static inline mode_t ext2_make_mode(struct ext2_sb_info *ei, umode_t i_mode)
{
umode_t mode;
if(S_ISDIR(i_mode) ||i_mode == 0)
{
mode = (00777 & ~ei->fs_dmask) | S_IFDIR;
}
else
{
mode = (00777 &~ei->fs_fmask) | S_IFREG;
}
return mode;
}
static inline void ext2_fill_inode(struct super_block *sb, struct inode *inode)
{
if (EXT2_SB(sb)->fs_fmask || EXT2_SB(sb)->fs_dmask) {
inode->i_mode = ext2_make_mode(EXT2_SB(sb), inode->i_mode);
}
if (EXT2_SB(sb)->s_uid) {
inode->i_uid = EXT2_SB(sb)->s_uid;
}
if (EXT2_SB(sb)->s_gid) {
inode->i_gid = EXT2_SB(sb)->s_gid;
}
return;
}
在fs/ext2/super.c中增加上面 ext2_sb_info结构中增加成员对应的解析选项:
enum {
...
Opt_uid, Opt_diskuid, Opt_gid, Opt_diskgid,Opt_fmask, Opt_dmask,
};
在tokens中增加相应的字符串映射:
static const match_table_t tokens = {
....
{Opt_uid, "uid=%u"},
{Opt_diskuid, "uid=%u:%u"},
{Opt_gid, "gid=%u"},
{Opt_diskgid, "gid=%u:%u"},
{Opt_dmask, "dmask=%o"},
{Opt_fmask, "fmask=%o"},
{Opt_err, NULL}
};
在解析mount选项的时候保存相应的值:
static int parse_options(char *options, struct super_block *sb)
{
...
case Opt_uid:
if (match_int(&args[0], &option))
return 0;
sbi->s_uid = sbi->s_diskuid = option;
break;
case Opt_diskuid:
if (match_int(&args[0], &option))
return 0;
sbi->s_uid = option;
if (match_int(&args[1], &option))
return 0;
sbi->s_diskuid = option;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
sbi->s_gid = sbi->s_diskgid = option;
break;
case Opt_diskgid:
if (match_int(&args[0], &option))
return 0;
sbi->s_gid = option;
if (match_int(&args[1], &option))
return 0;
sbi->s_diskgid = option;
break;
case Opt_dmask:
if (match_octal(&args[0], &option))
return 0;
sbi->fs_dmask = option;
break;
case Opt_fmask:
if (match_octal(&args[0], &option))
return 0;
sbi->fs_fmask = option;
break;
...
}
这样,相应的从外面 传进来的选项就已经可以解析并保存起来了,然后,就是要在读写inode的时候把它们相应的值转变过去:
fs/ext2/inode.c
在ext2_iget函数 中,添加ext2_fill_inode的调用如:
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
...
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
ext2_fill_inode(sb, inode);
...
}
还有static int __ext2_write_inode(struct inode *inode, int do_sync)函数中:
static int parse_options(char *options, struct super_block *sb)
{
...
ext2_get_inode_flags(ei);
ext2_fill_inode(sb, inode);
...
if (!(test_opt(sb, NO_UID32))) {
raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid));
raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid));
/*
* Fix up interopera.bility with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
if (!ei->i_dtime) {
raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid));
raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid));
} else {
raw_inode->i_uid_high = 0;
raw_inode->i_gid_high = 0;
}
} else {
raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(inode->i_uid));
raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(inode->i_gid));
raw_inode->i_uid_high = 0;
raw_inode->i_gid_high = 0;
}
这里都要做相应的更改,这样修改以后,挂载上去的文件的权限都及uid,gid都没有问题了,但是这个时候 我们发现新建文件还是会有问题,就是上面 列出来的,权限还是不对,这里还需要在创建文件或文件夹时做一些修改。
fs/ext2/namei.c
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
{
...
inode = ext2_new_inode(dir, mode);
ext2_fill_inode(dir->i_sb, inode);
...
}
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
...
inode = ext2_new_inode (dir, S_IFDIR | mode);
ext2_fill_inode(dir->i_sb, inode
...
这样,挂载的文件在android暂时就应该可以读写了,而且一般的操作应该是没问题,关于ext3、ext4的也跟这个类似,具体代码可以到下面这个链接去下载: