Showing posts with label Finder. Show all posts
Showing posts with label Finder. Show all posts

11 April 2025

Finder Comments (Get Info) internals & bugs in macOS

How to get (and modify) file comments

If you use "Get Info" in macOS, you can edit a comment for the item (file or folder). If you copy the file elsewhere, the comment it copied along.

Now, programmers like me sometimes want to be able to inspect or even modify the comment. For instance, Find Any File lets you search for comments on any disk (volume), so it has to read these comments.

Unfortunately, macOS's operations do not offer a convenient way to access these comments. The safe way is to use AppleScript, especially if you want to alter the comment (see StackOverflow: How to read file comment field and StackOverflow: Upload Comments to File Metadata "Get Info" Mac Command Line).

Now, if you only want to read the comment, using AppleScript is relatively slow. For a program like Find Any File that would search entire disks, this could take hours when it has to query the comments via AppleScript for millions of files.

The other method other people have proposed is to read the comment from an extended attribute (EA), which is much faster.

FAF does this now, and it seems to work quite well.

Until someone contacted me and said that FAF manages to find most but not all comments. After a bit of investigation I found that the comment stored in the EA was different from the one that Finder shows in Get Info! And that's not good for finding comments fast and reliably, at all.

The customer was kind enough to help me investigate this and here's my findings:

How macOS stores file comments

macOS may store the comment for a disk item in these places:

  1. In the EA attached to the file.
  2. In the hidden .DS_Store file in the same directory as the file in question.
  3. In the Spotlight database (this is optional)

It appears that Finder's Get Info, as well as its AppleScript's comment property accessor function, access the .DS_Store file directly to read or alter the comment, and if that happens, it also appears to update the EA alongside, while the Spotlight data gets updated whenever a file change is detected.

How can the comments get out of sync?

That happens due to an apparent bug in Finder or its services. Here's how to reproduce it:

  1. Set a comment for a file, using Get Info in Finder, e.g. set it to "original".
  2. Copy the file to a different location, e.g. into a folder next to it, keeping the original file name.
  3. Modify the comment of the copied file, e.g. to "changed".
  4. Copy the copied file back to the original location, choosing to replace the existing.
  5. Now check the comment in Finder's Get Info: It'll show "changed", which is the intended result.
  6. Now check its EA. In Terminal, use:
    xattr -px com.apple.metadata:kMDItemFinderComment /path/to/original_file | xxd -r -p | plutil -p -. This may show either an empty string or the old comment "original", but should instead be "changed".
  7. Even worse, Spotlight's comment may also be wrong. Check with:
    mdls /path/to/original_file | grep kMDItemFinderComment The Spotlight comment may automatically update later, though, e.g. when you open the file or modify it.

