Home | History | Annotate | Download | only in Misc
      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 
     87 typedef struct MetaDataInfo
     88 {
     89     CFStringRef URLString;
     90     CFStringRef referrer;
     91     CFStringRef path;
     92 } MetaDataInfo;
     93 
     94 static 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.
    149 static 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