bug: delete_extras_walk unconditionally calls rmdir on directories #43

Closed
opened 2026-07-20 14:59:02 +02:00 by TapTap · 0 comments
Owner

In src/shared/utils.c:84-121, the delete_extras_walk function has a bug in directory handling:

static void delete_extras_walk(const char* abs_path, const char* rel_path, ArrayList* manifest) {
  DIR* dir = opendir(abs_path);
  if (!dir) return;
  struct dirent* entry;
  while ((entry = readdir(dir)) != NULL) {
    // ... handles files and subdirectories recursively
  }
  closedir(dir);
  rmdir(abs_path);  // BUG: Always called, even for non-empty directories
}

The rmdir(abs_path) at the end is called unconditionally for every directory, including:

  1. The root destination directory itself -- which means if --delete is used, an attempt will be made to remove the root destination directory (which will likely fail with ENOTEMPTY since manifest files should remain)
  2. Any directory that still has files in the manifest -- rmdir will fail silently

If the directory still contains files (matched by manifest), rmdir will fail with ENOTEMPTY but the error is not checked. This is likely the intended behavior (failing silently for directories with preserved files), but it should at least log a debug message.

More critically, the root destination directory is always passed to rmdir after processing, which will always fail (since it contains preserved files).

Fix: Only call rmdir if the directory no longer contains any files after deletion processing, or at minimum check the return value.

Severity: medium

In src/shared/utils.c:84-121, the delete_extras_walk function has a bug in directory handling: ```c static void delete_extras_walk(const char* abs_path, const char* rel_path, ArrayList* manifest) { DIR* dir = opendir(abs_path); if (!dir) return; struct dirent* entry; while ((entry = readdir(dir)) != NULL) { // ... handles files and subdirectories recursively } closedir(dir); rmdir(abs_path); // BUG: Always called, even for non-empty directories } ``` The `rmdir(abs_path)` at the end is called unconditionally for every directory, including: 1. The root destination directory itself -- which means if --delete is used, an attempt will be made to remove the root destination directory (which will likely fail with ENOTEMPTY since manifest files should remain) 2. Any directory that still has files in the manifest -- rmdir will fail silently If the directory still contains files (matched by manifest), rmdir will fail with ENOTEMPTY but the error is not checked. This is likely the intended behavior (failing silently for directories with preserved files), but it should at least log a debug message. More critically, the root destination directory is always passed to rmdir after processing, which will always fail (since it contains preserved files). Fix: Only call rmdir if the directory no longer contains any files after deletion processing, or at minimum check the return value. Severity: medium
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: TapTap/FastSync#43