Not used by safcm but for other programs using safcm as library.
FormatFileUserGroup(x.New),
FormatFilePerm(x.New),
)
+ } else if x.Removed {
+ info = append(info,
+ ColorString(c.IsTTY, ColorRed, "removed"),
+ FormatFileType(x.Old),
+ FormatFileUserGroup(x.Old),
+ FormatFilePerm(x.Old),
+ )
} else {
if x.Old.Mode.Type() != x.New.Mode.Type() {
info = append(info, fmt.Sprintf("%s -> %s",
Gid: 2000,
},
},
+ {
+ Path: "removed: file",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: 0644,
+ User: "user",
+ Uid: 1000,
+ Group: "group",
+ Gid: 2000,
+ },
+ DataDiff: `@@ -1,2 +1 @@
+-content
+
+`,
+ },
+ {
+ Path: "removed: link",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeSymlink | 0777,
+ User: "user",
+ Uid: 1000,
+ Group: "group",
+ Gid: 2000,
+ },
+ DataDiff: `@@ -1 +1 @@
+-target
++
+`,
+ },
+ {
+ Path: "removed: directory",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeDir | 0755,
+ User: "user",
+ Uid: 1000,
+ Group: "group",
+ Gid: 2000,
+ },
+ },
{
Path: "type change: file -> dir",
Old: safcm.FileChangeInfo{
false,
false,
changes,
- `changed 9 file(s):
+ `changed 12 file(s):
"created: file": created, file, user(1000) group(2000), 0644
"created: link": created, symlink, user(1000) group(2000), 0777
+"removed: file": removed, file, user(1000) group(2000), 0644
+ @@ -1,2 +1 @@
+ -content
+
+"removed: link": removed, symlink, user(1000) group(2000), 0777
+ @@ -1 +1 @@
+ -target
+ +
+"removed: directory": removed, dir, user(1000) group(2000), 0755
"type change: file -> dir": file -> dir
@@ -1,2 +1 @@
-content
false,
true,
changes,
- "changed 9 file(s):\n\x1b[36m\"created: file\"\x1b[0m: \x1b[32mcreated\x1b[0m, file, user(1000) group(2000), 0644\n\x1b[36m\"created: link\"\x1b[0m: \x1b[32mcreated\x1b[0m, symlink, user(1000) group(2000), 0777\n\x1b[36m\"type change: file -> dir\"\x1b[0m: file -> dir\n @@ -1,2 +1 @@\n\x1b[31m -content\x1b[0m\n \n\x1b[36m\"user change\"\x1b[0m: user(1000) group(2000) -> user2(1001) group(2000)\n\x1b[36m\"group change\"\x1b[0m: user(1000) group(2000) -> user(1000) group2(2001)\n\x1b[36m\"mode change\"\x1b[0m: 0755 -> 0750\n\x1b[36m\"mode change (setuid)\"\x1b[0m: 0755 -> 04755\n\x1b[36m\"content change\"\x1b[0m:\n @@ -1,2 +1,2 @@\n\x1b[31m -old content\x1b[0m\n\x1b[32m +content\x1b[0m\n \n\x1b[36m\"multiple changes\"\x1b[0m: file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755\n @@ -1,2 +1 @@\n\x1b[31m -content\x1b[0m\n \n",
+ "changed 12 file(s):\n\x1b[36m\"created: file\"\x1b[0m: \x1b[32mcreated\x1b[0m, file, user(1000) group(2000), 0644\n\x1b[36m\"created: link\"\x1b[0m: \x1b[32mcreated\x1b[0m, symlink, user(1000) group(2000), 0777\n\x1b[36m\"removed: file\"\x1b[0m: \x1b[31mremoved\x1b[0m, file, user(1000) group(2000), 0644\n @@ -1,2 +1 @@\n\x1b[31m -content\x1b[0m\n \n\x1b[36m\"removed: link\"\x1b[0m: \x1b[31mremoved\x1b[0m, symlink, user(1000) group(2000), 0777\n @@ -1 +1 @@\n\x1b[31m -target\x1b[0m\n\x1b[32m +\x1b[0m\n\x1b[36m\"removed: directory\"\x1b[0m: \x1b[31mremoved\x1b[0m, dir, user(1000) group(2000), 0755\n\x1b[36m\"type change: file -> dir\"\x1b[0m: file -> dir\n @@ -1,2 +1 @@\n\x1b[31m -content\x1b[0m\n \n\x1b[36m\"user change\"\x1b[0m: user(1000) group(2000) -> user2(1001) group(2000)\n\x1b[36m\"group change\"\x1b[0m: user(1000) group(2000) -> user(1000) group2(2001)\n\x1b[36m\"mode change\"\x1b[0m: 0755 -> 0750\n\x1b[36m\"mode change (setuid)\"\x1b[0m: 0755 -> 04755\n\x1b[36m\"content change\"\x1b[0m:\n @@ -1,2 +1,2 @@\n\x1b[31m -old content\x1b[0m\n\x1b[32m +content\x1b[0m\n \n\x1b[36m\"multiple changes\"\x1b[0m: file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755\n @@ -1,2 +1 @@\n\x1b[31m -content\x1b[0m\n \n",
},
{
// only to places which are writable by the user which cannot be
// prevented.
- err := s.fileResolveIds(file)
- if err != nil {
- return err
- }
-
change := safcm.FileChange{
Path: file.Path,
- New: safcm.FileChangeInfo{
+ }
+ if file.Remove {
+ // Sanity check
+ if file.Data != nil {
+ return fmt.Errorf(".Remove needs .Data == nil: %v", file)
+ }
+ change.Removed = true
+ } else {
+ err := s.fileResolveIds(file)
+ if err != nil {
+ return err
+ }
+ change.New = safcm.FileChangeInfo{
Mode: file.Mode,
User: file.User,
Uid: file.Uid,
Group: file.Group,
Gid: file.Gid,
- },
+ }
}
debugf := func(format string, a ...interface{}) {
parentFd, baseName, err := OpenParentDirectoryNoSymlinks(file.Path)
if err != nil {
+ if os.IsNotExist(err) && file.Remove {
+ debugf("parent not present")
+ return nil
+ }
if os.IsNotExist(err) && s.req.DryRun {
change.Created = true
debugf("will create (parent missing)")
goto reopen
}
} else if os.IsNotExist(err) {
+ if file.Remove {
+ debugf("not present")
+ return nil
+ }
change.Created = true
debugf("will create")
} else {
// TODO: Add proper support for symlinks on BSD
change.Old.Mode |= 0777
}
- if change.Old.Mode != file.Mode {
+ if !file.Remove && change.Old.Mode != file.Mode {
if change.Old.Mode.Type() != file.Mode.Type() {
changeType = true
debugf("type differs %s -> %s",
// Compare user/group
change.Old.Uid = int(oldStat.Uid)
change.Old.Gid = int(oldStat.Gid)
- if change.Old.Uid != file.Uid || change.Old.Gid != file.Gid {
+ if !file.Remove &&
+ (change.Old.Uid != file.Uid || change.Old.Gid != file.Gid) {
changeUserOrGroup = true
debugf("uid/gid differs %d/%d -> %d/%d",
change.Old.Uid, change.Old.Gid,
}
oldData = buf[:n]
}
- if !changeType && file.Mode.Type() != fs.ModeDir {
+ if !file.Remove && !changeType && file.Mode.Type() != fs.ModeDir {
if !bytes.Equal(oldData, file.Data) {
changeData = true
debugf("content differs")
}
// No changes
- if !change.Created && !changeType &&
+ if !file.Remove && !change.Created && !changeType &&
!changePerm && !changeUserOrGroup &&
!changeData {
debugf("unchanged")
// the user knows exactly what was attempted.
s.resp.FileChanges = append(s.resp.FileChanges, change)
- if change.Created {
+ if file.Remove {
+ verbosef("removing")
+ } else if change.Created {
verbosef("creating")
} else {
verbosef("updating")
}
// We cannot rename over directories and vice versa
- if changeType && (change.Old.Mode.IsDir() || file.Mode.IsDir()) {
- debugf("removing (due to type change)")
+ if file.Remove ||
+ (changeType && (change.Old.Mode.IsDir() || file.Mode.IsDir())) {
+ if !file.Remove {
+ debugf("removing (due to type change)")
+ }
// In the past os.RemoveAll() was used here. However, this is
// difficult to implement manually with *at syscalls. To keep it
// simple only permit removing files and empty directories here. This
// also has the bonus of preventing data loss when (accidentally)
// replacing a directory tree with a file.
- const msg = "will not replace non-empty directory, " +
+ const msg1 = "will not remove non-empty directory, " +
+ "please remove manually"
+ const msg2 = "will not replace non-empty directory, " +
"please remove manually"
err := unix.Unlinkat(parentFd, baseName, 0 /* flags */)
if err != nil && !os.IsNotExist(err) {
if err2 == unix.ENOTDIR {
return err
} else if err2 == unix.ENOTEMPTY {
+ var msg string
+ if file.Remove {
+ msg = msg1
+ } else {
+ msg = msg2
+ }
return fmt.Errorf("%s", msg)
} else {
return err2
}
}
}
+ if file.Remove {
+ return nil
+ }
}
// Directory: create new directory, also type change to directory
nil,
},
+ {
+ "file: remove",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "file",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateFile("file", "content\n", 0644)
+ },
+ true,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "file",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: 0644,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ DataDiff: `@@ -1,2 +1 @@
+-content
+
+`,
+ },
+ },
+ },
+ []string{
+ `3: files: "file" (group): removing`,
+ },
+ nil,
+ },
+ {
+ "file: remove (dry-run)",
+ safcm.MsgSyncReq{
+ DryRun: true,
+ },
+ &safcm.File{
+ Path: "file",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateFile("file", "content\n", 0644)
+ },
+ true,
+ []ft.File{
+ root,
+ {
+ Path: "file",
+ Mode: 0644,
+ Data: []byte("content\n"),
+ },
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "file",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: 0644,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ DataDiff: `@@ -1,2 +1 @@
+-content
+
+`,
+ },
+ },
+ },
+ []string{
+ `3: files: "file" (group): removing`,
+ `4: files: "file" (group): dry-run, skipping changes`,
+ },
+ nil,
+ },
+ {
+ "file: remove (not present)",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "file",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ nil,
+ false,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{},
+ []string{
+ `4: files: "file" (group): not present`,
+ },
+ nil,
+ },
+ {
+ "file: remove (parent not present)",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "dir/file",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ nil,
+ false,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{},
+ []string{
+ `4: files: "dir/file" (group): parent not present`,
+ },
+ nil,
+ },
+ {
+ "file: remove (.Data set)",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "file",
+ Remove: true,
+ OrigGroup: "group",
+ Data: []byte("invalid"),
+ },
+ nil,
+ false,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{},
+ nil,
+ fmt.Errorf(".Remove needs .Data == nil: &{group file true ---------- 0 0 [105 110 118 97 108 105 100] []}"),
+ },
+
{
"file: permission",
safcm.MsgSyncReq{},
nil,
},
+ {
+ "symlink: remove",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "link",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateSymlink("link", "target")
+ },
+ true,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "link",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeSymlink | 0777,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ DataDiff: `@@ -1 +1 @@
+-target
++
+`,
+ },
+ },
+ },
+ []string{
+ `3: files: "link" (group): removing`,
+ },
+ nil,
+ },
+ {
+ "symlink: remove (dry-run)",
+ safcm.MsgSyncReq{
+ DryRun: true,
+ },
+ &safcm.File{
+ Path: "link",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateSymlink("link", "target")
+ },
+ true,
+ []ft.File{
+ root,
+ {
+ Path: "link",
+ Mode: fs.ModeSymlink | 0777,
+ Data: []byte("target"),
+ },
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "link",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeSymlink | 0777,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ DataDiff: `@@ -1 +1 @@
+-target
++
+`,
+ },
+ },
+ },
+ []string{
+ `3: files: "link" (group): removing`,
+ `4: files: "link" (group): dry-run, skipping changes`,
+ },
+ nil,
+ },
+
{
"symlink: content",
safcm.MsgSyncReq{},
nil,
},
+ {
+ "directory: remove",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "dir",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateDirectory("dir", 0755)
+ },
+ true,
+ []ft.File{
+ root,
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "dir",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeDir | 0755,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ },
+ },
+ },
+ []string{
+ `3: files: "dir" (group): removing`,
+ },
+ nil,
+ },
+ {
+ "directory: remove (dry-run)",
+ safcm.MsgSyncReq{
+ DryRun: true,
+ },
+ &safcm.File{
+ Path: "dir",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateDirectory("dir", 0755)
+ },
+ true,
+ []ft.File{
+ root,
+ {
+ Path: "dir",
+ Mode: fs.ModeDir | 0755,
+ },
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "dir",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeDir | 0755,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ },
+ },
+ },
+ []string{
+ `3: files: "dir" (group): removing`,
+ `4: files: "dir" (group): dry-run, skipping changes`,
+ },
+ nil,
+ },
+ {
+ "directory: remove (non-empty)",
+ safcm.MsgSyncReq{},
+ &safcm.File{
+ Path: "dir",
+ Remove: true,
+ OrigGroup: "group",
+ },
+ func() {
+ ft.CreateDirectory("dir", 0755)
+ ft.CreateFile("dir/file", "content\n", 0644)
+ },
+ true,
+ []ft.File{
+ root,
+ {
+ Path: "dir",
+ Mode: fs.ModeDir | 0755,
+ },
+ {
+ Path: "dir/file",
+ Mode: 0644,
+ Data: []byte("content\n"),
+ },
+ },
+ safcm.MsgSyncResp{
+ FileChanges: []safcm.FileChange{
+ {
+ Path: "dir",
+ Removed: true,
+ Old: safcm.FileChangeInfo{
+ Mode: fs.ModeDir | 0755,
+ User: user,
+ Uid: uid,
+ Group: group,
+ Gid: gid,
+ },
+ },
+ },
+ },
+ []string{
+ `3: files: "dir" (group): removing`,
+ },
+ fmt.Errorf("will not remove non-empty directory, please remove manually"),
+ },
+
{
"directory: permission",
safcm.MsgSyncReq{},
Path string
+ Remove bool
+
Mode fs.FileMode
User string
Uid int //lint:ignore ST1003 UID is too ugly
type FileChange struct {
Path string
Created bool
+ Removed bool
Old FileChangeInfo
New FileChangeInfo
DataDiff string