xref: /freebsd/tools/tools/nanobsd/mtree-dedup.awk (revision a3557ef0)
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# $FreeBSD$
28#
29
30#
31# Takes a meta-log created by installworld and friends, plus
32# additions from NanoBSD to augment its build to communicate when
33# files move around after installworld / installkernel phase of
34# NanoBSD.
35#
36# All mtree lines from the metafile have a path, followed by
37# a number of keywords.
38#
39# This script recognizes the following new keywords
40#
41# unlink[=x]	remove the path from the output.
42# copy_from=x	create new entry for path copied from
43#		the keywords from x.
44# move_from=x	create new entry for path copied from
45#		the keywords from x. Remove path from
46#		the output.
47#
48# In addition, when path matches a previous entry, the
49# new entry and previous entry are merged.
50#
51# Special note: when uid and uname are both present,
52# uid is ignored. Ditto gid/gname.
53#
54# Also, the paths above have to match exactly, so X
55# should start with "./".
56#
57
58function die(str)
59{
60	print str > "/dev/stderr";
61	exit 1;
62}
63
64function kv(str)
65{
66	if (split(str, xxx, "=") == 2) {
67		kv_key = xxx[1];
68		kv_value = xxx[2];
69	} else {
70		kv_key = str;
71		kv_value = nv;
72	}
73}
74
75# Output the mtree for path based on the kvs.
76function mtree_from_kvs(path, kvs)
77{
78	lv = path " ";
79	for (k in kvs) {
80		if (kvs[k] == nv)
81			lv = lv k " ";
82		else
83			lv = lv k "=" kvs[k] " ";
84	}
85	return lv;
86}
87
88# Parse the mtree line into path + KVs. Use a sentinal value
89# for a bare keyword, which is extremely unlikely to be used
90# for real.
91function line2kv(kvs, str)
92{
93	delete kvs;
94
95	n = split(str, yyy, " ");
96	for (i = 2; i <= n; i++) {
97		s = yyy[i];
98		if (split(s, xxx, "=") == 2)
99			kvs[xxx[1]] = xxx[2];
100		else
101			kvs[s] = nv;
102	}
103}
104
105
106# old += new
107function merge_kvs(old, new)
108{
109	for (k in new) {
110		# uname / uid -- last one wins.
111		if (k == "uid" && "uname" in old)
112			delete old["uname"]
113		if (k == "uname" && "uid" in old)
114			delete old["uid"];
115		# gname / gid -- last one wins.
116		if (k == "gid" && "gname" in old)
117			delete old["gname"]
118		if (k == "gname" && "gid" in old)
119			delete old["gid"];
120		# Otherwise newest value wins
121		old[k] = new[k];
122	}
123}
124
125# Process the line we've read in, per the comments below
126function process_line(path, new)
127{
128	# Clear kvs
129	line2kv(new_kvs, new);
130
131	if ("unlink" in new_kvs) {
132		# A file removed
133		# Sanity check to see if tree[path] exists?
134		# Makes sure when foo/bar/baz exists and foo/bar
135		# unlinked, baz is gone (for all baz).
136		if (path !~ "^\./")
137			die("bad path in : " new);
138		delete tree[path];		# unlink
139		return;
140	# } else if (new_kvs["append_from"]) { # not implemented
141	} else if ("copy_from" in new_kvs) {
142		# A file copied from another location, preserve its
143		# attribute for new file.
144		# Also merge any new attributes from this line.
145		from = new_kvs["copy_from"];
146		if (from !~ "^\./")
147			die("bad path in : " new);
148		delete new_kvs["copy_from"];
149		line2kv(old_kvs, tree[from]);	# old_kvs = kv's in entry
150		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
151		tree[path] = mtree_from_kvs(path, old_kvs);
152	} else if ("move_from" in new_kvs) {
153		# A file moved from another location, preserve its
154		# attribute for new file, and scrag old location
155		# Also merge any new attributes from this line.
156		from = new_kvs["move_from"];
157		if (from !~ "^\./")
158			die("bad path in : " new);
159		delete new_kvs["move_from"];
160		line2kv(old_kvs, tree[from]);	# old_kvs = kv's in entry
161		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
162		tree[path] = mtree_from_kvs(path, old_kvs);
163		delete tree[from];		# unlink
164	} else if (tree[path]) {	# Update existing entry with new line
165		line2kv(old_kvs, tree[path]);	# old_kvs = kv's in entry
166		merge_kvs(old_kvs, new_kvs);	# old_kvs += new_kvs
167		tree[path] = mtree_from_kvs(path, old_kvs);
168	} else {			# Add entry plus defaults
169		delete old_kvs;
170		merge_kvs(old_kvs, defaults);
171		merge_kvs(old_kvs, new_kvs);
172		tree[path] = mtree_from_kvs(path, old_kvs);
173	}
174}
175
176BEGIN {
177	nv = "___NO__VALUE___";
178
179	while ((getline < "/dev/stdin") > 0) {
180		if ($1 ~ "^#")
181			continue;
182		if ($1 == "/set") {
183			for (i = 2; i <= NF; i++) {
184				kv($i);
185				defaults[kv_key] = kv_value;
186			}
187		} else if ($1 == "/unset") {
188			for (i = 2; i <= NF; i++) {
189				kv($i);
190				delete defaults[kv_key];
191			}
192		} else
193			process_line($1, $0);
194	}
195
196	# Print the last set of defaults. This will carry
197	# over, I think, to makefs' defaults
198	print mtree_from_kvs("/set", defaults)
199	for (x in tree)
200		print tree[x];
201}
202