4 # Upgrade version 1 CNID databases to version 2
6 # $Id: cnid2_create.in,v 1.2 2005-04-28 20:49:19 bfernhomberg Exp $
8 # Copyright (C) Joerg Lenneis 2003
9 # All Rights Reserved. See COPYING.
18 my $toplevel = $ARGV[0];
20 # Assume our current directory is .AppleDB in the share directory as the default.
22 $toplevel = ".." unless $toplevel;
24 # Main file information data structure. Each entry in did_table points
25 # to a list of hashes. Key is a DID and each list member is a hash
26 # describing a file or directory in the directory corresponding to the
27 # DID. n_entries is the number of items found, output_list contains
28 # all entries that are eventually written to the output file.
34 # RootInfo values in the new format
36 my $ri_cnid = "00000000";
37 my $ri_dev = "1122334400000000";
38 my $ri_ino = "0000000000000000";
39 my $ri_did = "00000000";
40 my $ri_hexname = "526f6f74496e666f00"; # RootInfo\0
43 # Current CNID found in didname.db
46 # Largest CNID from cnid.db, used if we cannot find the current CNID in didname.db
49 # Number of bogus/invalid entries found in the DB
54 my $didname_dump = "didname.dump";
55 my $cnid_dump = "cnid.dump";
56 my $cnid2_dump = "cnid2.dump";
67 if ($_ eq "HEADER=END") {
72 die "No valid header found, invalid input file?" unless $header_ok;
80 printf "??:%s %-9s%-9s%-9s%-9s%s\n",
91 sub find_current_cnid()
96 my $compare = " " . $ri_did . $ri_hexname;
98 # get rid of "00" at the end, RootInfo in the old format does not have a trailing \0
102 open DIDNAME, "< $didname_dump" or die "Unable to open $didname_dump: $!";
103 skip_header(*DIDNAME);
108 last if $key eq "DATA=END";
111 last unless defined($key) and defined($val);
112 if ($key eq $compare) {
113 # \00\00\00\00RootInfo
127 if (length($entry->{cnid}) != 8 or length($entry->{name}) == 0) {
128 print_eentry("fmt", $entry);
135 sub create_did_table()
145 open CNID, "< $cnid_dump" or die "Unable to open $cnid_dump: $!";
152 if ($key eq "DATA=END") {
160 last unless defined($key) and defined($val);
162 # We do not worry about converting any of the
163 # integer values into binary form. They are in network byte order,
164 # so we know how to extend them. We just treat them as hexadecimal ASCII strings.
165 # The file name is also stored as a proper string, since we need to
166 # look for it in the file system.
170 $entry->{cnid} = $key;
171 $entry->{dev} = substr($val, 0, 8);
172 $entry->{ino} = substr($val, 8, 8);
173 $entry->{did} = substr($val, 16, 8);
174 $entry->{hexname} = substr($val, 24);
176 $len = length($entry->{hexname}) - 2;
180 $name .= chr(hex(substr($entry->{hexname}, $i, 2)));
183 $entry->{name} = $name;
185 if (verify_entry($entry)) {
186 push @{$did_table{$entry->{did}}}, $entry;
188 $cmax = $entry->{cnid} if $entry->{cnid} gt $cmax;
192 die "No valid end of data found, invalid input file?" unless $data_ok;
196 sub output_header($$$)
202 printf $handle "VERSION=3\nformat=bytevalue\ndatabase=%s\ntype=btree\nHEADER=END\n", $dbname;
205 sub expand_did_table($$)
208 my $basename = shift;
212 foreach $entry (@{$did_table{$did}}) {
213 $name = $basename . "/" . $entry->{name};
217 $entry->{type} = "00000001";
219 $entry->{type} = "00000000";
223 # The file/dir does not exist in the file system. This could result
224 # from a non-afpd rename that has not yet been picked up by afpd and is
225 # fixable. We need a guess at the type, though.
227 if ($did_table{$entry->{cnid}} and scalar(@{$did_table{$entry->{cnid}}}) > 0) {
229 # We have entries hanging off this entry in our table,
230 # so this must be a directory
231 $entry->{type} = "00000001";
233 # If this is actually an empty directory that was renamed,
234 # the entry will be deleted by afpd on the next access,
235 # so this should be safe
236 $entry->{type} = "00000000";
240 $entry->{reached} = 1;
241 push @output_list, $entry;
242 expand_did_table($entry->{cnid}, $name);
246 sub find_unreachable()
252 while (($did, $list) = each %did_table) {
253 foreach $entry (@{$list}) {
254 print_eentry("reach", $entry) unless $entry->{reached};
259 sub find_duplicates()
270 for (my $i = 0; $i < scalar(@output_list); $i++) {
272 $entry = $output_list[$i];
273 $cnid = $entry->{cnid};
274 $devino = $entry->{dev} . $entry->{ino};
275 $didname = $entry->{did} . $entry->{name};
276 if ($seen_cnid{$cnid}) {
277 print_eentry("exist_cnid", $entry);
280 if ($seen_cnid{$cnid}) {
281 print_eentry("dupl_cnid", $entry);
284 if ($seen_devino{$devino}) {
285 print_eentry("dupl_devino", $entry);
288 if ($seen_didname{$didname}) {
289 print_eentry("dupl_didname", $entry);
293 splice(@output_list, $i, 1);
295 $seen_cnid{$cnid} = 1;
296 $seen_devino{$devino} = 1;
297 $seen_didname{$didname} = 1;
303 print "Finding the current CNID from $didname_dump\n";
304 $current_cnid = find_current_cnid();
305 print "Reading in entries from $cnid_dump\n";
306 $max_cnid = create_did_table();
307 print "Found $n_entries entries\n";
309 if (not $current_cnid) {
310 print "Warning: could not find a valid entry for the current CNID in $didname_dump.\n";
311 print "Using maximum CNID value $max_cnid from $cnid_dump instead\n";
312 $current_cnid = $max_cnid;
314 print "Current CNID is $current_cnid\n";
317 print "Building directory tree according to cnid.db\n";
318 expand_did_table("00000002", $toplevel);
320 if ($n_entries > scalar(@output_list)) {
321 print "Looking for unreachable nodes in the directory tree:\n";
325 print "Looking for duplicate entries\n";
328 print "Creating $cnid2_dump\n";
329 open CNID2, "> $cnid2_dump" or die "Unable to open $cnid2_dump for writing: $!";
330 output_header(*CNID2, "cnid2.db", scalar(@output_list) + 1);
331 foreach my $entry (@output_list) {
332 print CNID2 " ", $entry->{cnid}, "\n";
333 print CNID2 " ", $entry->{cnid},
334 "00000000", $entry->{dev}, "00000000", $entry->{ino},
336 $entry->{did}, $entry->{hexname}, "\n";
338 print CNID2 " ", $ri_cnid, "\n";
339 print CNID2 " ", $ri_cnid, $ri_dev, $ri_ino, $current_cnid, $ri_did, $ri_hexname, "\n";
340 print CNID2 "DATA=END\n";
342 output_header(*CNID2, "devino.db", scalar(@output_list) + 1);
343 foreach my $entry (@output_list) {
344 print CNID2 " ", "00000000", $entry->{dev}, "00000000", $entry->{ino}, "\n";
345 print CNID2 " ", $entry->{cnid}, "\n";
347 print CNID2 " ", $ri_dev, $ri_ino, "\n";
348 print CNID2 " ", $ri_cnid, "\n";
349 print CNID2 "DATA=END\n";
351 output_header(*CNID2, "didname.db", scalar(@output_list) + 1);
352 foreach my $entry (@output_list) {
353 print CNID2 " ", $entry->{did}, $entry->{hexname}, "\n";
354 print CNID2 " ", $entry->{cnid}, "\n";
356 print CNID2 " ", $ri_did, $ri_hexname, "\n";
357 print CNID2 " ", $ri_cnid, "\n";
358 print CNID2 "DATA=END\n";