NSProgress - way to publish progress to make the file url unselectable in Finder?

So I'm in the middle of an asynchronous file operation. I publish an NSProgress and it displays wonderfully in Finder.

But it is a folder and while the operation is in progress the user should not be allowed to enter it, modify it, etc, while the work is being done. I want to do this to protect the user from doing something silly.

But Finder does not prevent the selection with the published progress. And while it would be kind of dumb to do - the user can just go about adding/removing contents to the folder while it has progress.

If I remember correctly publishing an NSProgress did use to prevent the file from being selectable in Finder until either the progress finished or my app is quit (or maybe not)? But now the user is free to select, edit, modify during progress which could cause problems if the user does something unexpectedly silly.

Is there a way to mark the file 'unselectable' with the published progress?

Thanks in advance.

Answered by DTS Engineer in 880199022

Man, there is so much history here...

I tried setting the creation date to: 1946-02-14 08:34:56 +0000 right after creating the folder. But it doesn't have the desired effect. Maybe I didn't convert the date correctly?

SO, I had a bit of loose time today, so I threw together this test code:

//
//  main.m
//  create_time_check
//
//  Created by Kevin Elliott on 3/16/26.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSURL* url = [NSURL fileURLWithPath: @"/Volumes/HomeBase/MassiveTestBed/MassiveTestDirOut/TestDir"];
        
        NSDate* date = NULL;
        NSError* err = NULL;
        if([url getResourceValue: &date forKey: NSURLCreationDateKey error: &err]) {
            NSLog(@"%@", date.debugDescription);
        }
        
        // Jan 24, 1984, the day the mac was announced...
        const CFAbsoluteTime kAbsoluteTimeMagicBusyCreationDate = -534528000.0;

        NSDate* magicDate = (NSDate*) CFBridgingRelease(CFDateCreate(kCFAllocatorDefault, kAbsoluteTimeMagicBusyCreationDate));
        
        NSLog(@"File is Busy: %d", [magicDate isEqualToDate: date]);
        
        NSURL* targetURL = [NSURL fileURLWithPath: @"/Volumes/HomeBase/MassiveTestBed/target"];
        
        if([targetURL setResourceValue: date forKey: NSURLCreationDateKey error: &err]) {
            NSLog(@"%@", date.debugDescription);
        }

    }
    return EXIT_SUCCESS;
}

If you point that code at any directory that's mid copy, that will let you pull the magic date value and then mark any object you want as "busy".

The background here is that the original MacOS classic epoch started at January 1, 1904, with the magic creation date being chosen as a semi-arbitrary[1] date that no one was ever going to "need" and/or was unlikely to accidentally set (like "NULL" or any small integer would have been).

However, the problem was/is that the UNIX epoch starts in 1970, making that date technically unrepresentable in any of our standard date formats. You can still export it in/out of the kernel through the right APIs[2] (notably, the Carbon File Manager), but that process was sufficiently annoying that at some point we picked another date which is what the code above extracts.

And, yes, we probably should have documented all of this, so PLEASE file about this and post the bug number back here.

[1] It's actually the day ENIAC was announced.

[2] Keep in mind that HFS+ data from uses the 1904 epoch, so the "raw" date values end up needing to be accessible.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Is there a way to mark the file 'unselectable' with the published progress?

I'm not sure if/where this was originally documented, but the Finder actually still does this the way macOS Classic did, which is by using type/creator codes. If you're not familiar with them, macOS Classic originally related files to the apps using two 32-bit values- "type" (meaning "what kind of file is this") and "creator" (meaning, what app should open this). By convention, these were constructed 4 ASCII characters, leading to definitions like these:

kFirstMagicBusyFiletype = 'bzy ' kLastMagicBusyFiletype = 'bzy?'

How you set them is a bit obscure, but "getattrlist/setattrlist" (see their man pages for full details) provides access to them through "ATTR_CMN_FNDRINFO". The data returned in the concatenation of the "FileInfo" and "ExtendedFileInfo" structures[1], with the type being in FileInfo.fileType. Note that the ATTR_CMN_FNDRINFO structures are used to store other data, so the normal behavior is to retrieve the current struct, modify it, then write it back out.

A bit of header digging also provides another option, which is to set:

kMagicBusyCreationDate = 0x4F3AFDB0

