@@ -114,7 +114,7 @@ func (fs *filesystem) newLisafsDentry(ctx context.Context, ino *lisafs.Inode) (*
114114 fs .client .CloseFD (ctx , ino .ControlFD , false /* flush */ )
115115 return nil , linuxerr .EIO
116116 }
117-
117+ isDir := ino . Stat . Mode & linux . FileTypeMask == linux . ModeDirectory
118118 inoKey := inoKeyFromStatx (& ino .Stat )
119119 // Common case. Performance hack which is used to allocate the dentry
120120 // and its inode together in the heap. This will help reduce allocations and memory
@@ -125,69 +125,73 @@ func (fs *filesystem) newLisafsDentry(ctx context.Context, ino *lisafs.Inode) (*
125125 d dentry
126126 i lisafsInode
127127 }{}
128- temp .d .inode = fs .getOrCreateInode (inoKey , func () {
129- fs .client .CloseFD (ctx , ino .ControlFD , false /* flush */ )
130- }, func () * inode {
131- temp .i = lisafsInode {
132- inode : inode {
133- fs : fs ,
134- inoKey : inoKey ,
135- ino : fs .inoFromKey (inoKey ),
136- mode : atomicbitops .FromUint32 (uint32 (ino .Stat .Mode )),
137- uid : atomicbitops .FromUint32 (uint32 (fs .opts .dfltuid )),
138- gid : atomicbitops .FromUint32 (uint32 (fs .opts .dfltgid )),
139- blockSize : atomicbitops .FromUint32 (hostarch .PageSize ),
140- readFD : atomicbitops .FromInt32 (- 1 ),
141- writeFD : atomicbitops .FromInt32 (- 1 ),
142- mmapFD : atomicbitops .FromInt32 (- 1 ),
143- },
144- controlFD : fs .client .NewFD (ino .ControlFD ),
145- }
146- temp .i .inode .init (& temp .i )
147- inode := & temp .i .inode
148- if ino .Stat .Mask & linux .STATX_UID != 0 {
149- inode .uid = atomicbitops .FromUint32 (dentryUID (lisafs .UID (ino .Stat .UID )))
150- }
151- if ino .Stat .Mask & linux .STATX_GID != 0 {
152- inode .gid = atomicbitops .FromUint32 (dentryGID (lisafs .GID (ino .Stat .GID )))
153- }
154- if ino .Stat .Mask & linux .STATX_SIZE != 0 {
155- inode .size = atomicbitops .FromUint64 (ino .Stat .Size )
156- }
157- if ino .Stat .Blksize != 0 {
158- inode .blockSize = atomicbitops .FromUint32 (ino .Stat .Blksize )
159- }
160- if ino .Stat .Mask & linux .STATX_ATIME != 0 {
161- inode .atime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Atime ))
162- } else {
163- inode .atime = atomicbitops .FromInt64 (fs .clock .Now ().Nanoseconds ())
164- }
165- if ino .Stat .Mask & linux .STATX_MTIME != 0 {
166- inode .mtime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Mtime ))
167- } else {
168- inode .mtime = atomicbitops .FromInt64 (fs .clock .Now ().Nanoseconds ())
169- }
170- if ino .Stat .Mask & linux .STATX_CTIME != 0 {
171- inode .ctime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Ctime ))
172- } else {
173- // Approximate ctime with mtime if ctime isn't available.
174- inode .ctime = atomicbitops .FromInt64 (inode .mtime .Load ())
175- }
176- if ino .Stat .Mask & linux .STATX_BTIME != 0 {
177- inode .btime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Btime ))
178- }
128+ // Force new inode creation for directory inodes to avoid hard-linking directories.
129+ // This also avoids a correctness issue when a directory is bind-mounted on the host:
130+ // different paths (e.g., /mnt/ and /mnt/a/b/c if /mnt/a/b/c is a bind mount of /mnt/)
131+ // can return the same device ID and inode number from a stat call.
132+ temp .d .inode = fs .getOrCreateInode (inoKey /* dontCache = */ , isDir ,
133+ func () { fs .client .CloseFD (ctx , ino .ControlFD , false /* flush */ ) },
134+ func () * inode {
135+ temp .i = lisafsInode {
136+ inode : inode {
137+ fs : fs ,
138+ inoKey : inoKey ,
139+ ino : fs .inoFromKey (inoKey ),
140+ mode : atomicbitops .FromUint32 (uint32 (ino .Stat .Mode )),
141+ uid : atomicbitops .FromUint32 (uint32 (fs .opts .dfltuid )),
142+ gid : atomicbitops .FromUint32 (uint32 (fs .opts .dfltgid )),
143+ blockSize : atomicbitops .FromUint32 (hostarch .PageSize ),
144+ readFD : atomicbitops .FromInt32 (- 1 ),
145+ writeFD : atomicbitops .FromInt32 (- 1 ),
146+ mmapFD : atomicbitops .FromInt32 (- 1 ),
147+ },
148+ controlFD : fs .client .NewFD (ino .ControlFD ),
149+ }
150+ temp .i .inode .init (& temp .i )
151+ inode := & temp .i .inode
152+ if ino .Stat .Mask & linux .STATX_UID != 0 {
153+ inode .uid = atomicbitops .FromUint32 (dentryUID (lisafs .UID (ino .Stat .UID )))
154+ }
155+ if ino .Stat .Mask & linux .STATX_GID != 0 {
156+ inode .gid = atomicbitops .FromUint32 (dentryGID (lisafs .GID (ino .Stat .GID )))
157+ }
158+ if ino .Stat .Mask & linux .STATX_SIZE != 0 {
159+ inode .size = atomicbitops .FromUint64 (ino .Stat .Size )
160+ }
161+ if ino .Stat .Blksize != 0 {
162+ inode .blockSize = atomicbitops .FromUint32 (ino .Stat .Blksize )
163+ }
164+ if ino .Stat .Mask & linux .STATX_ATIME != 0 {
165+ inode .atime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Atime ))
166+ } else {
167+ inode .atime = atomicbitops .FromInt64 (fs .clock .Now ().Nanoseconds ())
168+ }
169+ if ino .Stat .Mask & linux .STATX_MTIME != 0 {
170+ inode .mtime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Mtime ))
171+ } else {
172+ inode .mtime = atomicbitops .FromInt64 (fs .clock .Now ().Nanoseconds ())
173+ }
174+ if ino .Stat .Mask & linux .STATX_CTIME != 0 {
175+ inode .ctime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Ctime ))
176+ } else {
177+ // Approximate ctime with mtime if ctime isn't available.
178+ inode .ctime = atomicbitops .FromInt64 (inode .mtime .Load ())
179+ }
180+ if ino .Stat .Mask & linux .STATX_BTIME != 0 {
181+ inode .btime = atomicbitops .FromInt64 (dentryTimestamp (ino .Stat .Btime ))
182+ }
179183
180- if ino .Stat .Mask & linux .STATX_NLINK != 0 {
181- inode .nlink = atomicbitops .FromUint32 (ino .Stat .Nlink )
182- } else {
183- if ino .Stat .Mode & linux .FileTypeMask == linux .ModeDirectory {
184- inode .nlink = atomicbitops .FromUint32 (2 )
184+ if ino .Stat .Mask & linux .STATX_NLINK != 0 {
185+ inode .nlink = atomicbitops .FromUint32 (ino .Stat .Nlink )
185186 } else {
186- inode .nlink = atomicbitops .FromUint32 (1 )
187+ if isDir {
188+ inode .nlink = atomicbitops .FromUint32 (2 )
189+ } else {
190+ inode .nlink = atomicbitops .FromUint32 (1 )
191+ }
187192 }
188- }
189- return inode
190- })
193+ return inode
194+ })
191195
192196 temp .d .init ()
193197 fs .syncMu .Lock ()
0 commit comments