Skip to content

Conversation

@vic1707
Copy link
Contributor

@vic1707 vic1707 commented Sep 22, 2025

Fixes: #544

Added implementation for specifying custom modes (for files and dirs) as well as the user/group combe for ownership to trees entries

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a valuable feature to specify ownership and permissions for directory trees. The implementation is solid, with good refactoring to handle the new options and comprehensive tests. I have one high-severity suggestion regarding the handling of trees destined for the root filesystem directory (/) to prevent unintended and potentially dangerous modifications to the root directory's permissions. The documentation changes are thorough and accurately reflect the new functionality.

Comment on lines 357 to +398
if info.Mode().IsDir() {
return nil
// If nothing custom is required we skip directories generation
if options.dirMode == nil && options.user == (NodeUser{}) && options.group == (NodeGroup{}) {
return nil
}

if t.Exists(destPath) {
r.AddOnError(yamlPath, common.ErrNodeExists)
return nil
}
mode := util.IntToPtr(0755)
if options.dirMode != nil {
mode = options.dirMode
}
i, dir := t.AddDir(types.Directory{
Node: createNode(destPath, options.user, options.group),
DirectoryEmbedded1: types.DirectoryEmbedded1{
Mode: mode,
},
})
ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "directories", i), dir)
if i == 0 {
ts.AddTranslation(yamlPath, path.New("json", "storage", "directories"))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation applies the tree's ownership and mode to the filesystem's root directory (/) if a tree's path is not specified (or is explicitly /). This can have unintended and potentially harmful side effects, as users likely intend to set permissions for the tree's contents, not for the filesystem root itself. I suggest skipping the directory creation for the root of the source tree walk when the destination is /. This will prevent modification of /'s permissions while still correctly applying permissions to all files and subdirectories within the tree. This will require updating the test cases that expect / to be modified.

if info.Mode().IsDir() {
	// Don't apply tree ownership/mode to the filesystem root directory.
	if relPath == "." && destPath == "/" {
		return nil
	}

	// If nothing custom is required we skip directories generation
	if options.dirMode == nil && options.user == (NodeUser{}) && options.group == (NodeGroup{}) {
		return nil
	}

	if t.Exists(destPath) {
		r.AddOnError(yamlPath, common.ErrNodeExists)
		return nil
	}
	mode := util.IntToPtr(0755)
	if options.dirMode != nil {
		mode = options.dirMode
	}
	i, dir := t.AddDir(types.Directory{
		Node: createNode(destPath, options.user, options.group),
		DirectoryEmbedded1: types.DirectoryEmbedded1{
			Mode: mode,
		},
	})
	ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "directories", i), dir)
	if i == 0 {
		ts.AddTranslation(yamlPath, path.New("json", "storage", "directories"))
	}
}

@prestist
Copy link
Collaborator

prestist commented Nov 14, 2025

These changes look really good, lets go ahead and squash the changes into a commit with this message

  base/v0_7_exp: add ownership and mode support for trees

  Add support for setting user, group, file_mode, and dir_mode on
  trees to address the use case of deploying directory trees with
  specific ownership for rootless containers.

  Fixes: coreos/butane#544

Copy link
Collaborator

@prestist prestist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation was updated for stable variants (v1_6, v1_5, v1_4, etc.) but these variants don't actually have those fields because they use older base schemas.

*-exp.md should be the only doc's updated

* **target** (string): the target path of the link
* **_hard_** (boolean): a symbolic link is created if this is false, a hard one if this is true.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Ownership, file modes (using `file_mode`) and directories modes (using `dir_mode`) can be specified for the tree. If not specified, ownership is not preserved and file modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be changed, the feature is only for -exp spec, I feel like something went wrong when generating your docs.

@vic1707 vic1707 force-pushed the tree-permissions branch 2 times, most recently from 761c93d to d9d10bc Compare November 14, 2025 20:53
@vic1707
Copy link
Contributor Author

vic1707 commented Nov 14, 2025

Thanks for the review on this one, I just squashed everything.


You're right about the docs, I blindly ran the generation script after the original CI failed, that's on me.

Restoring the existing files (keeping only the *-exp.md ones) currently leads to failing CI (https://github.com/vic1707/butane/actions/runs/19377334435/job/55447945838?pr=5).

My current best guess is that I modified shared files amongst all versions of butane, so docs generation modifies everything, not just unreleased versions hence CI fail.
I'll try to look into it and #631 over the weekend

desc: a list of local directory trees to be embedded in the config. Ownership, file modes (using `file_mode`) and directories modes (using `dir_mode`) can be specified for the tree. If not specified, ownership is not preserved and file modes are set to 0755 if the local file is executable or 0644 otherwise. Attributes of files, directories, and symlinks can be overridden by creating a corresponding entry in the `files`, `directories`, or `links` section; such `files` entries must omit `contents` and such `links` entries must omit `target`.
transforms:
- regex: Ownership is not preserved.
- regex: If not specified, ownership is not preserved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to get around this, we can create a regex that removes bits we do not want for certain versions.. its regex so its not pretty buuuuut

            - regex: ", file modes \\(using `file_mode`\\) and directories modes \\(using `dir_mode`\\) can be specified for the tree\\."
              replacement: "."
              if:
                - variant: fcos
                  max: 1.6.0
                - variant: fiot
                  max: 1.0.0
                - variant: flatcar
                  max: 1.1.0
                - variant: openshift
                  max: 4.20.0
                - variant: r4e
                  max: 1.1.0

Should be what we want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ! nice !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're good to go 👍

  Add support for setting user, group, file_mode, and dir_mode on
  trees to address the use case of deploying directory trees with
  specific ownership for rootless containers.

  Fixes: coreos#544
Copy link
Collaborator

@prestist prestist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thank you for all your work here @vic1707 !

@prestist prestist merged commit 93465e9 into coreos:main Nov 17, 2025
8 checks passed
@jeppester
Copy link

@vic1707 Thank you for implementing this! 🏅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set owner for trees.

3 participants