(BTW, I suspect that this can also happening when you copy the file in Terminal or other methods that do not involve Finder, though then the result would be the other way around: The EA might be copied to the replaced file location, whereas the .DS_Store file won't get updated.)

Later I found an even easier way to mess up the comment:

After setting the comment with Get Info, rename the file. Check again with Get Info, and you'll see that it still has the comment, but if you now look at the EA, it'll show an empty comment.

So, there appears to be several bugs in Finder's copy operation (I've verified this in both macOS 10.13.6 and 15.4, so it's been around for a while):

Two kind of bugs

  1. Replacing or renaming a file should transfer the EAs but doesn't. Maybe Apple has reasons to keep some EAs of the replaced file, but the comment surely isn't one of them - the comment belongs to the file being copied.
  2. Copying or renaming a file that replaces another should also trigger an immediate update to the Spotlight importer, making sure it records the new comment. Or maybe Finder even does trigger the importer, but then there's a race condition bug that makes the importer read the outdated or yet non-existing comment in the .DS_Store, with Finder being too slow to update the comment in the .DS_Store in time.

That's my findings so far. I'll file a bug report with Apple but have little hope that this will get addressed, as I've file related bugs in this area before and nothing happened.

What does this mean for apps that search for file comments?

The only reliable way to get a file's comment is to use the slow AppleScript method right now (or read directly from .DS_Store, but that's undocumented and may break any time, because the file's format is private to Apple). Which means I might have to update Find Any File to use the much slower AppleScript method.

But for now, I'm working on a "matching script" for Find Any File that can identify and fix these out-of-sync comments. If one runs this script once on all volumes (which may take a while), the EAs would be up-to-date and FAF could search them quickly.

10 August 2018

Locating and updating symlinks and Finder Aliases with FAF

Today I renamed one of my internal disks in my Mac Pro. I then realized that I had created a few symlinks to that volume, and those would now become invalid.

For example, if the disk used to be called "Data" and is now called "Backups", then symlinks I may have created would still point to "/Volumes/Data/..." but need now point to the new name instead.

Since I knew that there would only be a handful of such symlink files on my other disks, I could easily update them by hand (using Terminal.app, with the "ln -s" command).

All I needed to do was to find all those symlinks first, making sure I would not miss any.

With Find Any File, this is quite easy. Set up a search like this:


To get the "File Type Code" option, you need to hold down the option (alt) key before clicking the popup-menu. Searching for files of type code "slnk" will address symlinks, and nothing else.

This will then find all matching symlinks, which you can then reveal in the Finder and manually update accordingly.

Similarly, you can also find related Finder Aliases, by searching like this:


After renaming a disk, updating Finder Aliases pointing to that disk is usually not necessary, because Aliases use redundant information to locate moved and renamed files.

However, if you should ever copy all your content to a new (larger) disk, file by file, Finder Aliases won't work any more if the targeted files have also been moved or their disk has been renamed.

So, it can't hurt to update your Aliases right away after moving or renaming the target item. To update your aliases, simply locate or reveal them in Finder, then select the Alias file and hit cmd+R to have it reveal its target. Should the target have been moved or renamed in the mean time, macOS will automatically update all the redundant information.

04 November 2014

Find Empty Folders - a free OS X tool

Today someone asked me if I knew a way to find and delete empty folders, even if they contain an invisible .DS_Store file.

I googled and found nothing.

So I quickly whipped something out with Real Studio.

The result can be found here:

http://www.tempel.org/FindEmptyFolders

07 July 2014

OS X: Moving Files to Trash from your own application, with optional user interaction!

When you're writing a interactive Mac OS X program, it's possible that you need to delete files.

And if they're files the user has access to, then it's often a good idea to move them to the trash instead of erasing them right away, in order to give the user the opportunity to restore them again (saving you the need to provide some kind of Undo command for the destructive operation).

Moving items to the Trash is rather complex, and so it's best to just let the Finder perform that task.

The Finder is, fortunately, still scriptable, and so this task should be easy. (I must say, though, that I don't know if you can get such an app into the App Store - if you know, please comment.)

The challenge is to have the Finder tell the user if there's a problem, such as when the item is locked or when an admin login is needed.


Now, if you're trying to be lazy, you'd just write an AppleScript and invoke that from your application (this is especially easy to do in Real Studio / Xojo, but not much harder in ObjectiveC, either).

The downside of using an AppleScript is that it won't show these dialogs - if the item can't be moved to the trash, the function will return and give you an error message telling you about the problem.

To solve this, you need to invoke the AppleEvent directly, without going through an AppleScript.

The Xojo code would look like this, simplified:

  dim ae as AppleEvent = NewAppleEvent ("core", "delo", "MACS")
  dim list as new AppleEventDescList
  list.AppendFolderItem (theItemToDelete)
  ae.DescListParam("----") = list
  ok = ae.send

However, this won't give us the user interaction either, yet. That's because Xojo's Send command is lacking the options that the OS X API offers.

We have to call the native AESend() function via a declare, instead:

  const kAEWaitReply = &h00000003 ' sender wants a reply and will wait
  const kAEAlwaysInteract = &h00000030 ' interact where appropriate
  const kAECanSwitchLayer = &h00000040 ' interaction may switch layer
  declare function AESend lib "Carbon" (ae as Integer, reply as Ptr, 
    sendMode as Integer, prio as Integer, timeoutTicks as Integer,
    idle as Ptr, filter as Ptr) as Integer
  dim sendMode as Integer
  sendMode = kAEWaitReply + kAEAlwaysInteract + kAECanSwitchLayer
  dim err as Integer = AESend (ae.Ptr, nil, sendMode, 0, 0, nil, nil)
        
The same can be applied to copying files with user interaction as well - use the "clon" instead of the "delo" value for the AppleEvent.

I've made a REALbasic / Xojo project with both a MoveItemsToTrash() and CopyItems() function for your own use. Download here.