本文由 資源共享網 – ziyuan 發布,轉載請注明出處,如有問題請聯系我們![免費]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:## <network_id> <semantics> <flags> <protofamily> <protoname> \# <device> <nametoaddr_libs>## The <device> and <nametoaddr_libs> 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 - - -12345678910111213
1.2 將exprots配置文件推送至/etc路徑下,exprots內容如下:
/persist/nfs_server -mask=255.255.255.0 -match=192.168.1.01
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
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 set123456789
1.2 掛載在/data/nfs_client目錄
busybox mount -t nfs -o nolock 192.168.1.1:/persist/nfs_server /mnt/nfs_client1
掛載時提示“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 denied12
對QNX側的/persist/nfs_server和Android側的/data/nfs_client進行檢查,發現權限都已經正確配置
drwxrwxrwx 2 root root 4096 Jan 01 00:01 /persist/nfs_server/1
drwxrwxrwx 2 root root 4096 1970-01-01 00:02 nfs_client/1
為了排查是QNX側還是Android側的問題,先在Android側嘗試將其他分區掛載到/data/nfs_client,發現可以成功
mount /dev/block/log /data/nfs_client/
EXT4-fs (vdm): mounted filesystem with ordered data mode. Opts: (null)12
因此更傾向于QNX側的問題,既然nfs_server的權限沒有問題,難道是/persist目錄的權限有問題,無法訪問/persist目錄才導致無法獲取/persist/目錄下nfs_server的信息?
于是檢查/persist的權限,發現是660
drw-rw----+ 5 root root 4096 Jan 01 00:01 /persist/1
于是將/persist的權限改至最大
chmod 777 /persist/1
但是chmod后,發現權限仍然沒改變:
drw-rw----+ 5 root root 4096 Jan 01 00:01 /persist/1
于是使用mount命令檢查,發現/persist上掛載了一個qnx6文件系統
/dev/disk/persist on /persist type qnx61
將這個文件系統umount,再次檢查/persist目錄的權限,發現和掛載了文件系統時的權限是不一樣的,這是因為將文件系統掛載到某個掛載點時,會將掛載點的權限、屬性都設置成默認值,而掛載了文件系統后,使用chmod對掛載點的權限進行修改,是不會起作用的
umount /persist/ls -ald /persist/
drwxrwxrwx 2 root root 4096 Jan 01 00:04 /persist/123
想要設定掛載后掛載目錄的權限,就需要用到mount命令的mntperms選項,就可以將掛載后的/persist目錄設置成自己需要的權限
mount mntperms=0777 /dev/disk/persist /persist/ls -ald /persist/
drwxrwxrwx+ 5 root root 4096 Jan 01 00:01 /persist/123
此時再次回到安卓進行掛載,發現可以掛載成功
busybox mount -t nfs -o nolock 192.168.1.1:/persist/nfs_server /mnt/nfs_clientmount |grep nfs192.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)123
再進一步進行驗證,確定時其他組用戶對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 deniedmkdir ttt
mkdir: 'ttt': I/O error1234
重新查閱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/12
用計算器轉換,發現其二進制是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.01
在Android側再次掛載,并嘗試創建文件,現象如下:
touch new_test
touch: 'new_test': I/O errorls -al new_test
-rw------- 1 root root 0 1970-01-01 02:22 new_test1234
可以看到,文件可以成功創建了,且uid和gid都是root,但是還是和創建目錄一樣,有IO error的問題。
寫入數據時提示“I/O error”
繼續分析I/O error的問題。
嘗試往文件寫入數據,發現可以成功寫入:
echo test_content > new_testcat new_test
test_content123
經過多次測試后,發現兩個現象:
1.發現在創建文件時,第一次執行都會提示IO error,用ls可以看到目錄下的文件,用echo方法往一個不存在的文件寫入數據時,文件雖然被創建,但是內容并沒有被寫入
echo "write test" > newfile
/system/bin/sh: can't create newfile: I/O errorls -al newfile
-rw------- 1 root root 0 1970-01-01 02:36 newfilecat newfile# 沒有任何東西輸出12345678
此時再次執行echo “write test” > newfile,則沒有IO error的提示,且內容被成功寫入
echo "write test" > newfilecat newfilewrite test123
2.第一次touch文件時,會有IO error提示(ls可以看到文件已經生成),當再次創建同名文件時,就不會出現IO error。
touch newfile2
touch: 'newfile2': I/O errortouch newfile2123
為了跟蹤創建過程中到底哪里出錯,在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: ) = 7write(2, "'newfile'", 9'newfile') = 9write(2, ": I/O error", 11: I/O error) = 11write(2, "\n", 1) = 1exit_group(1) = ?
+++ exited with 1 +++12345678910
查閱網上的資料,對比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_client1
不知道不同版本之間關于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,并嘗試創建文件和目錄,發現確實創建失敗

剛遇到這個問題也是一籌莫展,于是回到對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目錄了。
結論
無論是NFS Client端還是NFS Server端,相應的目錄的權限都需要配置得當,要弄清楚在兩邊會操作這些目錄的進程的uid和gid分別是哪些,才能做出正確的配置。
QNX端NFS server目錄,從/目錄開始,都要為其他用戶設置上x權限。注意,權限的配置應遵循最小權限原則,不需要全部目錄都開成777(前面的例子都是為了驗證分析),以/目錄為例,在/目錄的屬主和組用戶都是root的情況下,其實權限設置為661(即rw-rw—x)就足夠了,/persist目錄也一樣,只要開放x權限就可以了。其原因是,Android端的應用在NFS client目錄映射過來的操作,都不需要讀取這兩個目錄下的內容,只需要能打開真正的掛載目錄/persist/nfst即可。
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

