// pivotRoot will call pivot_root such that rootfs becomes the new root // filesystem, and everything else is cleaned up. func pivotRoot(rootfs string) error { // While the documentation may claim otherwise, pivot_root(".", ".") is // actually valid. What this results in is / being the new root but // /proc/self/cwd being the old root. Since we can play around with the cwd // with pivot_root this allows us to pivot without creating directories in // the rootfs. Shout-outs to the LXC developers for giving us this idea. oldroot, err := syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0) if err != nil { return err } defer syscall.Close(oldroot) newroot, err := syscall.Open(rootfs, syscall.O_DIRECTORY|syscall.O_RDONLY, 0) if err != nil { return err } defer syscall.Close(newroot) // Change to the new root so that the pivot_root actually acts on it. if err := syscall.Fchdir(newroot); err != nil { return err } if err := syscall.PivotRoot(".", "."); err != nil { return fmt.Errorf("pivot_root %s", err) } // Currently our "." is oldroot (according to the current kernel code). // However, purely for safety, we will fchdir(oldroot) since there isn't // really any guarantee from the kernel what /proc/self/cwd will be after a // pivot_root(2). if err := syscall.Fchdir(oldroot); err != nil { return err } // Make oldroot rprivate to make sure our unmounts don't propagate to the // host (and thus bork the machine). if err := syscall.Mount("", ".", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil { return err } // Preform the unmount. MNT_DETACH allows us to unmount /proc/self/cwd. if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil { return err } // Switch back to our shiny new root. if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %s", err) } return nil }
// Chdir changes the current working directory to the file, // which must be a directory. // If there is an error, it will be of type *PathError. func (f *File) Chdir() error { if f == nil { return ErrInvalid } if e := syscall.Fchdir(f.fd); e != nil { return &PathError{"chdir", f.name, e} } return nil }
// Chdir changes the current working directory to the file, // which must be a directory. // If there is an error, it will be of type *PathError. func (f *File) Chdir() error { if err := f.checkValid("chdir"); err != nil { return err } if e := syscall.Fchdir(f.fd); e != nil { return &PathError{"chdir", f.name, e} } return nil }
// dirfdAbs transforms the dirfd-relative "path" to an absolute one. If the // path is not already absolute, this function will change the working // directory. The caller has to chdir back. func dirfdAbs(dirfd int, path string) (absPath string, err error) { if filepath.IsAbs(path) { return path, nil } err = syscall.Fchdir(dirfd) if err != nil { return "", err } wd, err := os.Getwd() if err != nil { return "", err } return filepath.Join(wd, path), nil }
// Poor man's Openat func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { chdirMutex.Lock() defer chdirMutex.Unlock() if !filepath.IsAbs(path) { // Save the old working directory oldWd, err := os.Getwd() if err != nil { return -1, err } // Chdir to target directory err = syscall.Fchdir(dirfd) if err != nil { return -1, err } // Chdir back at the end defer os.Chdir(oldWd) } return syscall.Open(path, flags, mode) }
// Chdir changes the current working directory to the file, // which must be a directory. func (f *File) Chdir() Error { if e := syscall.Fchdir(f.fd); e != 0 { return &PathError{"chdir", f.name, Errno(e)} } return nil }