I believe the Finder is actually setting both values, but I haven't looked at the details in quite a while. It's very possible I've overlooked some edge case or other detail, so let me know if you have any problems and I'll take a closer look.

[1] The oldest part of macOS Classic could be charmingly direct in naming convention.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks for the info! I've been split trying to finish up a different piece of my app related to this.

I briefly tried to get this to work today using the old Carbon API which I must confess I'm not super familiar with (before my time) but didn't have any success.

Before I posted this thread I tried what looked like the most straightforward way to accomplish this:

[fileManager createDirectoryAtURL:someURL 
		withIntermediateDirectories:NO 
		attributes:@{NSFileBusy:@(YES)} 
		error:&error];

But no luck.

I briefly tried to get this to work today using the old Carbon API, which I must confess I'm not super familiar with (before my time), but didn't have any success.

Before I posted this thread, I tried what looked like the most straightforward way to accomplish this:

No, that won't work. NSFileBusy is looking at ExtendedFileInfo.extendedFinderFlags and checking if kExtendedFlagObjectIsBusy is set. I don't think it's really used today, but my recollection was that this meant “busy” in the file access/"locking" sense, which is different than the "creation" case of the Finder.

In any case, I'd start by checking/setting kMagicBusyCreationDate. I believe that's the Finder primary mechanism, and, more practically, it's not deprecated and easier to access than type/creator.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I tried setting the creation date to:

1946-02-14 08:34:56 +0000

right after creating the folder. but doesn't have the desired effect. Maybe I didn't convert the date correctly?

Accepted Answer

Man, there is so much history here...

I tried setting the creation date to: 1946-02-14 08:34:56 +0000 right after creating the folder. But it doesn't have the desired effect. Maybe I didn't convert the date correctly?

SO, I had a bit of loose time today, so I threw together this test code:

//
//  main.m
//  create_time_check
//
//  Created by Kevin Elliott on 3/16/26.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        NSURL* url = [NSURL fileURLWithPath: @"/Volumes/HomeBase/MassiveTestBed/MassiveTestDirOut/TestDir"];
        
        NSDate* date = NULL;
        NSError* err = NULL;
        if([url getResourceValue: &date forKey: NSURLCreationDateKey error: &err]) {
            NSLog(@"%@", date.debugDescription);
        }
        
        // Jan 24, 1984, the day the mac was announced...
        const CFAbsoluteTime kAbsoluteTimeMagicBusyCreationDate = -534528000.0;

        NSDate* magicDate = (NSDate*) CFBridgingRelease(CFDateCreate(kCFAllocatorDefault, kAbsoluteTimeMagicBusyCreationDate));
        
        NSLog(@"File is Busy: %d", [magicDate isEqualToDate: date]);
        
        NSURL* targetURL = [NSURL fileURLWithPath: @"/Volumes/HomeBase/MassiveTestBed/target"];
        
        if([targetURL setResourceValue: date forKey: NSURLCreationDateKey error: &err]) {
            NSLog(@"%@", date.debugDescription);
        }

    }
    return EXIT_SUCCESS;
}

If you point that code at any directory that's mid copy, that will let you pull the magic date value and then mark any object you want as "busy".

The background here is that the original MacOS classic epoch started at January 1, 1904, with the magic creation date being chosen as a semi-arbitrary[1] date that no one was ever going to "need" and/or was unlikely to accidentally set (like "NULL" or any small integer would have been).

However, the problem was/is that the UNIX epoch starts in 1970, making that date technically unrepresentable in any of our standard date formats. You can still export it in/out of the kernel through the right APIs[2] (notably, the Carbon File Manager), but that process was sufficiently annoying that at some point we picked another date which is what the code above extracts.

And, yes, we probably should have documented all of this, so PLEASE file about this and post the bug number back here.

[1] It's actually the day ENIAC was announced.

[2] Keep in mind that HFS+ data from uses the 1904 epoch, so the "raw" date values end up needing to be accessible.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

That does the thing! Thanks!

And, yes, we probably should have documented all of this, so PLEASE file about this and post the bug number back here.

I filed FB22260532

I filed FB22260532

Perfect, thank you!

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

NSProgress - way to publish progress to make the file url unselectable in Finder?
 
 
Q