Skip to content

Can't delete folder recursively due to MacOS resource forks (._* files) #4720

Closed

Description

@ollixx77

Is there an existing issue for this?

  • I have searched the existing issues

Midnight Commander version and build configuration

GNU Midnight Commander 4.8.33
Gebaut mit GLib 2.82.4
Gebaut mit S-Lang 2.3.3 mit terminfo-Datenbank
Gebaut mit libssh2 1.11.1
Mit eingebautem Editor
Mit Subshell-Unterstützung als Standard
Mit Unterstützung für Hintergrundoperationen
Mit Mausunterstützung für xterm
Mit Internationalisierungsunterstützung
Mit Unterstützung mehrerer Codepages
Virtuelles Dateisystem:
 cpiofs, tarfs, sfs, extfs, ftpfs, sftpfs, shell
Datentypen:
 char: 8; int: 32; long: 64; void *: 64; size_t: 64; off_t: 64; uintmax_t: 64;

Operating system

Darwin Mac.fritz.box 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:53:27 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6041 arm64

Is this issue reproducible using the latest version of Midnight Commander?

  • I confirm the issue is still reproducible with the latest version of Midnight Commander

How to reproduce

Deleting folder with some files and their pesty ._* counterparts created by MacOS
(recursive delete = yes)

Expected behavior

Folder get's deleted

Actual behavior

n files reside in this folder
having to delete this folder n+1 times until it's really gone
(recursive delete = yes)

Additional context

I think it has to something to do with this stupid .* files, which MacOS creates for every regular file.
When I get rid of these .
* files before and try to delete the folder, it works as expected.

