0

Im creating a FSEvents Stream with a path to directory:

- (BOOL) registerFSEventsObserverForURL:(NSURL*)url error:(NSError **)error
{
    BOOL succeeded = YES;
    FSEventStreamContext context;
    context.version = 0;
    context.info = (__bridge void*) self;
    context.retain = NULL;
    context.release = NULL;
    context.copyDescription = NULL;

    NSString* path = [url path];

    dev_t device = [[self class] deviceIDForPath:path];

    NSArray* pathArray = [NSArray arrayWithObject:path];
    FSEventStreamRef stream = FSEventStreamCreateRelativeToDevice(NULL, &FSEventCallback,
                                                                  &context,
                                                                  device,
                                                                  (__bridge CFArrayRef)pathArray,
                                                                  kFSEventStreamEventIdSinceNow,
                                                                  1.0,
                                                                  0);
// ...

}

+ (dev_t)deviceIDForPath:(NSString*)path {
    struct stat statbuf;
    int result = stat([path fileSystemRepresentation], &statbuf);
    if (result == -1) {
        printf ("error with stat.  %d\n", errno);
        return EXIT_FAILURE;
    }
    dev_t device = statbuf.st_dev;
    return device;
}

static void FSEventCallback(ConstFSEventStreamRef inStreamRef, 
                            void* inClientCallBackInfo, 
                            size_t inNumEvents, 
                            void* inEventPaths, 
                            const FSEventStreamEventFlags inEventFlags[], 
                            const FSEventStreamEventId inEventIds[])
{
        dev_t device = FSEventStreamGetDeviceBeingWatched(inStreamRef);
        for (int i=0; i<inNumEvents; i++)
        {
           NSString* path = ((__bridge NSArray*)inEventPaths)[i];
           NSURL* url = [NSURL fileURLWithPath: path]; // Here I'm getting a non valid URL.
        }
}

So for example if url is 'file:///Users/user1/Pictures', when Im getting the callback the path is as expected: 'Users/user1/Pictures' but I don't know how to get a file url from that path. I think I need to somehow find the root path for device ID and then call [NSURL fileURLWithPath:relativeToURL:]. My question is: how do I get a root url for a device ID?

Sanich
  • 1,739
  • 6
  • 25
  • 43

1 Answers1

0

It's probably just easier to obtain the device root path at the beginning and remember it.

You can use -[NSURL getResourceValue:forKey:error:] with the key NSURLVolumeURLKey to get an NSURL for the volume's root directory.

Or, if you prefer, you can call statfs() on the original path. It will fill in a struct statfs with info about the file system. Among the fields of that structure is f_mntonname, which is the path where the file system is mounted.

To answer your original question, you'd have to enumerate the mounted file systems and look for one who's device ID matches. You can use -[NSFileManager mountedVolumeURLsIncludingResourceValuesForKeys:options:] or getfsstat() for the enumeration. With the former, you'd still need to do statfs() to get the device ID. With the latter, the struct statfs are returned directly.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Please notice this line in the callback function: `dev_t device = FSEventStreamGetDeviceBeingWatched(inStreamRef);` I can have the device ID from the stream ref. – Sanich Jan 01 '19 at 07:39
  • I understand that, but don't know why you're pointing it out. You provided the device ID to the `FSEventStream` API, so it's not valuable to get it back. You already knew it. I'm suggesting that you obtain the file system root directory (from the path) when creating the stream, similarly to how you obtain the device ID there. You can store it in a property. If, for some reason, you don't want to do that, I also told you how to get the root folder from the device ID in the last paragraph. – Ken Thomases Jan 01 '19 at 07:47
  • The thing is that I want to create streams to different devices and provide the same callback function. – Sanich Jan 01 '19 at 07:49
  • That's what the `info` field of the context is for, to give you back info that you need to distinguish the sources of the callback. If you want to use multiple streams with the same owner, even (since you're using `self` for the `info`), then maybe use a dictionary one of whose values is `self` and another which is the root folder. OR, as I keep pointing out, use the technique I explain in the last paragraph of my answer. – Ken Thomases Jan 01 '19 at 19:32