4

I am writing a tool that offers the option to trash selected items (files, folders). Usually, I'd call -[NSFileManager trashItemAtURL:...] for each of those items, as it's also explained in this and in this SO question.

However, these do not work when trying to delete files from a directory owned by a different user, such as root. My tool shall offer the same option as the Finder in this case, i.e. ask the user to authorize the operation by providing the credentials of an Admin user, and then my app would move the items to the Trash like the Finder does.

I've tried solving this by using a privileged helper, as outline by the EvenBetterAuthorizationSample example code, using launchd, SMJobBless and XPC Services.

The problem with that is, however, that the privileged helper runs as the root user, without knowledge of the current user my app runs under. The result is that, when it trashes a file, it ends up in the root user's Trash folder and not, as the Finder would do it, in the user's Trash folder.

How do I solve this, i.e. how do I move items not owned by the user to the current user's trash instead of the root user's Trash?

Is there some trick I can use that would let me keep using one of the existing trash oder recycle functions?

Doing the move myself is not going to work properly because for Put Back to work, the Trash's .DS_Store file would need to be updated, and there's no API for that, AFAIK.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
  • There's a Unix command geteuid() I think, to get the "effective" user instead of the user you're running as. If your daemon is launched per-user, I'd expect that to be set. Have you tried that? – uliwitness Jun 28 '19 at 18:38
  • You mean getuid - geteuid always is 0 for such a daemon, but getuid did indeed return the user's id. I did try that when launching my helper via `AuthorizationExecuteWithPrivileges`: In 10.15 (b1) getuid returns the root user and not the current user as it used to before. I will test if that is different with a user-specific launchd. – Thomas Tempelmann Jun 30 '19 at 10:51

1 Answers1

1

I have almost found a solution:

Analysis

When the helper is run, e.g. from launchd, or via AuthorizationExecuteWithPrivileges (under macOS 10.15), it may be running as root, with no knowledge of the logged-in user, hence it cannot determine the user's Trash folder.

Oddly, the Environment variables (see man env) may even show the current user's name and home dir, yet the real user id, which one can query with getuid(), will return 0 (root), which also results in NSUserName()and NSHomeDirectory() returning the root user's information. And it appears that trashItemAtURL and related functions rely on NSHomeDirectory() to determine the Trash folder location.

Half-working solution

Fortunately, there is a way to change the real user id, with setreuid.

Therefore, in my testing, when I call setreuid (501, 0) (501 being the uid of the currently logged-in user), then trashItemAtURL does move the file to the user's Trash folder, indeed, along with automatic renaming where necessary.

However, this does not make the Put Back work, the way it would when trashing the same file using the Finder.

Making Put Back work

Looks like the reason for Put Back not working comes from a deeper issue: It appears to be a long-standing bug in the macOS framework, see this bug report.

Which basically means: This is the best we can get out of it until Apple fixes the underlying bug.

The only working alternative to make Put Back work is to ask the Finder to trash the items using AppleEvents / AppleScript.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149