rfjakob/gocryptfs

macOS uses inode number 1 on exFAT

ueberbelichtetesfoto opened this issue · 20 comments

As referenced in #556, starting from this comment #556 (comment) I hijacked the issue to discover that macOS uses the inode number 1 on an exFAT filesystem.

This leads to the error message
panic: using reserved ID 1 for inode number
when using gocryptfs later than v1.8.0.

Regarding @rfjakob's request

I would just try and see what

rm foo
touch foo
ls -li foo

says on exfat.

$> rm foo # make sure foo doesn't exist
rm: foo: No such file or directory
$> touch foo
$> ls -li foo
1 -rwxrwxrwx  1 user  group  0 27 Jul 10:04 foo

Interestingly:

$> touch foo2
$> ls -li foo*
1 -rwxrwxrwx  1 user  group  0 27 Jul 10:04 foo
1 -rwxrwxrwx  1 user  group  0 27 Jul 10:06 foo2

But that's probably some optimization, because:

$> rm foo foo2
$> echo "text" >> foo
$> touch foo2
$> ls -li foo*
5163394 -rwxrwxrwx  1 user  group  5 27 Jul 10:08 foo
      1 -rwxrwxrwx  1 user  group  0 27 Jul 10:08 foo2

The problem with macOS is probably, that when opening a directory in the Finder, the Finder immediately places a file called .DS_Store in that directory.
This file stores things like how the folder's contents should be displayed, such as list, details or symbols, and in the case of the latter, the specific positions of these symbols.

Initially this file is empty, and thus probably gets inode number 1, once the directory is opened for the first time in the Finder.

Thus: When one only works with directories on the command line and never creates empty files, this issue is unlikely to hit anybody.
The question remains, why gocryptfs v1.8.0 is unaffected.
Probably because of go-fuse?

Edit: Interestingly this "empty file inode number 1 sharing" does not happen on macOS's native APFS:

$> touch foo foo2
$> ls -li foo*
36977462 -rw-r--r--  1 user  group  0 27 Jul 10:30 foo
36977463 -rw-r--r--  1 user  group  0 27 Jul 10:30 foo2
$> touch foo2
$> ls -li foo*
1 -rwxrwxrwx  1 user  group  0 27 Jul 10:04 foo
1 -rwxrwxrwx  1 user  group  0 27 Jul 10:06 foo2

Oh dear that's not good

