1/* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#import <WebKit/WebNSFileManagerExtras.h> 30 31#import "WebKitNSStringExtras.h" 32#import "WebNSURLExtras.h" 33#import <JavaScriptCore/Assertions.h> 34#import <WebKitSystemInterface.h> 35#import <sys/stat.h> 36 37@implementation NSFileManager (WebNSFileManagerExtras) 38 39- (NSString *)_webkit_carbonPathForPath:(NSString *)posixPath 40{ 41 OSStatus error; 42 FSRef ref, rootRef, parentRef; 43 FSCatalogInfo info; 44 NSMutableArray *carbonPathPieces; 45 HFSUniStr255 nameString; 46 47 // Make an FSRef. 48 error = FSPathMakeRef((const UInt8 *)[posixPath fileSystemRepresentation], &ref, NULL); 49 if (error != noErr) { 50 return nil; 51 } 52 53 // Get volume refNum. 54 error = FSGetCatalogInfo(&ref, kFSCatInfoVolume, &info, NULL, NULL, NULL); 55 if (error != noErr) { 56 return nil; 57 } 58 59 // Get root directory FSRef. 60 error = FSGetVolumeInfo(info.volume, 0, NULL, kFSVolInfoNone, NULL, NULL, &rootRef); 61 if (error != noErr) { 62 return nil; 63 } 64 65 // Get the pieces of the path. 66 carbonPathPieces = [NSMutableArray array]; 67 for (;;) { 68 error = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, &nameString, NULL, &parentRef); 69 if (error != noErr) { 70 return nil; 71 } 72 [carbonPathPieces insertObject:[NSString stringWithCharacters:nameString.unicode length:nameString.length] atIndex:0]; 73 if (FSCompareFSRefs(&ref, &rootRef) == noErr) { 74 break; 75 } 76 ref = parentRef; 77 } 78 79 // Volume names need trailing : character. 80 if ([carbonPathPieces count] == 1) { 81 [carbonPathPieces addObject:@""]; 82 } 83 84 return [carbonPathPieces componentsJoinedByString:@":"]; 85} 86 87typedef struct MetaDataInfo 88{ 89 CFStringRef URLString; 90 CFStringRef referrer; 91 CFStringRef path; 92} MetaDataInfo; 93 94static void *setMetaData(void* context) 95{ 96 MetaDataInfo *info = (MetaDataInfo *)context; 97 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 98 WKSetMetadataURL((NSString *)info->URLString, (NSString *)info->referrer, (NSString *)info->path); 99 100 if (info->URLString) 101 CFRelease(info->URLString); 102 if (info->referrer) 103 CFRelease(info->referrer); 104 if (info->path) 105 CFRelease(info->path); 106 107 free(info); 108 [pool drain]; 109 110 return 0; 111} 112 113- (void)_webkit_setMetadataURL:(NSString *)URLString referrer:(NSString *)referrer atPath:(NSString *)path 114{ 115 ASSERT(URLString); 116 ASSERT(path); 117 118 NSURL *URL = [NSURL _web_URLWithUserTypedString:URLString]; 119 if (URL) 120 URLString = [[URL _web_URLByRemovingUserInfo] _web_userVisibleString]; 121 122 // Spawn a background thread for WKSetMetadataURL because this function will not return until mds has 123 // journaled the data we're're trying to set. Depending on what other I/O is going on, it can take some 124 // time. 125 pthread_t tid; 126 pthread_attr_t attr; 127 pthread_attr_init(&attr); 128 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 129 130 MetaDataInfo *info = malloc(sizeof(MetaDataInfo)); 131 132 info->URLString = URLString ? CFStringCreateCopy(0, (CFStringRef)URLString) : 0; 133 info->referrer = referrer ? CFStringCreateCopy(0, (CFStringRef)referrer) : 0; 134 info->path = path ? CFStringCreateCopy(0, (CFStringRef)path) : 0; 135 136 pthread_create(&tid, &attr, setMetaData, info); 137 pthread_attr_destroy(&attr); 138} 139 140- (NSString *)_webkit_startupVolumeName 141{ 142 NSString *path = [self _webkit_carbonPathForPath:@"/"]; 143 return [path substringToIndex:[path length]-1]; 144} 145 146// -[NSFileManager fileExistsAtPath:] returns NO if there is a broken symlink at the path. 147// So we use this function instead, which returns YES if there is anything there, including 148// a broken symlink. 149static BOOL fileExists(NSString *path) 150{ 151 struct stat statBuffer; 152 return !lstat([path fileSystemRepresentation], &statBuffer); 153} 154 155- (NSString *)_webkit_pathWithUniqueFilenameForPath:(NSString *)path 156{ 157 // "Fix" the filename of the path. 158 NSString *filename = [[path lastPathComponent] _webkit_filenameByFixingIllegalCharacters]; 159 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename]; 160 161 if (fileExists(path)) { 162 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename. 163 NSString *extensions = nil; 164 NSString *pathWithoutExtensions; 165 NSString *lastPathComponent = [path lastPathComponent]; 166 NSRange periodRange = [lastPathComponent rangeOfString:@"."]; 167 168 if (periodRange.location == NSNotFound) { 169 pathWithoutExtensions = path; 170 } else { 171 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1]; 172 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location]; 173 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent]; 174 } 175 176 for (unsigned i = 1; ; i++) { 177 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i]; 178 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber; 179 if (!fileExists(path)) 180 break; 181 } 182 } 183 184 return path; 185} 186 187@end 188 189#ifdef BUILDING_ON_TIGER 190 191@implementation NSFileManager (WebNSFileManagerTigerForwardCompatibility) 192 193- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error 194{ 195 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 196 ASSERT_ARG(error, !error); 197 198 return [self directoryContentsAtPath:path]; 199} 200 201- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path error:(NSError **)error 202{ 203 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 204 ASSERT_ARG(error, !error); 205 206 return [self pathContentOfSymbolicLinkAtPath:path]; 207} 208 209- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error 210{ 211 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 212 ASSERT_ARG(error, !error); 213 214 return [self fileSystemAttributesAtPath:path]; 215} 216 217- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error 218{ 219 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 220 ASSERT_ARG(error, !error); 221 222 return [self fileAttributesAtPath:path traverseLink:NO]; 223} 224 225- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error 226{ 227 // The implementation of moveItemAtPath:toPath:error: interacts with the NSFileManager's delegate. 228 // We are not matching that behaviour at the moment, but it should not be a problem as any client 229 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 230 // as that method is not available on Tiger. 231 return [self movePath:srcPath toPath:dstPath handler:nil]; 232} 233 234- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error 235{ 236 // The implementation of removeItemAtPath:error: interacts with the NSFileManager's delegate. 237 // We are not matching that behaviour at the moment, but it should not be a problem as any client 238 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 239 // as that method is not available on Tiger. 240 return [self removeFileAtPath:path handler:nil]; 241} 242 243@end 244 245#endif 246