| 3393 | } |
| 3394 | |
| 3395 | static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up) |
| 3396 | { |
| 3397 | DIR *dir; |
| 3398 | struct dirent *e; |
| 3399 | int ret = 0, original_len = path->len, len, kept_down = 0; |
| 3400 | int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY); |
| 3401 | int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL); |
| 3402 | int purge_original_cwd = (flag & REMOVE_DIR_PURGE_ORIGINAL_CWD); |
| 3403 | struct object_id submodule_head; |
| 3404 | |
| 3405 | if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) && |
| 3406 | !repo_resolve_gitlink_ref(the_repository, path->buf, |
| 3407 | "HEAD", &submodule_head)) { |
| 3408 | /* Do not descend and nuke a nested git work tree. */ |
| 3409 | if (kept_up) |
| 3410 | *kept_up = 1; |
| 3411 | return 0; |
| 3412 | } |
| 3413 | |
| 3414 | flag &= ~REMOVE_DIR_KEEP_TOPLEVEL; |
| 3415 | dir = opendir(path->buf); |
| 3416 | if (!dir) { |
| 3417 | if (errno == ENOENT) |
| 3418 | return keep_toplevel ? -1 : 0; |
| 3419 | else if (errno == EACCES && !keep_toplevel) |
| 3420 | /* |
| 3421 | * An empty dir could be removable even if it |
| 3422 | * is unreadable: |
| 3423 | */ |
| 3424 | return rmdir(path->buf); |
| 3425 | else |
| 3426 | return -1; |
| 3427 | } |
| 3428 | strbuf_complete(path, '/'); |
| 3429 | |
| 3430 | len = path->len; |
| 3431 | while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) { |
| 3432 | struct stat st; |
| 3433 | |
| 3434 | strbuf_setlen(path, len); |
| 3435 | strbuf_addstr(path, e->d_name); |
| 3436 | if (lstat(path->buf, &st)) { |
| 3437 | if (errno == ENOENT) |
| 3438 | /* |
| 3439 | * file disappeared, which is what we |
| 3440 | * wanted anyway |
| 3441 | */ |
| 3442 | continue; |
| 3443 | /* fall through */ |
| 3444 | } else if (S_ISDIR(st.st_mode)) { |
| 3445 | if (!remove_dir_recurse(path, flag, &kept_down)) |
| 3446 | continue; /* happy */ |
| 3447 | } else if (!only_empty && |
| 3448 | (!unlink(path->buf) || errno == ENOENT)) { |
| 3449 | continue; /* happy, too */ |
| 3450 | } |
| 3451 | |
| 3452 | /* path too long, stat fails, or non-directory still exists */ |
no test coverage detected