How to find the mountpoint a file resides on?

You may either call the mount command and parse its output to find the longest common prefix with your path, or use the stat system call to get the device a file resides on and go up the tree until you get to a different device.

In Python, stat may be used as follows (untested and may have to be extended to handle symlinks and exotic stuff like union mounts):

def find_mount_point(path):
    path = os.path.abspath(path)
    orig_dev = os.stat(path).st_dev

    while path != '/':
        dir = os.path.dirname(path)
        if os.stat(dir).st_dev != orig_dev:
            # we crossed the device border
            break
        path = dir
    return path

Edit: I didn't know about os.path.ismount until just now. This simplifies things greatly.

def find_mount_point(path):
    path = os.path.abspath(path)
    while not os.path.ismount(path):
        path = os.path.dirname(path)
    return path

Since python is not a requirement:

df "$filename" | awk 'NR==1 {next} {print $6; exit}'

The NR==1 {next} is to skip the header line that df outputs. $6 is the mount point. exit is to make sure we output only one line.


Since nowadays we can't really reliably parse the contents of mount in systems where a filesystem was mounted by UUID or LABEL, as the output can contain something like:

(...)
/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000 on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
(...)

we need a more robust solution (e.g., think about what "chopping" parts of a path like the above may lead to, and if we would want something like that).

One such solution (which, by the way, tries not to reinvent the wheel) is to simply use the stat command to discover the mountpoint where a file resides, as in:

$ stat --printf "%h:%m:%i\n" Talks
6:/media/lattes:461246

In the output above, we can see that:

  • the number of hardlinks (%h) in Talks is 6
  • the mountpoint (%m) is /media/lattes
  • its inode number (%i) is 461246.

Just for the record, this is with the version of stat from GNU coreutils, which means that some other versions (e.g., the BSDs) may not have it by default (but you can always install it with your preferred package manager).


@larsmans Very good answer, this was very helpfull! I have implemented this in Golang where I needed it.

For people who are interested in the code (this has been tested for OS X and Linux):

package main

import (
    "os"
    "fmt"
    "syscall"
    "path/filepath"
)

func Mountpoint(path string) string {
    pi, err := os.Stat(path)
    if err != nil {
        return ""
    }

    odev := pi.Sys().(*syscall.Stat_t).Dev

    for path != "/" {
        _path := filepath.Dir(path)

        in, err := os.Stat(_path)
        if err != nil {
            return ""
        }

        if odev != in.Sys().(*syscall.Stat_t).Dev {
            break
        }

        path = _path
    }

    return path
}

func main() {
    path, _ := filepath.Abs("./")
    dir := filepath.Dir(path)
    fmt.Println("Path", path)
    fmt.Println("Dir", dir)
    fmt.Println("Mountpoint", Mountpoint(path))
}