This blog entry is a new one. No re-publication of an old blog entry. It’s not one of the newest features in Solaris, but in comparison to other features, it’s still the new kid on the block and one of the less known ones. It’s about retention of files in ZFS. I think it was introduced to the ZFS in Oracle Solaris in 2022. It’s in the ZFS Storage Appliance since then. I will give you an overview of the feature in this blog entry.
Data retention?
So, what is retention? Wikipedia defines it as “Data retention defines the policies of persistent data and records management for meeting legal and business data archival requirements.”
In a company you often need store data for certain amounts of time. Often because of regulatory requirements. In this time you don’t want your data modified in this time? You want to be sure that the DICOM files of your MRI are still there in a number of years and haven’t been edited or deleted. You want the documents about repairs and spare parts of the aircraft you are using to get into your vacation stored in a way, that don’t allow deletion and editing. You want to be sure, that the documents you are using for your or your companies declaration to the tax authority is stored in a way that ensures you can’t delete it or modify it. All this areas are heavily regulated, there are many organizatorital rules in place, you can support with technology.
In recent time another usecase got equally important. Fore example: You want to ensure, that your backup isn’t modifed in the retention time of your backup by a malicious process encrypting all your data. It can be even a safety net against user error: For example, accidentally deleting files or filesystems because you clicked too high or too low and hit the filesystem with the backups.
File Retention in ZFS
A filesystem with retention capabilities can help with implementing and enforcing a data retention policy. Like not allowing the deletion of a file for a certain time on a operating system level, so even a shell user can’t delete or modify it. However, as usual, the following is valid: Such a file system is a technology helping you to implement a policy; it’s not the policy itself. Technologies must be embedded in policies.
In regards of ZFS file retention Solaris enforces the configured policies with no way around it, except you configured it this way to allow this for certain users (you will learn about this later).
You have to see this feature in tandem with other features like auditing allowing to log every successful or unsuccessful attempt to change configurations.
This tutorial
In this tutorial I will show a subset of the capabilities of the file retention feature of the ZFS filesystem. As the ZFS Storage Appliance uses Solaris under the hood, you can assume it’s the feature enabling the retention feature of the ZFS Storage appliance.
Preparations
In my example I will a ZFS pool based on files in a ZFS filesystem. Just to make things easier for me
root@testbed:~# mkfile 1g testpool
root@testbed:~# lofiadm -a /root/testpool
/dev/lofi/1
root@testbed:~# zpool create testpool /dev/lofi/1
Okay, now I will create a pool with a retention policy. I will explain mandatory
later.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/mandatory
cannot create 'testpool/mandatory': mandatory retention may not be enabled on a pool with no redundancy
Okay, first thing to keep in mind. No mandatory retention without a pool with redundancy, so either mirror or RAIDZ1-3
root@testbed:~# mkfile 1g testpool_mirror
root@testbed:~# lofiadm -a /root/testpool_mirror
/dev/lofi/2
root@testbed:~# zpool attach testpool /dev/lofi/1 /dev/lofi/2
root@testbed:~# zpool status testpool
pool: testpool
id: 4597756362022219442
state: ONLINE
scan: resilvered 88K in 1s with 0 errors on Fri Apr 18 20:41:01 2025
config:
NAME STATE READ WRITE CKSUM
testpool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
/dev/lofi/1 ONLINE 0 0 0
/dev/lofi/2 ONLINE 0 0 0
errors: No known data errors
Retention types
Solaris knows two types of retention.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/mandatory
root@testbed:~# zfs create -o retention.policy=privileged testpool/privileged
The difference is rather simple. When you use privileged
retention, a sufficiently privileged user can delete files even when the retention time hasn’t passed and so override the retention. You can even destroy the whole filesystem or pool. The necessary privileged is FILE_RETENTION_OVERRIDE
. The root
user has this privilege per default; however, you can delegate this privilege.
I will explain later what I’m doing; just take away from this example that I’m able to destroy the dataset with the privileged
retention policy.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/deletiondemomandatory
root@testbed:~# zfs create -o retention.policy=privileged testpool/deletiondemoprivileged
root@testbed:~# zfs set retention.period.default=600s testpool/deletiondemomandatory
root@testbed:~# zfs set retention.period.default=600s testpool/deletiondemoprivileged
root@testbed:~# mkfile 1k /testpool/deletiondemomandatory/testfile1
root@testbed:~# touch -R /testpool/deletiondemomandatory/testfile1
root@testbed:~# mkfile 1k /testpool/deletiondemoprivileged/testfile1
root@testbed:~# touch -R /testpool/deletiondemoprivileged/testfile1
root@testbed:~# zfs destroy testpool/deletiondemoprivileged
I’m not able to destroy the dataset when the pool uses the mandatory
policy and still has a single retained file.
root@testbed:~# zfs destroy testpool/deletiondemomandatory
cannot destroy 'testpool/deletiondemomandatory': has retained files
With the necessary privileges, you can even delete the file, as long it’s a dataset with the privileged
policy:
root@testbed:~# zfs create -o retention.policy=privileged testpool/deletiondemoprivileged
root@testbed:~# zfs set retention.period.default=600s testpool/deletiondemoprivileged
root@testbed:~# mkfile 1k /testpool/deletiondemoprivileged/testfile1
root@testbed:~# touch -R /testpool/deletiondemoprivileged/testfile1
root@testbed:~# rm /testpool/deletiondemoprivileged/testfile1
rm: /testpool/deletiondemoprivileged/testfile1: override protection 400 (yes/no)? yes
When there is no retained file in a filesystem with the mandatory
policy however, you can delete the file system or the pool.
root@testbed:~# zfs destroy testpool/deletiondemomandatory
root@testbed:~#
It’s not possible to change the retention policy once it’s set. Obviously, because you then could set it to privileged and would be able to destroy it or to delete files. You don’t want this.
root@testbed:~# zfs set retention.policy=mandatory testpool/privileged
Cannot set property for 'testpool/privileged': 'retention.policy' is readonly
There are several properties around retention you can set. To make my next examples a little bit easier, I will use one of them at this time in my example. The property is called retention.period.default
.
root@testbed:/testpool# zfs set retention.period.default=180s testpool/mandatory
I set it to 180s. So, whenever I retain a file without specifying an exact time, this will be the default retention time. As it would be a little bit impractical for this tutorial to use, for example, 30 years, I will use 3 minutes.
My first retained file
For my examples, I will use the filesystem with mandatory
retention. As you see, even root is bound by the mandatory
retention.
root@testbed:/testpool/mandatory# mkfile 1k testfile1
root@testbed:/testpool/mandatory# touch -R testfile1
I created a file and then touched the file with the -R
option. This tells ZFS to retain the file until the retention time has passed. As I didn’t specify a retention date and time, the default retention time is used. In my example: The already mentioned 3 minutes.
Okay … let’s try to delete the file.
root@testbed:/testpool/mandatory# date
Fri Apr 18 20:54:48 CEST 2025
root@testbed:/testpool/mandatory# rm testfile1
rm: testfile1: override protection 400 (yes/no)? yes
rm: testfile1 not removed: Insufficient privileges
root@testbed:/testpool/mandatory# date
Fri Apr 18 20:55:06 CEST 2025
root@testbed:/testpool/mandatory# date
Fri Apr 18 20:56:13 CEST 2025
root@testbed:/testpool/mandatory# rm testfile1
rm: testfile1: override protection 400 (yes/no)? yes
rm: testfile1 not removed: Insufficient privileges
root@testbed:/testpool/mandatory# rm testfile1
rm: testfile1: override protection 400 (yes/no)? yes
rm: testfile1 not removed: Insufficient privileges
As you see, my attempts to delete the file are blocked with rm: testfile1 not removed: Insufficient privileges
until the 3 minutes have passed.
root@testbed:/testpool/mandatory# date
Fri Apr 18 20:57:52 CEST 2025
root@testbed:/testpool/mandatory# rm testfile1
rm: testfile1: override protection 400 (yes/no)? yes
root@testbed:/testpool/mandatory# date
Fri Apr 18 20:58:34 CEST 2025
Okay, you can check the time when the retention of a file ends with ls. The interesting line is the rtime
line. In my example, the retention ends on Apr 18 21:07:23 2025.
root@testbed:/testpool/mandatory# touch -R testfile2
root@testbed:/testpool/mandatory# ls -l -%all testfile2
-r-------- 1 root root 1024 Apr 18 2025 testfile2
timestamp: atime Apr 18 21:03:27 2025
timestamp: ctime Apr 18 21:04:23 2025
timestamp: mtime Apr 18 21:03:27 2025
timestamp: crtime Apr 18 21:03:27 2025
timestamp: rtime Apr 18 21:07:23 2025
Let’s try to delete it again.
root@testbed:/testpool/mandatory# date
Fri Apr 18 21:06:50 CEST 2025
root@testbed:/testpool/mandatory# rm testfile2
rm: testfile2: override protection 400 (yes/no)? y
rm: testfile2 not removed: Insufficient privileges
root@testbed:/testpool/mandatory# ls -l -%rtime testfile2
-r-------- 1 root root 1024 Apr 18 21:07 testfile2
root@testbed:/testpool/mandatory# date
Fri Apr 18 21:07:54 CEST 2025
root@testbed:/testpool/mandatory# rm testfile2
rm: testfile2: override protection 400 (yes/no)? yes
But retention has other consequences. You can’t change the file. Before deleting the file in the example before I tried to change the file:
root@testbed:~# echo test >> /testpool/mandatory/testfile2
-bash: /testpool/mandatory/testfile2: Insufficient privileges
You can’t move it.
root@testbed:~# mv /testpool/mandatory/testfile2 /testpool/mandatory/dir1/dir2
mv: cannot rename /testpool/mandatory/testfile2 to /testpool/mandatory/dir1/dir2/testfile2: Insufficient privileges
Let’s assume you have retained a file once. You can prolong the time by using touch -R
multiple times.
root@testbed:~# ls -l -%all /testpool/mandatory/testfile2
-r-------- 1 root root 1024 Apr 19 2025 /testpool/mandatory/testfile2
timestamp: atime Apr 19 17:26:29 2025
timestamp: ctime Apr 19 17:34:47 2025
timestamp: mtime Apr 19 17:26:29 2025
timestamp: crtime Apr 19 17:26:29 2025
timestamp: rtime Apr 19 17:37:47 2025
root@testbed:~# touch -R /testpool/mandatory/testfile2
root@testbed:~# ls -l -%all /testpool/mandatory/testfile2
-r-------- 1 root root 1024 Apr 19 2025 /testpool/mandatory/testfile2
timestamp: atime Apr 19 17:26:29 2025
timestamp: ctime Apr 19 17:35:49 2025
timestamp: mtime Apr 19 17:26:29 2025
timestamp: crtime Apr 19 17:26:29 2025
timestamp: rtime Apr 19 17:38:49 2025
So far, we only used the default retention time. But you can specify one as well. For example, I want to set a retention time of 18:01 on April 19th, 2025.
root@testbed:~# ls -l -%all /testpool/mandatory/testfile2
-r-------- 1 root root 1024 Apr. 19 17:38 /testpool/mandatory/testfile2
timestamp: atime Apr. 19 17:26:29 2025
timestamp: ctime Apr. 19 17:35:49 2025
timestamp: mtime Apr. 19 17:26:29 2025
timestamp: crtime Apr. 19 17:26:29 2025
timestamp: rtime Apr. 19 17:38:49 2025
root@testbed:~# touch -R 0419180125 /testpool/mandatory/testfile2
root@testbed:~# ls -l -%all /testpool/mandatory/testfile2
-r-------- 1 root root 1024 Apr. 19 2025 /testpool/mandatory/testfile2
timestamp: atime Apr. 19 17:26:29 2025
timestamp: ctime Apr. 19 17:40:03 2025
timestamp: mtime Apr. 19 17:26:29 2025
timestamp: crtime Apr. 19 17:26:29 2025
timestamp: rtime Apr. 19 18:01:00 2025
root@testbed:~# rm /testpool/mandatory/testfile2
rm: /testpool/mandatory/testfile2: override protection 400 (yes/no)? yes
rm: /testpool/mandatory/testfile2 not removed: Insufficient privileges
Thousand ways to leave^H^H^H^H^Htrigger your retention
Okay, there aren’t a thousand ways to trigger file retention, but there are a few. Another way is to use an atime
in the future and then remove the write permissions of the file.
root@testbed:~# ls -l -%all /testpool/mandatory/testfile4
-rw------- 1 root root 1024 Apr 19 17:46 /testpool/mandatory/testfile4
timestamp: atime Apr 19 17:46:09 2025
timestamp: ctime Apr 19 17:46:09 2025
timestamp: mtime Apr 19 17:46:09 2025
timestamp: crtime Apr 19 17:46:09 2025
root@testbed:~# touch -a 0419180125 /testpool/mandatory/testfile4
root@testbed:~# chmod ugo-w /testpool/mandatory/testfile4
root@testbed:~# ls -l -%all /testpool/mandatory/testfile4
-r-------- 1 root root 1024 Apr 19 2025 /testpool/mandatory/testfile4
timestamp: atime Apr 19 18:01:00 2025
timestamp: ctime Apr 19 17:47:31 2025
timestamp: mtime Apr 19 17:46:09 2025
timestamp: crtime Apr 19 17:46:09 2025
timestamp: rtime Apr 19 18:01:00 2025
Recently I wrote a blog entry about attributes: You can trigger retention by setting an atime
in the future and then set the readonly
attribute.
root@testbed:~# mkfile 1k /testpool/mandatory/testfile5
root@testbed:~# touch -a 0419180125 /testpool/mandatory/testfile5
root@testbed:~# ls -l -%all /testpool/mandatory/testfile5
-rw------- 1 root root 1024 Apr 19 17:49 /testpool/mandatory/testfile5
timestamp: atime Apr 19 18:01:00 2025
timestamp: ctime Apr 19 17:49:36 2025
timestamp: mtime Apr 19 17:49:36 2025
timestamp: crtime Apr 19 17:49:36 2025
root@testbed:~# chmod S+vreadonly /testpool/mandatory/testfile5
root@testbed:~# ls -l -%all /testpool/mandatory/testfile5
-r-------- 1 root root 1024 Apr 19 2025 /testpool/mandatory/testfile5
timestamp: atime Apr 19 18:01:00 2025
timestamp: ctime Apr 19 17:50:33 2025
timestamp: mtime Apr 19 17:49:36 2025
timestamp: crtime Apr 19 17:49:36 2025
timestamp: rtime Apr 19 18:01:00 2025
Delete on expiry
Data retention is not only about preventing deletion or modification of data until the retention period has been expired. Sometimes it’s about getting rid of the data after the retention period. The retention feature in ZFS helps you to implement such a policy.
Again, I will configured a filesystem with mandatory
retention and a default retention period of 180s.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/deleteonexpiry
root@testbed:~# zfs set retention.period.default=180s testpool/deleteonexpiry
To activate deletion on retention expiry, I have to activate this policy:
root@testbed:~# zfs set retention.policy.onexpiry=delete testpool/deleteonexpiry
Now after the expiry of the retention the file is deleted by the system.
root@testbed:~# mkfile 1k /testpool/deleteonexpiry/testfile1
root@testbed:~# touch -R /testpool/deleteonexpiry/testfile1
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:16:22 CEST 2025
-r-------- 1 root root 1024 Apr 19 2025 /testpool/deleteonexpiry/testfile1
timestamp: atime Apr 19 20:15:29 2025
timestamp: ctime Apr 19 20:15:41 2025
timestamp: mtime Apr 19 20:15:29 2025
timestamp: crtime Apr 19 20:15:29 2025
timestamp: rtime Apr 19 20:18:41 2025
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:17:24 CEST 2025
-r-------- 1 root root 1024 Apr 19 2025 /testpool/deleteonexpiry/testfile1
timestamp: atime Apr 19 20:15:29 2025
timestamp: ctime Apr 19 20:15:41 2025
timestamp: mtime Apr 19 20:15:29 2025
timestamp: crtime Apr 19 20:15:29 2025
timestamp: rtime Apr 19 20:18:41 2025
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:19:25 CEST 2025
/testpool/deleteonexpiry/testfile1: No such file or directory
As useful as this feature is, there may be a small problem. Think about backup tools that manage their backup files in a database. They have their own retention policies and clean up the database and the backup files on their own.
However, when you configure deletion on expiry, the file is deleted as soon as the retention period has passed. The issue is: When the retention period has not passed, the file is not deletable; if the retention period has passed, there is no file.
So, there is no time slot for the tool to delete the file on its own. It’s now dependent on the backup tool if it accepts a situation where the backup file has already been deleted and simply ignores that there was no file to delete.
For such tools, another property was introduced. You can configure a grace period. The file isn’t deleted right after the expiry of the retention, but only after the additional grace period. If your cleanup is running, for example, once a day, you can configure a grace period of one day. Only expired files left over by the clean-up tool would then be deleted by the delete-on-expiry policy.
At first, I’m configuring a deletegrace
period of 60s seconds. So after expiry the system won’t delete the file for additional 60 seconds.
root@testbed:~# zfs set retention.period.deletegrace=60s
Let`s retain a new file
root@testbed:~# mkfile 1k /testpool/deleteonexpiry/testfile1
root@testbed:~# touch -R /testpool/deleteonexpiry/testfile1
Lets check what
s happen now :
root@testbed:~# mkfile 1k /testpool/deleteonexpiry/testfile1
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:27:27 CEST 2025
-r-------- 1 root root 1024 Apr 19 2025 /testpool/deleteonexpiry/testfile1
timestamp: atime Apr 19 20:24:29 2025
timestamp: ctime Apr 19 20:27:13 2025
timestamp: mtime Apr 19 20:24:29 2025
timestamp: crtime Apr 19 20:24:29 2025
timestamp: rtime Apr 19 20:30:13 2025
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:30:24 CEST 2025
-r-------- 1 root root 1024 Apr 19 20:30 /testpool/deleteonexpiry/testfile1
timestamp: atime Apr 19 20:24:29 2025
timestamp: ctime Apr 19 20:27:13 2025
timestamp: mtime Apr 19 20:24:29 2025
timestamp: crtime Apr 19 20:24:29 2025
timestamp: rtime Apr 19 20:30:13 2025
Without the grace period, the file would have been deleted already at this point in time.
root@testbed:~# date ; ls -l -%all /testpool/deleteonexpiry/testfile1
Sat Apr 19 20:31:30 CEST 2025
/testpool/deleteonexpiry/testfile1: No such file or directory
Hold on expiry
There are situations where you can’t delete any data. There are situations where you are told “Keep everything, we may need it! Don’t dare to delete something. Don’t recycle tapes. Nothing until we tell you otherwise”.
I’m pretty sure you don’t want to explain to someone that ZFS auto-deleted a file on your fileservers or backup server, because the retention expired after you got such a notification. Furthermore, you don’t want to explain to the same someone, that a retained file could be deleted by users, because the retention period expired.
Out of this reason, you can set another on-expiry-policy. It’s called hold
:
root@testbed:~# zfs create -o retention.policy=mandatory testpool/holdonexpiry
root@testbed:~# zfs set retention.period.default=180s testpool/holdonexpiry
root@testbed:~# zfs set retention.policy.onexpiry=hold testpool/holdonexpiry
Now let’s retain a file.
root@testbed:~# mkfile 1k /testpool/holdonexpiry/testfile1
root@testbed:~# touch -R /testpool/holdonexpiry/testfile1
I should be able to delete the file on Apr 19 21:03:48 2025:
root@testbed:~# date ; ls -l -%all /testpool/holdonexpiry/testfile1
Sat Apr 19 21:01:35 CEST 2025
-r-------- 1 root root 1024 Apr 19 2025 /testpool/holdonexpiry/testfile1
timestamp: atime Apr 19 21:00:31 2025
timestamp: ctime Apr 19 21:00:48 2025
timestamp: mtime Apr 19 21:00:31 2025
timestamp: crtime Apr 19 21:00:31 2025
timestamp: rtime Apr 19 21:03:48 2025
However, because of the hold
policy, ZFS doesn’t allow the deletion of the file after the expiry of the retention period. And as the policy isn’t delete
, the file isn’t deleted after expiry as well.
root@testbed:~# date ; ls -l -%all /testpool/holdonexpiry/testfile1
Sat Apr 19 21:04:26 CEST 2025
-r-------- 1 root root 1024 Apr 19 21:03 /testpool/holdonexpiry/testfile1
timestamp: atime Apr 19 21:00:31 2025
timestamp: ctime Apr 19 21:00:48 2025
timestamp: mtime Apr 19 21:00:31 2025
timestamp: crtime Apr 19 21:00:31 2025
timestamp: rtime Apr 19 21:03:48 2025
root@testbed:~# rm /testpool/holdonexpiry/testfile1
rm: /testpool/holdonexpiry/testfile1: override protection 400 (yes/no)? yes
rm: /testpool/holdonexpiry/testfile1 not removed: Insufficient privileges
The destruction of the ZFS filesystem isn’t possible, too .
root@testbed:~# zfs destroy testpool/holdonexpiry
cannot destroy 'testpool/holdonexpiry': has retention.policy.onexpiry set to 'hold'
When you get the notice that the hold has ended, you can set the on-expiry-policy to off, and you are now able to delete the file.
root@testbed:~# zfs set retention.policy.onexpiry=off testpool/holdonexpiry
root@testbed:~# rm /testpool/holdonexpiry/testfile1
rm: /testpool/holdonexpiry/testfile1: override protection 400 (yes/no)? yes
root@testbed:~#
Or you could throw away the whole dataset as well.
root@testbed:~# zfs destroy testpool/holdonexpiry
Auto retention
Sometimes you don’t want to tell the system to retain the file. For example, because you have no way to integrate any of the ways to trigger the retention in your application.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/autoretention
root@testbed:~# zfs set retention.period.default=180s testpool/autoretention
With retention.period.grace
property you can set the period of time after the last modification to the file the retention should auto-trigger.
root@testbed:~# zfs set retention.period.grace=60s testpool/autoretention
Let’s try to delete the file after 10 seconds.
root@testbed:~# mkfile 1k /testpool/autoretention/testfile1
root@testbed:~# sleep 10; rm /testpool/autoretention/testfile1
This will obviously work. The grace period hasn’t passed at that moment. So, the file is not retained. Now, let’s wait for 60 seconds.
root@testbed:~# mkfile 1k /testpool/autoretention/testfile1
root@testbed:~# sleep 60; rm /testpool/autoretention/testfile1
rm: /testpool/autoretention/testfile1: override protection 400 (yes/no)? yes
rm: /testpool/autoretention/testfile1 not removed: Insufficient privileges
The grace period is not counted from the creation of the file, but from the last change. Let’s write something into the file roughly every 10 seconds.
root@testbed:~# mkfile 1k /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
At least 60 seconds should have passed since creation now. But I’m still able to write into this file.
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
root@testbed:~# sleep 10; echo narf >> /testpool/autoretention/testfile2
I’m now waiting for 70 seconds. If nobody else is writing to this file, the grace period should pass in this time, and the file should be retained.
root@testbed:~# sleep 70; echo narf >> /testpool/autoretention/testfile2
-bash: /testpool/autoretention/testfile2: Insufficient privileges
And indeed, it is.
Zero length files
Retention behaves a little bit differently on files that are retained when they have a length of zero. For my test, I’m creating yet another ZFS filesystem with mandatory retention.
root@testbed:~# zfs create -o retention.policy=mandatory testpool/zerolength
root@testbed:~# zfs set retention.period.default=180s testpool/zerolength
Now I’m creating a zero-length file with touch
.
root@testbed:~# touch /testpool/zerolength/testfile1
root@testbed:~# ls -/V /testpool/zerolength/testfile1
-rw-r--r-- 1 root root 0 Apr 20 10:02 /testpool/zerolength/testfile1
{archive,av_modified}
When I’m triggering retention for this file, we will see a new attribute assigned to the file.
root@testbed:~# touch -R /testpool/zerolength/testfile1
root@testbed:~# ls -/V /testpool/zerolength/testfile1
-rw-r--r-- 1 root root 0 Apr 20 10:02 /testpool/zerolength/testfile1
{archive,appendonly,av_modified}
The attribute appendonly
has been added to the file. And this gives you an indication about the different behavior. You can’t delete this file.
root@testbed:~# rm /testpool/zerolength/testfile1
rm: /testpool/zerolength/testfile1 not removed: Insufficient privileges
You can’t overwrite it.
root@testbed:~# echo test > /testpool/zerolength/testfile1
-bash: /testpool/zerolength/testfile1: Insufficient privileges
But you can append to it
root@testbed:~# echo test >> /testpool/zerolength/testfile1
root@testbed:~# echo test >> /testpool/zerolength/testfile1
Okay, let’s assume you know there will be no further additions to the file. Now you can remove all write permissions from the file, and the file gets a normal retained file.
root@testbed:~# chmod ugo-w /testpool/zerolength/testfile1
Again, the attributes give you an indication about what happens now. The attribute appendonly
disappears, and you will see readonly
instead.
root@testbed:~# ls -/V /testpool/zerolength/testfile1
-r--r--r-- 1 root root 5 Apr 20 10:14 /testpool/zerolength/testfile1
{archive,readonly,av_modified}
And indeed, you can’t append to this file any longer.
root@testbed:~# echo test >> /testpool/zerolength/testfile1
-bash: /testpool/zerolength/testfile1: Insufficient privileges
Of course, after the retention has expired, you are able to delete the file again:
root@testbed:~# date ; ls -l -%all /testpool/zerolength/testfile1
Sun Apr 20 10:12:53 CEST 2025
-r--r--r-- 1 root root 10 Apr 20 10:05 /testpool/zerolength/testfile1
timestamp: atime Apr 20 10:04:23 2025
timestamp: ctime Apr 20 10:04:58 2025
timestamp: mtime Apr 20 10:03:55 2025
timestamp: crtime Apr 20 10:02:39 2025
timestamp: rtime Apr 20 10:05:59 2025
root@testbed:~# rm /testpool/zerolength/testfile1
rm: /testpool/zerolength/testfile1: override protection 444 (yes/no)? yes
root@testbed:~#
Do you want to learn more?
The file retention feature has its own section in the documentation at oracle. You will find it here: docs.oracle.com: Retaining Files on Your ZFS File System