Reading view

There are new articles available, click to refresh the page.

From quarantine to provenance: how xattrs are copied

In the previous article, I outlined what extended attributes do, and how they work in macOS. I also started to explain how some are considered ephemeral, while others are persistent. This article continues from there, by documenting how macOS decides what to do with them when a file containing xattrs is copied.

Although Apple does now explain a little about this in the context of the FileProvider framework and syncing with cloud services, the only useful documentation is provided in man xattr_name_with_flags, and two source code files that are part of the open source copyfile component.

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 handled when the file is copied in various ways. Rather than change the file system, Apple opted for what’s 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 those 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. In recent years, Apple has added one flag, B, and there’s another to come with Sequoia.

Xattr flags

Flags can be upper or lower case letters C, N, P, S or B, and invariably follow the # separator, which is presumably otherwise forbidden from use in a xattr’s name. Upper case sets or enables that property, while lower case clears or disables that property. There are currently (macOS 14.6.1) five properties:

  • C: XATTR_FLAG_CONTENT_DEPENDENT, which ties the flag and the file contents, so the xattr is rewritten when the file data changes. This is normally used for checksums and hashes, text encoding, and position information. The xattr is preserved for copy and share, but not in a safe save.
  • P: XATTR_FLAG_NO_EXPORT, which doesn’t export or share the xattr, but normally preserves it during copying.
  • N: XATTR_FLAG_NEVER_PRESERVE, which ensures the xattr is never copied, even when copying the file.
  • S: XATTR_FLAG_SYNCABLE, which ensures the xattr is preserved during syncing with services such as iCloud Drive. Default behaviour is for xattrs to be stripped during syncing, to minimise the amount of data to be transferred, but this will override that.
  • B: XATTR_FLAG_ONLY_BACKUP, which keeps the xattr only in backups, including Time Machine (added recently).

These operate within another general restriction of xattrs: their name cannot exceed a maximum of 127 UTF-8 characters.

Defaults

macOS provides a standard ‘whitelist’ of default flag settings for different types of xattr. These aren’t contained in a configuration file, but are baked into the xattr flag code, where as of macOS 14.6.1 the following default flags are set for different types of xattr (* here represents the wild card):

  • com.apple.quarantinePCS
  • com.apple.TextEncodingCS
  • com.apple.metadata:kMDItemCollaborationIdentifierB
  • com.apple.metadata:kMDItemIsSharedB
  • com.apple.metadata:kMDItemSharedItemCurrentUserRoleB
  • com.apple.metadata:kMDItemOwnerNameB
  • com.apple.metadata:kMDItemFavoriteRankB
  • com.apple.metadata:* (except those above) – PS
  • com.apple.security.*S
  • com.apple.ResourceForkPCS
  • com.apple.FinderInfoPCS
  • com.apple.root.installedPC

Copy intents

Also contained in the source code is a table of intents, that explains how different types of copy are affected by different combinations of xattr flag. Currently, those are:

  • XATTR_OPERATION_INTENT_COPY – a simple copy, preserves xattrs that don’t have flag N or B
  • XATTR_OPERATION_INTENT_SAVE – save, where the content may be changing, preserves xattrs that don’t have flag C or N or B
  • XATTR_OPERATION_INTENT_SHARE – share or export, preserves xattrs that don’t have flag P or N or B
  • XATTR_OPERATION_INTENT_SYNC – sync to a service such as iCloud Drive, preserves xattrs if they have flag S, or have neither N nor B
  • XATTR_OPERATION_INTENT_BACKUP – back up, e.g. using Time Machine, preserves xattrs that don’t have flag N

Use

If you want a xattr preserved when it passes through iCloud, you therefore need to give it a name ending 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 that is stripped even when you just make a local copy of that file, append #N to its name.

There’s a further limit imposed on xattrs synced by FileProvider, including those for iCloud Drive, that strips all individual xattrs that are larger than a certain size. Apple gives that as “about 32KiB total for each item”, and my measurements performed in the recent past put that at about 32,650 bytes, slightly less than 32,767.

In itself, this information is valuable if you ever use any metadata stored in xattrs. It’s used in my intergrity-checking utilities Dintch, Fintch and cintch to ensure the xattr containing a file’s hash isn’t stripped by passage through iCloud Drive, for instance. On Tuesday morning next week, once Sequoia has been released, I’ll explain how Apple has extended this system to achieve something that many have been wishing for.

兔走临龙,也不能完全不立 Flag 对吧?

去年,还是完成了一些事情的。

比如,一向不太愿意接商单的我,放开态度接品牌的约稿合作了。之前那么多年都几乎不接,从 2021 年开始松口,接了六个项目。前年和去年因为经济上的缺口,开始增加合作的次数。2022 年依然保持着全年六个的节奏,算是勉强平了缺口吧。去年为了把人情借款全部还清,为了续上小柒在舞蹈上的课时费,为了置换一些家里用了几年甚至十几年的破破烂烂的大家电,接单数量一下子翻了一倍,总共接了十三个项目。链接汇总如下:

