@@ -38,13 +38,15 @@ import (
3838)
3939
4040var (
41- // This map holds data about the state of the system's filesystems.
41+ // These maps hold data about the state of the system's filesystems.
4242 //
43- // It only contains one Mount per filesystem, even if there are
43+ // They only contain one Mount per filesystem, even if there are
4444 // additional bind mounts, since we want to store fscrypt metadata in
45- // only one place per filesystem. If it is ambiguous which Mount should
46- // be used, an explicit nil entry is stored.
45+ // only one place per filesystem. When it is ambiguous which Mount
46+ // should be used for a filesystem, mountsByDevice will contain an
47+ // explicit nil entry, and mountsByPath won't contain an entry.
4748 mountsByDevice map [DeviceNumber ]* Mount
49+ mountsByPath map [string ]* Mount
4850 // Used to make the mount functions thread safe
4951 mountMutex sync.Mutex
5052 // True if the maps have been successfully initialized.
@@ -197,18 +199,18 @@ func findMainMount(filesystemMounts []*Mount) *Mount {
197199 // since non-last mounts were already excluded earlier.
198200 //
199201 // Also build the set of all mounted subtrees.
200- mountsByPath := make (map [string ]* mountpointTreeNode )
202+ filesystemMountsByPath := make (map [string ]* mountpointTreeNode )
201203 allSubtrees := make (map [string ]bool )
202204 for _ , mnt := range filesystemMounts {
203- mountsByPath [mnt .Path ] = & mountpointTreeNode {mount : mnt }
205+ filesystemMountsByPath [mnt .Path ] = & mountpointTreeNode {mount : mnt }
204206 allSubtrees [mnt .Subtree ] = true
205207 }
206208
207209 // Divide the mounts into non-overlapping trees of mountpoints.
208- for path , mntNode := range mountsByPath {
210+ for path , mntNode := range filesystemMountsByPath {
209211 for path != "/" && mntNode .parent == nil {
210212 path = filepath .Dir (path )
211- if parent := mountsByPath [path ]; parent != nil {
213+ if parent := filesystemMountsByPath [path ]; parent != nil {
212214 mntNode .parent = parent
213215 parent .children = append (parent .children , mntNode )
214216 }
@@ -233,7 +235,7 @@ func findMainMount(filesystemMounts []*Mount) *Mount {
233235 // *all* mounted subtrees. Equivalently, select a mountpoint tree in
234236 // which every uncontained subtree is mounted.
235237 var mainMount * Mount
236- for _ , mntNode := range mountsByPath {
238+ for _ , mntNode := range filesystemMountsByPath {
237239 mnt := mntNode .mount
238240 if mntNode .parent != nil {
239241 continue
@@ -260,8 +262,10 @@ func findMainMount(filesystemMounts []*Mount) *Mount {
260262
261263// This is separate from loadMountInfo() only for unit testing.
262264func readMountInfo (r io.Reader ) error {
263- mountsByPath := make (map [string ]* Mount )
264265 mountsByDevice = make (map [DeviceNumber ]* Mount )
266+ mountsByPath = make (map [string ]* Mount )
267+ allMountsByDevice := make (map [DeviceNumber ][]* Mount )
268+ allMountsByPath := make (map [string ]* Mount )
265269
266270 scanner := bufio .NewScanner (r )
267271 for scanner .Scan () {
@@ -281,19 +285,22 @@ func readMountInfo(r io.Reader) error {
281285 // Note this overrides the info if we have seen the mountpoint
282286 // earlier in the file. This is correct behavior because the
283287 // mountpoints are listed in mount order.
284- mountsByPath [mnt .Path ] = mnt
288+ allMountsByPath [mnt .Path ] = mnt
285289 }
286290 // For each filesystem, choose a "main" Mount and discard any additional
287291 // bind mounts. fscrypt only cares about the main Mount, since it's
288- // where the fscrypt metadata is stored. Store all main Mounts in
289- // mountsByDevice so that they can be found by device number later.
290- allMountsByDevice := make (map [DeviceNumber ][]* Mount )
291- for _ , mnt := range mountsByPath {
292+ // where the fscrypt metadata is stored. Store all the main Mounts in
293+ // mountsByDevice and mountsByPath so that they can be found later.
294+ for _ , mnt := range allMountsByPath {
292295 allMountsByDevice [mnt .DeviceNumber ] =
293296 append (allMountsByDevice [mnt .DeviceNumber ], mnt )
294297 }
295298 for deviceNumber , filesystemMounts := range allMountsByDevice {
296- mountsByDevice [deviceNumber ] = findMainMount (filesystemMounts )
299+ mnt := findMainMount (filesystemMounts )
300+ mountsByDevice [deviceNumber ] = mnt // may store an explicit nil entry
301+ if mnt != nil {
302+ mountsByPath [mnt .Path ] = mnt
303+ }
297304 }
298305 return nil
299306}
@@ -329,11 +336,9 @@ func AllFilesystems() ([]*Mount, error) {
329336 return nil , err
330337 }
331338
332- mounts := make ([]* Mount , 0 , len (mountsByDevice ))
333- for _ , mount := range mountsByDevice {
334- if mount != nil {
335- mounts = append (mounts , mount )
336- }
339+ mounts := make ([]* Mount , 0 , len (mountsByPath ))
340+ for _ , mount := range mountsByPath {
341+ mounts = append (mounts , mount )
337342 }
338343
339344 sort .Sort (PathSorter (mounts ))
@@ -359,18 +364,38 @@ func FindMount(path string) (*Mount, error) {
359364 if err := loadMountInfo (); err != nil {
360365 return nil , err
361366 }
367+ // First try to find the mount by the number of the containing device.
362368 deviceNumber , err := getNumberOfContainingDevice (path )
363369 if err != nil {
364370 return nil , err
365371 }
366372 mnt , ok := mountsByDevice [deviceNumber ]
367- if ! ok {
368- return nil , errors .Errorf ("couldn't find mountpoint containing %q" , path )
373+ if ok {
374+ if mnt == nil {
375+ return nil , filesystemLacksMainMountError (deviceNumber )
376+ }
377+ return mnt , nil
378+ }
379+ // The mount couldn't be found by the number of the containing device.
380+ // Fall back to walking up the directory hierarchy and checking for a
381+ // mount at each directory path. This is necessary for btrfs, where
382+ // files report a different st_dev from the /proc/self/mountinfo entry.
383+ curPath , err := canonicalizePath (path )
384+ if err != nil {
385+ return nil , err
369386 }
370- if mnt == nil {
371- return nil , filesystemLacksMainMountError (deviceNumber )
387+ for {
388+ mnt := mountsByPath [curPath ]
389+ if mnt != nil {
390+ return mnt , nil
391+ }
392+ // Move to the parent directory unless we have reached the root.
393+ parent := filepath .Dir (curPath )
394+ if parent == curPath {
395+ return nil , errors .Errorf ("couldn't find mountpoint containing %q" , path )
396+ }
397+ curPath = parent
372398 }
373- return mnt , nil
374399}
375400
376401// GetMount is like FindMount, except GetMount also returns an error if the path
@@ -520,12 +545,19 @@ func (mnt *Mount) getFilesystemUUID() (string, error) {
520545}
521546
522547// makeLink creates the contents of a link file which will point to the given
523- // filesystem. This will be a string of the form "UUID=<uuid>\nPATH=<path>\n".
524- // An error is returned if the filesystem's UUID cannot be determined.
548+ // filesystem. This will normally be a string of the form
549+ // "UUID=<uuid>\nPATH=<path>\n". If the UUID cannot be determined, the UUID
550+ // portion will be omitted.
525551func makeLink (mnt * Mount ) (string , error ) {
526552 uuid , err := mnt .getFilesystemUUID ()
527553 if err != nil {
528- return "" , & ErrMakeLink {mnt , err }
554+ // The UUID could not be determined. This happens for btrfs
555+ // filesystems, as the device number found via
556+ // /dev/disk/by-uuid/* for btrfs filesystems differs from the
557+ // actual device number of the mounted filesystem. Just rely
558+ // entirely on the fallback to mountpoint path.
559+ log .Print (err )
560+ return fmt .Sprintf ("%s=%s\n " , pathToken , mnt .Path ), nil
529561 }
530562 return fmt .Sprintf ("%s=%s\n %s=%s\n " , uuidToken , uuid , pathToken , mnt .Path ), nil
531563}
0 commit comments