Commits

Jeff Laing committed da9b6e9

Add setmodtime:to: member to manipulate file modification times.
Modify getfileinfo: to convert st_mtime and st_birthtime members to NSDate

Comments (0)

Files changed (3)

MobileDeviceAccess.h

  *	- \p "st_size" - number of "bytes" in file
  *
  *	- \p "LinkTarget" - target of symbolic link (only if st_ifmt="S_IFLNK")
+ *
+ *	- \p "st_birthtime" - time that file was created
+ *
+ *	- \p "st_mtime" - time that file was modified
  */
 - (NSDictionary*)getFileInfo:(NSString*)path;
 
 - (BOOL)symlink:(NSString*)linkname to:(NSString*)target;
 
 /**
+ * Set file modification time for a file on the device
+ * @param pathname Full pathname of file to modify
+ * @param timestamp Time to set it's modification time to
+ */
+- (BOOL)setmodtime:(NSString*)pathname to:(NSDate*)timestamp;
+
+/**
  * Copy the contents of a local file or directory (on the Mac) to
  * a directory on the device.  The copy is recursive (for directories)
  * and will copy symbolic links as links.

MobileDeviceAccess.m

 // opaque structures
 typedef struct _afc_directory			*afc_directory;
 typedef struct _afc_dictionary			*afc_dictionary;
+typedef struct _afc_operation			*afc_operation;
 
 // Messages passed to device notification callbacks: passed as part of
 // am_device_notification_callback_info.
 uint32_t AFCConnectionGetSocketBlockSize(afc_connection conn);
 uint32_t AFCConnectionSetSocketBlockSize(afc_connection conn, uint32_t size);
 
+CFStringRef AFCCopyErrorString(afc_connection a);
+CFTypeRef AFCConnectionCopyLastErrorInfo(afc_connection a);
+
 // 0001b8e6 T _AFCConnectionCopyLastErrorInfo
 // 0001a8b6 T _AFCConnectionCreate
 // 0001b8ca T _AFCConnectionGetStatus
 // 0001a867 T _AFCConnectionGetTypeID
 // 0001ae99 T _AFCConnectionInvalidate
-// 0001b662 T _AFCConnectionProcessOperation
 // 0001b8c1 T _AFCConnectionProcessOperations
 // 0001abdc T _AFCConnectionScheduleWithRunLoop
 // 0001b623 T _AFCConnectionSubmitOperation
 // 0001ad5a T _AFCConnectionUnscheduleFromRunLoop
 
+// 000000000000603e T _AFCConnectionProcessOperations
+// 0000000000005df8 T _AFCConnectionSetCallBack
+// 00000000000064cc T _AFCConnectionSubmitOperation
+
+// 0000000000008512 t _AFCLockCreate
+// 0000000000007a83 t _AFCLockFree
+// 00000000000084db t _AFCLockGetTypeID
+// 0000000000007e69 t _AFCLockLock
+// 00000000000083f2 t _AFCLockTryLock
+// 000000000000810c t _AFCLockUnlock
+
 const char * AFCGetClientVersionString(void);		// "@(#)PROGRAM:afc  PROJECT:afc-80"
 
 // directory related functions
 typedef void (*NOTIFY_CALLBACK)(CFStringRef notification, void* data);
 mach_error_t AMDListenForNotifications(am_service socket, NOTIFY_CALLBACK cb, void* data);
 
+// New style - seems to formalise the creation of the "request packet" seperately
+// from the execution.
+afc_error_t AFCConnectionProcessOperation(afc_connection a1, afc_operation op);
+afc_error_t AFCOperationGetResultStatus(afc_operation op);
+CFTypeRef AFCOperationGetResultObject(afc_operation op);
+CFTypeID AFCOperationGetTypeID(afc_operation op);
+// 0000000000002dd3 T _AFCOperationGetState
+// 00000000000030c5 T _AFCOperationCopyPacketData
+afc_error_t AFCOperationSetContext(afc_operation op, void *ctx);
+void *AFCOperationGetContext(afc_operation op);
+
+// each of these returns an op, with the appropriate request encoded.  The value of ctx is
+// available via AFCOperationGetContext()
+afc_operation AFCOperationCreateGetConnectionInfo(CFAllocatorRef allocator, void *ctx);
+afc_operation AFCOperationCreateGetDeviceInfo(CFAllocatorRef allocator, void *ctx);
+afc_operation AFCOperationCreateGetFileHash(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateGetFileInfo(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateLinkPath(CFAllocatorRef allocator, uint32_t mode, CFStringRef filename1, CFStringRef filename2, void *ctx);
+afc_operation AFCOperationCreateMakeDirectory(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateOpenFile(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateReadDirectory(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateRemovePath(CFAllocatorRef allocator, CFStringRef filename, void *ctx);
+afc_operation AFCOperationCreateRenamePath(CFAllocatorRef allocator, CFStringRef oldname, CFStringRef newname, void *ctx);
+afc_operation AFCOperationCreateSetConnectionOptions(CFAllocatorRef allocator, CFDictionaryRef dict, void *ctx);
+afc_operation AFCOperationCreateSetModTime(CFAllocatorRef allocator, CFStringRef filename, uint64_t mtm, void *ctx);
+
 @interface AMDevice(Private)
 - (am_service)_startService:(NSString*)name;
 @end
 // 2009-08-06 23:37:23.089 MobileDeviceAccess[1800:813]  st_nlink = 1
 // 2009-08-06 23:37:23.090 MobileDeviceAccess[1800:813]  st_size = 29260
 // 2009-08-06 23:37:23.091 MobileDeviceAccess[1800:813]  st_ifmt = S_IFREG
+//    "st_birthtime" = 9223372036854775807;
+//    "st_mtime" = 280689926000000000;
 
 // /Applications
 // 2009-08-06 23:39:01.872 MobileDeviceAccess[1864:813]  st_blocks = 8
 // 2009-08-06 23:39:01.877 MobileDeviceAccess[1864:813]  st_ifmt = S_IFLNK
 // 2009-08-06 23:39:01.873 MobileDeviceAccess[1864:813]  LinkTarget = /var/stash/Applications.pwn
 
+-(void) fix_date_entry:(NSString*)key in:(NSMutableDictionary *)dict
+{
+	id d = [dict objectForKey:key];
+	if (d) {
+		long v = [d doubleValue] / 1000000000.0;
+		d = [NSDate dateWithTimeIntervalSince1970:v];
+		[dict setObject:d forKey:key];
+	}
+}
+
 - (NSDictionary*)getFileInfo:(NSString*)path
 {
 	if (!path) {
 			[result setObject:path forKey:@"path"];
 			AFCKeyValueClose(dict);
 			[self clearLastError];
+
+			// fix the ones we know are dates
+			[self fix_date_entry:@"st_birthtime" in:result];
+			[self fix_date_entry:@"st_mtime" in:result];
 			return [NSDictionary dictionaryWithDictionary:result];
 		}
 	}
 	return [self checkStatus:AFCLinkPath(_afc, 2, [target UTF8String], [path UTF8String]) from:"AFCLinkPath"];
 }
 
+- (bool)processOp:(afc_operation)op from:(const char *)func
+{
+	BOOL result = NO;
+	if ([self checkStatus:AFCConnectionProcessOperation(_afc, op) from:"AFCConnectionProcessOperation"]) {
+		afc_error_t e = AFCOperationGetResultStatus(op);
+		if (e == 0) {
+			[self clearLastError];
+			result = YES;
+		} else {
+			CFTypeRef err = AFCConnectionCopyLastErrorInfo(_afc);
+			if (err) {
+				id msg = [(id)err objectForKey:@"NSLocalizedDescription"];
+				if (msg) {
+					[self setLastError:msg];
+				} else {
+					[self setLastError:[NSString stringWithFormat:@"Error %ld occurred, no message",e]];
+				}
+				CFRelease(err);
+			} else {
+				[self setLastError:[NSString stringWithFormat:@"Error %ld occurred, no error info",e]];
+			}
+		}
+	}
+	CFRelease(op);
+	return result;
+}
+
+/**
+ * Set file modification time for a file on the device
+ * @param filename Full pathname of file to modify
+ * @param modtime time to set it to
+ */
+- (BOOL)setmodtime:(NSString*)path to:(NSDate*)timestamp
+{
+	if (!path) {
+		[self setLastError:@"Path is nil"];
+		return NO;
+	}
+	if (!timestamp) {
+		[self setLastError:@"timestamp is nil"];
+		return NO;
+	}
+	if (![self ensureConnectionIsOpen]) return NO;
+
+	double tm = [timestamp timeIntervalSince1970] * 1000000000;
+	void *op = AFCOperationCreateSetModTime(NULL,(CFStringRef)path, tm, self);
+	if (op) {
+		return [self processOp:op from:"setmodtime:to:"];
+	}
+	[self setLastError:@"AFCOperationCreateSetModTime() failed"];
+	return NO;
+}
+
 - (AFCFileReference*)openForRead:(NSString*)path
 {
 	if (![self ensureConnectionIsOpen]) return nil;

MobileDeviceAccess.xcodeproj/project.pbxproj

 			isa = PBXProject;
 			buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "MobileDeviceAccess" */;
 			compatibilityVersion = "Xcode 3.1";
+			developmentRegion = English;
 			hasScannedForEncodings = 1;
+			knownRegions = (
+				English,
+				Japanese,
+				French,
+				German,
+			);
 			mainGroup = 0867D691FE84028FC02AAC07 /* MobileDeviceAccess */;
 			productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
 			projectDirPath = "";
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.