包括 2022 年的六个项目(OPPO Find X5 文章 / 视频索尼 SONY LinkBuds方太集成烹饪中心TCL Q10 冰箱OPPO Find N2vivo X90)在内,我基本上做到了最开始对自己的要求:不写违背内心的东西,借商单的机会夹带一些面向大众的设计科普,并倡导一些自己相信的理念。在索尼的项目上,我还借机做了一期《设以观复》和两条简短的分析视频(包装设计 / 形态生成逻辑)。虽然这三期视频都不是索尼要求做的,但我自己有很多话想说,也就花心思做了出来。

我知道,其实很多时候大家是不怎么看这些商单的文章的,或者看了也不会信,觉得你都是吹牛拍马屁的东西。但这些事情你左右不了别人怎么想,只能是约束自己,但求一个问心无愧就好。有时候筱烨会觉得,我花了那么多心思和精力去做的一篇文章,反反复复斟酌表达的字句,认认真真拍的样张和产品照,得不到应有的流量和反馈,很替我感到不值。可这是无法控制的事情,平台的发展变化和人的流动是我左右不了的,文章和视频的用心程度与流量本来也不是必然的关联,一款产品铺天盖地的广告也不是大家乐意看的,但只要有人在这里驻足停留过,感受到我的诚意,觉得有一点点启发,哪怕就一两个人,这点星火之光传递出去了,就值得我写。

直到前两天,还有一些偶遇的观众在视频下留言,说你这个分析给了我很多启发,太棒太好了,结果一看,发现是两三年前的,顿时觉得相见恨晚!我觉得,这种肯定虽然来得迟了一些,但他们确确实实看见我了,看懂我在说的,理解了这些需要想一想才明白的事情,那我就有继续做下去的动力。虽然就算没有这些我也会继续做,因为输出内容本身就是自我构建的一部分,一切创作都是自传性质的,所以我不会停下来。

以上是其一。

其二,把家里的三大件都换新了。

冰箱,是 2012 年从红点跳槽去嘉兰图,搬家去南光村时,跟筱烨在实体商场里买的。具体多少钱我不记得了,大概是什么家电下乡的款式,补贴后差不多七八百上下的样子吧。一直用到我们搬去龙华、再搬回南山、再搬到龙岗,跟着我们跑遍了深圳。制冷倒是没什么大问题,就是不密封了,容量也太小了。从我们两个人,用到一家四口另加十几只小动物,作为日常储量的仓库,是不够用了。于是,在 2023 年底换了一台大的。

同时期买了一台波轮洗衣机,因为不好用,三年后搬家就没带走。当时租了一间没有家具的房子,两个人狠狠操办了一翻,当时还因此上了一次《南方都市报》的专访。那会还买了一台热水器,可惜没跟着带走。搬去龙华之后换了一台滚筒洗衣机,算下来也是用了八九年的老伙伴了,年老力衰,皮带和电机都有问题,洗起衣服来跟地震似的哐哐响,这样吵了一两年或者两三年吧,不得不换掉了。这也是给家里完成的的一件大事。

第三件就是前两天刚换上的电视机。原本的电视是 55 寸的 TCL,其实也没什么大问题,就是反应慢了一些。年前买了新的遥控器,发现其实是遥控器的问题,心说还可以再干几年。谁知道,就是年二九那晚,九点来钟,正准备出门去宠物医院的店长家里给她喂猫,这电视突然就坏了。一开始还以为是小故障,我一检查,好家伙,有声音没画面,仔细一看,画面隐约可见,但只有特定角度下隐隐能看到。得,背光烧了。先不说修一下大几百,赶上半台电视的价格,这大过年的,也没人来修啊,况且人家有没有对应的背光零件可以换上去也是一个巨大的问号。心一铁,觉得也是用了快五年的老电视了,换台新的吧。等过了除夕和初一二三那几天,就下单了创维 75 寸的新款。来安装电视的师傅说,大概就是前一阵回南天湿气重,给弄坏的。

虽然去年没什么成果,但给家里更新了三件大家电,也算是一点成绩吧。

虽然我过去一年很疲惫,但起码,家是顾上了,人情债还了,小柒的舞也续上了。

这两年我老跟筱烨说,我们家就存不下来钱,刚进一笔,就立刻出去一笔,尤其有了孩子以后,花钱的地方太多了,何况我们还没停过救助动物。家里就跟个水池子一样,一边进一边出,一刻也不敢停,进的也留不下。她总安慰我,说毕竟还是越过越好了,相比前些年,起码日子是慢慢过顺了的。是,也算是细水长流了。

