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