Activity

  1. added theissue type on Jun 4, 2025
  2. added this to the Future Releases milestone on Jun 4, 2025
  3. zyv commented on Jun 4, 2025

    @zyv
    Member

    Could you provide a sample tree that reproduces the issue? I've never seen anything like that on my Mac. From your description, it seems that you are working with a system that does not support metadata.

  4. ollixx77 commented on Jun 4, 2025

    @ollixx77
    Author

    Something like this?

    64 -rwx------ 1 olli staff 4,0K 4 Jun 18:24 ._file1*
    64 -rwx------ 1 olli staff 4,0K 4 Jun 18:24 ._file2*
    64 -rwx------ 1 olli staff 4,0K 4 Jun 18:24 ._file3*
    0 -rwx------@ 1 olli staff 0B 4 Jun 18:24 file1*
    0 -rwx------@ 1 olli staff 0B 4 Jun 18:24 file2*
    0 -rwx------@ 1 olli staff 0B 4 Jun 18:24 file3*

    Also very strange that I don't see this files in the finder (although I switched on to see hidden files ⇧⌘.)
    Addition: happens only on external media as SD-Cards or USB-Sticks. Internal drive is not affected.

  5. zyv commented on Jun 4, 2025

    @zyv
    Member

    Something like this?

    No, I meant the actual archive of files that I can unpack and try out on my system... but see below:

    Addition: happens only on external media as SD-Cards or USB-Sticks. Internal drive is not affected.

    I see. This corroborates my statement above that you are working with file systems that do not support metadata, such as ExFAT. In this case, macOS stores the metadata ("resource fork") in "shadow" files (._*). If an external tool removes the shadow files, the behavior is undefined.

    It's possible that we're removing some of those files, and then they're automatically reappearing. I'm not sure what we can do about it. One option is to sort the file listing before removal so that these special files are removed last (at which point they won't exist anyway).

    What do you think, @aborodin?

  6. ollixx77 commented on Jun 4, 2025

    @ollixx77
    Author

    I tried it, but sorting doesn't help 🤷‍♂️
    Does mc care about the sort order, when deleting files in a sub-folder?

  7. zyv commented on Jun 4, 2025

    @zyv
    Member

    I tried it, but sorting doesn't help 🤷‍♂️ Does mc care about the sort order, when deleting files in a sub-folder?

    I'm not talking about the panels' sorting order, but rather the internal order in which mc deletes files.

  8. zyv commented on Jun 5, 2025

    @zyv
    Member

    This should properly delete the forks (untested); I'm just not sure what to do about the return status...

    diff --git a/lib/fs.h b/lib/fs.h
    index 8c5d26f7f..70f41b21c 100644
    --- a/lib/fs.h
    +++ b/lib/fs.h
    @@ -103,10 +103,11 @@
     #define MAXNAMLEN NAME_MAX
     #endif
     
    -#define MC_MAXFILENAMELEN MAXNAMLEN
    +#define MC_MAXFILENAMELEN        MAXNAMLEN
     
    -#define DIR_IS_DOT(x)     ((x)[0] == '.' && (x)[1] == '\0')
    -#define DIR_IS_DOTDOT(x)  ((x)[0] == '.' && (x)[1] == '.' && (x)[2] == '\0')
    +#define DIR_IS_DOT(x)            ((x)[0] == '.' && (x)[1] == '\0')
    +#define DIR_IS_DOTDOT(x)         ((x)[0] == '.' && (x)[1] == '.' && (x)[2] == '\0')
    +#define FILE_IS_RESOURCE_FORK(x) ((x)[0] == '.' && (x)[1] == '_' && (x)[2] != '\0')
     
     /*** enums ***************************************************************************************/
     
    diff --git a/src/filemanager/file.c b/src/filemanager/file.c
    index 54ece4899..41429ef99 100644
    --- a/src/filemanager/file.c
    +++ b/src/filemanager/file.c
    @@ -1441,7 +1441,7 @@ try_erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
       ignore_all -> remove as much as possible
     */
     static FileProgressStatus
    -recursive_erase (file_op_context_t *ctx, const vfs_path_t *vpath)
    +recursive_erase (file_op_context_t *ctx, const vfs_path_t *vpath, gboolean delete_forks)
     {
         struct vfs_dirent *next;
         DIR *reading;
    @@ -1467,9 +1467,17 @@ recursive_erase (file_op_context_t *ctx, const vfs_path_t *vpath)
                 return FILE_RETRY;
             }
             if (S_ISDIR (buf.st_mode))
    -            return_status = recursive_erase (ctx, tmp_vpath);
    +        {
    +            return_status = recursive_erase (ctx, tmp_vpath, FALSE);
    +            return_status = recursive_erase (ctx, tmp_vpath, TRUE);
    +        }
             else
    -            return_status = erase_file (ctx, tmp_vpath);
    +        {
    +            if (!FILE_IS_RESOURCE_FORK (next->d_name) || delete_forks)
    +                return_status = erase_file (ctx, tmp_vpath);
    +            else
    +                return_status = FILE_CONT;
    +        }
             vfs_path_free (tmp_vpath, TRUE);
         }
         mc_closedir (reading);
    @@ -3351,7 +3359,10 @@ erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
             // not empty
             error = query_recursive (ctx, vfs_path_as_str (vpath));
             if (error == FILE_CONT)
    -            error = recursive_erase (ctx, vpath);
    +        {
    +            error = recursive_erase (ctx, vpath, FALSE);
    +            error = recursive_erase (ctx, vpath, TRUE);
    +        }
             return error;
         }
  9. changed the title [-]Can't delete folder recursivly (MacOS ._* files)[/-] [+]Can't delete folder recursively due to MacOS resource forks (`._*` files)[/+] on Jun 9, 2025
  10. added
    area: coreIssues not related to a specific subsystem
    ver: 4.8.33Reproducible in version 4.8.33
    and removed
    needs triageNeeds triage by maintainers
    on Jun 9, 2025
  11. removed this from the Future Releases milestone on Jun 9, 2025
  12. 14 remaining items

  13. added 4 commits that reference this issue on May 9, 2026
  14. modified the milestones: 4.9.1, 4.9.0 on May 10, 2026
  15. added a commit that references this issue on May 10, 2026
  16. added a commit that references this issue on May 17, 2026
  17. added a commit that references this issue on Oct 17, 2026
  18. added 5 commits that reference this issue on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues not related to a specific subsystemprio: mediumHas the potential to affect progressver: 4.8.33Reproducible in version 4.8.33

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions