blob: 64b10c2d0033ef257a23012f2d2d14a683781e7e [file] [log] [blame]
// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(adg): How to handle partially copied trees?
// TODO(adg): Do permissions checking up front?
package main
import (
"upspin.io/errors"
"upspin.io/path"
"upspin.io/upspin"
)
// copy recursively copies the specified source paths to the given destination.
// It uses Client.PutDuplicate to copy files, so file content is not copied;
// the underlying DirBlocks do not change.
func (s *server) copy(dst upspin.PathName, srcs []upspin.PathName) error {
// Check that the destination exists and is a directory.
dstEntry, err := s.cli.Lookup(dst, true)
if err != nil {
return err
}
if !dstEntry.IsDir() {
return errors.E(dst, errors.NotDir)
}
// Iterate through sources and copy them recursively.
for _, src := range srcs {
// Lookup src, but don't follow links.
// We will make a copy of those links, not traverse them.
srcEntry, err := s.cli.Lookup(src, false)
if err != nil {
return err
}
if err := s.copyEntry(dst, srcEntry); err != nil {
return err
}
}
return nil
}
// copyEntry copies the given entry to the given destination directory.
// If the entry is a directory then the directory is copied recursively.
// If the entry is a link then an equivalent link is created in dstDir.
// This function assuems that dstDir exists and is a directory.
func (s *server) copyEntry(dstDir upspin.PathName, srcEntry *upspin.DirEntry) error {
srcPath, err := path.Parse(srcEntry.Name)
if err != nil {
return err
}
if srcPath.NElem() == 0 {
// The browser user interface doesn't allow you to select a
// root for a copy, so this shouldn't come up in practice.
return errors.E(srcEntry.Name, errors.Str("cannot copy a root"))
}
dstDirPath, _ := path.Parse(dstDir)
if dstDirPath.HasPrefix(srcPath) {
return errors.E(srcEntry.Name, errors.Str("cannot copy a directory into one of its sub-directories"))
}
dst := path.Join(dstDir, srcPath.Elem(srcPath.NElem()-1))
switch {
case srcEntry.IsDir():
// Recur into directories.
if _, err := s.cli.MakeDirectory(dst); err != nil {
return err
}
dir, err := s.cli.DirServer(srcEntry.Name)
if err != nil {
return err
}
des, err := dir.Glob(upspin.AllFilesGlob(srcEntry.Name))
if err != nil && err != upspin.ErrFollowLink {
return err
}
for _, de := range des {
if err := s.copyEntry(dst, de); err != nil {
return err
}
}
case srcEntry.IsLink():
if _, err := s.cli.PutLink(srcEntry.Link, dst); err != nil {
return err
}
default:
if _, err := s.cli.PutDuplicate(srcEntry.Name, dst); err != nil {
return err
}
}
return nil
}