]> mj.ucw.cz Git - libucw.git/blob - lib/buckettool.c
open -> sh_open
[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   int errors = 0;
147   int fatal_errors = 0;
148
149   fd = sh_open(obuck_name, O_RDWR);
150   if (fd < 0)
151     die("Unable to open the bucket file %s: %m", obuck_name);
152   for(;;)
153     {
154       oid = pos >> OBUCK_SHIFT;
155       i = sh_pread(fd, &h, sizeof(h), pos);
156       if (!i)
157         break;
158       if (i != sizeof(h))
159         printf("%08x  incomplete header\n", oid);
160       else if (h.magic == OBUCK_INCOMPLETE_MAGIC)
161         printf("%08x  incomplete file\n", oid);
162       else if (h.magic != OBUCK_MAGIC)
163         printf("%08x  invalid header magic\n", oid);
164       else if (h.oid != oid && h.oid != OBUCK_OID_DELETED)
165         printf("%08x  invalid header backlink\n", oid);
166       else
167         {
168           end = (pos + sizeof(h) + h.length + 4 + OBUCK_ALIGN - 1) & ~(sh_off_t)(OBUCK_ALIGN - 1);
169           if (sh_pread(fd, &chk, 4, end-4) != 4)
170             printf("%08x  missing trailer\n", oid);
171           else if (chk != OBUCK_TRAILER)
172             printf("%08x  mismatched trailer\n", oid);
173           /* OK */
174           pos = end;
175           continue;
176         }
177       errors++;
178       end = pos;
179       do
180         {
181           if (pos - end > 0x10000000)
182             {
183               printf("*** skipped for too long, giving up\n");
184               fatal_errors++;
185               goto finish;
186             }
187           end += OBUCK_ALIGN;
188           if (sh_pread(fd, &nh, sizeof(nh), end) != sizeof(nh))
189             {
190               printf("*** unable to find next header\n");
191               if (fix)
192                 {
193                   printf("*** truncating file\n");
194                   ftruncate(fd, pos);
195                 }
196               else
197                 printf("*** would truncate the file here\n");
198               goto finish;
199             }
200         }
201       while (nh.magic != OBUCK_MAGIC ||
202              (nh.oid != (oid_t)(end >> OBUCK_SHIFT) && nh.oid != OBUCK_OID_DELETED));
203       printf("*** match at oid %08x\n", end >> OBUCK_SHIFT);
204       if (fix)
205         {
206           h.magic = OBUCK_MAGIC;
207           h.oid = OBUCK_OID_DELETED;
208           h.length = h.orig_length = end - pos - sizeof(h) - 4;
209           sh_pwrite(fd, &h, sizeof(h), pos);
210           chk = OBUCK_TRAILER;
211           sh_pwrite(fd, &chk, 4, end-4);
212           printf("*** replaced the invalid chunk by a DELETED bucket of size %d\n", end - pos);
213         }
214       pos = end;
215     }
216  finish:
217   close(fd);
218   if (!fix && errors || fatal_errors)
219     exit(1);
220 }
221
222 int
223 main(int argc, char **argv)
224 {
225   int i, op;
226
227   op = 0;
228   while ((i = getopt(argc, argv, "lLd:x:icfF")) != -1)
229     switch (i)
230       {
231       case 'l':
232         list(0);
233         op++;
234         break;
235       case 'L':
236         list(1);
237         op++;
238         break;
239       case 'd':
240         delete(optarg);
241         op++;
242         break;
243       case 'x':
244         extract(optarg);
245         op++;
246         break;
247       case 'i':
248         insert();
249         op++;
250         break;
251       case 'c':
252         cat();
253         op++;
254         break;
255       case 'f':
256         fsck(0);
257         op++;
258         break;
259       case 'F':
260         fsck(1);
261         op++;
262         break;
263       default:
264         help();
265       }
266   if (optind < argc || !op)
267     help();
268
269   return 0;
270 }