| 222 | } |
| 223 | |
| 224 | func TestBashRecreateDirectory(t *testing.T) { |
| 225 | // macOS `stat` takes different arguments than BusyBox and GNU coreutils. |
| 226 | if output, err := exec.CommandContext(t.Context(), "stat", "--help").CombinedOutput(); err != nil { |
| 227 | t.Skip(`requires "stat" executable`) |
| 228 | } else if !strings.Contains(string(output), "%a") { |
| 229 | t.Skip(`requires "stat" with access format sequence`) |
| 230 | } |
| 231 | |
| 232 | dir := t.TempDir() |
| 233 | assert.NilError(t, os.Mkdir(filepath.Join(dir, "d"), 0o755)) |
| 234 | assert.NilError(t, os.WriteFile(filepath.Join(dir, "d", ".hidden"), nil, 0o644)) // #nosec G306 OK permissions for a temp dir in a test |
| 235 | assert.NilError(t, os.WriteFile(filepath.Join(dir, "d", "file"), nil, 0o644)) // #nosec G306 OK permissions for a temp dir in a test |
| 236 | |
| 237 | stat := func(args ...string) string { |
| 238 | cmd := exec.CommandContext(t.Context(), "stat", "-c", "%i %#a %N") |
| 239 | cmd.Args = append(cmd.Args, args...) |
| 240 | out, err := cmd.CombinedOutput() |
| 241 | |
| 242 | t.Helper() |
| 243 | assert.NilError(t, err, string(out)) |
| 244 | return string(out) |
| 245 | } |
| 246 | |
| 247 | var before, after struct{ d, f, dInode, dPerms string } |
| 248 | |
| 249 | before.d = stat(filepath.Join(dir, "d")) |
| 250 | before.f = stat( |
| 251 | filepath.Join(dir, "d", ".hidden"), |
| 252 | filepath.Join(dir, "d", "file"), |
| 253 | ) |
| 254 | |
| 255 | cmd := exec.CommandContext(t.Context(), "bash") |
| 256 | cmd.Args = append(cmd.Args, "-ceu", "--", |
| 257 | bashRecreateDirectory+` recreate "$@"`, "-", |
| 258 | filepath.Join(dir, "d"), "0740") |
| 259 | // The assertion below expects alphabetically sorted filenames. |
| 260 | // Set an empty environment to always use the default/standard locale. |
| 261 | cmd.Env = []string{ |
| 262 | // Preserve the path to find bash tools (i.e., mktemp) |
| 263 | "PATH=" + os.Getenv("PATH"), |
| 264 | } |
| 265 | output, err := cmd.CombinedOutput() |
| 266 | assert.NilError(t, err, string(output)) |
| 267 | assert.Assert(t, cmp.Regexp(`^`+ |
| 268 | `[+] chmod 0740 [^ ]+/tmp.[^ /]+\n`+ |
| 269 | `[+] mv [^ ]+/d/.hidden [^ ]+/d/file [^ ]+/tmp.[^ /]+\n`+ |
| 270 | `[+] rmdir [^ ]+/d\n`+ |
| 271 | `[+] mv [^ ]+/tmp.[^ /]+ [^ ]+/d\n`+ |
| 272 | `$`, string(output))) |
| 273 | |
| 274 | after.d = stat(filepath.Join(dir, "d")) |
| 275 | after.f = stat( |
| 276 | filepath.Join(dir, "d", ".hidden"), |
| 277 | filepath.Join(dir, "d", "file"), |
| 278 | ) |
| 279 | |
| 280 | _, err = fmt.Sscan(before.d, &before.dInode, &before.dPerms) |
| 281 | assert.NilError(t, err) |