Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions base/v0_7_exp/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,12 @@ type Timeouts struct {
}

type Tree struct {
Local string `yaml:"local"`
Path *string `yaml:"path"`
Group NodeGroup `yaml:"group"`
Local string `yaml:"local"`
Path *string `yaml:"path"`
User NodeUser `yaml:"user"`
FileMode *int `yaml:"file_mode"`
DirMode *int `yaml:"dir_mode"`
}

type Unit struct {
Expand Down
77 changes: 65 additions & 12 deletions base/v0_7_exp/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,29 +333,69 @@ func (c Config) processTrees(ret *types.Config, options common.TranslateOptions)
destBaseDir = *tree.Path
}

walkTree(yamlPath, &ts, &r, t, srcBaseDir, destBaseDir, options)
walkTree(yamlPath, &ts, &r, t, treeWalkOptions{
srcBaseDir: srcBaseDir,
destBaseDir: destBaseDir,
TranslateOptions: options,
user: tree.User,
group: tree.Group,
fileMode: tree.FileMode,
dirMode: tree.DirMode,
})
}
return ts, r
}

func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report.Report, t *nodeTracker, srcBaseDir, destBaseDir string, options common.TranslateOptions) {
type treeWalkOptions struct {
srcBaseDir string
destBaseDir string
common.TranslateOptions
user NodeUser
group NodeGroup
fileMode *int
dirMode *int
}

func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report.Report, t *nodeTracker, options treeWalkOptions) {
// The strategy for errors within WalkFunc is to add an error to
// the report and return nil, so walking continues but translation
// will fail afterward.
err := filepath.Walk(srcBaseDir, func(srcPath string, info os.FileInfo, err error) error {
err := filepath.Walk(options.srcBaseDir, func(srcPath string, info os.FileInfo, err error) error {
if err != nil {
r.AddOnError(yamlPath, err)
return nil
}
relPath, err := filepath.Rel(srcBaseDir, srcPath)
relPath, err := filepath.Rel(options.srcBaseDir, srcPath)
if err != nil {
r.AddOnError(yamlPath, err)
return nil
}
destPath := slashpath.Join(destBaseDir, filepath.ToSlash(relPath))
destPath := slashpath.Join(options.destBaseDir, filepath.ToSlash(relPath))

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"))
}
Comment on lines 357 to +398

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"))
	}
}

} else if info.Mode().IsRegular() {
i, file := t.GetFile(destPath)
if file != nil {
Expand All @@ -369,9 +409,7 @@ func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report
return nil
}
i, file = t.AddFile(types.File{
Node: types.Node{
Path: destPath,
},
Node: createNode(destPath, options.user, options.group),
})
ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "files", i), file)
if i == 0 {
Expand Down Expand Up @@ -400,6 +438,9 @@ func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report
if info.Mode()&0111 != 0 {
mode = 0755
}
if options.fileMode != nil {
mode = *options.fileMode
}
file.Mode = &mode
ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "mode"))
}
Expand All @@ -416,9 +457,7 @@ func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report
return nil
}
i, link = t.AddLink(types.Link{
Node: types.Node{
Path: destPath,
},
Node: createNode(destPath, options.user, options.group),
})
ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "links", i), link)
if i == 0 {
Expand All @@ -441,6 +480,20 @@ func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report
r.AddOnError(yamlPath, err)
}

func createNode(destPath string, user NodeUser, group NodeGroup) types.Node {
return types.Node{
Path: destPath,
User: types.NodeUser{
ID: user.ID,
Name: user.Name,
},
Group: types.NodeGroup{
ID: group.ID,
Name: group.Name,
},
}
}

