Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,22 @@

@implementation NSFileManager (TOFileSystemDirectoryEnumerator)

- (NSDirectoryEnumerator<NSURL *> *)to_fileSystemEnumeratorForDirectoryAtURL:(NSURL *)url
{
- (NSDirectoryEnumerator<NSURL *> *)to_fileSystemEnumeratorForDirectoryAtURL:(NSURL *)url {
// Set the keys for the properties we wish to capture
NSArray *keys = @[NSURLIsDirectoryKey,
NSURLFileSizeKey,
NSURLCreationDateKey,
NSURLContentModificationDateKey];
NSArray * const keys = @[NSURLIsDirectoryKey,
NSURLFileSizeKey,
NSURLCreationDateKey,
NSURLContentModificationDateKey];

// Set the flags for the enumerator
NSDirectoryEnumerationOptions options = NSDirectoryEnumerationSkipsHiddenFiles |
NSDirectoryEnumerationSkipsSubdirectoryDescendants;
const NSDirectoryEnumerationOptions options = NSDirectoryEnumerationSkipsHiddenFiles |
NSDirectoryEnumerationSkipsSubdirectoryDescendants;

// Create the enumerator
NSDirectoryEnumerator<NSURL *> *urlEnumerator = [self enumeratorAtURL:url
includingPropertiesForKeys:keys
options:options
errorHandler:nil];
NSDirectoryEnumerator<NSURL *> * const urlEnumerator = [self enumeratorAtURL:url
includingPropertiesForKeys:keys
options:options
errorHandler:nil];
return urlEnumerator;
}

Expand Down
29 changes: 11 additions & 18 deletions TOFileSystemObserver/Categories/NSURL+TOFileSystemAttributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,39 +27,34 @@

@implementation NSURL (TOFileSystemAttributes)

- (BOOL)to_isCopying
{
- (BOOL)to_isCopying {
// When files are still being copied, their
// modification date is equal to the current device time.
NSDate *modificationDate = self.to_modificationDate;
NSDate * const modificationDate = self.to_modificationDate;
if (modificationDate == nil) { return NO; }
return [modificationDate timeIntervalSinceDate:[NSDate date]]
> (-kTOFileSystemObserverCopyingTimeDelay - FLT_EPSILON);
}

- (BOOL)to_isDirectory
{
- (BOOL)to_isDirectory {
NSNumber *isDirectory;
[self getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
return isDirectory.boolValue;
}

- (long long)to_size
{
- (long long)to_size {
NSNumber *fileSize;
[self getResourceValue:&fileSize forKey:NSURLFileSizeKey error:nil];
return fileSize.longLongValue;
}

- (NSDate *)to_creationDate
{
- (NSDate *)to_creationDate {
NSDate *creationDate;
[self getResourceValue:&creationDate forKey:NSURLCreationDateKey error:nil];
return creationDate;
}

- (NSDate *)to_modificationDate
{
- (NSDate *)to_modificationDate {
[self removeCachedResourceValueForKey:NSURLContentModificationDateKey];
NSDate *modificationDate;
[self getResourceValue:&modificationDate forKey:NSURLContentModificationDateKey error:nil];
Expand All @@ -72,26 +67,24 @@ - (NSDate *)to_modificationDate
// that don't fill d_type, like NFS or FAT — falls back to lstat. Exposed (not
// static) so the DT_UNKNOWN branch can be exercised by unit tests, since it's
// not reachable on APFS where readdir always reports a concrete type.
BOOL TOFileSystemDirEntryIsCountable(const char *parentPath, const struct dirent *entry)
{
BOOL TOFileSystemDirEntryIsCountable(const char *parentPath, const struct dirent *entry) {
if (entry->d_name[0] == '.') { return NO; }
if (entry->d_type == DT_REG || entry->d_type == DT_DIR) { return YES; }
if (entry->d_type != DT_UNKNOWN) { return NO; }

char fullPath[PATH_MAX];
int written = snprintf(fullPath, sizeof(fullPath), "%s/%s", parentPath, entry->d_name);
const int written = snprintf(fullPath, sizeof(fullPath), "%s/%s", parentPath, entry->d_name);
if (written <= 0 || written >= (int)sizeof(fullPath)) { return NO; }

struct stat st;
if (lstat(fullPath, &st) != 0) { return NO; }
return S_ISREG(st.st_mode) || S_ISDIR(st.st_mode);
}

- (NSInteger)to_numberOfSubItems
{
- (NSInteger)to_numberOfSubItems {
// Do it using POSIX APIs to avoid needing to load in all of the file names
const char *path = [self.path cStringUsingEncoding:NSUTF8StringEncoding];
DIR *directory = opendir(path);
const char * const path = [self.path cStringUsingEncoding:NSUTF8StringEncoding];
DIR * const directory = opendir(path);
if (directory == NULL) { return 0; }

NSInteger numberOfItems = 0;
Expand Down
49 changes: 22 additions & 27 deletions TOFileSystemObserver/Categories/NSURL+TOFileSystemUUID.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,35 @@

@implementation NSURL (TOFileSystemUUID)

+ (void)to_setKeyNamePrefix:(NSString *)prefix
{
+ (void)to_setKeyNamePrefix:(NSString *)prefix {
kTOFileSystemAttributeKey = [NSString stringWithFormat:@"%@.fileSystemObserver.UUID", prefix];
}

- (NSString *)to_fileSystemUUID
{
const char *filePath = [self.path fileSystemRepresentation];
const char *keyName = kTOFileSystemAttributeKey.UTF8String;
- (NSString *)to_fileSystemUUID {
const char * const filePath = [self.path fileSystemRepresentation];
const char * const keyName = kTOFileSystemAttributeKey.UTF8String;

// Allocate a buffer for the value (UUID values are always 36 characters)
char value[36];

// Fetch the value from disk. A short read means there's no valid UUID stored.
ssize_t bytesRead = getxattr(filePath, keyName, value, sizeof(value), 0, 0);
const ssize_t bytesRead = getxattr(filePath, keyName, value, sizeof(value), 0, 0);
if (bytesRead != (ssize_t)sizeof(value)) {
return nil;
}

// Convert to a string, and return if successful
NSString *uuid = [[NSString alloc] initWithBytes:value length:bytesRead encoding:NSUTF8StringEncoding];
NSString * const uuid = [[NSString alloc] initWithBytes:value length:bytesRead encoding:NSUTF8StringEncoding];
if (uuid.length == 0) {
return nil;
}

// Verify to make sure the provided value is a valid UUID string
NSString *uuidPattern = @"\\A[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\Z";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:uuidPattern
options:NSRegularExpressionCaseInsensitive
error:nil];
NSRange range = [regex rangeOfFirstMatchInString:uuid options:0 range:NSMakeRange(0, uuid.length)];
NSString * const uuidPattern = @"\\A[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\Z";
NSRegularExpression * const regex = [NSRegularExpression regularExpressionWithPattern:uuidPattern
options:NSRegularExpressionCaseInsensitive
error:nil];
const NSRange range = [regex rangeOfFirstMatchInString:uuid options:0 range:NSMakeRange(0, uuid.length)];

// A valid regex was found.
if (range.location == NSNotFound) {
Expand All @@ -67,8 +65,7 @@ - (NSString *)to_fileSystemUUID
return uuid;
}

- (BOOL)to_setFileSystemUUID:(NSString *)uuid
{
- (BOOL)to_setFileSystemUUID:(NSString *)uuid {
if (uuid.length == 0) { return NO; }
if (uuid.length != 36) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
Expand All @@ -77,29 +74,28 @@ - (BOOL)to_setFileSystemUUID:(NSString *)uuid
}

// Determine the file path and destination key
const char *filePath = [self.path fileSystemRepresentation];
const char *keyName = kTOFileSystemAttributeKey.UTF8String;
const char * const filePath = [self.path fileSystemRepresentation];
const char * const keyName = kTOFileSystemAttributeKey.UTF8String;

// Convert the string to a C byte string
const char *uuidString = [uuid cStringUsingEncoding:NSUTF8StringEncoding];
const char * const uuidString = [uuid cStringUsingEncoding:NSUTF8StringEncoding];
if (uuidString == NULL) { return NO; }

// Save it to this file. UUID strings are always 36 ASCII bytes.
return setxattr(filePath, keyName, uuidString, 36, 0, 0) == 0;
}

- (BOOL)to_setFileSystemUUIDIfAbsent:(NSString *)uuid
{
- (BOOL)to_setFileSystemUUIDIfAbsent:(NSString *)uuid {
if (uuid.length == 0) { return NO; }
if (uuid.length != 36) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"UUID must be 36 characters long!"
userInfo:nil];
}

const char *filePath = [self.path fileSystemRepresentation];
const char *keyName = kTOFileSystemAttributeKey.UTF8String;
const char *uuidString = [uuid cStringUsingEncoding:NSUTF8StringEncoding];
const char * const filePath = [self.path fileSystemRepresentation];
const char * const keyName = kTOFileSystemAttributeKey.UTF8String;
const char * const uuidString = [uuid cStringUsingEncoding:NSUTF8StringEncoding];
if (uuidString == NULL) { return NO; }

// XATTR_CREATE makes setxattr fail with EEXIST if the attribute is already
Expand All @@ -108,9 +104,8 @@ - (BOOL)to_setFileSystemUUIDIfAbsent:(NSString *)uuid
return setxattr(filePath, keyName, uuidString, 36, 0, XATTR_CREATE) == 0;
}

- (NSString *)to_generateFileSystemUUID
{
NSString *uuid = [NSUUID UUID].UUIDString;
- (NSString *)to_generateFileSystemUUID {
NSString * const uuid = [NSUUID UUID].UUIDString;
if (![self to_setFileSystemUUID:uuid]) { return nil; }
return uuid;
}
Expand Down
21 changes: 7 additions & 14 deletions TOFileSystemObserver/Entities/Changes/TOFileSystemChanges.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,28 @@ @interface TOFileSystemChanges ()

@implementation TOFileSystemChanges

- (instancetype)initWithFileSystemObserver:(TOFileSystemObserver *)fileSystemObserver
{
- (instancetype)initWithFileSystemObserver:(TOFileSystemObserver *)fileSystemObserver {
if (self = [super init]) {
_fileSystemObserver = fileSystemObserver;
}
return self;
}

- (void)addDiscoveredItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL
{
- (void)addDiscoveredItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL {
if (_discoveredItems == nil) {
_discoveredItems = [NSMutableDictionary dictionary];
}
_discoveredItems[uuid] = fileURL;
}

- (void)addModifiedItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL
{
- (void)addModifiedItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL {
if (_modifiedItems == nil) {
_modifiedItems = [NSMutableDictionary dictionary];
}
_modifiedItems[uuid] = fileURL;
}

- (void)addDeletedItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL
{
- (void)addDeletedItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL {
if (_deletedItems == nil) {
_deletedItems = [NSMutableDictionary dictionary];
}
Expand All @@ -68,21 +64,18 @@ - (void)addDeletedItemWithUUID:(NSString *)uuid fileURL:(NSURL *)fileURL

- (void)addMovedItemWithUUID:(NSString *)uuid
oldFileURL:(NSURL *)oldFileURL
newFileURL:(NSURL *)newFileURL
{
newFileURL:(NSURL *)newFileURL {
if (_movedItems == nil) {
_movedItems = [NSMutableDictionary dictionary];
}
_movedItems[uuid] = @[oldFileURL, newFileURL];
}

- (void)setIsFullScan
{
- (void)setIsFullScan {
self.isFullScan = YES;
}

- (NSString *)description
{
- (NSString *)description {
return [NSString stringWithFormat:@"Discovered items: %@\nModified items: %@\nDeleted Items: %@\nMoved items: %@\n",
self.discoveredItems, self.modifiedItems, self.deletedItems, self.movedItems];
}
Expand Down
Loading
Loading