1/* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 <WebCore/FoundationExtras.h> 34#import <WebKitSystemInterface.h> 35#import <pthread.h> 36#import <sys/mount.h> 37#import <JavaScriptCore/Assertions.h> 38 39@implementation NSFileManager (WebNSFileManagerExtras) 40 41- (BOOL)_webkit_removeFileOnlyAtPath:(NSString *)path 42{ 43 struct statfs buf; 44 BOOL result = unlink([path fileSystemRepresentation]) == 0; 45 46 // For mysterious reasons, MNT_DOVOLFS is the flag for "supports resource fork" 47 if ((statfs([path fileSystemRepresentation], &buf) == 0) && !(buf.f_flags & MNT_DOVOLFS)) { 48 NSString *lastPathComponent = [path lastPathComponent]; 49 if ([lastPathComponent length] != 0 && ![lastPathComponent isEqualToString:@"/"]) { 50 NSString *resourcePath = [[path stringByDeletingLastPathComponent] stringByAppendingString:[@"._" stringByAppendingString:lastPathComponent]]; 51 if (unlink([resourcePath fileSystemRepresentation]) != 0) { 52 result = NO; 53 } 54 } 55 } 56 57 return result; 58} 59 60- (void)_webkit_backgroundRemoveFileAtPath:(NSString *)path 61{ 62 NSFileManager *manager; 63 NSString *moveToSubpath; 64 NSString *moveToPath; 65 int i; 66 67 manager = [NSFileManager defaultManager]; 68 69 i = 0; 70 moveToSubpath = [path stringByDeletingLastPathComponent]; 71 do { 72 moveToPath = [NSString stringWithFormat:@"%@/.tmp%d", moveToSubpath, i]; 73 i++; 74 } while ([manager fileExistsAtPath:moveToPath]); 75 76 if ([manager moveItemAtPath:path toPath:moveToPath error:NULL]) 77 [NSThread detachNewThreadSelector:@selector(_performRemoveFileAtPath:) toTarget:self withObject:moveToPath]; 78} 79 80- (void)_webkit_backgroundRemoveLeftoverFiles:(NSString *)path 81{ 82 NSFileManager *manager; 83 NSString *leftoverSubpath; 84 NSString *leftoverPath; 85 int i; 86 87 manager = [NSFileManager defaultManager]; 88 leftoverSubpath = [path stringByDeletingLastPathComponent]; 89 90 i = 0; 91 while (1) { 92 leftoverPath = [NSString stringWithFormat:@"%@/.tmp%d", leftoverSubpath, i]; 93 if (![manager fileExistsAtPath:leftoverPath]) { 94 break; 95 } 96 [NSThread detachNewThreadSelector:@selector(_performRemoveFileAtPath:) toTarget:self withObject:leftoverPath]; 97 i++; 98 } 99} 100 101- (NSString *)_webkit_carbonPathForPath:(NSString *)posixPath 102{ 103 OSStatus error; 104 FSRef ref, rootRef, parentRef; 105 FSCatalogInfo info; 106 NSMutableArray *carbonPathPieces; 107 HFSUniStr255 nameString; 108 109 // Make an FSRef. 110 error = FSPathMakeRef((const UInt8 *)[posixPath fileSystemRepresentation], &ref, NULL); 111 if (error != noErr) { 112 return nil; 113 } 114 115 // Get volume refNum. 116 error = FSGetCatalogInfo(&ref, kFSCatInfoVolume, &info, NULL, NULL, NULL); 117 if (error != noErr) { 118 return nil; 119 } 120 121 // Get root directory FSRef. 122 error = FSGetVolumeInfo(info.volume, 0, NULL, kFSVolInfoNone, NULL, NULL, &rootRef); 123 if (error != noErr) { 124 return nil; 125 } 126 127 // Get the pieces of the path. 128 carbonPathPieces = [NSMutableArray array]; 129 for (;;) { 130 error = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, &nameString, NULL, &parentRef); 131 if (error != noErr) { 132 return nil; 133 } 134 [carbonPathPieces insertObject:[NSString stringWithCharacters:nameString.unicode length:nameString.length] atIndex:0]; 135 if (FSCompareFSRefs(&ref, &rootRef) == noErr) { 136 break; 137 } 138 ref = parentRef; 139 } 140 141 // Volume names need trailing : character. 142 if ([carbonPathPieces count] == 1) { 143 [carbonPathPieces addObject:@""]; 144 } 145 146 return [carbonPathPieces componentsJoinedByString:@":"]; 147} 148 149typedef struct MetaDataInfo 150{ 151 NSString *URLString; 152 NSString *referrer; 153 NSString *path; 154} MetaDataInfo; 155 156static void *setMetaData(void* context) 157{ 158 MetaDataInfo *info = (MetaDataInfo *)context; 159 WKSetMetadataURL(info->URLString, info->referrer, info->path); 160 161 HardRelease(info->URLString); 162 HardRelease(info->referrer); 163 HardRelease(info->path); 164 165 free(info); 166 return 0; 167} 168 169- (void)_webkit_setMetadataURL:(NSString *)URLString referrer:(NSString *)referrer atPath:(NSString *)path 170{ 171 ASSERT(URLString); 172 ASSERT(path); 173 174 NSURL *URL = [NSURL _web_URLWithUserTypedString:URLString]; 175 if (URL) 176 URLString = [[URL _web_URLByRemovingUserInfo] _web_userVisibleString]; 177 178 // Spawn a background thread for WKSetMetadataURL because this function will not return until mds has 179 // journaled the data we're're trying to set. Depending on what other I/O is going on, it can take some 180 // time. 181 pthread_t tid; 182 pthread_attr_t attr; 183 pthread_attr_init(&attr); 184 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 185 186 MetaDataInfo *info = malloc(sizeof(MetaDataInfo)); 187 188 info->URLString = HardRetainWithNSRelease([URLString copy]); 189 info->referrer = HardRetainWithNSRelease([referrer copy]); 190 info->path = HardRetainWithNSRelease([path copy]); 191 192 pthread_create(&tid, &attr, setMetaData, info); 193 pthread_attr_destroy(&attr); 194} 195 196- (NSString *)_webkit_startupVolumeName 197{ 198 NSString *path = [self _webkit_carbonPathForPath:@"/"]; 199 return [path substringToIndex:[path length]-1]; 200} 201 202- (NSString *)_webkit_pathWithUniqueFilenameForPath:(NSString *)path 203{ 204 // "Fix" the filename of the path. 205 NSString *filename = [[path lastPathComponent] _webkit_filenameByFixingIllegalCharacters]; 206 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename]; 207 208 NSFileManager *fileManager = [NSFileManager defaultManager]; 209 if ([fileManager fileExistsAtPath:path]) { 210 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename. 211 NSString *extensions = nil; 212 NSString *pathWithoutExtensions; 213 NSString *lastPathComponent = [path lastPathComponent]; 214 NSRange periodRange = [lastPathComponent rangeOfString:@"."]; 215 216 if (periodRange.location == NSNotFound) { 217 pathWithoutExtensions = path; 218 } else { 219 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1]; 220 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location]; 221 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent]; 222 } 223 224 NSString *pathWithAppendedNumber; 225 unsigned i; 226 227 for (i = 1; 1; i++) { 228 pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i]; 229 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber; 230 if (![fileManager fileExistsAtPath:path]) { 231 break; 232 } 233 } 234 } 235 236 return path; 237} 238 239@end 240 241 242#ifdef BUILDING_ON_TIGER 243@implementation NSFileManager (WebNSFileManagerTigerForwardCompatibility) 244 245- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error 246{ 247 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 248 ASSERT_ARG(error, !error); 249 250 return [self directoryContentsAtPath:path]; 251} 252 253- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path error:(NSError **)error 254{ 255 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 256 ASSERT_ARG(error, !error); 257 258 return [self pathContentOfSymbolicLinkAtPath:path]; 259} 260 261- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error 262{ 263 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 264 ASSERT_ARG(error, !error); 265 266 return [self fileSystemAttributesAtPath:path]; 267} 268 269- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error 270{ 271 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 272 ASSERT_ARG(error, !error); 273 274 return [self fileAttributesAtPath:path traverseLink:NO]; 275} 276 277- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error 278{ 279 // The implementation of moveItemAtPath:toPath:error: interacts with the NSFileManager's delegate. 280 // We are not matching that behaviour at the moment, but it should not be a problem as any client 281 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 282 // as that method is not available on Tiger. 283 return [self movePath:srcPath toPath:dstPath handler:nil]; 284} 285 286- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error 287{ 288 // The implementation of removeItemAtPath:error: interacts with the NSFileManager's delegate. 289 // We are not matching that behaviour at the moment, but it should not be a problem as any client 290 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 291 // as that method is not available on Tiger. 292 return [self removeFileAtPath:path handler:nil]; 293} 294 295@end 296#endif 297