Android挂载QNX的NFS服务问题记录

Android挂载QNX的NFS服务问题记录

    • 配置方法
    • 挂载时提示“Permission denied”
    • 挂载后对挂载目录只有读权限,无法写入数据
    • 写入数据时提示“I/O error”
    • Android侧,非root权限用户在NFS client目录下创建数据,提示“Permission Denied”
    • 结论
    • 小提示

在QNX开启NFS服务,由QNX上的guest OS–Android作为客户端,将QNX的目录作为网络设备以NFS的方式挂载。过程中主要遇到三个问题,下面分别描述配置方法,配置过程遇到的问题、分析思路以及解决方案。

配置方法

QNX侧
1.1 将netconfig配置文件保存至/etc目录下,netconfig内容如下:

# Entries consist of:
#
#            \
#                
#
# The  and  fields are always empty.
#
udp6       tpi_clts      v     inet6    udp     -       -
tcp6       tpi_cots_ord  v     inet6    tcp     -       -
udp        tpi_clts      v     inet     udp     -       -
tcp        tpi_cots_ord  v     inet     tcp     -       -
rawip      tpi_raw       -     inet      -      -       -
local      tpi_cots_ord  -     loopback  -      -       -

1.2 将exprots配置文件推送至/etc路径下,exprots内容如下:

/persist/nfs_server -mask=255.255.255.0 -match=192.168.1.0

exports文件定义了NFS服务端提供给客户端的挂载点,以及相关的挂载权限,以上述配置内容为例,设置的挂载点为/persist/nfs_server,允许的挂载的客户端IP为必须属于192.168.1这个网段,即IP必须满足IP & 255.255.255.0=192.168.1.0。其他可配置的选项还有-norsvd,-ro,-root等,每个选项的详细介绍可以查询QNX官方开发者网站。

1.3 执行rpcbind和nfsd

rpcbind
nfsd -c /etc/exports -t

Android侧
1.1 kernel内核配置NFS client

+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_NFS_V4_1_MIGRATION=y
+CONFIG_NFS_USE_LEGACY_DNS=y
+# CONFIG_SUNRPC_DEBUG is not set

1.2 挂载在/data/nfs_client目录

busybox mount -t nfs -o nolock 192.168.1.1:/persist/nfs_server /mnt/nfs_client

挂载时提示“Permission denied”

完成上述配置后,在Android端进行挂载时,遇到了“Permission denied”的错误

busybox mount -t nfs -o nolock 192.168.1.1:/persist/nfs_server /mnt/nfs_client
mount: mounting 192.168.1.1:/persist/nfs_server on /data/nfs_client failed: Permission denied

对QNX侧的/persist/nfs_server和Android侧的/data/nfs_client进行检查,发现权限都已经正确配置

drwxrwxrwx   2 root      root           4096 Jan 01 00:01 /persist/nfs_server/
drwxrwxrwx 2 root root 4096 1970-01-01 00:02 nfs_client/

为了排查是QNX侧还是Android侧的问题,先在Android侧尝试将其他分区挂载到/data/nfs_client,发现可以成功

mount /dev/block/log /data/nfs_client/
EXT4-fs (vdm): mounted filesystem with ordered data mode. Opts: (null)

因此更倾向于QNX侧的问题,既然nfs_server的权限没有问题,难道是/persist目录的权限有问题,无法访问/persist目录才导致无法获取/persist/目录下nfs_server的信息?
于是检查/persist的权限,发现是660

drw-rw----+  5 root      root           4096 Jan 01 00:01 /persist/

于是将/persist的权限改至最大

chmod 777 /persist/

但是chmod后,发现权限仍然没改变:

drw-rw----+  5 root      root           4096 Jan 01 00:01 /persist/

于是使用mount命令检查,发现/persist上挂载了一个qnx6文件系统

/dev/disk/persist on /persist type qnx6

将这个文件系统umount,再次检查/persist目录的权限,发现和挂载了文件系统时的权限是不一样的,这是因为将文件系统挂载到某个挂载点时,会将挂载点的权限、属性都设置成默认值,而挂载了文件系统后,使用chmod对挂载点的权限进行修改,是不会起作用的

umount /persist/
ls -ald /persist/
drwxrwxrwx   2 root      root           4096 Jan 01 00:04 /persist/

想要设定挂载后挂载目录的权限,就需要用到mount命令的mntperms选项,就可以将挂载后的/persist目录设置成自己需要的权限

mount mntperms=0777 /dev/disk/persist /persist/
ls -ald /persist/
drwxrwxrwx+  5 root      root           4096 Jan 01 00:01 /persist/

此时再次回到安卓进行挂载,发现可以挂载成功

