Index: src/OFFileIRIHandler.m ================================================================== --- src/OFFileIRIHandler.m +++ src/OFFileIRIHandler.m @@ -28,10 +28,13 @@ #include "platform.h" #ifdef HAVE_SYS_STAT_H # include #endif #include +#ifdef OF_LINUX +# include +#endif #ifdef OF_WINDOWS # include #endif #ifdef OF_DJGPP # include @@ -44,10 +47,11 @@ # include #endif #import "OFFileIRIHandler.h" #import "OFArray.h" +#import "OFData.h" #import "OFDate.h" #import "OFFile.h" #import "OFFileManager.h" #import "OFIRI.h" #import "OFLocale.h" @@ -535,10 +539,48 @@ CloseHandle(handle); } # endif } #endif + +#ifdef OF_LINUX +static void +setExtendedAttributes(OFMutableFileAttributes attributes, OFIRI *IRI) +{ + OFString *path = IRI.fileSystemRepresentation; + OFStringEncoding encoding = [OFLocale encoding]; + const char *cPath = [path cStringWithEncoding: encoding]; + ssize_t size = llistxattr(cPath, NULL, 0); + char *list = OFAllocMemory(1, size); + OFMutableArray *names = nil; + + @try { + char *name; + + if ((size = llistxattr(cPath, list, size)) < 0) + 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 OFFileIRIHandler + (void)initialize { #ifdef OF_WINDOWS @@ -639,10 +681,14 @@ #ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS if (S_ISLNK(s.st_mode)) setSymbolicLinkDestinationAttribute(ret, IRI); #endif + +#ifdef OF_LINUX + setExtendedAttributes(ret, IRI); +#endif objc_autoreleasePoolPop(pool); return ret; } @@ -1486,6 +1532,37 @@ objc_autoreleasePoolPop(pool); return true; } + +#ifdef OF_LINUX +- (OFData *)extendedAttributeForName: (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]; + ssize_t size = lgetxattr(cPath, cName, NULL, 0); + void *value = OFAllocMemory(1, size); + OFData *data; + + @try { + if ((size = lgetxattr(cPath, cName, value, size)) < 0) + @throw [OFGetItemAttributesFailedException + exceptionWithIRI: IRI + errNo: errno]; + + data = [OFData dataWithItems: value count: size]; + } @finally { + OFFreeMemory(value); + } + + [data retain]; + + objc_autoreleasePoolPop(pool); + + return [data autorelease]; +} +#endif @end Index: src/OFFileManager.h ================================================================== --- src/OFFileManager.h +++ src/OFFileManager.h @@ -31,10 +31,13 @@ # define OF_FILE_MANAGER_SUPPORTS_LINKS # endif # if (defined(OF_HAVE_SYMLINK) && !defined(OF_AMIGAOS)) || defined(OF_WINDOWS) # define OF_FILE_MANAGER_SUPPORTS_SYMLINKS # endif +# ifdef OF_LINUX +# define OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES +# endif #endif @class OFArray OF_GENERIC(ObjectType); @class OFConstantString; @class OFDate; @@ -56,10 +59,11 @@ * * @ref OFFileLastAccessDate * * @ref OFFileModificationDate * * @ref OFFileStatusChangeDate * * @ref OFFileCreationDate * * @ref OFFileSymbolicLinkDestination + * * @ref OFFileExtendedAttributesNames * * Other IRI schemes might not have all keys and might have keys not listed. */ typedef OFConstantString *OFFileAttributeKey; @@ -186,17 +190,26 @@ * via @ref OFDictionary#fileCreationDate. */ extern const OFFileAttributeKey OFFileCreationDate; /** - * @brief The destination of a symbolic link as an OFString. + * @brief The destination of a symbolic link as an @ref OFString. * * For convenience, a category on @ref OFDictionary is provided to access this * via @ref OFDictionary#fileSymbolicLinkDestination. */ extern const OFFileAttributeKey OFFileSymbolicLinkDestination; +/** + * @brief The names of the extended attributes as an @ref OFArray of + * @ref OFString. + * + * For convenience, a category on @ref OFDictionary is provided to access this + * via @ref OFDictionary#fileExtendedAttributesNames. + */ +extern const OFFileAttributeKey OFFileExtendedAttributesNames; + /** * @brief A regular file. */ extern const OFFileAttributeType OFFileTypeRegular; @@ -657,20 +670,51 @@ * The destination IRI must have a full path, which means it must include the * name of the item. * * This method is not available for all IRIs. * - * @note On Windows, this requires at least Windows Vista and administrator - * privileges! + * @note For file IRIs on Windows, this requires at least Windows Vista and + * administrator privileges! * * @param IRI The IRI to the item which should symbolically link to the target * @param target The target of the symbolic link + * @throw OFOFCreateSymbolicLinkFailedException Creating a symbolic link failed * @throw OFUnsupportedProtocolException No handler is registered for the IRI's * scheme */ - (void)createSymbolicLinkAtIRI: (OFIRI *)IRI withDestinationPath: (OFString *)target; + +#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES +/** + * @brief Returns the extended attribute with the specified name for the + * specified path. + * + * @param name The name of the extended attribute + * @param path The path of the item to return the extended attribute from + * @throw OFGetItemAttributesFailedException Getting the extended attribute + * failed + */ +- (OFData *)extendedAttributeForName: (OFString *)name + ofItemAtPath: (OFString *)path; +#endif + +/** + * @brief Returns the extended attribute with the specified name for the + * specified IRI. + * + * This method is not available for all IRIs. + * + * @param name The name of the extended attribute + * @param IRI The IRI of the item to return the extended attribute from + * @throw OFGetItemAttributesFailedException Getting the extended attribute + * failed + * @throw OFUnsupportedProtocolException No handler is registered for the IRI's + * scheme + */ +- (OFData *)extendedAttributeForName: (OFString *)name + ofItemAtIRI: (OFIRI *)IRI; @end @interface OFDictionary (FileAttributes) /** * @brief The @ref OFFileSize key from the dictionary. @@ -753,8 +797,16 @@ * @brief The @ref OFFileSymbolicLinkDestination key from the dictionary. * * @throw OFUndefinedKeyException The key is missing */ @property (readonly, nonatomic) OFString *fileSymbolicLinkDestination; + +/** + * @brief The @ref OFFileExtendedAttributesNames key from the dictionary. + * + * @throw OFUndefinedKeyException The key is missing + */ +@property (readonly, nonatomic) + OFArray OF_GENERIC(OFString *) *fileExtendedAttributesNames; @end OF_ASSUME_NONNULL_END Index: src/OFFileManager.m ================================================================== --- src/OFFileManager.m +++ src/OFFileManager.m @@ -886,10 +886,40 @@ [self createSymbolicLinkAtIRI: [OFIRI fileIRIWithPath: path] withDestinationPath: target]; objc_autoreleasePoolPop(pool); } #endif + +- (OFData *)extendedAttributeForName: (OFString *)name ofItemAtIRI: (OFIRI *)IRI +{ + OFIRIHandler *IRIHandler; + + if (IRI == nil) + @throw [OFInvalidArgumentException exception]; + + if ((IRIHandler = [OFIRIHandler handlerForIRI: IRI]) == nil) + @throw [OFUnsupportedProtocolException exceptionWithIRI: IRI]; + + return [IRIHandler extendedAttributeForName: name ofItemAtIRI: IRI]; +} + +#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES +- (OFData *)extendedAttributeForName: (OFString *)name + ofItemAtPath: (OFString *)path +{ + void *pool = objc_autoreleasePoolPush(); + OFData *ret; + + ret = [self extendedAttributeForName: name + ofItemAtIRI: [OFIRI fileIRIWithPath: path]]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} +#endif @end @implementation OFDefaultFileManager - (instancetype)autorelease { @@ -973,6 +1003,11 @@ - (OFString *)fileSymbolicLinkDestination { return attributeForKeyOrException(self, OFFileSymbolicLinkDestination); } + +- (OFArray OF_GENERIC(OFString *) *)fileExtendedAttributesNames +{ + return attributeForKeyOrException(self, OFFileExtendedAttributesNames); +} @end Index: src/OFFileManagerConstants.inc ================================================================== --- src/OFFileManagerConstants.inc +++ src/OFFileManagerConstants.inc @@ -26,10 +26,12 @@ const OFFileAttributeKey OFFileModificationDate = @"OFFileModificationDate"; const OFFileAttributeKey OFFileStatusChangeDate = @"OFFileStatusChangeDate"; const OFFileAttributeKey OFFileCreationDate = @"OFFileCreationDate"; const OFFileAttributeKey OFFileSymbolicLinkDestination = @"OFFileSymbolicLinkDestination"; +const OFFileAttributeKey OFFileExtendedAttributesNames = + @"OFFileExtendedAttributesNames"; const OFFileAttributeType OFFileTypeRegular = @"OFFileTypeRegular"; const OFFileAttributeType OFFileTypeDirectory = @"OFFileTypeDirectory"; const OFFileAttributeType OFFileTypeSymbolicLink = @"OFFileTypeSymbolicLink"; const OFFileAttributeType OFFileTypeFIFO = @"OFFileTypeFIFO"; Index: src/OFIRIHandler.h ================================================================== --- src/OFIRIHandler.h +++ src/OFIRIHandler.h @@ -18,10 +18,11 @@ #import "OFString.h" OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); +@class OFData; @class OFDate; @class OFIRI; @class OFStream; /** @@ -128,10 +129,14 @@ * @brief Returns the attributes for the item at the specified IRI. * * @param IRI The IRI to return the attributes for * @return A dictionary of attributes for the specified IRI, with the keys of * type @ref OFFileAttributeKey + * @throw OFGetItemAttributesFailedException Failed to get the attributes of + * the item + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (OFFileAttributes)attributesOfItemAtIRI: (OFIRI *)IRI; /** * @brief Sets the attributes for the item at the specified IRI. @@ -138,33 +143,47 @@ * * All attributes not part of the dictionary are left unchanged. * * @param attributes The attributes to set for the specified IRI * @param IRI The IRI of the item to set the attributes for + * @@throw OFSetItemAttributesFailedException Failed to set the attributes of + * the item + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme + * @throw OFNotImplementedException Setting one or more of the specified + * attributes is not implpemented for the + * specified item */ - (void)setAttributes: (OFFileAttributes)attributes ofItemAtIRI: (OFIRI *)IRI; /** * @brief Checks whether a file exists at the specified IRI. * * @param IRI The IRI to check * @return A boolean whether there is a file at the specified IRI + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (bool)fileExistsAtIRI: (OFIRI *)IRI; /** * @brief Checks whether a directory exists at the specified IRI. * * @param IRI The IRI to check * @return A boolean whether there is a directory at the specified IRI + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (bool)directoryExistsAtIRI: (OFIRI *)IRI; /** * @brief Creates a directory at the specified IRI. * * @param IRI The IRI of the directory to create + * @throw OFCreateDirectoryFailedException Creating the directory failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (void)createDirectoryAtIRI: (OFIRI *)IRI; /** * @brief Returns an array with the IRIs of the items in the specified @@ -172,19 +191,26 @@ * * @note `.` and `..` are not part of the returned array. * * @param IRI The IRI to the directory whose items should be returned * @return An array with the IRIs of the items in the specified directory + * @throw OFOpenItemFailedException Opening the directory failed + * @throw OFReadFailedException Reading from the directory failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (OFArray OF_GENERIC(OFIRI *) *)contentsOfDirectoryAtIRI: (OFIRI *)IRI; /** * @brief Removes the item at the specified IRI. * * If the item at the specified IRI is a directory, it is removed recursively. * * @param IRI The IRI to the item which should be removed + * @throw OFRemoveItemFailedException Removing the item failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (void)removeItemAtIRI: (OFIRI *)IRI; /** * @brief Creates a hard link for the specified item. @@ -194,10 +220,15 @@ * * This method is not available for all IRIs. * * @param source The IRI to the item for which a link should be created * @param destination The IRI to the item which should link to the source + * @throw OFLinkItemFailedException Linking the item failed + * @throw OFUnsupportedProtocolException The handler cannot handle the scheme + * of one of the IRIs + * @throw OFNotImplementedException Hardlinks are not implemented for the + * specified IRI */ - (void)linkItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; /** * @brief Creates a symbolic link for an item. @@ -210,10 +241,13 @@ * @note On Windows, this requires at least Windows Vista and administrator * privileges! * * @param IRI The IRI to the item which should symbolically link to the target * @param target The target of the symbolic link + * @throw OFCreateSymbolicLinkFailed Creating a symbolic link failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (void)createSymbolicLinkAtIRI: (OFIRI *)IRI withDestinationPath: (OFString *)target; /** @@ -230,10 +264,13 @@ * @param source The file, directory or symbolic link to copy * @param destination The destination IRI * @return True if an efficient copy was performed, false if an efficient copy * was not possible. Note that errors while performing a copy are * reported via exceptions and not by returning false! + * @throw OFCopyItemFailedException Copying failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (bool)copyItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; /** * @brief Tries to efficiently move an item. If a move would only be possible @@ -248,10 +285,27 @@ * @param source The item to rename * @param destination The new name for the item * @return True if an efficient move was performed, false if an efficient move * was not possible. Note that errors while performing a move are * reported via exceptions and not by returning false! + * @throw OFMoveItemFailedException Moving failed + * @throw OFUnsupportedProtocolException The handler cannot handle the IRI's + * scheme */ - (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination; + +/** + * @brief Returns the extended attribute with the specified name for the + * specified IRI. + * + * This method is not available for all IRIs. + * + * @param name The name of the extended attribute + * @param IRI The IRI of the item to return the extended attribute from + * @throw OFGetItemAttributesFailedException Getting the extended attribute + * failed + */ +- (OFData *)extendedAttributeForName: (OFString *)name + ofItemAtIRI: (OFIRI *)IRI; @end OF_ASSUME_NONNULL_END Index: src/OFIRIHandler.m ================================================================== --- src/OFIRIHandler.m +++ src/OFIRIHandler.m @@ -211,6 +211,11 @@ - (bool)moveItemAtIRI: (OFIRI *)source toIRI: (OFIRI *)destination { return false; } + +- (OFData *)extendedAttributeForName: (OFString *)name ofItemAtIRI: (OFIRI *)IRI +{ + return nil; +} @end