Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 270 lines (219 sloc) 8.906 kb
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
1 //
2 // SUBinaryDeltaTool.m
3 // Sparkle
4 //
5 // Created by Mark Rowe on 2009-06-01.
6 // Copyright 2009 Mark Rowe. All rights reserved.
7 //
8
3ff2d82 Fix warnings and binary compatibility issues that crop up when building ...
Mark Rowe authored
9 #define _DARWIN_NO_64_BIT_INODE 1
10
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
11 #include "SUBinaryDeltaCommon.h"
12 #include "SUBinaryDeltaApply.h"
13 #include <CommonCrypto/CommonDigest.h>
14 #include <Foundation/Foundation.h>
15 #include <fcntl.h>
16 #include <fts.h>
17 #include <libgen.h>
18 #include <stdio.h>
19 #include <sys/mman.h>
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <xar/xar.h>
24
25 extern int bsdiff(int argc, const char **argv);
26
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
27 @interface CreateBinaryDeltaOperation : NSOperation
28 {
29 NSString *_relativePath;
30 NSString *_fromPath;
31 NSString *_toPath;
32 NSString *_resultPath;
33 }
34 - (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree;
35
36 - (NSString *)relativePath;
37 - (NSString *)resultPath;
38 @end
39
40 @implementation CreateBinaryDeltaOperation
41
42 - (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree
43 {
44 if ((self = [super init])) {
45 _relativePath = [relativePath copy];
46 _fromPath = [[oldTree stringByAppendingPathComponent:relativePath] retain];
47 _toPath = [[newTree stringByAppendingPathComponent:relativePath] retain];
48 }
49 return self;
50 }
51
52 - (NSString *)relativePath
53 {
54 return [[_relativePath retain] autorelease];
55 }
56
57 - (NSString *)resultPath
58 {
59 return [[_resultPath retain] autorelease];
60 }
61
62 - (void)main
63 {
64 NSString *temporaryFile = temporaryFilename(@"BinaryDelta");
65 const char *argv[] = {"/usr/bin/bsdiff", [_fromPath fileSystemRepresentation], [_toPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]};
66 int result = bsdiff(4, argv);
67 if (!result)
68 _resultPath = [temporaryFile retain];
69 }
70
71 @end
72
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
73 static NSDictionary *infoForFile(FTSENT *ent)
74 {
75 NSData *hash = hashOfFile(ent);
76 NSNumber *size = nil;
77 if (ent->fts_info != FTS_D)
78 size = [NSNumber numberWithUnsignedLongLong:ent->fts_statp->st_size];
79 return [NSDictionary dictionaryWithObjectsAndKeys:hash, @"hash", [NSNumber numberWithUnsignedShort:ent->fts_info], @"type", size, @"size", nil];
80 }
81
82 static NSString *absolutePath(NSString *path)
83 {
84 NSURL *url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
85 return [[url absoluteURL] path];
86 }
87
88 static NSString *temporaryPatchFile(NSString *patchFile)
89 {
90 NSString *path = absolutePath(patchFile);
91 NSString *directory = [path stringByDeletingLastPathComponent];
92 NSString *file = [path lastPathComponent];
93 return [NSString stringWithFormat:@"%@/.%@.tmp", directory, file];
94 }
95
96 static BOOL shouldSkipDeltaCompression(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
97 {
3ff2d82 Fix warnings and binary compatibility issues that crop up when building ...
Mark Rowe authored
98 unsigned long long fileSize = [[newInfo objectForKey:@"size"] unsignedLongLongValue];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
99 if (fileSize < 4096)
100 return YES;
101
102 if (!originalInfo)
103 return YES;
104
105 if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
106 return YES;
107
108 return NO;
109 }
110
111 static BOOL shouldDeleteThenExtract(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
112 {
113 if (!originalInfo)
114 return NO;
115
116 if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
117 return YES;
118
119 return NO;
120 }
121
122 int main(int argc, char **argv)
123 {
124 if (argc != 5) {
125 usage:
126 fprintf(stderr, "Usage: BinaryDelta [create | apply] before-tree after-tree patch-file\n");
127 exit(1);
128 }
129
130 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
131
132 NSString *command = [NSString stringWithUTF8String:argv[1]];
133 NSString *oldPath = [NSString stringWithUTF8String:argv[2]];
134 NSString *newPath = [NSString stringWithUTF8String:argv[3]];
135 NSString *patchFile = [NSString stringWithUTF8String:argv[4]];
136
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
137 if ([command isEqualToString:@"apply"]) {
138 int result = applyBinaryDelta(oldPath, newPath, patchFile);
139 [pool drain];
140 return result;
141 }
142 if (![command isEqualToString:@"create"]) {
143 [pool drain];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
144 goto usage;
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
145 }
146
147 NSMutableDictionary *originalTreeState = [NSMutableDictionary dictionary];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
148
149 const char *sourcePaths[] = {[oldPath fileSystemRepresentation], 0};
150 FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
151 if (!fts) {
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
152 [pool drain];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
153 perror("fts_open");
154 return 1;
155 }
156
157 fprintf(stderr, "Processing %s...", [oldPath UTF8String]);
158 FTSENT *ent = 0;
159 while ((ent = fts_read(fts))) {
160 if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
161 continue;
162
163 NSString *key = pathRelativeToDirectory(oldPath, [NSString stringWithUTF8String:ent->fts_path]);
164 if (![key length])
165 continue;
166
167 NSDictionary *info = infoForFile(ent);
168 [originalTreeState setObject:info forKey:key];
169 }
170 fts_close(fts);
171
172 NSString *beforeHash = hashOfTree(oldPath);
173
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
174 NSMutableDictionary *newTreeState = [NSMutableDictionary dictionary];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
175 for (NSString *key in originalTreeState)
176 {
177 [newTreeState setObject:[NSNull null] forKey:key];
178 }
179
180 fprintf(stderr, "\nProcessing %s... ", [newPath UTF8String]);
181 sourcePaths[0] = [newPath fileSystemRepresentation];
182 fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
183 if (!fts) {
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
184 [pool drain];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
185 perror("fts_open");
186 return 1;
187 }
188
189
190 while ((ent = fts_read(fts))) {
191 if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
192 continue;
193
194 NSString *key = pathRelativeToDirectory(newPath, [NSString stringWithUTF8String:ent->fts_path]);
195 if (![key length])
196 continue;
197
198 NSDictionary *info = infoForFile(ent);
199 NSDictionary *oldInfo = [originalTreeState objectForKey:key];
200
201 if ([info isEqual:oldInfo])
202 [newTreeState removeObjectForKey:key];
203 else
204 [newTreeState setObject:info forKey:key];
205 }
206 fts_close(fts);
207
208 NSString *afterHash = hashOfTree(newPath);
209
210 fprintf(stderr, "\nGenerating delta... ");
211
212 NSString *temporaryFile = temporaryPatchFile(patchFile);
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
213 xar_t x = xar_open([temporaryFile fileSystemRepresentation], WRITE);
214 xar_opt_set(x, XAR_OPT_COMPRESSION, "bzip2");
215 xar_subdoc_t attributes = xar_subdoc_new(x, "binary-delta-attributes");
216 xar_subdoc_prop_set(attributes, "before-sha1", [beforeHash UTF8String]);
217 xar_subdoc_prop_set(attributes, "after-sha1", [afterHash UTF8String]);
218
219 NSOperationQueue *deltaQueue = [[NSOperationQueue alloc] init];
220 NSMutableArray *deltaOperations = [NSMutableArray array];
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
221
222 NSArray *keys = [[newTreeState allKeys] sortedArrayUsingSelector:@selector(compare:)];
223 for (NSString* key in keys) {
224 id value = [newTreeState valueForKey:key];
225
226 if ([value isEqual:[NSNull null]]) {
5975703 @andymatuschak Fixed some silly compiler warnings.
andymatuschak authored
227 xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], (char *)"", 1);
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
228 assert(newFile);
229 xar_prop_set(newFile, "delete", "true");
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
230 continue;
231 }
232
233 NSDictionary *originalInfo = [originalTreeState objectForKey:key];
234 NSDictionary *newInfo = [newTreeState objectForKey:key];
235 if (shouldSkipDeltaCompression(key, originalInfo, newInfo)) {
236 NSString *path = [newPath stringByAppendingPathComponent:key];
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
237 xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]);
238 assert(newFile);
239 if (shouldDeleteThenExtract(key, originalInfo, newInfo))
240 xar_prop_set(newFile, "delete-then-extract", "true");
241 } else {
242 CreateBinaryDeltaOperation *operation = [[CreateBinaryDeltaOperation alloc] initWithRelativePath:key oldTree:oldPath newTree:newPath];
243 [deltaQueue addOperation:operation];
244 [deltaOperations addObject:operation];
245 [operation release];
246 }
247 }
248
249 [deltaQueue waitUntilAllOperationsAreFinished];
250 [deltaQueue release];
251
252 for (CreateBinaryDeltaOperation *operation in deltaOperations) {
253 NSString *resultPath = [operation resultPath];
254 xar_file_t newFile = xar_add_frompath(x, 0, [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]);
255 assert(newFile);
256 xar_prop_set(newFile, "binary-delta", "true");
257 unlink([resultPath fileSystemRepresentation]);
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
258 }
259
6169234 Rework the BinaryDelta tool to use NSOperationQueue in order to be sligh...
Mark Rowe authored
260 xar_close(x);
c3d8254 Add a command-line tool that can generate and apply a binary delta betwe...
Mark Rowe authored
261
262 unlink([patchFile fileSystemRepresentation]);
263 link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]);
264 unlink([temporaryFile fileSystemRepresentation]);
265 fprintf(stderr, "Done!\n");
266
267 [pool drain];
268 return 0;
269 }
Something went wrong with that request. Please try again.