busybox mount -t nfs -o nolock 192.168.1.1:/persist/nfs_server /mnt/nfs_client
mount |grep nfs
192.168.1.1:/persist/nfs_server on /data/nfs_client type nfs (rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nolock,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=tcp,local_lock=all,addr=192.168.1.1)

再进一步进行验证,确定时其他组用户对QNX的/persist/nfs_server缺少x权限时就会出现permission denied的问题,因为对目录的执行权限其实就是cd,即打开目录。
只是这里还是想不通的一点是,NFS挂载时,在QNX这一边,看起来并不是一个root用户的进程去访问/persist/nfs_server并传输数据,否则就不会需要给其他组用户设置上x权限了。

挂载后对挂载目录只有读权限,无法写入数据

上一个问题解决后,在Android侧可以正常挂载QNX的NFS目录,但是又遇到了另一个问题,在往挂载目录写入数据时,又提示了“Permission denied”。

touch file11
touch: 'file11': Permission denied
mkdir ttt
mkdir: 'ttt': I/O error

重新查阅QNX官方开发网站,按照里面的描述,默认情况下,NFS端的目录就是可读写权限,如果想设置只读权限,可以在/etc/exports文件加上-ro选项将目录权限设置为只读。
使用mkdir命令创建目录提示I/O error后,再次ls检查目录的情况,发现其实ttt目录其实有创建成功,但是这个目录的uid和gid非常奇怪,是数值非常大的数字。

ls -al ttt/
drwx------ 2 4294967294 4294967294 4096 1970-01-01 01:48 ttt/

用计算器转换,发现其二进制是111…1110,总共32位,这个其实就是-2的补码,重新看会QNX官方开发者文档中关于/etc/exports的描述,在-root选项的描述里看到,默认情况下,QNX的root uid在NFS客户端是映射为-2的,可以通过-root指定为其他数字。

-root=uid
Map root’s uid (real user ID). By default, root is mapped to -2.

-2这个uid在Android系统应该是无效的,猜测是因为这个问题导致创建文件和目录异常。于是在QNX侧的/etc/exports文件加上-root=0的选项

/persist/nfs_server -root=0  -mask=255.255.255.0 -match=192.168.1.0

在Android侧再次挂载,并尝试创建文件,现象如下:

touch new_test
touch: 'new_test': I/O error
ls -al new_test
-rw------- 1 root root 0 1970-01-01 02:22 new_test

可以看到,文件可以成功创建了,且uid和gid都是root,但是还是和创建目录一样,有IO error的问题。

写入数据时提示“I/O error”

继续分析I/O error的问题。
尝试往文件写入数据,发现可以成功写入:

echo test_content > new_test
cat new_test
test_content

经过多次测试后,发现两个现象:
1.发现在创建文件时,第一次执行都会提示IO error,用ls可以看到目录下的文件,用echo方法往一个不存在的文件写入数据时,文件虽然被创建,但是内容并没有被写入

echo "write test" > newfile
/system/bin/sh: can't create newfile: I/O error

ls -al newfile
-rw------- 1 root root 0 1970-01-01 02:36 newfile

cat newfile
# 没有任何东西输出

此时再次执行echo “write test” > newfile,则没有IO error的提示,且内容被成功写入

echo "write test" > newfile
cat newfile
write test

2.第一次touch文件时,会有IO error提示(ls可以看到文件已经生成),当再次创建同名文件时,就不会出现IO error。

touch newfile2
touch: 'newfile2': I/O error
touch newfile2

为了跟踪创建过程中到底哪里出错,在Android端使用strace命令跟踪touch的执行过程,省略中间过程,发现在执行openat的调用出错,返回了EIO,即I/O error。

strace touch newfile
...
openat(AT_FDCWD, "newfile", O_RDONLY|O_CREAT, 0666) = -1 EIO (I/O error)
write(2, "touch: ", 7touch: )                  = 7
write(2, "'newfile'", 9'newfile')                = 9
write(2, ": I/O error", 11: I/O error)             = 11
write(2, "\n", 1
)                       = 1
exit_group(1)                           = ?
+++ exited with 1 +++

查阅网上的资料,对比open函数,openat多了传入文件的路径可以为相对路径的功能,其他地方和open函数一样。
正当无从下手分析时,同事经过不断测试和网上查阅资料,发现了只要在Android端挂载NFS时,加上nfsvers=2的选项,即可解决创建文件和目录时提示的I/O error问题。

busybox mount -t nfs -o nolock,nfsvers=2 192.168.1.1:/persist/nfs_server /data/nfs_client

不知道不同版本之间关于openat这个系统调用的支持是否有差异才导致I/O error的问题,还是QNX和Android之间需要使用一样的NFS版本,最后根本的原因还是不清楚~。(更新:V3的问题,参见评论大神的回复,是内核NFS驱动的bug,更新到新版本的内核即可解决)

