@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in @@ -13,11 +13,13 @@ * file. */ #include "config.h" -#define _LARGEFILE64_SOURCE +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE +#endif #include #include #ifdef HAVE_DIRENT_H @@ -28,30 +30,40 @@ #include "platform.h" #ifdef HAVE_SYS_STAT_H # include #endif #include +#if defined(OF_LINUX) || defined(OF_MACOS) +# include +#endif #ifdef OF_WINDOWS # include #endif +#ifdef OF_DJGPP +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif #ifdef HAVE_PWD_H # include #endif #ifdef HAVE_GRP_H # include #endif -#import "OFFileURIHandler.h" +#import "OFFileIRIHandler.h" #import "OFArray.h" +#import "OFData.h" #import "OFDate.h" #import "OFFile.h" #import "OFFileManager.h" +#import "OFIRI.h" #import "OFLocale.h" #import "OFNumber.h" #import "OFSystemInfo.h" -#import "OFURI.h" #ifdef OF_HAVE_THREADS # import "OFMutex.h" #endif @@ -75,13 +87,15 @@ # include # include #endif #ifdef OF_AMIGAOS +# define Class IntuitionClass # include # include # include +# undef Class # ifdef OF_AMIGAOS4 # define DeleteFile(path) Delete(path) # endif #endif @@ -455,34 +469,14 @@ } #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS static void setSymbolicLinkDestinationAttribute(OFMutableFileAttributes attributes, - OFURI *URI) -{ - OFString *path = URI.fileSystemRepresentation; -# ifndef OF_WINDOWS - OFStringEncoding encoding = [OFLocale encoding]; - char destinationC[PATH_MAX]; - ssize_t length; - OFString *destination; - - length = readlink([path cStringWithEncoding: encoding], destinationC, - PATH_MAX); - - if (length < 0) - @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI - errNo: errno]; - - destination = [OFString stringWithCString: destinationC - encoding: encoding - length: length]; - - [attributes setObject: destination - forKey: OFFileSymbolicLinkDestination]; -# else + OFIRI *IRI) +{ + OFString *path = IRI.fileSystemRepresentation; +# ifdef OF_WINDOWS HANDLE handle; OFString *destination; if (createSymbolicLinkWFuncPtr == NULL) return; @@ -489,11 +483,11 @@ if ((handle = CreateFileW(path.UTF16String, 0, (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL)) == INVALID_HANDLE_VALUE) @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: lastError()]; @try { union { char bytes[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; @@ -504,16 +498,16 @@ if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer.bytes, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &size, NULL)) @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: lastError()]; if (buffer.data.ReparseTag != IO_REPARSE_TAG_SYMLINK) @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: lastError()]; # define slrb buffer.data.SymbolicLinkReparseBuffer tmp = slrb.PathBuffer + (slrb.SubstituteNameOffset / sizeof(wchar_t)); @@ -529,22 +523,117 @@ forKey: OFFileSymbolicLinkDestination]; # undef slrb } @finally { CloseHandle(handle); } +# elif defined(OF_HURD) + OFStringEncoding encoding = [OFLocale encoding]; + int fd; + OFMutableData *destinationData; + OFString *destination; + + fd = open([path cStringWithEncoding: encoding], O_RDONLY | O_NOLINK); + if (fd == -1) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: errno]; + + @try { + char buffer[512]; + ssize_t length; + + destinationData = [OFMutableData data]; + while ((length = read(fd, buffer, 512)) > 0) + [destinationData addItems: buffer count: length]; + } @finally { + close(fd); + } + + destination = [OFString stringWithCString: destinationData.items + encoding: encoding + length: destinationData.count]; + + [attributes setObject: destination + forKey: OFFileSymbolicLinkDestination]; +# else + OFStringEncoding encoding = [OFLocale encoding]; + char destinationC[PATH_MAX]; + ssize_t length; + OFString *destination; + + length = readlink([path cStringWithEncoding: encoding], destinationC, + PATH_MAX); + + if (length < 0) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: errno]; + + destination = [OFString stringWithCString: destinationC + encoding: encoding + length: length]; + + [attributes setObject: destination + forKey: OFFileSymbolicLinkDestination]; +# endif +} +#endif + +#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES +static void +setExtendedAttributes(OFMutableFileAttributes attributes, OFIRI *IRI) +{ + OFString *path = IRI.fileSystemRepresentation; + OFStringEncoding encoding = [OFLocale encoding]; + const char *cPath = [path cStringWithEncoding: encoding]; +# if defined(OF_LINUX) + ssize_t size = llistxattr(cPath, NULL, 0); +# elif defined(OF_MACOS) + ssize_t size = listxattr(cPath, NULL, 0, XATTR_NOFOLLOW); +# endif + char *list = OFAllocMemory(1, size); + OFMutableArray *names = nil; + + @try { + char *name; + +# if defined(OF_LINUX) + if ((size = llistxattr(cPath, list, size)) < 0) +# elif defined(OF_MACOS) + if ((size = listxattr(cPath, list, size, XATTR_NOFOLLOW)) < 0) # endif + return; + + names = [OFMutableArray array]; + name = list; + + while (size > 0) { + size_t length = strlen(name); + + [names addObject: [OFString stringWithCString: name + encoding: encoding + length: length]]; + + name += length + 1; + size -= length + 1; + } + } @finally { + OFFreeMemory(list); + } + + [attributes setObject: names forKey: OFFileExtendedAttributesNames]; } #endif -@implementation OFFileURIHandler +@implementation OFFileIRIHandler + (void)initialize { #ifdef OF_WINDOWS HMODULE module; #endif - if (self != [OFFileURIHandler class]) + if (self != [OFFileIRIHandler class]) return; #if defined(OF_FILE_MANAGER_SUPPORTS_OWNER) && defined(OF_HAVE_THREADS) passwdMutex = [[OFMutex alloc] init]; atexit(releasePasswdMutex); @@ -553,15 +642,15 @@ readdirMutex = [[OFMutex alloc] init]; atexit(releaseReaddirMutex); #endif #ifdef OF_WINDOWS - if ((module = LoadLibrary("msvcrt.dll")) != NULL) + if ((module = GetModuleHandle("msvcrt.dll")) != NULL) _wutime64FuncPtr = (int (*)(const wchar_t *, struct __utimbuf64 *))GetProcAddress(module, "_wutime64"); - if ((module = LoadLibrary("kernel32.dll")) != NULL) { + if ((module = GetModuleHandleA("kernel32.dll")) != NULL) { createSymbolicLinkWFuncPtr = (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, DWORD)) GetProcAddress(module, "CreateSymbolicLinkW"); createHardLinkWFuncPtr = (WINAPI BOOLEAN (*)(LPCWSTR, LPCWSTR, @@ -585,41 +674,41 @@ return false; return S_ISDIR(s.st_mode); } -- (OFStream *)openItemAtURI: (OFURI *)URI mode: (OFString *)mode +- (OFStream *)openItemAtIRI: (OFIRI *)IRI mode: (OFString *)mode { void *pool = objc_autoreleasePoolPush(); OFFile *file = [[OFFile alloc] - initWithPath: URI.fileSystemRepresentation + initWithPath: IRI.fileSystemRepresentation mode: mode]; objc_autoreleasePoolPop(pool); return [file autorelease]; } -- (OFFileAttributes)attributesOfItemAtURI: (OFURI *)URI +- (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI { OFMutableFileAttributes ret = [OFMutableDictionary dictionary]; void *pool = objc_autoreleasePoolPush(); OFString *path; int error; Stat s; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![[URI scheme] isEqual: _scheme]) + if (![[IRI scheme] isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - path = URI.fileSystemRepresentation; + path = IRI.fileSystemRepresentation; if ((error = lstatWrapper(path, &s)) != 0) @throw [OFGetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: error]; if (s.st_size < 0) @throw [OFOutOfRangeException exception]; @@ -634,24 +723,28 @@ setOwnerAndGroupAttributes(ret, &s); setDateAttributes(ret, &s); #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS if (S_ISLNK(s.st_mode)) - setSymbolicLinkDestinationAttribute(ret, URI); + setSymbolicLinkDestinationAttribute(ret, IRI); +#endif + +#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES + setExtendedAttributes(ret, IRI); #endif objc_autoreleasePoolPop(pool); return ret; } - (void)of_setLastAccessDate: (OFDate *)lastAccessDate andModificationDate: (OFDate *)modificationDate - ofItemAtURI: (OFURI *)URI + ofItemAtIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes OF_DIRECT { - OFString *path = URI.fileSystemRepresentation; + OFString *path = IRI.fileSystemRepresentation; OFFileAttributeKey attributeKey = (modificationDate != nil ? OFFileModificationDate : OFFileLastAccessDate); if (lastAccessDate == nil) lastAccessDate = modificationDate; @@ -665,16 +758,22 @@ (__time64_t)lastAccessDate.timeIntervalSince1970, .modtime = (__time64_t)modificationDate.timeIntervalSince1970 }; - if (_wutime64FuncPtr([path UTF16String], ×) != 0) + if (_wutime64FuncPtr([path UTF16String], ×) != 0) { + int errNo = errno; + + if (errNo == EACCES && [self directoryExistsAtIRI: IRI]) + errNo = EISDIR; + @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey - errNo: errno]; + errNo: errNo]; + } } else { struct _utimbuf times = { .actime = (time_t)lastAccessDate.timeIntervalSince1970, .modtime = (time_t)modificationDate.timeIntervalSince1970 @@ -686,16 +785,22 @@ else status = _utime( [path cStringWithEncoding: [OFLocale encoding]], ×); - if (status != 0) + if (status != 0) { + int errNo = errno; + + if (errNo == EACCES && [self directoryExistsAtIRI: IRI]) + errNo = EISDIR; + @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey - errNo: errno]; + errNo: errNo]; + } } #elif defined(OF_AMIGAOS) /* AmigaOS does not support access time. */ OFTimeInterval modificationTime = modificationDate.timeIntervalSince1970; @@ -726,11 +831,11 @@ # else if (!SetFileDate([path cStringWithEncoding: [OFLocale encoding]], &date) != 0) # endif @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey errNo: lastError()]; #else OFTimeInterval lastAccessTime = lastAccessDate.timeIntervalSince1970; @@ -737,36 +842,36 @@ OFTimeInterval modificationTime = modificationDate.timeIntervalSince1970; struct timeval times[2] = { { .tv_sec = (time_t)lastAccessTime, - .tv_usec = - (int)((lastAccessTime - times[0].tv_sec) * 1000000) + .tv_usec = (int)((lastAccessTime - + (time_t)lastAccessTime) * 1000000) }, { .tv_sec = (time_t)modificationTime, - .tv_usec = (int)((modificationTime - times[1].tv_sec) * - 1000000) + .tv_usec = (int)((modificationTime - + (time_t)modificationTime) * 1000000) }, }; if (utimes([path cStringWithEncoding: [OFLocale encoding]], times) != 0) @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey errNo: errno]; #endif } - (void)of_setPOSIXPermissions: (OFNumber *)permissions - ofItemAtURI: (OFURI *)URI + ofItemAtIRI: (OFIRI *)IRI attributes: (OFFileAttributes)attributes OF_DIRECT { #ifdef OF_FILE_MANAGER_SUPPORTS_PERMISSIONS mode_t mode = (mode_t)permissions.unsignedLongValue; - OFString *path = URI.fileSystemRepresentation; + OFString *path = IRI.fileSystemRepresentation; int status; # ifdef OF_WINDOWS if ([OFSystemInfo isWindowsNT]) status = _wchmod(path.UTF16String, mode); @@ -775,11 +880,11 @@ status = chmod( [path cStringWithEncoding: [OFLocale encoding]], mode); if (status != 0) @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: OFFilePOSIXPermissions errNo: errno]; #else OF_UNRECOGNIZED_SELECTOR @@ -786,16 +891,16 @@ #endif } - (void)of_setOwnerAccountName: (OFString *)owner andGroupOwnerAccountName: (OFString *)group - ofItemAtURI: (OFURI *)URI + ofItemAtIRI: (OFIRI *)IRI attributeKey: (OFFileAttributeKey)attributeKey attributes: (OFFileAttributes)attributes OF_DIRECT { #ifdef OF_FILE_MANAGER_SUPPORTS_OWNER - OFString *path = URI.fileSystemRepresentation; + OFString *path = IRI.fileSystemRepresentation; uid_t uid = -1; gid_t gid = -1; OFStringEncoding encoding; if (owner == nil && group == nil) @@ -811,11 +916,11 @@ struct passwd *passwd; if ((passwd = getpwnam([owner cStringWithEncoding: encoding])) == NULL) @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey errNo: errno]; uid = passwd->pw_uid; @@ -825,11 +930,11 @@ struct group *group_; if ((group_ = getgrnam([group cStringWithEncoding: encoding])) == NULL) @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey errNo: errno]; gid = group_->gr_gid; @@ -840,32 +945,32 @@ } # endif if (chown([path cStringWithEncoding: encoding], uid, gid) != 0) @throw [OFSetItemAttributesFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI attributes: attributes failedAttribute: attributeKey errNo: errno]; #else OF_UNRECOGNIZED_SELECTOR #endif } -- (void)setAttributes: (OFFileAttributes)attributes ofItemAtURI: (OFURI *)URI +- (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); OFEnumerator OF_GENERIC(OFFileAttributeKey) *keyEnumerator; OFEnumerator *objectEnumerator; OFFileAttributeKey key; id object; OFDate *lastAccessDate, *modificationDate; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; keyEnumerator = [attributes keyEnumerator]; objectEnumerator = [attributes objectEnumerator]; @@ -874,22 +979,22 @@ if ([key isEqual: OFFileModificationDate] || [key isEqual: OFFileLastAccessDate]) continue; else if ([key isEqual: OFFilePOSIXPermissions]) [self of_setPOSIXPermissions: object - ofItemAtURI: URI + ofItemAtIRI: IRI attributes: attributes]; else if ([key isEqual: OFFileOwnerAccountName]) [self of_setOwnerAccountName: object andGroupOwnerAccountName: nil - ofItemAtURI: URI + ofItemAtIRI: IRI attributeKey: key attributes: attributes]; else if ([key isEqual: OFFileGroupOwnerAccountName]) [self of_setOwnerAccountName: nil andGroupOwnerAccountName: object - ofItemAtURI: URI + ofItemAtIRI: IRI attributeKey: key attributes: attributes]; else @throw [OFNotImplementedException exceptionWithSelector: _cmd @@ -900,53 +1005,48 @@ modificationDate = [attributes objectForKey: OFFileModificationDate]; if (lastAccessDate != nil || modificationDate != nil) [self of_setLastAccessDate: lastAccessDate andModificationDate: modificationDate - ofItemAtURI: URI + ofItemAtIRI: IRI attributes: attributes]; objc_autoreleasePoolPop(pool); } -- (bool)fileExistsAtURI: (OFURI *)URI +- (bool)fileExistsAtIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); Stat s; bool ret; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - if (statWrapper(URI.fileSystemRepresentation, &s) != 0) { - objc_autoreleasePoolPop(pool); - return false; - } - - ret = S_ISREG(s.st_mode); + ret = (statWrapper(IRI.fileSystemRepresentation, &s) == 0); objc_autoreleasePoolPop(pool); return ret; } -- (bool)directoryExistsAtURI: (OFURI *)URI +- (bool)directoryExistsAtIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); Stat s; bool ret; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - if (statWrapper(URI.fileSystemRepresentation, &s) != 0) { + if (statWrapper(IRI.fileSystemRepresentation, &s) != 0) { objc_autoreleasePoolPop(pool); return false; } ret = S_ISDIR(s.st_mode); @@ -954,22 +1054,22 @@ objc_autoreleasePoolPop(pool); return ret; } -- (void)createDirectoryAtURI: (OFURI *)URI +- (void)createDirectoryAtIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); OFString *path; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - path = URI.fileSystemRepresentation; + path = IRI.fileSystemRepresentation; #if defined(OF_WINDOWS) int status; if ([OFSystemInfo isWindowsNT]) @@ -978,45 +1078,45 @@ status = _mkdir( [path cStringWithEncoding: [OFLocale encoding]]); if (status != 0) @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: errno]; #elif defined(OF_AMIGAOS) BPTR lock; if ((lock = CreateDir( [path cStringWithEncoding: [OFLocale encoding]])) == 0) @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: lastError()]; UnLock(lock); #else if (mkdir([path cStringWithEncoding: [OFLocale encoding]], 0777) != 0) @throw [OFCreateDirectoryFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: errno]; #endif objc_autoreleasePoolPop(pool); } -- (OFArray OF_GENERIC(OFURI *) *)contentsOfDirectoryAtURI: (OFURI *)URI +- (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI { - OFMutableArray *URIs = [OFMutableArray array]; + OFMutableArray *IRIs = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); OFString *path; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - path = URI.fileSystemRepresentation; + path = IRI.fileSystemRepresentation; #if defined(OF_WINDOWS) HANDLE handle; path = [path stringByAppendingString: @"\\*"]; @@ -1025,11 +1125,11 @@ WIN32_FIND_DATAW fd; if ((handle = FindFirstFileW(path.UTF16String, &fd)) == INVALID_HANDLE_VALUE) @throw [OFOpenItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI mode: nil errNo: lastError()]; @try { do { @@ -1040,12 +1140,12 @@ continue; file = [[OFString alloc] initWithUTF16String: fd.cFileName]; @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; } @finally { [file release]; } } while (FindNextFileW(handle, &fd)); @@ -1063,11 +1163,11 @@ if ((handle = FindFirstFileA( [path cStringWithEncoding: encoding], &fd)) == INVALID_HANDLE_VALUE) @throw [OFOpenItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI mode: nil errNo: lastError()]; @try { do { @@ -1079,12 +1179,12 @@ file = [[OFString alloc] initWithCString: fd.cFileName encoding: encoding]; @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; } @finally { [file release]; } } while (FindNextFileA(handle, &fd)); @@ -1102,11 +1202,11 @@ BPTR lock; if ((lock = Lock([path cStringWithEncoding: encoding], SHARED_LOCK)) == 0) @throw [OFOpenItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI mode: nil errNo: lastError()]; @try { # ifdef OF_AMIGAOS4 @@ -1115,11 +1215,11 @@ if ((context = ObtainDirContextTags(EX_FileLockInput, lock, EX_DoCurrentDir, TRUE, EX_DataFields, EXF_NAME, TAG_END)) == NULL) @throw [OFOpenItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI mode: nil errNo: lastError()]; @try { while ((ed = ExamineDir(context)) != NULL) { @@ -1126,12 +1226,12 @@ OFString *file = [[OFString alloc] initWithCString: ed->Name encoding: encoding]; @try { - [URIs addObject: [URI - URIByAppendingPathComponent: file]]; + [IRIs addObject: [IRI + IRIByAppendingPathComponent: file]]; } @finally { [file release]; } } } @finally { @@ -1140,21 +1240,21 @@ # else struct FileInfoBlock fib; if (!Examine(lock, &fib)) @throw [OFOpenItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI mode: nil errNo: lastError()]; while (ExNext(lock, &fib)) { OFString *file = [[OFString alloc] initWithCString: fib.fib_FileName encoding: encoding]; @try { - [URIs addObject: - [URI URIByAppendingPathComponent: file]]; + [IRIs addObject: + [IRI IRIByAppendingPathComponent: file]]; } @finally { [file release]; } } # endif @@ -1169,11 +1269,11 @@ } #else OFStringEncoding encoding = [OFLocale encoding]; DIR *dir; if ((dir = opendir([path cStringWithEncoding: encoding])) == NULL) - @throw [OFOpenItemFailedException exceptionWithURI: URI + @throw [OFOpenItemFailedException exceptionWithIRI: IRI mode: nil errNo: errno]; # if !defined(HAVE_READDIR_R) && defined(OF_HAVE_THREADS) @try { @@ -1219,12 +1319,12 @@ continue; file = [[OFString alloc] initWithCString: dirent->d_name encoding: encoding]; @try { - [URIs addObject: - [URI URIByAppendingPathComponent: file]]; + [IRIs addObject: + [IRI IRIByAppendingPathComponent: file]]; } @finally { [file release]; } } } @finally { @@ -1233,41 +1333,41 @@ [readdirMutex unlock]; # endif } #endif - [URIs makeImmutable]; + [IRIs makeImmutable]; objc_autoreleasePoolPop(pool); - return URIs; + return IRIs; } -- (void)removeItemAtURI: (OFURI *)URI +- (void)removeItemAtIRI: (OFIRI *)IRI { void *pool = objc_autoreleasePoolPush(); OFString *path; int error; Stat s; - if (URI == nil) + if (IRI == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - path = URI.fileSystemRepresentation; + path = IRI.fileSystemRepresentation; if ((error = lstatWrapper(path, &s)) != 0) - @throw [OFRemoveItemFailedException exceptionWithURI: URI + @throw [OFRemoveItemFailedException exceptionWithIRI: IRI errNo: error]; if (S_ISDIR(s.st_mode)) { - OFArray OF_GENERIC(OFURI *) *contents; + OFArray OF_GENERIC(OFIRI *) *contents; @try { - contents = [self contentsOfDirectoryAtURI: URI]; + contents = [self contentsOfDirectoryAtIRI: IRI]; } @catch (id e) { /* * Only convert exceptions to * OFRemoveItemFailedException that have an errNo * property. This covers all I/O related exceptions @@ -1274,20 +1374,20 @@ * from the operations used to remove an item, all * others should be left as is. */ if ([e respondsToSelector: @selector(errNo)]) @throw [OFRemoveItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: [e errNo]]; @throw e; } - for (OFURI *item in contents) { + for (OFIRI *item in contents) { void *pool2 = objc_autoreleasePoolPush(); - [self removeItemAtURI: item]; + [self removeItemAtIRI: item]; objc_autoreleasePoolPop(pool2); } #ifndef OF_AMIGAOS @@ -1301,11 +1401,11 @@ status = rmdir( [path cStringWithEncoding: [OFLocale encoding]]); if (status != 0) @throw [OFRemoveItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: errno]; } else { int status; # ifdef OF_WINDOWS @@ -1316,27 +1416,27 @@ status = unlink( [path cStringWithEncoding: [OFLocale encoding]]); if (status != 0) @throw [OFRemoveItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: errno]; #endif } #ifdef OF_AMIGAOS if (!DeleteFile([path cStringWithEncoding: [OFLocale encoding]])) @throw [OFRemoveItemFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI errNo: lastError()]; #endif objc_autoreleasePoolPop(pool); } #ifdef OF_FILE_MANAGER_SUPPORTS_LINKS -- (void)linkItemAtURI: (OFURI *)source toURI: (OFURI *)destination +- (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { void *pool = objc_autoreleasePoolPush(); OFString *sourcePath, *destinationPath; if (source == nil || destination == nil) @@ -1353,52 +1453,52 @@ OFStringEncoding encoding = [OFLocale encoding]; if (link([sourcePath cStringWithEncoding: encoding], [destinationPath cStringWithEncoding: encoding]) != 0) @throw [OFLinkItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: errno]; # else if (createHardLinkWFuncPtr == NULL) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; if (!createHardLinkWFuncPtr(destinationPath.UTF16String, sourcePath.UTF16String, NULL)) @throw [OFLinkItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: lastError()]; # endif objc_autoreleasePoolPop(pool); } #endif #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS -- (void)createSymbolicLinkAtURI: (OFURI *)URI +- (void)createSymbolicLinkAtIRI: (OFIRI *)IRI withDestinationPath: (OFString *)target { void *pool = objc_autoreleasePoolPush(); OFString *path; - if (URI == nil || target == nil) + if (IRI == nil || target == nil) @throw [OFInvalidArgumentException exception]; - if (![URI.scheme isEqual: _scheme]) + if (![IRI.scheme isEqual: _scheme]) @throw [OFInvalidArgumentException exception]; - path = URI.fileSystemRepresentation; + path = IRI.fileSystemRepresentation; # ifndef OF_WINDOWS OFStringEncoding encoding = [OFLocale encoding]; if (symlink([target cStringWithEncoding: encoding], [path cStringWithEncoding: encoding]) != 0) @throw [OFCreateSymbolicLinkFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI target: target errNo: errno]; # else if (createSymbolicLinkWFuncPtr == NULL) @throw [OFNotImplementedException exceptionWithSelector: _cmd @@ -1405,31 +1505,31 @@ object: self]; if (!createSymbolicLinkWFuncPtr(path.UTF16String, target.UTF16String, 0)) @throw [OFCreateSymbolicLinkFailedException - exceptionWithURI: URI + exceptionWithIRI: IRI target: target errNo: lastError()]; # endif objc_autoreleasePoolPop(pool); } #endif -- (bool)moveItemAtURI: (OFURI *)source toURI: (OFURI *)destination +- (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { void *pool; if (![source.scheme isEqual: _scheme] || ![destination.scheme isEqual: _scheme]) return false; - if ([self fileExistsAtURI: destination]) + if ([self fileExistsAtIRI: destination]) @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: EEXIST]; pool = objc_autoreleasePoolPush(); #ifdef OF_AMIGAOS @@ -1438,12 +1538,12 @@ if (!Rename([source.fileSystemRepresentation cStringWithEncoding: encoding], [destination.fileSystemRepresentation cStringWithEncoding: encoding])) @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: lastError()]; #else int status; # ifdef OF_WINDOWS @@ -1462,15 +1562,113 @@ } # endif if (status != 0) @throw [OFMoveItemFailedException - exceptionWithSourceURI: source - destinationURI: destination + exceptionWithSourceIRI: source + destinationIRI: destination errNo: errno]; #endif objc_autoreleasePoolPop(pool); return true; } + +#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES +- (OFData *)extendedAttributeDataForName: (OFString *)name + ofItemAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = IRI.fileSystemRepresentation; + OFStringEncoding encoding = [OFLocale encoding]; + const char *cPath = [path cStringWithEncoding: encoding]; + const char *cName = [name cStringWithEncoding: encoding]; +# if defined(OF_LINUX) + ssize_t size = lgetxattr(cPath, cName, NULL, 0); +# elif defined(OF_MACOS) + ssize_t size = getxattr(cPath, cName, NULL, 0, 0, XATTR_NOFOLLOW); +# endif + void *value = OFAllocMemory(1, size); + OFData *data; + + @try { +# if defined(OF_LINUX) + if ((size = lgetxattr(cPath, cName, value, size)) < 0) +# elif defined(OF_MACOS) + if ((size = getxattr(cPath, cName, value, size, 0, + XATTR_NOFOLLOW)) < 0) +# endif + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: errno]; + + data = [OFData dataWithItems: value count: size]; + } @finally { + OFFreeMemory(value); + } + + [data retain]; + + objc_autoreleasePoolPop(pool); + + return [data autorelease]; +} + +- (void)setExtendedAttributeData: (OFData *)data + forName: (OFString *)name + ofItemAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = IRI.fileSystemRepresentation; + OFStringEncoding encoding = [OFLocale encoding]; + +# if defined(OF_LINUX) + if (lsetxattr([path cStringWithEncoding: encoding], + [name cStringWithEncoding: encoding], data.items, + data.count * data.itemSize, 0) != 0) { +# elif defined(OF_MACOS) + if (setxattr([path cStringWithEncoding: encoding], + [name cStringWithEncoding: encoding], data.items, + data.count * data.itemSize, 0, XATTR_NOFOLLOW) != 0) { +# endif + int errNo = errno; + + /* TODO: Add an attribute (prefix?) for extended attributes? */ + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: [OFDictionary dictionary] + failedAttribute: @"" + errNo: errNo]; + } + + objc_autoreleasePoolPop(pool); +} + +- (void)removeExtendedAttributeForName: (OFString *)name + ofItemAtIRI: (OFIRI *)IRI +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = IRI.fileSystemRepresentation; + OFStringEncoding encoding = [OFLocale encoding]; + +# if defined(OF_LINUX) + if (lremovexattr([path cStringWithEncoding: encoding], + [name cStringWithEncoding: encoding]) != 0) { +# elif defined(OF_MACOS) + if (removexattr([path cStringWithEncoding: encoding], + [name cStringWithEncoding: encoding], XATTR_NOFOLLOW) != 0) { +# endif + int errNo = errno; + + /* TODO: Add an attribute (prefix?) for extended attributes? */ + @throw [OFSetItemAttributesFailedException + exceptionWithIRI: IRI + attributes: [OFDictionary dictionary] + failedAttribute: @"" + errNo: errNo]; + } + + objc_autoreleasePoolPop(pool); +} +#endif @end