]> arthur.barton.de Git - netatalk.git/blob - bin/cnid/cnid2_create.in
Added cnid2_create upgrade script to bin/cnid.
[netatalk.git] / bin / cnid / cnid2_create.in
1 #!@PERL@
2
3 use strict;
4
5
6 ### Globals
7
8 my $toplevel = $ARGV[0];
9
10 # Assume our current directory is .AppleDB in the share directory as the default. 
11
12 $toplevel = ".." unless $toplevel;
13
14 # Main file information data structure. Each entry in did_table points
15 # to a list of hashes. Key is a DID and each list member a hash
16 # describing a file or directory in the directory corresponding to the
17 # DID. n_entries is the number of items found, output_list contains
18 # all entries that are eventually written to the output file.
19
20 my %did_table;
21 my $n_entries;
22 my @output_list;
23
24 # RootInfo values in the new format
25
26 my $ri_cnid    = "00000000";
27 my $ri_dev     = "1122334400000000";
28 my $ri_ino     = "0000000000000000";
29 my $ri_did     = "00000000";
30 my $ri_hexname = "526f6f74496e666f00";  # RootInfo\0
31
32
33 # Current CNID found in didname.db
34 my $current_cnid;
35
36 # Largest CNID from cnid.db, used if we cannot find the current CNID in didname.db 
37 my $max_cnid;
38
39 # Number of bogus/invalid entries found in the DB 
40 my $errors_found;
41
42 # Filenames
43
44 my $didname_dump = "didname.dump";
45 my $cnid_dump    = "cnid.dump";
46 my $cnid2_dump   = "cnid2.dump";
47
48 ### Subroutines
49
50 sub skip_header($)
51 {
52     my $handle = shift;
53     my $header_ok;
54
55     while (<$handle>) {
56         chomp;
57         if ($_ eq "HEADER=END") {
58             $header_ok = 1;
59             last;
60         }
61     }
62     die "No valid header found, invalid input file?" unless $header_ok;
63 }
64
65 sub print_eentry($$)
66 {
67     my $reason = shift;
68     my $entry  = shift;
69
70     printf "??:%-6s%-9s%-9s%-9s%-9s%s\n",
71     $reason,
72     $entry->{cnid},
73     $entry->{dev},
74     $entry->{ino},
75     $entry->{did},
76     $entry->{name};
77     
78     $errors_found++;
79 }
80
81 sub find_current_cnid()
82 {
83     my $key;
84     my $val;
85     my $cnid;
86     my $compare = " " . $ri_did . $ri_hexname;
87
88     # get rid of "00" at the end, RootInfo in the old format does not have a trailing \0
89
90     $compare =~ s/00$//;
91     
92     open DIDNAME, "< $didname_dump" or die "Unable to open $didname_dump: $!";
93     skip_header(*DIDNAME);
94
95     while (1) {
96         $key = <DIDNAME>;
97         chomp $key;
98         last if $key eq "DATA=END";
99         $val = <DIDNAME>;
100         chomp $val;
101         last unless defined($key) and defined($val);    
102         if ($key eq $compare) {
103             # \00\00\00\00RootInfo
104             $val =~ s/^ //;
105             $cnid = $val;
106             last;
107         }       
108     }
109     close DIDNAME;
110     return $cnid;
111 }
112
113 sub verify_entry($)
114 {
115     my $entry = shift;
116
117     if (length($entry->{cnid}) != 8 or length($entry->{name}) == 0) {
118         print_eentry("fmt", $entry);
119         return 0;
120     } else {
121         return 1;
122     }
123 }
124
125 sub create_did_table()
126 {    
127     my $data_ok;
128     my $key;
129     my $val;
130     my $len;    
131     my $i;
132     my $name;
133     my $cmax;
134
135     open CNID, "< $cnid_dump" or die "Unable to open $cnid_dump: $!";
136     skip_header(*CNID);
137
138     while (1) {
139         $key = <CNID>;
140         chomp $key;
141         $key =~ s/^ //;
142         if ($key eq "DATA=END") {
143             $data_ok = 1;
144             last;
145         }
146         my $val = <CNID>;
147         chomp $val;
148         $val =~ s/^ //;
149
150         last unless defined($key) and defined($val);
151     
152         # We do not worry about converting any of the
153         # integer values into binary form. They are in network byte order, 
154         # so we know how to extend them. We just treat them as hexadecimal ASCII strings.
155         # The file name is also stored as a proper string, since we need to 
156         # look for it in the file system.
157
158         my $entry;
159     
160         $entry->{cnid}    = $key;
161         $entry->{dev}     = substr($val, 0,   8);
162         $entry->{ino}     = substr($val, 8,   8);
163         $entry->{did}     = substr($val, 16,  8);
164         $entry->{hexname} = substr($val, 24);
165     
166         $len = length($entry->{hexname}) - 2;
167         $i = 0;
168         $name = '';
169         while ($i < $len) {
170             $name .= chr(hex(substr($entry->{hexname}, $i, 2)));
171             $i += 2;
172         }
173         $entry->{name} = $name;
174
175         if (verify_entry($entry)) {
176             push @{$did_table{$entry->{did}}}, $entry;
177             $n_entries++;
178             $cmax = $entry->{cnid} if $entry->{cnid} gt $cmax;
179         }
180     }
181     close CNID;
182     die "No valid end of data found, invalid input file?" unless $data_ok;
183     return $cmax;
184 }
185
186 sub output_header($$$)
187 {
188     my $handle = shift;
189     my $dbname = shift;
190     my $n      = shift;
191     
192     printf $handle "VERSION=3\nformat=bytevalue\ndatabase=%s\n", $dbname;
193     printf $handle "type=hash\nh_nelem=%i\nHEADER=END\n", $n;
194 }
195
196 sub expand_did_table($$)
197 {
198     my $did = shift;
199     my $basename = shift;
200     my $entry;
201     my $name;
202
203     foreach $entry (@{$did_table{$did}}) {
204         $name = $basename . "/" . $entry->{name};
205
206         if ( -e $name ) { 
207             if ( -d $name ) { 
208                 $entry->{type} = "00000001"; 
209             } else { 
210                 $entry->{type} = "00000000"; 
211             } 
212         } else { 
213             
214             # The file/dir does not exist in the file system. This could result
215             # from a non-afpd rename that has not yet been picked up by afpd and is
216             # fixable. We need a guess at the type, though.
217             
218             if (scalar(@{$did_table{$entry->{cnid}}}) > 0) {
219
220                 # We have entries hanging off this entry in our table,
221                 # so this must be a directory
222                 $entry->{type} = "00000001";
223             } else {
224                 # If this is actually an empty directory that was renamed, 
225                 # the entry will be deleted by afpd on the next access, 
226                 # so this should be safe 
227                 $entry->{type} = "00000000";            
228             }
229         }
230
231         $entry->{reached} = 1;
232         push @output_list, $entry;
233         expand_did_table($entry->{cnid}, $name);
234     }    
235 }
236
237 sub find_unreachable()
238 {
239     my $did;
240     my $list;
241     my $entry;
242
243     while (($did, $list) = each %did_table) {
244         foreach $entry (@{$list}) {
245             print_eentry("reach", $entry) unless $entry->{reached};
246         }
247     }
248 }
249
250 print "Finding the current CNID from $didname_dump\n";
251 $current_cnid = find_current_cnid();
252 print "Reading in entries from $cnid_dump\n";
253 $max_cnid = create_did_table();
254 print "Found $n_entries entries\n";
255
256 if (not $current_cnid) {
257     print "Warning: could not find a valid entry for the current CNID in $didname_dump.\n";
258     print "Using maximum CNID value $max_cnid from $cnid_dump instead\n";
259     $current_cnid = $max_cnid;
260 } else {
261     print "Current CNID is $current_cnid\n";
262 }
263
264 print "Building directory tree according to cnid.db\n";
265 expand_did_table("00000002", $toplevel);
266
267 if ($n_entries > scalar(@output_list)) {
268     print "Looking for unreachable nodes in the directory tree:\n";
269     find_unreachable();
270 }
271
272 print "Creating $cnid2_dump\n";
273 open CNID2, "> $cnid2_dump" or die "Unable to open $cnid2_dump for writing: $!";
274 output_header(*CNID2, "cnid2.db", scalar(@output_list) + 1);
275 foreach my $entry (@output_list) {
276     print CNID2 " ", $entry->{cnid}, "\n";
277     print CNID2 " ", $entry->{cnid}, 
278                      "00000000", $entry->{dev}, "00000000", $entry->{ino},
279                      $entry->{type},
280                      $entry->{did}, $entry->{hexname}, "\n";
281 }
282 print CNID2 " ", $ri_cnid, "\n";
283 print CNID2 " ", $ri_cnid, $ri_dev, $ri_ino, $current_cnid, $ri_did, $ri_hexname, "\n";
284 print CNID2 "DATA=END\n";
285
286 output_header(*CNID2, "devino.db", scalar(@output_list) + 1);
287 foreach my $entry (@output_list) {
288     print CNID2 " ", "00000000", $entry->{dev}, "00000000", $entry->{ino}, "\n";
289     print CNID2 " ", $entry->{cnid}, "\n";
290 }
291 print CNID2 " ", $ri_dev, $ri_ino, "\n";
292 print CNID2 " ", $ri_cnid, "\n";
293 print CNID2 "DATA=END\n";
294
295 output_header(*CNID2, "didname.db", scalar(@output_list) + 1);
296 foreach my $entry (@output_list) {
297     print CNID2 " ", $entry->{did}, $entry->{hexname}, "\n";
298     print CNID2 " ", $entry->{cnid}, "\n";
299 }
300 print CNID2 " ", $ri_did, $ri_hexname, "\n";
301 print CNID2 " ", $ri_cnid, "\n";
302 print CNID2 "DATA=END\n";
303
304 close CNID2;
305
306 exit 0;