Skip to content

Commit 8c73785

Browse files
MartinLau7abbeycode
authored andcommitted
Support archival and restoration of POSIX permissions (#84)
Merging @MartinLau7's code from PR #84, as a WIP
1 parent 8c6f740 commit 8c73785

9 files changed

Lines changed: 195 additions & 44 deletions

File tree

Source/UZKArchive.h

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -593,26 +593,26 @@ compressionMethod:(UZKCompressionMethod)method
593593
error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:compressionMethod:password:error: instead, and if using the progress block, replace with NSProgress as described in the README");
594594

595595
/**
596-
* Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting
597-
* presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before
598-
* the write begins. For a large archive, this can be slow. On the other hand, when not overwriting,
599-
* the size of the archive will grow each time the file is written.
600-
*
601-
* @param data Data to write into the archive
602-
* @param filePath The full path to the target file in the archive
603-
* @param fileDate The timestamp of the file in the archive. Uses the current time if nil
604-
* @param method The UZKCompressionMethod to use (Default, None, Fastest, Best)
605-
* @param password Override the password associated with the archive (not recommended)
606-
* @param overwrite If YES, and the file exists, delete it before writing. If NO, append
607-
* the data into the archive without removing it first (legacy Objective-Zip
608-
* behavior)
609-
* @param error Contains an NSError object when there was an error writing to the archive
610-
*
611-
* @return YES if successful, NO on error
596+
Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting
597+
presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before
598+
the write begins. For a large archive, this can be slow. On the other hand, when not overwriting,
599+
the size of the archive will grow each time the file is written.
600+
601+
@param data Data to write into the archive
602+
@param filePath The full path to the target file in the archive
603+
@param fileDate The timestamp of the file in the archive. Uses the current time if nil
604+
@param posixPermissions the source posix permissions of the file in the archive
605+
@param method The UZKCompressionMethod to use (Default, None, Fastest, Best)
606+
@param password Override the password associated with the archive (not recommended)
607+
@param overwrite If YES, and the file exists, delete it before writing. If NO,
608+
append the data into the archive without removing it first (legacy Objective-Zip behavior)
609+
@param error Contains an NSError object when there was an error writing to the archive
610+
@return YES if successful, NO on error
612611
*/
613612
- (BOOL)writeData:(NSData *)data
614613
filePath:(NSString *)filePath
615614
fileDate:(nullable NSDate *)fileDate
615+
posixPermissions:(unsigned long)posixPermissions
616616
compressionMethod:(UZKCompressionMethod)method
617617
password:(nullable NSString *)password
618618
overwrite:(BOOL)overwrite
@@ -800,20 +800,21 @@ compressionMethod:(UZKCompressionMethod)method
800800
* be slow. On the other hand, when not overwriting, the size of the archive will grow each time
801801
* the file is written.
802802
*
803-
* @param filePath The full path to the target file in the archive
804-
* @param fileDate The timestamp of the file in the archive. Uses the current time if nil
805-
* @param method The UZKCompressionMethod to use (Default, None, Fastest, Best)
806-
* @param overwrite If YES, and the file exists, delete it before writing. If NO, append
807-
* the data into the archive without removing it first (legacy Objective-Zip
808-
* behavior)
809-
* @param preCRC The CRC-32 for the data being sent. Only necessary if encrypting the file.
810-
Pass 0 otherwise
811-
* @param password Override the password associated with the archive (not recommended)
812-
* @param error Contains an NSError object when there was an error writing to the archive
813-
* @param action Contains your code to loop through the source bytes and write them to the
814-
* archive. Each time a chunk of data is ready to be written, call writeData,
815-
* passing in a pointer to the bytes and their length. Return YES if successful,
816-
* or NO on error (in which case, you should assign the actionError parameter
803+
* @param filePath The full path to the target file in the archive
804+
* @param fileDate The timestamp of the file in the archive. Uses the current time if nil
805+
* @param posixPermissions the source posix permissions of the file in the archive
806+
* @param method The UZKCompressionMethod to use (Default, None, Fastest, Best)
807+
* @param overwrite If YES, and the file exists, delete it before writing. If NO, append
808+
* the data into the archive without removing it first (legacy Objective-Zip
809+
* behavior)
810+
* @param preCRC The CRC-32 for the data being sent. Only necessary if encrypting the file.
811+
Pass 0 otherwise
812+
* @param password Override the password associated with the archive (not recommended)
813+
* @param error Contains an NSError object when there was an error writing to the archive
814+
* @param action Contains your code to loop through the source bytes and write them to the
815+
* archive. Each time a chunk of data is ready to be written, call writeData,
816+
* passing in a pointer to the bytes and their length. Return YES if successful,
817+
* or NO on error (in which case, you should assign the actionError parameter
817818
*
818819
* - *writeData* Call this block to write some bytes into the archive. It returns NO if the
819820
* write failed. If this happens, you should return from the action block, and
@@ -824,6 +825,7 @@ compressionMethod:(UZKCompressionMethod)method
824825
*/
825826
- (BOOL)writeIntoBuffer:(NSString *)filePath
826827
fileDate:(nullable NSDate *)fileDate
828+
posixPermissions:(unsigned long)posixPermissions
827829
compressionMethod:(UZKCompressionMethod)method
828830
overwrite:(BOOL)overwrite
829831
CRC:(unsigned long)preCRC

Source/UZKArchive.m

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,10 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory
626626
UZKLogDebug("Closing file handle");
627627
[deflatedFileHandle closeFile];
628628

629-
NSDictionary* attribs = [NSDictionary dictionaryWithObjectsAndKeys:info.timestamp, NSFileModificationDate, nil];
629+
// Retain the permission attribute of a file
630+
NSDictionary* attribs = @{NSFileModificationDate: info.timestamp,
631+
NSFilePosixPermissions: info.posixPermissions};
632+
630633
[[NSFileManager defaultManager] setAttributes:attribs ofItemAtPath:path error:nil];
631634

632635
if (!extractSuccess) {
@@ -1058,6 +1061,7 @@ - (BOOL)writeData:(NSData *)data
10581061
return [self writeData:data
10591062
filePath:filePath
10601063
fileDate:nil
1064+
posixPermissions:0
10611065
compressionMethod:UZKCompressionMethodDefault
10621066
password:nil
10631067
overwrite:YES
@@ -1072,6 +1076,7 @@ - (BOOL)writeData:(NSData *)data
10721076
return [self writeData:data
10731077
filePath:filePath
10741078
fileDate:nil
1079+
posixPermissions:0
10751080
compressionMethod:UZKCompressionMethodDefault
10761081
password:nil
10771082
overwrite:YES
@@ -1087,6 +1092,7 @@ - (BOOL)writeData:(NSData *)data
10871092
return [self writeData:data
10881093
filePath:filePath
10891094
fileDate:fileDate
1095+
posixPermissions:0
10901096
compressionMethod:UZKCompressionMethodDefault
10911097
password:nil
10921098
overwrite:YES
@@ -1119,6 +1125,7 @@ - (BOOL)writeData:(NSData *)data
11191125
return [self writeData:data
11201126
filePath:filePath
11211127
fileDate:fileDate
1128+
posixPermissions:0
11221129
compressionMethod:method
11231130
password:password
11241131
overwrite:YES
@@ -1145,18 +1152,20 @@ - (BOOL)writeData:(NSData *)data
11451152

11461153
- (BOOL)writeData:(NSData *)data
11471154
filePath:(NSString *)filePath
1148-
fileDate:(NSDate *)fileDate
1155+
fileDate:(nullable NSDate *)fileDate
1156+
posixPermissions:(unsigned long)posixPermissions
11491157
compressionMethod:(UZKCompressionMethod)method
1150-
password:(NSString *)password
1158+
password:(nullable NSString *)password
11511159
overwrite:(BOOL)overwrite
1152-
error:(NSError * __autoreleasing*)error
1160+
error:(NSError *__autoreleasing*)error
11531161
{
11541162
#pragma clang diagnostic push
11551163
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
11561164
return [self writeData:data
11571165
filePath:filePath
11581166
fileDate:fileDate
1159-
compressionMethod:method
1167+
posixPermissions:posixPermissions
1168+
compressionMethod:UZKCompressionMethodDefault
11601169
password:password
11611170
overwrite:overwrite
11621171
progress:nil
@@ -1172,6 +1181,27 @@ - (BOOL)writeData:(NSData *)data
11721181
overwrite:(BOOL)overwrite
11731182
progress:(void (^)(CGFloat percentCompressed))progressBlock
11741183
error:(NSError * __autoreleasing*)error
1184+
{
1185+
return [self writeData:data
1186+
filePath:filePath
1187+
fileDate:fileDate
1188+
posixPermissions:0
1189+
compressionMethod:method
1190+
password:password
1191+
overwrite:overwrite
1192+
error:error];
1193+
}
1194+
1195+
1196+
- (BOOL)writeData:(NSData *)data
1197+
filePath:(NSString *)filePath
1198+
fileDate:(NSDate *)fileDate
1199+
posixPermissions:(unsigned long)posixPermissions
1200+
compressionMethod:(UZKCompressionMethod)method
1201+
password:(NSString *)password
1202+
overwrite:(BOOL)overwrite
1203+
progress:(void (^)(CGFloat percentCompressed))progressBlock
1204+
error:(NSError * __autoreleasing*)error
11751205
{
11761206
UZKCreateActivity("Writing Data");
11771207

@@ -1228,6 +1258,7 @@ - (BOOL)writeData:(NSData *)data
12281258
}
12291259
filePath:filePath
12301260
fileDate:fileDate
1261+
posixPermissions:posixPermissions
12311262
compressionMethod:method
12321263
password:password
12331264
overwrite:overwrite
@@ -1243,6 +1274,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
12431274
{
12441275
return [self writeIntoBuffer:filePath
12451276
fileDate:nil
1277+
posixPermissions:0
12461278
compressionMethod:UZKCompressionMethodDefault
12471279
overwrite:YES
12481280
CRC:0
@@ -1258,6 +1290,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
12581290
{
12591291
return [self writeIntoBuffer:filePath
12601292
fileDate:fileDate
1293+
posixPermissions:0
12611294
compressionMethod:UZKCompressionMethodDefault
12621295
overwrite:YES
12631296
CRC:0
@@ -1274,6 +1307,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
12741307
{
12751308
return [self writeIntoBuffer:filePath
12761309
fileDate:fileDate
1310+
posixPermissions:0
12771311
compressionMethod:method
12781312
overwrite:YES
12791313
CRC:0
@@ -1291,6 +1325,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
12911325
{
12921326
return [self writeIntoBuffer:filePath
12931327
fileDate:fileDate
1328+
posixPermissions:0
12941329
compressionMethod:method
12951330
overwrite:overwrite
12961331
CRC:0
@@ -1309,6 +1344,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
13091344
{
13101345
return [self writeIntoBuffer:filePath
13111346
fileDate:fileDate
1347+
posixPermissions:0
13121348
compressionMethod:method
13131349
overwrite:overwrite
13141350
CRC:preCRC
@@ -1319,6 +1355,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
13191355

13201356
- (BOOL)writeIntoBuffer:(NSString *)filePath
13211357
fileDate:(NSDate *)fileDate
1358+
posixPermissions:(unsigned long)posixPermissions
13221359
compressionMethod:(UZKCompressionMethod)method
13231360
overwrite:(BOOL)overwrite
13241361
CRC:(uLong)preCRC
@@ -1380,6 +1417,7 @@ - (BOOL)writeIntoBuffer:(NSString *)filePath
13801417
}
13811418
filePath:filePath
13821419
fileDate:fileDate
1420+
posixPermissions:posixPermissions
13831421
compressionMethod:method
13841422
password:password
13851423
overwrite:overwrite
@@ -1922,6 +1960,7 @@ - (BOOL)performActionWithArchiveOpen:(void(^)(NSError * __autoreleasing*innerErr
19221960
- (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerError))write
19231961
filePath:(NSString *)filePath
19241962
fileDate:(NSDate *)fileDate
1963+
posixPermissions:(unsigned long)posixPermissions
19251964
compressionMethod:(UZKCompressionMethod)method
19261965
password:(NSString *)password
19271966
overwrite:(BOOL)overwrite
@@ -1973,6 +2012,12 @@ - (BOOL)performWriteAction:(int(^)(uLong *crc, NSError * __autoreleasing*innerEr
19732012
UZKLogDebug("Making zip_fileinfo struct for date %{time_t}ld", lrint(fileDate.timeIntervalSince1970));
19742013
zip_fileinfo zi = [UZKArchive zipFileInfoForDate:fileDate];
19752014

2015+
if (posixPermissions > 0) {
2016+
2017+
// Revert the value of NSFilePosixPermissions to zip external_fa raw data
2018+
zi.external_fa = (posixPermissions + 32768) << 16;
2019+
}
2020+
19762021
const char *passwordStr = NULL;
19772022

19782023
if (password) {
@@ -2653,6 +2698,7 @@ + (zip_fileinfo)zipFileInfoForDate:(NSDate *)fileDate
26532698
zi.tmz_date.tm_year = (uInt)date.year;
26542699
zi.internal_fa = 0;
26552700
zi.external_fa = 0;
2701+
// zi.external_fa = 2175008768; // default posixPermissions value on zip: 2175008768 (0644U)
26562702
zi.dosDate = 0;
26572703

26582704
return zi;

Source/UZKFileInfo.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,24 @@ typedef NS_ENUM(NSInteger, UZKCompressionMethod) {
7373
*/
7474
@property (readonly) BOOL isDirectory;
7575

76+
/**
77+
* YES if the item is a symLink
78+
*/
79+
@property (readonly) BOOL isSymLink;
80+
81+
/**
82+
* YES if the item is a resource fork
83+
*/
84+
@property (readonly) BOOL isResourceFork;
85+
7686
/**
7787
* The type of compression
7888
*/
7989
@property (readonly) UZKCompressionMethod compressionMethod;
8090

81-
91+
/**
92+
@brief posixPermissions (posixPermissions of the file,The value from the file attributes - NSFilePosixPermissions)
93+
*/
94+
@property (nonatomic, readonly) NSNumber *posixPermissions;
8295

8396
@end

Source/UZKFileInfo.m

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,18 @@ - (instancetype)initWithFileInfo:(unz_file_info64 *)fileInfo filename:(NSString
3535
_zipTMUDate = fileInfo->tmu_date;
3636
_CRC = fileInfo->crc;
3737
_isEncryptedWithPassword = (fileInfo->flag & 1) != 0;
38-
_isDirectory = [filename hasSuffix:@"/"];
38+
// _isDirectory = [filename hasSuffix:@"/"];
39+
_isDirectory = [self isDirectoryWith:fileInfo];
40+
_isSymLink = [self isSymLinkWith:fileInfo->external_fa];
3941

4042
if (_isDirectory) {
4143
_filename = [_filename substringToIndex:_filename.length - 1];
4244
}
4345

46+
_isResourceFork = [[filename pathComponents].firstObject isEqualToString:@"__MACOSX"];
4447
_compressionMethod = [self readCompressionMethod:fileInfo->compression_method
4548
flag:fileInfo->flag];
49+
_posixPermissions = @((fileInfo->external_fa >> 16) ? (fileInfo->external_fa >> 16) : 0755U);
4650
}
4751
return self;
4852
}
@@ -102,4 +106,20 @@ - (NSDate *)readDate:(tm_unz)date
102106
return [calendar dateFromComponents:components];
103107
}
104108

109+
- (BOOL)isDirectoryWith:(unz_file_info64 *)fileInfo {
110+
// NSLog(@"ISDIR__ %@", S_ISDIR(fileInfo->external_fa)?@"YES":@"NO");
111+
uLong type = fileInfo->external_fa >> 0x1D & 0x1F;
112+
if (0 == (fileInfo->version >> 8)) { //is DOS-archive
113+
type = fileInfo->external_fa >> 4;
114+
return (type == 0x01) && ![self isSymLinkWith:fileInfo->external_fa];
115+
}
116+
return (0x02 == type) && ![self isSymLinkWith:fileInfo->external_fa];
117+
}
118+
119+
- (BOOL)isSymLinkWith:(uLong)externalFileAttributes {
120+
121+
uLong type = externalFileAttributes >> 0x1D & 0x1F;
122+
return 0x05 == type;
123+
}
124+
105125
@end

Tests/FileDescriptorUsageTests.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ - (void)testFileDescriptorUsage_WriteIntoArchive
146146
BOOL writeResult = [archive writeData:newFileData
147147
filePath:fileName
148148
fileDate:[NSDate date]
149+
posixPermissions:0
149150
compressionMethod:UZKCompressionMethodDefault
150151
password:nil
151152
overwrite:YES

0 commit comments

Comments
 (0)