其三,没有什么值得书写的其三了。

其实前面两点也可以归拢到一点:起码顾上家里了。

如果说还有什么值得记录下来的,那就是今年春节期间,我把自己从囚笼里释放出来了。

一、被家族群隐形除名,任何活动不再叫上我;

二、和我妈袒露了三十多年来憋在心里的恶气,告诉她我既不恨她也不爱她,她的不在场和不作为在我的克制和理性中,演变成了我的锋利和独立。

筱烨和小柒,同时都是我的好朋友和战友,互相帮助、鼓励、撑腰、温暖,我好像也没什么再怕的了。

姑姑们愿意怎么双标,那是她们的事,但骂我的话,对我的无端指责,我不会忘记。她们是如何自私地维护自己,如何地冷漠和虚情假意,我也不在乎了。你们去那个重男轻女的大家族里抱团也好,对我们家冷暴力也好,那都是你们的业,我都不想参与了。假如将来,在温室中成长的三个弟弟妹妹不理解我,那也就算了,玫瑰花不需要也不应该承受和理解莲从淤泥中挤出来的苦。从前二十几年的虚幻泡影中醒来的感觉,其实挺好的。

但和她们不同,我妈,是我最大的心病。这是真的病。

她这三十七年来的不在场和不作为,让我在我爸这个巨婴的「照顾」下扭曲地度过了所有成长阶段。除了阿嬷,唯一的阿嬷,我在一个没有关注、没有照顾的危险环境中自顾自的长大成现在这样。被同学和老师用「你没有妈」反复凌辱的时候,我构建出了「我妈是英雄」的故事来武装自己;在六年级第一个学期开学时,我望着右前方的红旗和人群,理解了「人生是旷野」的含义;在每一次独自面对未来的时候,我都给他们安排了了不起的角色故事,让我的悲凉变成理所当然的剧情;在每一次的呼喊都得不到回应时,我早早地找到了自己;在她年复一年、日复一日的赌局中,在她每一次努力逃离她的不幸却无视我的不幸时,我把悲凉吃了下肚,救了自己一命。

如果不是 Lizilong 自杀,我还没意识到自己内心之强大。尽管这种所谓的坚强,是建立在无数个自我欺骗之中的。但在小柒进入我的人生后,我才充分明白了,那些都是我在创伤后应激综合症下对自己的保护,是我自己让我自己活了下来,是我一次次打消了自己纵身一跃的冲动,那些美梦无人告诉过我,是我游向了岸边。

她是空白的。

尽管她说了一些细节,但是,一共就那么几个屈指可数的细节,连个完整的画面都没有的仅仅只有细节的细节。当我终于从自己的孩子身上看到,我的母亲从来就不爱我时,那一片空白中渐渐燃起了熊熊大火。阿嬷临终前的三个月对我说,你爸妈靠不住,以后怎么办;姨婆在阿嬷过世后,也跟我说他们不是你的靠山,要照顾好自己;就连姑婆都和我说过,你爸妈不管你,靠不住,自己在外面要小心。临近三十,我眼前的迷雾才慢慢散开。

我不想冤枉他们。但所有线索都指向同一个地方时,那就是真相。

她是空白,所以我不会恨她,但我也不会爱她。

我说出来了。

我终于不用假装了。

她说要弥补,可是碎了一地的玻璃渣要怎么补?不用补了。我已经自己把它们捡起来,粘成一个闪闪发光的玻璃渣球。我不需要谁来弥补,因为人无法两次踏入同一条河,过去的就是既定事实,是补不了的。人要改变一个习惯,不是通过额外培养一个新习惯来实现的,而是在习惯回路中替换惯常行为,来转移的。已经无法挽回的事情,不在习惯回路里。赎罪券买的只是她自己的心安理得,并不能改变任何事情。

但,我也无法掩盖 CPTSD 对我的影响

我做不到原谅。但我一直在努力,对抗这头怪兽。

写出来,不是如某些网友说的「你还是在乎」,而是我完成了这一个「仪式」,我要为自己记录下来。写作不只是我的爱好,更是我的心理治疗手段,是华生从战场上回来之后的那种写作,是把碎片一片片捡起来拼成马赛克的那种写作。即便后半辈子我都要与之战斗,起码我已经知道我的敌人是谁、在哪。只要怪兽有名字,它就可以被杀死。

回顾到此结束,今年,2024 年,我要完成一些目标:

1、从三月起,每月至少产出 1 条视频

2、每日饮水量平均数达到 2.5L 以上

3、三月起,每月累计跑 40km 以上

4、恢复周期性运动训练,目标是增肌,体重 65-67kg,体脂率 15-16%;

5、每日至少冥想 10 分钟,睡眠 7 小时,日照 75 分钟,0:30 前入睡;

6、读完 7 本书

7、完成捐发

明年二月,我回来验收。

❌