From 2825521337c795905701d1b820cb93b2804b5be3 Mon Sep 17 00:00:00 2001 From: ArthurWuTW <36653598+ArthurWuTW@users.noreply.github.com> Date: Thu, 29 May 2025 10:16:45 -0400 Subject: [PATCH] tmpfs: Add support for noatime mount option 'noatime' flag disables updates to file access times when files are read. This can reduce unnecessary writes and improve performance, especially in read-heavy workloads. Previously, tmpfs did not recognize the 'noatime' mount option and would return an error. With this change, tmpfs now properly accepts and handles the 'noatime' option. Fixes: #26102 Signed-off-by: Arthur Wu --- docs/source/markdown/options/mount.md | 2 ++ pkg/util/mount_opts.go | 4 ++++ pkg/util/utils_test.go | 10 ++++++++-- test/apiv2/44-mounts.at | 10 +++++++++- test/e2e/run_volume_test.go | 14 ++++++++++++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/source/markdown/options/mount.md b/docs/source/markdown/options/mount.md index 0d298c61b4..622f3f4d9b 100644 --- a/docs/source/markdown/options/mount.md +++ b/docs/source/markdown/options/mount.md @@ -89,6 +89,8 @@ Options specific to type=**tmpfs** and **ramfs**: - *tmpcopyup*: Enable copyup from the image directory at the same location to the tmpfs/ramfs. Used by default. +- *noatime*: Disable updating file access times when the file is read. + - *notmpcopyup*: Disable copying files from the image to the tmpfs/ramfs. - *U*, *chown*: *true* or *false* (default if unspecified: *false*). Recursively change the owner and group of the source volume based on the UID and GID of the container. diff --git a/pkg/util/mount_opts.go b/pkg/util/mount_opts.go index 4e37fd74a0..e48ed1cc0a 100644 --- a/pkg/util/mount_opts.go +++ b/pkg/util/mount_opts.go @@ -185,6 +185,10 @@ func processOptionsInternal(options []string, isTmpfs bool, sourcePath string, g return nil, fmt.Errorf("the 'U' option can only be set once: %w", ErrDupeMntOption) } foundU = true + case "noatime": + if !isTmpfs { + return nil, fmt.Errorf("the 'noatime' option is only allowed with tmpfs mounts: %w", ErrBadMntOption) + } default: return nil, fmt.Errorf("unknown mount option %q: %w", opt, ErrBadMntOption) } diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index 524f1f0250..797e481772 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -715,10 +715,10 @@ func TestProcessOptions(t *testing.T) { }{ { name: "tmpfs", - options: []string{"rw", "size=512m"}, + options: []string{"rw", "size=512m", "noatime"}, isTmpfs: true, sourcePath: "", - expected: []string{"nodev", "nosuid", "rprivate", "rw", "size=512m", "tmpcopyup"}, + expected: []string{"nodev", "nosuid", "rprivate", "rw", "size=512m", "tmpcopyup", "noatime"}, }, { name: "duplicate idmap option", @@ -810,6 +810,12 @@ func TestProcessOptions(t *testing.T) { options: []string{"bind"}, expected: []string{"nodev", "nosuid", "bind", "private"}, }, + { + name: "noatime allowed only with tmpfs", + sourcePath: "/path/to/source", + options: []string{"noatime"}, + expectErr: true, + }, } for _, tt := range tests { diff --git a/test/apiv2/44-mounts.at b/test/apiv2/44-mounts.at index d54669e7d0..a2efb935cb 100644 --- a/test/apiv2/44-mounts.at +++ b/test/apiv2/44-mounts.at @@ -8,7 +8,7 @@ t POST containers/create?name=hostconfig_test \ Image=$IMAGE \ Cmd='["df","-P","'$tmpfs_name'"]' \ HostConfig='{"Binds":["/tmp/doesnotexist:/test1"]' \ - TmpFs="{\"$tmpfs_name\":\"rw\"}}" \ + TmpFs="{\"$tmpfs_name\":\"rw,noatime\"}}" \ 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") @@ -29,3 +29,11 @@ t GET containers/${cid}/logs?stdout=true 200 # null bytes in the outfile. like "$(tr -d \\0 <$WORKDIR/curl.result.out)" ".* ${tmpfs_name}" \ "'df' output includes tmpfs name" + +# Reject 'noatime' for bind mount +t POST libpod/containers/create \ + Image=$IMAGE \ + Mounts='[{"type":"bind","source":"/nosuchdir","destination":"/data","options":["noatime"]}]' \ + 500 \ + .cause="invalid mount option" \ + .message~"the 'noatime' option is only allowed with tmpfs mounts" diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index ae4625055c..b0020e1c67 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -1131,4 +1131,18 @@ RUN chmod 755 /test1 /test2 /test3`, ALPINE) outTest := podmanTest.PodmanExitCleanly("run", "--rm", "--mount", fmt.Sprintf("type=volume,src=%s,dest=/mnt", volName), ALPINE, "ls", "/mnt") Expect(outTest.OutputToString()).To(ContainSubstring("testfile")) }) + + It("podman run --tmpfs with noatime option", func() { + session := podmanTest.Podman([]string{"run", "--rm", "--tmpfs", "/mytmpfs:noatime", ALPINE, "grep", "mytmpfs", "/proc/self/mountinfo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + output := session.OutputToString() + Expect(output).To(ContainSubstring("noatime")) + + session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", "/mytmpfs", ALPINE, "grep", "mytmpfs", "/proc/self/mountinfo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + output = session.OutputToString() + Expect(output).ToNot(ContainSubstring("noatime")) + }) })