Add cp, folder mv, get -r, and unlock/lock to remember the key
Filesystem basics: mv now moves folders as well as files, and like mv(1) a NEW ending in / or naming an existing folder means into it. cp copies a file, or a folder with -r, by writing new records that share the original's objects, so it is instant and takes no space; rm already keeps objects another record still uses. New records go first, so an interrupted move leaves both names, never neither. get -r extracts a folder, keeping its structure. vault unlock [--for 5m] checks the passphrase and starts the binary again as a hidden __agent process, handing it the suite ID and seed over a pipe. The agent keeps them in mlocked memory, with core dumps off and (on Linux) PR_SET_DUMPABLE cleared, serves them on a Unix socket in a 0700 per-user directory whose owner is checked on every use, and wipes them and exits at a hard deadline or on vault lock. Commands ask it first unless VAULT_PASSPHRASE is set, and genkey stops any agent left for that path. Guide, man pages (new vault-cp, vault-unlock, vault-lock), README and screenshots updated; the guide's error table is split so it fits.
This commit is contained in:
@@ -586,23 +586,28 @@ func (s *store) removeAll(cat map[string][]*record, names []string, done func(na
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) rename(oldName, newName string) error {
|
||||
if !validName(newName) {
|
||||
return fmt.Errorf("invalid vault path %q", newName)
|
||||
// copyAll stores each pair's first name again under its second, sharing its
|
||||
// objects, then with move deletes the first names. New records go first, so
|
||||
// an interruption leaves a file under both names, never neither.
|
||||
func (s *store) copyAll(cat map[string][]*record, pairs [][2]string, move bool, done func(from, to string)) error {
|
||||
var old []string
|
||||
for _, p := range pairs {
|
||||
r := *cat[p[0]][0]
|
||||
r.Name = p[1]
|
||||
if !move {
|
||||
r.Added = time.Now().UTC()
|
||||
}
|
||||
if err := s.writeRecord(&r); err != nil {
|
||||
return err
|
||||
}
|
||||
cat[p[1]] = []*record{&r}
|
||||
old = append(old, p[0])
|
||||
done(p[0], p[1])
|
||||
}
|
||||
cat, r, err := s.lookup(oldName)
|
||||
if err != nil {
|
||||
return err
|
||||
if !move {
|
||||
return nil
|
||||
}
|
||||
if len(cat[newName]) > 0 {
|
||||
return fmt.Errorf("%s already exists", newName)
|
||||
}
|
||||
moved := *r
|
||||
moved.Name = newName
|
||||
if err := s.writeRecord(&moved); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.remove(cat, oldName, false)
|
||||
return s.removeAll(cat, old, func(string) {})
|
||||
}
|
||||
|
||||
// get decrypts name to out. Nothing appears at out until every object,
|
||||
@@ -612,6 +617,10 @@ func (s *store) get(name, out string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.getRecord(r, out)
|
||||
}
|
||||
|
||||
func (s *store) getRecord(r *record, out string) error {
|
||||
if _, err := os.Lstat(out); err == nil {
|
||||
return fmt.Errorf("%s already exists", out)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user