Android侧,非root权限用户在NFS client目录下创建数据,提示“Permission Denied”

解决上述问题后,Android侧,root用户可以在NFS client目录下读写数据,包括创建文件和目录。但是在实际的应用场景中,java app需要读写这个NFS client目录,这个时候发现,java app在NFS client目录下创建文件和目录时会失败,提示permission denied,也就是只有root用户才能成功创建文件和目录,但是java app是不允许被设定为root权限的。

为了便于分析问题,我们直接在Android通过su system或者su shell切换到非root的shell,并尝试创建文件和目录,发现确实创建失败

Android挂载QNX的NFS服务问题记录_第1张图片

刚遇到这个问题也是一筹莫展,于是回到对Permission Denied的思考,QNX端NFS server目录权限已经全部设置为777,那么是否有可能是NFS server目录的上级目录的问题?在我的验证环境中,QNX端NFS server目录为/persist/nfst,persist目录和nfst目录已经全部变成777了。
在这里插入图片描述
在这里插入图片描述

那么剩下可以配置就剩下QNX的/目录了,于是检查/目录的权限:
在这里插入图片描述
可以看到,QNX的/目录对非root用户,是没有任何操作权限的。于是我们尝试remount这个/目录,为其他用户组加大权限
在这里插入图片描述
这个时候,再次回到Android端,发现非root权限也可以成功创建目录了!
在这里插入图片描述
于是不断修改QNX端的/目录权限来排查到底是rwx哪个权限位起了限制,最后发现如果/目录没有对root以外的用户开放x,即可执行权限的情况下,Android端的非root用户就会无法创建文件和目录。所以/目录的其他用户的x权限必须要设置,否则就会出现这个问题。目录的可执行操作,其实就是打开这个目录,如果缺少对应的x权限,cd 目录的时候将会因为Permission Denied而操作失败。NFS的底层细节无从知晓,但是按照上述的现象和分析,应该是Android端的创建操作映射到QNX端时,会有从/目录开始,一直cd到NFS server目录的操作,当给各级目录的其他用户都设置x权限后,非root用户就可以打开NFS server目录了。

结论

  1. 无论是NFS Client端还是NFS Server端,相应的目录的权限都需要配置得当,要弄清楚在两边会操作这些目录的进程的uid和gid分别是哪些,才能做出正确的配置。

  2. QNX端NFS server目录,从/目录开始,都要为其他用户设置上x权限。注意,权限的配置应遵循最小权限原则,不需要全部目录都开成777(前面的例子都是为了验证分析),以/目录为例,在/目录的属主和组用户都是root的情况下,其实权限设置为661(即rw-rw—x)就足够了,/persist目录也一样,只要开放x权限就可以了。其原因是,Android端的应用在NFS client目录映射过来的操作,都不需要读取这两个目录下的内容,只需要能打开真正的挂载目录/persist/nfst即可。

  3. QNX侧的/etc/exports中,-root=选项的设置。这个选项的意思是,NFS client端(即Android)的root用户,映射到QNX端的id是多少。查阅QNX的文档可知,如果不显示设置这个值,那么client端的root用户在QNX下的uid会默认设置为-2。

如果没有加载-root=0的选项,所以我们可以看到,Android NFS client目录,用root用户创建的值,uid和gid是一个很大的数,其实这个就是-2的补码:
QNX侧
在这里插入图片描述
Android侧
drwx------ 2 4294967294 4294967294

如果在/etc/exports中,设置了-root=0,那么root用户创建的文件和目录,在两边对应的uid和gid都是0。

-2这样的映射,就会把Android的操作在QNX映射成一个nobody的uid的操作(QNX没有这个uid),这样安全性其实更强,因为直接映射成0的话,相当于把QNX侧的root权限暴露给作为客户端的Android。而变成nobody这样的用户,那么对整个QNX系统的全部文件和目录恶言,Android就只获得了其他用户的权限,起到了访问控制的作用。当然映射成nobody这样的uid的话,QNX的权限需要额外做些配置(毕竟配置root映射为0的时候就是交出了root用户给Android,root是万能的),主要是对其他用户的配置。

小提示

使用toybox的mount命令挂载nfs时会提示Invalid Argument,其原因是因为toybox的mount命令是用vers来指定版本号的,用nfsvers是无法识别的。

mount -t nfs -o nolock,rw,vers=2

而busybox的mount则两个写法都支持,即可以支持vers=,也支持nfsvers=

busybox mount -t nfs -o nolock,rw,vers=2

busybox mount -t nfs -o nolock,rw,nfsvers=2

你可能感兴趣的:(文件系统,Android,算法,动态规划,android)