]> mj.ucw.cz Git - libucw.git/blob - lib/buckettool.c
Added a simple utility for handling of buckets and repairing damaged
[libucw.git] / lib / buckettool.c
1 /*
2  *      Sherlock Library -- Bucket Manipulation Tool
3  *
4  *      (c) 2001 Martin Mares <mj@ucw.cz>
5  */
6
7 #include "lib/lib.h"
8 #include "lib/bucket.h"
9 #include "lib/fastbuf.h"
10 #include "lib/lfs.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17
18 static void
19 help(void)
20 {
21   fprintf(stderr, "\
22 Usage: buckettool <commands>\n\
23 \n\
24 Commands:\n\
25 -l\t\tlist all buckets\n\
26 -L\t\tlist all buckets including deleted ones\n\
27 -d <obj>\tdelete bucket\n\
28 -x <obj>\textract bucket\n\
29 -i\t\tinsert bucket\n\
30 -c\t\tconcatenate and dump all buckets\n\
31 -f\t\taudit bucket file structure\n\
32 -F\t\taudit and fix bucket file structure\n\
33 ");
34   exit(1);
35 }
36
37 static oid_t
38 parse_id(char *c)
39 {
40   char *e;
41   oid_t o = strtoul(c, &e, 16);
42   if (e && *e)
43     die("Invalid object ID: %s", c);
44   return o;
45 }
46
47 static void
48 list(int full)
49 {
50   struct obuck_header h;
51
52   obuck_init(0);
53   if (obuck_find_first(&h, full))
54     do
55       {
56         if (h.oid == OBUCK_OID_DELETED)
57           printf("DELETED  %6d\n", h.length);
58         else
59           printf("%08x %6d %6d\n", h.oid, h.length, h.orig_length);
60       }
61     while (obuck_find_next(&h, full));
62   obuck_cleanup();
63 }
64
65 static void
66 delete(char *id)
67 {
68   oid_t oid = parse_id(id);
69   obuck_init(1);
70   obuck_delete(oid);
71   obuck_cleanup();
72 }
73
74 static void
75 extract(char *id)
76 {
77   struct fastbuf *b;
78   byte buf[1024];
79   int l;
80   struct obuck_header h;
81
82   h.oid = parse_id(id);
83   obuck_init(0);
84   obuck_find_by_oid(&h);
85   b = obuck_fetch();
86   while ((l = bread(b, buf, sizeof(buf))))
87     fwrite(buf, 1, l, stdout);
88   obuck_fetch_end(b);
89   obuck_cleanup();
90 }
91
92 static void
93 insert(void)
94 {
95   struct fastbuf *b;
96   byte buf[1024];
97   int l;
98   struct obuck_header h;
99
100   obuck_init(1);
101   b = obuck_create();
102   while ((l = fread(buf, 1, sizeof(buf), stdin)))
103     bwrite(b, buf, l);
104   obuck_create_end(b, &h);
105   obuck_cleanup();
106   printf("%08x %d %d\n", h.oid, h.length, h.orig_length);
107 }
108
109 static void
110 cat(void)
111 {
112   struct obuck_header h;
113   struct fastbuf *b;
114   byte buf[1024];
115   int l, lf;
116
117   obuck_init(0);
118   if (obuck_find_first(&h, 0))
119     do
120       {
121         printf("### %08x %6d %6d\n", h.oid, h.length, h.orig_length);
122         b = obuck_fetch();
123         lf = 1;
124         while ((l = bread(b, buf, sizeof(buf))))
125           {
126             fwrite(buf, 1, l, stdout);
127             lf = (buf[l-1] == '\n');
128           }
129         obuck_fetch_end(b);
130         if (!lf)
131           printf("\n# <missing EOL>\n");
132       }
133     while (obuck_find_next(&h, 0));
134   obuck_cleanup();
135 }
136
137 static void
138 fsck(int fix)
139 {
140   int fd, i;
141   struct obuck_header h, nh;
142   sh_off_t pos = 0;
143   sh_off_t end;
144   oid_t oid;
145   u32 chk;
146
147   fd = open(obuck_name, O_RDWR);
148   if (fd < 0)
149     die("Unable to open the bucket file %s: %m", obuck_name);
150   for(;;)
151     {
152       oid = pos >> OBUCK_SHIFT;
153       i = sh_pread(fd, &h, sizeof(h), pos);
154       if (!i)
155         break;
156       if (i != sizeof(h))
157         printf("%08x  incomplete header\n", oid);
158       else if (h.magic == OBUCK_INCOMPLETE_MAGIC)
159         printf("%08x  incomplete file\n", oid);
160       else if (h.magic != OBUCK_MAGIC)
161         printf("%08x  invalid header magic\n", oid);
162       else if (h.oid != oid && h.oid != OBUCK_OID_DELETED)
163         printf("%08x  invalid header backlink\n", oid);
164       else
165         {
166           end = (pos + sizeof(h) + h.length + 4 + OBUCK_ALIGN - 1) & ~(sh_off_t)(OBUCK_ALIGN - 1);
167           if (sh_pread(fd, &chk, 4, end-4) != 4)
168             printf("%08x  missing trailer\n", oid);
169           else if (chk != OBUCK_TRAILER)
170             printf("%08x  mismatched trailer\n", oid);
171           /* OK */
172           pos = end;
173           continue;
174         }
175       end = pos;
176       do
177         {
178           if (pos - end > 0x10000000)
179             {
180               printf("*** skipped for too long, giving up\n");
181               goto finish;
182             }
183           end += OBUCK_ALIGN;
184           if (sh_pread(fd, &nh, sizeof(nh), end) != sizeof(nh))
185             {
186               printf("*** unable to find next header\n");
187               if (fix)
188                 {
189                   printf("*** truncating file\n");
190                   ftruncate(fd, pos);
191                 }
192               else
193                 printf("*** would truncate the file here\n");
194               goto finish;
195             }
196         }
197       while (nh.magic != OBUCK_MAGIC ||
198              (nh.oid != (oid_t)(end >> OBUCK_SHIFT) && nh.oid != OBUCK_OID_DELETED));
199       printf("*** match at oid %08x\n", end >> OBUCK_SHIFT);
200       if (fix)
201         {
202           h.magic = OBUCK_MAGIC;
203           h.oid = OBUCK_OID_DELETED;
204           h.length = h.orig_length = end - pos - sizeof(h) - 4;
205           sh_pwrite(fd, &h, sizeof(h), pos);
206           chk = OBUCK_TRAILER;
207           sh_pwrite(fd, &chk, 4, end-4);
208           printf("*** replaced the invalid chunk by a DELETED bucket of size %d\n", end - pos);
209         }
210       pos = end;
211     }
212  finish:
213   close(fd);
214 }
215
216 int
217 main(int argc, char **argv)
218 {
219   int i, op;
220
221   op = 0;
222   while ((i = getopt(argc, argv, "lLd:x:icfF")) != -1)
223     switch (i)
224       {
225       case 'l':
226         list(0);
227         op++;
228         break;
229       case 'L':
230         list(1);
231         op++;
232         break;
233       case 'd':
234         delete(optarg);
235         op++;
236         break;
237       case 'x':
238         extract(optarg);
239         op++;
240         break;
241       case 'i':
242         insert();
243         op++;
244         break;
245       case 'c':
246         cat();
247         op++;
248         break;
249       case 'f':
250         fsck(0);
251         op++;
252         break;
253       case 'F':
254         fsck(1);
255         op++;
256         break;
257       default:
258         help();
259       }
260   if (optind < argc || !op)
261     help();
262
263   return 0;
264 }