cog/Frameworks/Sparkle/SUBinaryDeltaCommon.m

166 lines
4.7 KiB
Objective-C

//
// SUBinaryDeltaCommon.m
// Sparkle
//
// Created by Mark Rowe on 2009-06-01.
// Copyright 2009 Mark Rowe. All rights reserved.
//
#include "SUBinaryDeltaCommon.h"
#include <CommonCrypto/CommonDigest.h>
#include <Foundation/Foundation.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
extern int xar_close(void*) __attribute__((weak_import));
int binaryDeltaSupported(void)
{
// OS X 10.4 didn't include libxar, so we link against it weakly.
// This checks whether libxar is available at runtime.
return xar_close != 0;
}
int compareFiles(const FTSENT **a, const FTSENT **b)
{
return strcoll((*a)->fts_name, (*b)->fts_name);
}
NSString *pathRelativeToDirectory(NSString *directory, NSString *path)
{
NSUInteger directoryLength = [directory length];
if ([path hasPrefix:directory])
return [path substringFromIndex:directoryLength];
return path;
}
NSString *temporaryFilename(NSString *base)
{
NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]];
char buffer[MAXPATHLEN];
strcpy(buffer, [template fileSystemRepresentation]);
return [NSString stringWithUTF8String:mktemp(buffer)];
}
static void _hashOfBuffer(unsigned char *hash, const char* buffer, size_t bufferLength)
{
assert(bufferLength <= UINT32_MAX);
CC_SHA1_CTX hashContext;
CC_SHA1_Init(&hashContext);
CC_SHA1_Update(&hashContext, buffer, (CC_LONG)bufferLength);
CC_SHA1_Final(hash, &hashContext);
}
static void _hashOfFile(unsigned char* hash, FTSENT *ent)
{
if (ent->fts_info == FTS_SL) {
char linkDestination[MAXPATHLEN + 1];
ssize_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN);
if (linkDestinationLength < 0) {
perror("readlink");
return;
}
_hashOfBuffer(hash, linkDestination, linkDestinationLength);
return;
}
if (ent->fts_info == FTS_F) {
int fileDescriptor = open(ent->fts_path, O_RDONLY);
if (fileDescriptor == -1) {
perror("open");
return;
}
size_t fileSize = (size_t)ent->fts_statp->st_size;
if (fileSize == 0) {
_hashOfBuffer(hash, NULL, 0);
close(fileDescriptor);
return;
}
void *buffer = mmap(0, fileSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fileDescriptor, 0);
if (buffer == (void*)-1) {
close(fileDescriptor);
perror("mmap");
return;
}
_hashOfBuffer(hash, buffer, fileSize);
munmap(buffer, fileSize);
close(fileDescriptor);
return;
}
if (ent->fts_info == FTS_D)
memset(hash, 0xdd, CC_SHA1_DIGEST_LENGTH);
}
NSData *hashOfFile(FTSENT *ent)
{
unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
_hashOfFile(fileHash, ent);
return [NSData dataWithBytes:fileHash length:CC_SHA1_DIGEST_LENGTH];
}
NSString *hashOfTree(NSString *path)
{
const char *sourcePaths[] = {[path UTF8String], 0};
FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
if (!fts) {
perror("fts_open");
return nil;
}
CC_SHA1_CTX hashContext;
CC_SHA1_Init(&hashContext);
FTSENT *ent = 0;
while ((ent = fts_read(fts))) {
if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
continue;
unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
_hashOfFile(fileHash, ent);
CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash));
NSString *relativePath = pathRelativeToDirectory(path, [NSString stringWithUTF8String:ent->fts_path]);
NSData *relativePathBytes = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
CC_SHA1_Update(&hashContext, [relativePathBytes bytes], (uint32_t)[relativePathBytes length]);
}
fts_close(fts);
unsigned char hash[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(hash, &hashContext);
char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1];
size_t i;
for (i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
sprintf(hexHash + i * 2, "%02x", hash[i]);
return [NSString stringWithUTF8String:hexHash];
}
void removeTree(NSString *path)
{
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
[[NSFileManager defaultManager] removeItemAtPath:path error:0];
#else
[[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
#endif
}
void copyTree(NSString *source, NSString *dest)
{
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
[[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:0];
#else
[[NSFileManager defaultManager] copyPath:source toPath:dest handler:nil];
#endif
}