Can you run statfs ( https://github.com/rfjakob/gocryptfs/tree/master/contrib/statfs , run "go build" there and you get the executable) on foo ? I need to get the filesystem id for exfat so I can detect it on startup.

This should be the relevant output for exFAT:

"Fsid": {
	"Val": [
		16777233,
		35
	]
},
"Owner": 0,
"Type": 35,
"Flags": 2101784,
"Fssubtype": 0,
"Fstypename": [
	101,
	120,
	102,
	97,
	116,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0
],

Just for completion, this is the output for APFS

    "Fsid": {
            "Val": [
                    16777224,
                    26
            ]
    },
    "Owner": 0,
    "Type": 26,
    "Flags": 76582912,
    "Fssubtype": 1,
    "Fstypename": [
            97,
            112,
            102,
            115,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0
    ],

Perfect, thanks! Could you try passing -sharedstorage on mounting? This should work around the issue, as it disables hard links.

This worked.

Now gocryptfs v2.0.1 has no problems anymore, even though on the exFAT disk there are still files with inode id 1.
For reference, I'm still using FUSE 3.11.2.

Thank you.

What are the downsides of this?

Downside is that metadata caching is disabled, leading to slower operation. Second downside is that this workaround probably stopped working with 1bc1db6 just now.

Ok, final workaround merged just now. Could you test?

You should see

  • The message ExFAT detected, disabling hard links when mounting
  • Stable inode numbers
  • inode number 1 passed through to the plaintext folder

c3c9513 works like a charm now, except that I didn't see that message.

Hmm the missing message worries me a bit, but maybe it went to syslog. Could you try again with -nosyslog?

Here:

$> gocryptfs -fusedebug -fg -d -nosyslog /Volumes/T7-0-data1/backup-data/ ./plain/
	cli args: ["gocryptfs" "-fusedebug" "-fg" "-d" "-nosyslog" "/Volumes/T7-0-data1/backup-data/" "./plain/"]
	Password: 
	Decrypting master key
	cryptocore.New: key=32 bytes, aeadType=BackendGoGCM, IVBitLen=128, useHKDF=true, forceDecode=false
	contentenc.New: plainBS=4096, forceDecode=false
	CryptoCore.Wipe: Only nil'ing stdlib refs
	cryptocore.New: key=32 bytes, aeadType=BackendGoGCM, IVBitLen=128, useHKDF=true, forceDecode=false
	contentenc.New: plainBS=4096, forceDecode=false
	nametransform.New: longNames=true, raw64=true, badname=[]
	frontendArgs: {
		"Cipherdir": "/Volumes/T7-0-data1/backup-data",
		"PlaintextNames": false,
		"LongNames": true,
		"PreserveOwner": false,
		"ForceOwner": null,
		"ConfigCustom": false,
		"NoPrealloc": false,
		"SerializeReads": false,
		"ForceDecode": false,
		"Exclude": null,
		"ExcludeWildcard": null,
		"ExcludeFrom": null,
		"Suid": false,
		"KernelCache": false,
		"SharedStorage": false
	}
	18:29:41.391680 rx 2: INIT n0 {7.19 Ra 0x200000 CASE_INSENSITIVE,XTIMES,VOL_RENAME,ATOMIC_O_TRUNC,0x18000000} 
	18:29:41.391811 tx 2:     OK, {7.8 Ra 0x200000  0/0 Wr 0x20000 Tg 0x0}
	18:29:41.391965 rx 3: STATFS n1 
	18:29:41.391985 rx 2: STATFS n1 
	18:29:41.392055 tx 2:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.392055 tx 3:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.392527 rx 3: STATFS n1 
	18:29:41.392540 rx 2: STATFS n1 
	18:29:41.392604 tx 2:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.392609 tx 3:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.393578 rx 3: STATFS n1 
	18:29:41.393661 tx 3:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.394177 rx 2: STATFS n1 
	18:29:41.394223 tx 2:     OK, {blocks (2607424,2607424)/15261345 files 2607424/15261345 bs131072 nl0 frs131072}
	18:29:41.394318 rx 3: ACCESS n1 {u=501 g=20 x} 
	18:29:41.394566 tx 3:     OK
	18:29:41.394843 rx 2: LOOKUP n1 [".go-fuse-epoll-hack"] 20b
	18:29:41.394920 tx 2:     OK, {n18446744073709551615 g0 tE=0s tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395049 rx 3: GETATTR n18446744073709551615  
	18:29:41.395091 tx 3:     OK, {tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395210 rx 2: GETATTR n18446744073709551615  
	18:29:41.395289 tx 2:     OK, {tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395434 rx 3: ACCESS n18446744073709551615 {u=501 g=20 w,r} 
	18:29:41.395443 tx 3:     OK
	18:29:41.395522 rx 2: GETATTR n18446744073709551615  
	18:29:41.395565 tx 2:     OK, {tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395629 rx 3: OPEN n18446744073709551615 {RDWR} 
	18:29:41.395640 tx 3:     OK, {Fh 18446744073709551615 }
	18:29:41.395779 rx 2: SETATTR n18446744073709551615 {size 0, fh 18446744073709551615} 
	18:29:41.395809 tx 2:     OK, {tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395863 rx 3: GETATTR n18446744073709551615  
	18:29:41.395894 tx 3:     OK, {tA=0s {M0100644 SZ=0 L=1 0:0 0 0:18446744073709551615 A 0.000000 M 0.000000 C 0.000000}}
	18:29:41.395977 rx 2: FLUSH n18446744073709551615 {Fh 18446744073709551615} 
	18:29:41.396018 tx 2:     OK
	18:29:41.396080 rx 3: RELEASE n18446744073709551615 {Fh 18446744073709551615 RDWR  L0} 
	18:29:41.396089 tx 3:     OK
	Filesystem mounted and ready.

I tried with -sharedstorage, too, but it doesn't show up there either.

When exactly is the message expected to appear?

Message is:

detectQuirks: ExFAT detected, disabling hard links. See https://github.com/rfjakob/gocryptfs/issues/585 for why.

Just in case, you need the statfs again (performed on the encrypted directory):

$> ./statfs /Volumes/T7-0-data1/backup-data/ | head -n 40
{
	"Bsize": 131072,
	"Iosize": 131072,
	"Blocks": 15261345,
	"Bfree": 2607424,
	"Bavail": 2607424,
	"Files": 15261345,
	"Ffree": 2607424,
	"Fsid": {
		"Val": [
			16777233,
			40
		]
	},
	"Owner": 0,
	"Type": 40,
	"Flags": 2101784,
	"Fssubtype": 0,
	"Fstypename": [
		101,
		120,
		102,
		97,
		116,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0
	],
	"Mntonname": [
		47,
		86,
		111,

Oh dear, "Type" is 40 now. It was 35 in your earlier post

That is really strange.
I haven't changed anything and copied the command from the bash history. O.o

Oh, maybe:
I updated from macOS 11.4 to macOS 11.5.1 one day after the post with the 35.

@ueberbelichtetesfoto could you try again with latest master? I check f_fstypename now, which should be stable, but, lacking a Mac, could not test it.

Will take some time. Next time I can test will be this weekend.

I think this looks as you wanted it to be:

$> gocryptfs --version
gocryptfs v2.0.1-49-g2d386fc.HEAD without_openssl; go-fuse v2.1.1-0.20210802120645-15a8bb029a4e; 2021-08-16 go1.16.3 darwin/amd64

$> gocryptfs /Volumes/T7-0-data1/backup-data/ ./plain/
Password: 
Decrypting master key
DetectQuirks: ExFAT detected, disabling hard links. See https://github.com/rfjakob/gocryptfs/issues/585 for why.
Filesystem mounted and ready.

Also creating and dealing with empty files works just fine.
They get inode number 1 in both, the underlying ciphertext directory and the overlaying plaintext directory.

Edit: Creating two empty files leads to the two files sharing the inode number 1.
Is this expected?
Should I still use -sharedstorage?

I think this looks as you wanted it to be:

Yes looks good, thanks for testing!

Edit: Creating two empty files leads to the two files sharing the inode number 1.
Is this expected?

Yes, they internally get a different identifier called "generation number", but to the user only the inode number is visible.

Should I still use -sharedstorage?

Not needed, no!