1/* 2 * Copyright (C) 2005 Apple Computer, 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 <WebCore/FoundationExtras.h> 32#import <WebKit/WebKitNSStringExtras.h> 33#import <WebKitSystemInterface.h> 34#import <wtf/Assertions.h> 35 36#import <pthread.h> 37#import <sys/mount.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 // Spawn a background thread for WKSetMetadataURL because this function will not return until mds has 175 // journaled the data we're're trying to set. Depending on what other I/O is going on, it can take some 176 // time. 177 pthread_t tid; 178 pthread_attr_t attr; 179 pthread_attr_init(&attr); 180 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 181 182 MetaDataInfo *info = malloc(sizeof(MetaDataInfo)); 183 184 info->URLString = HardRetainWithNSRelease([URLString copy]); 185 info->referrer = HardRetainWithNSRelease([referrer copy]); 186 info->path = HardRetainWithNSRelease([path copy]); 187 188 pthread_create(&tid, &attr, setMetaData, info); 189 pthread_attr_destroy(&attr); 190} 191 192- (NSString *)_webkit_startupVolumeName 193{ 194 NSString *path = [self _webkit_carbonPathForPath:@"/"]; 195 return [path substringToIndex:[path length]-1]; 196} 197 198- (NSString *)_webkit_pathWithUniqueFilenameForPath:(NSString *)path 199{ 200 // "Fix" the filename of the path. 201 NSString *filename = [[path lastPathComponent] _webkit_filenameByFixingIllegalCharacters]; 202 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename]; 203 204 NSFileManager *fileManager = [NSFileManager defaultManager]; 205 if ([fileManager fileExistsAtPath:path]) { 206 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename. 207 NSString *extensions = nil; 208 NSString *pathWithoutExtensions; 209 NSString *lastPathComponent = [path lastPathComponent]; 210 NSRange periodRange = [lastPathComponent rangeOfString:@"."]; 211 212 if (periodRange.location == NSNotFound) { 213 pathWithoutExtensions = path; 214 } else { 215 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1]; 216 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location]; 217 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent]; 218 } 219 220 NSString *pathWithAppendedNumber; 221 unsigned i; 222 223 for (i = 1; 1; i++) { 224 pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i]; 225 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber; 226 if (![fileManager fileExistsAtPath:path]) { 227 break; 228 } 229 } 230 } 231 232 return path; 233} 234 235@end 236 237 238#ifdef BUILDING_ON_TIGER 239@implementation NSFileManager (WebNSFileManagerTigerForwardCompatibility) 240 241- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error 242{ 243 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 244 ASSERT_ARG(error, !error); 245 246 return [self directoryContentsAtPath:path]; 247} 248 249- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path error:(NSError **)error 250{ 251 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 252 ASSERT_ARG(error, !error); 253 254 return [self pathContentOfSymbolicLinkAtPath:path]; 255} 256 257- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path error:(NSError **)error 258{ 259 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 260 ASSERT_ARG(error, !error); 261 262 return [self fileSystemAttributesAtPath:path]; 263} 264 265- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error 266{ 267 // We don't report errors via the NSError* output parameter, so ensure that the caller does not expect us to do so. 268 ASSERT_ARG(error, !error); 269 270 return [self fileAttributesAtPath:path traverseLink:NO]; 271} 272 273- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error 274{ 275 // The implementation of moveItemAtPath:toPath:error: interacts with the NSFileManager's delegate. 276 // We are not matching that behaviour at the moment, but it should not be a problem as any client 277 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 278 // as that method is not available on Tiger. 279 return [self movePath:srcPath toPath:dstPath handler:nil]; 280} 281 282- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error 283{ 284 // The implementation of removeItemAtPath:error: interacts with the NSFileManager's delegate. 285 // We are not matching that behaviour at the moment, but it should not be a problem as any client 286 // expecting that would need to call setDelegate: first which will generate a compile-time warning, 287 // as that method is not available on Tiger. 288 return [self removeFileAtPath:path handler:nil]; 289} 290 291@end 292#endif 293