xref: /freebsd/tools/tools/nanobsd/mtree-dedup.awk (revision 315ee00f)
1#!/usr/bin/awk -f
2
3#
4# Copyright (c) 2015 M. Warner Losh <imp@FreeBSD.org>
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27#
28
29#
30# Takes a meta-log created by installworld and friends, plus
31# additions from NanoBSD to augment its build to communicate when
32# files move around after installworld / installkernel phase of
33# NanoBSD.
34#
35# All mtree lines from the metafile have a path, followed by
36# a number of keywords.
37#
38# This script recognizes the following new keywords
39#
40# unlink[=x]	remove the path from the output.
41# copy_from=x	create new entry for path copied from
42#		the keywords from x.
43# move_from=x	create new entry for path copied from
44#		the keywords from x. Remove path from
45#		the output.
46#
47# In addition, when path matches a previous entry, the
48# new entry and previous entry are merged.
49#
50# Special note: when uid and uname are both present,
51# uid is ignored. Ditto gid/gname.
52#
53# Also, the paths above have to match exactly, so X
54# should start with "./".
55#
56
57function die(str)
58{
59	print str > "/dev/stderr";
60	exit 1;
61}
62
63function kv(str)
64{
65	if (split(str, xxx, "=") == 2) {
66		kv_key = xxx[1];
67		kv_value = xxx[2];
68	} else {
69		kv_key = str;
70		kv_value = nv;
71	}
72}
73
74# Output the mtree for path based on the kvs.
75function mtree_from_kvs(path, kvs)
76{
77	lv = path " ";
78	for (k in kvs) {
79		if (kvs[k] == nv)
80			lv = lv k " ";
81		else
82			lv = lv k "=" kvs[k] " ";
83	}
84	return lv;
85}
86
87# Parse the mtree line into path + KVs. Use a sentinal value
88# for a bare keyword, which is extremely unlikely to be used
89# for real.
90function line2kv(kvs, str)
91{
92	delete kvs;
93
94	n = split(str, yyy, " ");
95	for (i = 2; i <= n; i++) {
96		s = yyy[i];
97		if (split(s, xxx, "=") == 2)
98			kvs[xxx[1]] = xxx[2];
99		else
100			kvs[s] = nv;
101	}
102}
103
104
105# old += new
106function merge_kvs(old, new)
107{
108	for (k in new) {
109		# uname / uid -- last one wins.
110		if (k == "uid" && "uname" in old)
111			delete old["uname"]
112		if (k == "uname" && "uid" in old)
113			delete old["uid"];
114		# gname / gid -- last one wins.
115		if (k == "gid" && "gname" in old)
116			delete old["gname"]
117		if (k == "gname" && "gid" in old)
118			delete old["gid"];
119		# Otherwise newest value wins
120		old[k] = new[k];
121	}
122}
123
124# Process the line we've read in, per the comments below
125function process_line(path, new)
126{
127	# Clear kvs
128	line2kv(new_kvs, new);
129
130	if ("unlink" in new_kvs) {
131		# A file removed
132		# Sanity check to see if tree[path] exists?
133		# Makes sure when foo/bar/baz exists and foo/bar
134		# unlinked, baz is gone (for all baz).
135		if (path !~ "^\./")
136			die("bad path in : " new);
137		delete tree[path];		# unlink
138		return;
139	# } else if (new_kvs["append_from"]) { # not implemented
140	} else if ("copy_from" in new_kvs) {
141		# A file copied from another location, preserve its
142		# attribute for new file.
143		# Also merge any new attributes from this line.
144		from = new_kvs["copy_from"];
145		if (from !~ "^\./")
146			die("bad path in : " new);
147		delete new_kvs["copy_from"];
148		line2kv(old_kvs, tree[from]);	# old_kvs = kv's in entry
149		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
150		tree[path] = mtree_from_kvs(path, old_kvs);
151	} else if ("move_from" in new_kvs) {
152		# A file moved from another location, preserve its
153		# attribute for new file, and scrag old location
154		# Also merge any new attributes from this line.
155		from = new_kvs["move_from"];
156		if (from !~ "^\./")
157			die("bad path in : " new);
158		delete new_kvs["move_from"];
159		line2kv(old_kvs, tree[from]);	# old_kvs = kv's in entry
160		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
161		tree[path] = mtree_from_kvs(path, old_kvs);
162		delete tree[from];		# unlink
163	} else if (tree[path]) {	# Update existing entry with new line
164		line2kv(old_kvs, tree[path]);	# old_kvs = kv's in entry
165		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
166		tree[path] = mtree_from_kvs(path, old_kvs);
167	} else {			# Add entry plus defaults
168		delete old_kvs;
169		merge_kvs(old_kvs, defaults);
170		merge_kvs(old_kvs, new_kvs);
171		tree[path] = mtree_from_kvs(path, old_kvs);
172	}
173}
174
175BEGIN {
176	nv = "___NO__VALUE___";
177
178	while ((getline < "/dev/stdin") > 0) {
179		if ($1 ~ "^#")
180			continue;
181		if ($1 == "/set") {
182			for (i = 2; i <= NF; i++) {
183				kv($i);
184				defaults[kv_key] = kv_value;
185			}
186		} else if ($1 == "/unset") {
187			for (i = 2; i <= NF; i++) {
188				kv($i);
189				delete defaults[kv_key];
190			}
191		} else
192			process_line($1, $0);
193	}
194
195	# Print the last set of defaults. This will carry
196	# over, I think, to makefs' defaults
197	print mtree_from_kvs("/set", defaults)
198	for (x in tree)
199		print tree[x];
200}
201