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