func (constor *Constor) Symlink(header *fuse.InHeader, pointedTo string, linkName string, out *fuse.EntryOut) (code fuse.Status) { inode := constor.inodemap.findInodePtr(header.NodeId) if inode == nil { constor.error("inode == nil") return fuse.ENOENT } err := constor.copyup(inode) if err != nil { constor.error("copyup failed for %s - %s", inode.id, err) return fuse.ToStatus(err) } dirpath := constor.getPath(0, inode.id) entrypath := Path.Join(dirpath, linkName) constor.log("%s <- %s/%s", pointedTo, inode.id, linkName) syscall.Unlink(entrypath) // remove a deleted entry err = syscall.Symlink(pointedTo, entrypath) if err != nil { constor.error("Symlink failed %s <- %s : %s", pointedTo, entrypath, err) return fuse.ToStatus(err) } id := constor.setid(entrypath, "") if id == "" { constor.error("setid failed on %s", entrypath) return fuse.ENOENT } if err := constor.createPath(id); err != nil { constor.error("createPath failed on %s : %s", id, err) return fuse.ToStatus(err) } path := constor.getPath(0, id) err = syscall.Symlink(pointedTo, path) if err != nil { constor.error("Symlink failed %s <- %s : %s", pointedTo, path, err) return fuse.ToStatus(err) } err = syscall.Lchown(path, int(header.Uid), int(header.Gid)) if err != nil { constor.error("Chown failed on %s : %s", path, err) return fuse.ToStatus(err) } return constor.Lookup(header, linkName, out) }
func (constor *Constor) Symlink(header *fuse.InHeader, pointedTo string, linkName string, out *fuse.EntryOut) (code fuse.Status) { constor.log("%d %s <- %s, uid: %d, gid: %d", header.NodeId, pointedTo, linkName, header.Uid, header.Gid) parentino := header.NodeId path, err := constor.dentrymap.getPath(parentino) if err != nil { return fuse.ToStatus(err) } if err = constor.createPath(path); err != nil { return fuse.ToStatus(err) } pathl := Path.Join(constor.layers[0], path, linkName) syscall.Unlink(pathl) // remove a deleted entry err = syscall.Symlink(pointedTo, pathl) if err != nil { return fuse.ToStatus(err) } err = syscall.Lchown(pathl, int(header.Uid), int(header.Gid)) if err != nil { return fuse.ToStatus(err) } return constor.Lookup(header, linkName, out) }
// ApplyLayer parses a diff in the standard layer format from `layer`, and // applies it to the directory `dest`. func ApplyLayer(dest string, layer Archive) error { // We need to be able to set any perms oldmask := syscall.Umask(0) defer syscall.Umask(oldmask) layer, err := DecompressStream(layer) if err != nil { return err } tr := tar.NewReader(layer) var dirs []*tar.Header // Iterate through the files in the archive. for { hdr, err := tr.Next() if err == io.EOF { // end of tar archive break } if err != nil { return err } // Normalize name, for safety and for a simple is-root check hdr.Name = filepath.Clean(hdr.Name) if !strings.HasSuffix(hdr.Name, "/") { // Not the root directory, ensure that the parent directory exists // This happened in some tests where an image had a tarfile without any // parent directories parent := filepath.Dir(hdr.Name) parentPath := filepath.Join(dest, parent) if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { err = os.MkdirAll(parentPath, 600) if err != nil { return err } } } // Skip AUFS metadata dirs if strings.HasPrefix(hdr.Name, ".wh..wh.") { continue } path := filepath.Join(dest, hdr.Name) base := filepath.Base(path) if strings.HasPrefix(base, ".wh.") { originalBase := base[len(".wh."):] originalPath := filepath.Join(filepath.Dir(path), originalBase) if err := os.RemoveAll(originalPath); err != nil { return err } } else { // If path exits we almost always just want to remove and replace it // The only exception is when it is a directory *and* the file from // the layer is also a directory. Then we want to merge them (i.e. // just apply the metadata from the layer). hasDir := false if fi, err := os.Lstat(path); err == nil { if fi.IsDir() && hdr.Typeflag == tar.TypeDir { hasDir = true } else { if err := os.RemoveAll(path); err != nil { return err } } } switch hdr.Typeflag { case tar.TypeDir: if !hasDir { err = os.Mkdir(path, os.FileMode(hdr.Mode)) if err != nil { return err } } dirs = append(dirs, hdr) case tar.TypeReg, tar.TypeRegA: // Source is regular file file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode)) if err != nil { return err } if _, err := io.Copy(file, tr); err != nil { file.Close() return err } file.Close() case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: mode := uint32(hdr.Mode & 07777) switch hdr.Typeflag { case tar.TypeBlock: mode |= syscall.S_IFBLK case tar.TypeChar: mode |= syscall.S_IFCHR case tar.TypeFifo: mode |= syscall.S_IFIFO } if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil { return err } case tar.TypeLink: if err := os.Link(filepath.Join(dest, hdr.Linkname), path); err != nil { return err } case tar.TypeSymlink: if err := os.Symlink(hdr.Linkname, path); err != nil { return err } default: utils.Debugf("unhandled type %d\n", hdr.Typeflag) } if err = syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil { return err } // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if hdr.Typeflag != tar.TypeSymlink { err = syscall.Chmod(path, uint32(hdr.Mode&07777)) if err != nil { return err } } // Directories must be handled at the end to avoid further // file creation in them to modify the mtime if hdr.Typeflag != tar.TypeDir { ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and if hdr.Typeflag != tar.TypeSymlink { if err := syscall.UtimesNano(path, ts); err != nil { return err } } else { if err := LUtimesNano(path, ts); err != nil { return err } } } } } for _, hdr := range dirs { path := filepath.Join(dest, hdr.Name) ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} if err := syscall.UtimesNano(path, ts); err != nil { return err } } return nil }
// Lchown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link itself. // If there is an error, it will be of type *PathError. func Lchown(name string, uid, gid int) error { if e := syscall.Lchown(name, uid, gid); e != nil { return &PathError{"lchown", name, e} } return nil }
// Lchown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link itself. func Lchown(name string, uid, gid int) Error { if e := syscall.Lchown(name, uid, gid); e != 0 { return &PathError{"lchown", name, Errno(e)} } return nil }
func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error { switch hdr.Typeflag { case tar.TypeDir: // Create directory unless it exists as a directory already. // In that case we just want to merge the two if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) { if err := os.Mkdir(path, os.FileMode(hdr.Mode)); err != nil { return err } } case tar.TypeReg, tar.TypeRegA: // Source is regular file file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode)) if err != nil { return err } if _, err := io.Copy(file, reader); err != nil { file.Close() return err } file.Close() case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: mode := uint32(hdr.Mode & 07777) switch hdr.Typeflag { case tar.TypeBlock: mode |= syscall.S_IFBLK case tar.TypeChar: mode |= syscall.S_IFCHR case tar.TypeFifo: mode |= syscall.S_IFIFO } if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil { return err } case tar.TypeLink: if err := os.Link(filepath.Join(extractDir, hdr.Linkname), path); err != nil { return err } case tar.TypeSymlink: if err := os.Symlink(hdr.Linkname, path); err != nil { return err } case tar.TypeXGlobalHeader: utils.Debugf("PAX Global Extended Headers found and ignored") return nil default: return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag) } if err := syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil { return err } // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if hdr.Typeflag != tar.TypeSymlink { if err := syscall.Chmod(path, uint32(hdr.Mode&07777)); err != nil { return err } } ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and if hdr.Typeflag != tar.TypeSymlink { if err := syscall.UtimesNano(path, ts); err != nil { return err } } else { if err := LUtimesNano(path, ts); err != nil { return err } } return nil }
func (constor *Constor) copyup(inode *Inode) error { src, err := constor.getPath(inode.ino) if err != nil { return err } dst, err := constor.dentrymap.getPath(inode.ino) if err != nil { return err } err = constor.createPath(Path.Dir(dst)) if err != nil { return err } dst = Path.Join(constor.layers[0], dst) fi, err := os.Lstat(src) if err != nil { return err } if fi.Mode()&os.ModeSymlink == os.ModeSymlink { linkName, err := os.Readlink(src) if err != nil { return err } err = os.Symlink(linkName, dst) if err != nil { return err } } else if fi.Mode()&os.ModeDir == os.ModeDir { err := os.Mkdir(dst, fi.Mode()) if err != nil { return err } } else { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer out.Close() _, err = io.Copy(out, in) if err != nil { return err } err = out.Close() if err != nil { return err } } stat := syscall.Stat_t{} if err = syscall.Lstat(src, &stat); err != nil { return err } if fi.Mode()&os.ModeSymlink != os.ModeSymlink { if err = syscall.Chmod(dst, stat.Mode); err != nil { return err } } if err = syscall.Lchown(dst, int(stat.Uid), int(stat.Gid)); err != nil { return err } if fi.Mode()&os.ModeSymlink != os.ModeSymlink { if err = syscall.UtimesNano(dst, []syscall.Timespec{stat.Atim, stat.Mtim}); err != nil { return err } } inoitoa := strconv.Itoa(int(stat.Ino)) inobyte := []byte(inoitoa) // if err = syscall.Setxattr(dst, INOXATTR, inobyte, 0); err != nil { // return err // } if err = Lsetxattr(dst, INOXATTR, inobyte, 0); err != nil { return err } inode.layer = 0 path, err := constor.dentrymap.getPath(inode.ino) constor.log("ino %d file %s", inode.ino, path) return nil }
func (k *PosixKernel) Lchown(path string, uid, gid int) uint64 { return Errno(syscall.Lchown(path, uid, gid)) }
func (constor *Constor) copyup(inode *Inode) error { constor.log("%s", inode.id) if inode.layer == 0 { return nil } src := constor.getPath(inode.layer, inode.id) if src == "" { return syscall.EIO } dst := constor.getPath(0, inode.id) if dst == "" { return syscall.EIO } fi, err := os.Lstat(src) if err != nil { return err } if fi.Mode()&os.ModeSymlink == os.ModeSymlink { linkName, err := os.Readlink(src) if err != nil { return err } err = os.Symlink(linkName, dst) if err != nil { return err } } else if fi.Mode()&os.ModeDir == os.ModeDir { err := os.Mkdir(dst, fi.Mode()) if err != nil { return err } } else { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer out.Close() _, err = io.Copy(out, in) if err != nil { return err } err = out.Close() if err != nil { return err } } stat := syscall.Stat_t{} if err = syscall.Lstat(src, &stat); err != nil { return err } if fi.Mode()&os.ModeSymlink != os.ModeSymlink { if err = syscall.Chmod(dst, stat.Mode); err != nil { return err } } if err = syscall.Lchown(dst, int(stat.Uid), int(stat.Gid)); err != nil { return err } links, err := Lgetxattr(src, LINKSXATTR) if err == nil && len(links) > 0 { err := Lsetxattr(dst, LINKSXATTR, links, 0) if err != nil { return err } } if fi.Mode()&os.ModeSymlink != os.ModeSymlink { if err = syscall.UtimesNano(dst, []syscall.Timespec{stat.Atim, stat.Mtim}); err != nil { return err } } inode.layer = 0 constor.log("done", inode.id) return nil }
func (constor *Constor) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status { var err error uid := -1 gid := -1 inode := constor.inodemap.findInodePtr(input.NodeId) if inode == nil { constor.error("inode nil") return fuse.EIO } constor.log("%s %d", inode.id, input.Valid) // if ((input.Valid & fuse.FATTR_FH) !=0) && ((input.Valid & (fuse.FATTR_ATIME | fuse.FATTR_MTIME)) == 0) { if ((input.Valid & fuse.FATTR_FH) != 0) && ((input.Valid & fuse.FATTR_SIZE) != 0) { ptr := uintptr(input.Fh) F := constor.getfd(ptr) if F == nil { constor.error("F == nil for %s", inode.id) return fuse.EIO } if F.layer != 0 && inode.layer == -1 { /* FIXME handle this valid case */ // file is in lower layer, opened, deleted, setattr-called constor.error("FSetAttr F.layer=%d inode.layer=%d", F.layer, inode.layer) return fuse.EIO } if F.layer != 0 && inode.layer != 0 { err := constor.copyup(inode) if err != nil { constor.error("copyup failed for %s - %s", inode.id, err) return fuse.ToStatus(err) } path := constor.getPath(0, inode.id) syscall.Close(F.fd) fd, err := syscall.Open(path, F.flags, 0) if err != nil { constor.error("open failed on %s - %s", path, err) return fuse.ToStatus(err) } F.fd = fd F.layer = 0 constor.log("reset fd for %s", path) } else if F.layer != 0 && inode.layer == 0 { // when some other process already has done a copyup syscall.Close(F.fd) path := constor.getPath(0, inode.id) fd, err := syscall.Open(path, F.flags, 0) if err != nil { constor.error("open failed on %s - %s", path, err) return fuse.ToStatus(err) } F.fd = fd F.layer = 0 constor.log("reset fd for %s", path) } if F.layer != 0 { constor.error("layer not 0") return fuse.EIO } if input.Valid&fuse.FATTR_MODE != 0 { permissions := uint32(07777) & input.Mode err = syscall.Fchmod(F.fd, permissions) if err != nil { constor.error("Fchmod failed on %s - %d : %s", F.id, permissions, err) return fuse.ToStatus(err) } } if input.Valid&(fuse.FATTR_UID) != 0 { uid = int(input.Uid) } if input.Valid&(fuse.FATTR_GID) != 0 { gid = int(input.Gid) } if input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0 { err = syscall.Fchown(F.fd, uid, gid) if err != nil { constor.error("Fchown failed on %s - %d %d : %s", F.id, uid, gid, err) return fuse.ToStatus(err) } } if input.Valid&fuse.FATTR_SIZE != 0 { err := syscall.Ftruncate(F.fd, int64(input.Size)) if err != nil { constor.error("Ftruncate failed on %s - %d : %s", F.id, input.Size, err) return fuse.ToStatus(err) } } if input.Valid&(fuse.FATTR_ATIME|fuse.FATTR_MTIME|fuse.FATTR_ATIME_NOW|fuse.FATTR_MTIME_NOW) != 0 { now := time.Now() var tv []syscall.Timeval tv = make([]syscall.Timeval, 2) if input.Valid&fuse.FATTR_ATIME_NOW != 0 { tv[0].Sec = now.Unix() tv[0].Usec = now.UnixNano() / 1000 } else { tv[0].Sec = int64(input.Atime) tv[0].Usec = int64(input.Atimensec / 1000) } if input.Valid&fuse.FATTR_MTIME_NOW != 0 { tv[1].Sec = now.Unix() tv[1].Usec = now.UnixNano() / 1000 } else { tv[1].Sec = int64(input.Atime) tv[1].Usec = int64(input.Atimensec / 1000) } err := syscall.Futimes(F.fd, tv) if err != nil { constor.error("Futimes failed on %s : %s", F.id, err) return fuse.ToStatus(err) } } stat := syscall.Stat_t{} err = syscall.Fstat(F.fd, &stat) if err != nil { constor.error("Fstat failed on %s : %s", F.id, err) return fuse.ToStatus(err) } attr := (*fuse.Attr)(&out.Attr) attr.FromStat(&stat) attr.Ino = idtoino(inode.id) return fuse.OK } if inode.layer == -1 { return fuse.ENOENT } if inode.layer != 0 { err = constor.copyup(inode) if err != nil { constor.error("copyup failed for %s - %s", inode.id, err) return fuse.ToStatus(err) } } stat := syscall.Stat_t{} path := constor.getPath(0, inode.id) // just to satisfy PJD tests if input.Valid == 0 { err = syscall.Lchown(path, uid, gid) if err != nil { return fuse.ToStatus(err) } } if input.Valid&fuse.FATTR_MODE != 0 { permissions := uint32(07777) & input.Mode err = syscall.Chmod(path, permissions) if err != nil { constor.error("Lchmod failed on %s - %d : %s", path, permissions, err) return fuse.ToStatus(err) } } if input.Valid&(fuse.FATTR_UID) != 0 { uid = int(input.Uid) } if input.Valid&(fuse.FATTR_GID) != 0 { gid = int(input.Gid) } if input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0 { constor.log("%s %d %d", path, uid, gid) err = syscall.Lchown(path, uid, gid) if err != nil { constor.error("Lchown failed on %s - %d %d : %s", path, uid, gid, err) return fuse.ToStatus(err) } } if input.Valid&fuse.FATTR_SIZE != 0 { err = syscall.Truncate(path, int64(input.Size)) if err != nil { constor.error("Truncate failed on %s - %d : %s", path, input.Size, err) return fuse.ToStatus(err) } } if input.Valid&(fuse.FATTR_ATIME|fuse.FATTR_MTIME|fuse.FATTR_ATIME_NOW|fuse.FATTR_MTIME_NOW) != 0 { now := time.Now() var atime *time.Time var mtime *time.Time if input.Valid&fuse.FATTR_ATIME_NOW != 0 { atime = &now } else { t := time.Unix(int64(input.Atime), int64(input.Atimensec)) atime = &t } if input.Valid&fuse.FATTR_MTIME_NOW != 0 { mtime = &now } else { t := time.Unix(int64(input.Mtime), int64(input.Mtimensec)) mtime = &t } fi, err := os.Lstat(path) if err != nil { return fuse.ToStatus(err) } if fi.Mode()&os.ModeSymlink != os.ModeSymlink { // FIXME: there is no Lchtimes err = os.Chtimes(path, *atime, *mtime) if err != nil { constor.error("Chtimes failed on %s : %s", path, err) return fuse.ToStatus(err) } } else { constor.error("Chtimes on Symlink not supported") } } attr := (*fuse.Attr)(&out.Attr) err = constor.Lstat(inode.layer, inode.id, &stat) if err != nil { constor.error("Lstat failed on %s : %s", inode.id, err) return fuse.ToStatus(err) } attr.FromStat(&stat) attr.Ino = stat.Ino return fuse.ToStatus(err) }