func (c Config) addMountUnits(config *types.Config, ts *translate.TranslationSet) {
if len(c.Storage.Filesystems) == 0 {
return
Expand Down
157 changes: 156 additions & 1 deletion base/v0_7_exp/translate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,7 @@ func TestTranslateTree(t *testing.T) {
inDirs []Directory
inLinks []Link
outFiles []types.File
outDirs []types.Directory
outLinks []types.Link
report string
skip func(t *testing.T)
Expand Down Expand Up @@ -1643,6 +1644,160 @@ func TestTranslateTree(t *testing.T) {
report: "error at $.storage.trees.0: " + common.ErrTreeNotDirectory.Error() + "\n" +
"error at $.storage.trees.1: " + osStatName + " %FilesDir%" + string(filepath.Separator) + "nonexistent: " + osNotFound + "\n",
},
// Permissions and ownership
{
dirFiles: map[string]os.FileMode{
"tree/file": 0600,
"tree/subdir/file": 0644,
"tree2/file": 0600,
},
dirLinks: map[string]string{
"tree/subdir/link": "../file",
},
inTrees: []Tree{
{
Local: "tree",
FileMode: util.IntToPtr(0777),
User: NodeUser{
Name: util.StrToPtr("bovik"),
},
Group: NodeGroup{
ID: util.IntToPtr(1000),
},
},
{
Local: "tree2",
DirMode: util.IntToPtr(0777),
Path: util.StrToPtr("/etc"),
},
},
outDirs: []types.Directory{
{
Node: types.Node{
Group: types.NodeGroup{
ID: util.IntToPtr(1000),
},
Path: "/",
User: types.NodeUser{
Name: util.StrToPtr("bovik"),
},
},
DirectoryEmbedded1: types.DirectoryEmbedded1{
Mode: util.IntToPtr(0755),
},
},
{
Node: types.Node{
Group: types.NodeGroup{
ID: util.IntToPtr(1000),
},
Path: "/subdir",
User: types.NodeUser{
Name: util.StrToPtr("bovik"),
},
},
DirectoryEmbedded1: types.DirectoryEmbedded1{
Mode: util.IntToPtr(0755),
},
},
{
Node: types.Node{
Path: "/etc",
},
DirectoryEmbedded1: types.DirectoryEmbedded1{
Mode: util.IntToPtr(0777),
},
},
},
outFiles: []types.File{
{
Node: types.Node{
Path: "/file",
User: types.NodeUser{
Name: util.StrToPtr("bovik"),
},
Group: types.NodeGroup{
ID: util.IntToPtr(1000),
},
},
FileEmbedded1: types.FileEmbedded1{
Contents: types.Resource{
Source: util.StrToPtr("data:,tree%2Ffile"),
Compression: util.StrToPtr(""),
},
Mode: util.IntToPtr(0777),
},
},
{
Node: types.Node{
Path: "/subdir/file",
User: types.NodeUser{
Name: util.StrToPtr("bovik"),
},
Group: types.NodeGroup{
ID: util.IntToPtr(1000),
},
},
FileEmbedded1: types.FileEmbedded1{
Contents: types.Resource{
Source: util.StrToPtr("data:,tree%2Fsubdir%2Ffile"),
Compression: util.StrToPtr(""),
},
Mode: util.IntToPtr(0777),
},
},
{
Node: types.Node{
Path: "/etc/file",
},
FileEmbedded1: types.FileEmbedded1{
Contents: types.Resource{
Source: util.StrToPtr("data:,tree2%2Ffile"),
Compression: util.StrToPtr(""),
},
Mode: util.IntToPtr(0644),
},
},
},
outLinks: []types.Link{
{
Node: types.Node{
Path: "/subdir/link",
User: types.NodeUser{
Name: util.StrToPtr("bovik"),
},
Group: types.NodeGroup{
ID: util.IntToPtr(1000),
},
},
LinkEmbedded1: types.LinkEmbedded1{
Target: util.StrToPtr("../file"),
},
},
},
},
// Overwrite via tree ownership fails
{
dirFiles: map[string]os.FileMode{
"tree/etc/file": 0600,
},
inDirs: []Directory{
{Path: "/etc"},
},
inTrees: []Tree{
{
Local: "tree",
FileMode: util.IntToPtr(0777),
User: NodeUser{
Name: util.StrToPtr("bovik"),
},
Group: NodeGroup{
ID: util.IntToPtr(1000),
},
},
},
report: "error at $.storage.trees.0: " + common.ErrNodeExists.Error() + "\n",
},
}

for i, test := range tests {
Expand Down Expand Up @@ -1728,7 +1883,7 @@ func TestTranslateTree(t *testing.T) {
assert.NoError(t, translations.DebugVerifyCoverage(actual), "incomplete TranslationSet coverage")

assert.Equal(t, test.outFiles, actual.Storage.Files, "files mismatch")
assert.Equal(t, []types.Directory(nil), actual.Storage.Directories, "directories mismatch")
assert.Equal(t, test.outDirs, actual.Storage.Directories, "directories mismatch")
assert.Equal(t, test.outLinks, actual.Storage.Links, "links mismatch")
})
}
Expand Down
10 changes: 9 additions & 1 deletion docs/config-fcos-v1_7-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,17 @@ The Fedora CoreOS configuration is a YAML document conforming to the following s
* **_needs_network_** (boolean): whether or not the device requires networking.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to enable cex compatibility for luks. If omitted, defaults to false.
* **_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`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
* **_file_mode_** (integer): Custom permissions to apply to files
* **_dir_mode_** (integer): Custom permissions to apply to directories
* **_user_** (object): User owner of the tree
* **_name_** (string): username
* **_id_** (integer): uid
* **_group_** (object): Group owner of the tree
* **_name_** (string): group name
* **_id_** (integer): gid
* **_systemd_** (object): describes the desired state of the systemd units.
* **_units_** (list of objects): the list of systemd units. Every unit must have a unique `name`.
* **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service").
Expand Down
10 changes: 9 additions & 1 deletion docs/config-fiot-v1_1-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,17 @@ The Fedora IoT configuration is a YAML document conforming to the following spec
* **_name_** (string): the group name of the group.
* **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`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
* **_file_mode_** (integer): Custom permissions to apply to files
* **_dir_mode_** (integer): Custom permissions to apply to directories
* **_user_** (object): User owner of the tree
* **_name_** (string): username
* **_id_** (integer): uid
* **_group_** (object): Group owner of the tree
* **_name_** (string): group name
* **_id_** (integer): gid
* **_systemd_** (object): describes the desired state of the systemd units.
* **_units_** (list of objects): the list of systemd units. Every unit must have a unique `name`.
* **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service").
Expand Down
10 changes: 9 additions & 1 deletion docs/config-flatcar-v1_2-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,17 @@ The Flatcar configuration is a YAML document conforming to the following specifi
* **pin** (string): the clevis pin.
* **config** (string): the clevis configuration JSON.
* **_needs_network_** (boolean): whether or not the device requires networking.
* **_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`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
* **_file_mode_** (integer): Custom permissions to apply to files
* **_dir_mode_** (integer): Custom permissions to apply to directories
* **_user_** (object): User owner of the tree
* **_name_** (string): username
* **_id_** (integer): uid
* **_group_** (object): Group owner of the tree
* **_name_** (string): group name
* **_id_** (integer): gid
* **_systemd_** (object): describes the desired state of the systemd units.
* **_units_** (list of objects): the list of systemd units. Every unit must have a unique `name`.
* **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service").
Expand Down
10 changes: 9 additions & 1 deletion docs/config-openshift-v4_21-exp.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,17 @@ The OpenShift configuration is a YAML document conforming to the following speci
* **_needs_network_** (boolean): whether or not the device requires networking.
* **_cex_** (object): describes the IBM Crypto Express (CEX) card configuration for the luks device.
* **_enabled_** (boolean): whether or not to enable cex compatibility for luks. If omitted, defaults to false.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Symlinks must not be present. Ownership is not preserved. File modes are set to 0755 if the local file is executable or 0644 otherwise. File attributes can be overridden by creating a corresponding entry in the `files` section; such entries must omit `contents`.
* **_trees_** (list of objects): a list of local directory trees to be embedded in the config. Symlinks must not be present. 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. File attributes can be overridden by creating a corresponding entry in the `files` section; such entries must omit `contents`.
* **local** (string): the base of the local directory tree, relative to the directory specified by the `--files-dir` command-line argument.
* **_path_** (string): the path of the tree within the target system. Defaults to `/`.
* **_file_mode_** (integer): Custom permissions to apply to files
* **_dir_mode_** (integer): Custom permissions to apply to directories
* **_user_** (object): User owner of the tree
* **_name_** (string): username
* **_id_** (integer): uid
* **_group_** (object): Group owner of the tree
* **_name_** (string): group name
* **_id_** (integer): gid
* **_systemd_** (object): describes the desired state of the systemd units.
* **_units_** (list of objects): the list of systemd units. Every unit must have a unique `name`.
* **name** (string): the name of the unit. This must be suffixed with a valid unit type (e.g. "thing.service").
Expand Down
Loading
Loading