Extended attributes, like the quarantine flag and those storing details of the URL from which an item was downloaded, are becoming increasingly common. Some files rely on them, and removal of a key extended attribute (xattr) may render that file useless. Although generally preserved when copying and moving files within a Mac, iCloud imposes restrictions on them, and will strip many. This article details which xattrs are preserved across iCloud, and what you can do to ensure your xattrs aren’t tampered with.
Extended attributes (xattrs)
Files in macOS can have three parts: their data, which is stored in the main part of a disk; attributes or file system metadata, which include information such as when the file was created, last changed, and last accessed; and xattrs, which can contain all sorts of more or less valuable information, and are stored in the file system metadata.
Everyday actions such as duplicating or moving a file have different consequences on those three parts. Whilst the data should always be preserved, the file’s attributes may change, for example showing a different date of creation. When duplicating, copying or moving a file between local storage, xattrs should remain unchanged, as they normally do.
When copying files across networks, provided that both file systems support xattrs (or know how to handle them) and the protocol used is good (SMB and AFP, for instance), then all xattrs should be transferred faithfully. That’s the case when using file sharing in macOS, and it even works nicely over AirDrop. Cloud services like iCloud are different, though, as they may be vast distributed databases which provide a virtual file system. Some handle xattrs fine, but some ignore them completely.
iCloud sits somewhere in the middle: it normally preserves some xattrs, but some it will always strip. Its behaviour also depends on whether you access the file from the same system which put it into iCloud, or from another system connected through the same user account. As behaviour with xattrs is determined by iCloud rather than macOS, it can change without any warning; it also appears to be undocumented. The results given here apply to iCloud accessed using Catalina 10.15.3 on 17 March 2020. You’re likely to get the same results from other versions of macOS and iOS, but it could all change tomorrow or later this year, regardless of which operating system you’re using.
When one Mac both uploads the file to iCloud and accesses that same file, all xattrs appear to be preserved. This ensures that, if you put your Desktop and Documents folders in iCloud, those files are handled essentially the same as they would be in local storage. However, when you access files which have been uploaded to iCloud from a different Mac or iOS device, iCloud doesn’t preserve all xattrs by any means. Some are often but not always preserved, others usually stripped, and one type invariably stripped.
The following major xattrs are consistently preserved across iCloud:
- com.apple.security.* (probably)
The following major xattrs should be generally preserved across iCloud:
These are relatively recent additions, and in previous iCloud behaviour these have been stripped. Strangely, if their name is suffixed with #S (see below) they will be stripped rather than preserved.
The following major xattrs are usually stripped across iCloud:
- third-party xattrs such as net_sourceforge_skim-app_text_notes, whose name doesn’t start with com.apple.
These can be preserved if their name is suffixed with #S (see below), but without that you should expect them to be stripped. Note that the com.apple.macl xattr is normally protected by SIP, but when copied that protection is normally removed.
One major type of xattr appears always to be stripped across iCloud:
This is the modern xattr equivalent of the Mac’s traditional ‘resource fork’, and appears invariably to be stripped even if its name is suffixed with #S.
These behaviours apply regardless of whether the file is passed through an iCloud Drive folder, or Desktop & Documents in iCloud, provided that the file is being accessed from a different Mac or device from the one which uploaded the file.
How to preserve xattrs which are normally stripped
If you need to share or move files using iCloud and want to preserve all their xattrs, one simple way is to compress the file into a Zip archive first, upload it to iCloud, download it on the other system, and decompress the file. Standard Zip options, as offered by Archive Utility, preserve all xattrs, although at the command line you can use options which will strip them all instead. Other archive and compression utilities should offer similar features.
There is also a more sophisticated system of flags which is built into macOS, but unfortunately causes problems of its own.
In 2013, as part of its enhancements for iCloud in particular, Apple added support for flags on xattrs to indicate how those xattrs should be treated when the file is copied in various ways. Rather than change the file system, Apple opted for what is perhaps best seen as an elegant kludge: appending characters to the end of the xattr’s name.
If you work with xattrs, you’ve probably already seen this in xattrs whose name ends with a hash # then one or more characters: that’s actually the flags, not part of the name, what Apple refers to as a ‘property list’. To avoid confusion I won’t use that term here, but refer to them as xattr flags. A common example of this is
com.apple.lastuseddate#PS, which is seen quite widely.
Flags can be upper or lower case letters C, N, P and S, and invariably follow the # separator, which is presumably otherwise forbidden from use in a xattr’s name. Upper case sets (enables) that property, whilst lower case clears (disables) that property. The properties include:
XATTR_FLAG_CONTENT_DEPENDENT, which ties the flag and the file contents, so the xattr is rewritten when the file data changes. This can be used for checksums and hashes, text encoding, and position information. The xattr is preserved for copy and share, but not in a safe save.
XATTR_FLAG_NO_EXPORT, which doesn’t export the xattr, but normally preserves it during copying.
XATTR_FLAG_NEVER_PRESERVE, which ensures the xattr is never copied, even when copying the file.
XATTR_FLAG_SYNCABLE, which ensures the xattr is preserved even during syncing. Default behaviour is for xattrs to be stripped during syncing, to minimise the amount of data to be transferred, but this will override that default.
These must operate within another general restriction of xattrs: their name cannot exceed a maximum of 127 UTF-8 characters.
There is a set of system defaults which is baked into the xattr flag code, where as of 2013 the following default flags are set for different types of xattr (* here represents the wild card):
- com.apple.quarantine – PCS
- com.apple.TextEncoding – CS
- com.apple.metadata:* – PS
- com.apple.security.* – S
- com.apple.ResourceFork – PCS
- com.apple.FinderInfo – PCS
If you want a xattr preserved when it passes through iCloud, you therefore need to give it a name which ends in the xattr flag S, such as
co.eclecticlight.MyTest#S. Sure enough, when xattrs with that flag are passed through iCloud Drive, those xattrs are preserved even if the default rule would treat them differently. Similarly, to have a xattr which is stripped even when you just make a local copy of that file, append #N to it. The exception to this is com.apple.ResourceFork, which appears to be stripped by iCloud whether or not it has #S appended to it.
The biggest problem with using xattr flags is that, although they have been a part of macOS for seven years, the great majority of macOS and its software cannot handle them. Append #S to the end of most xattr names and they cease to function properly, and the apps which are supposed to use those xattrs don’t recognise that xattr any more. This is because most software is oblivious to xattr flags. So having added a #S flag to a xattr name to ensure it’s preserved, you then have to strip the #S again before trying to use the file. It’s simpler just to use Zip.