]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
First cut 1.23 -- kes07Jul02
[bacula/bacula] / bacula / src / stored / bscan.c
1 /*
2  *
3  *  Program to scan a Bacula tape and compare it with
4  *    the catalog and optionally synchronize the catalog
5  *    with the tape.
6  *
7  *   Kern E. Sibbald, December 2001
8  *
9  *
10  *   Version $Id$
11  */
12 /*
13    Copyright (C) 2001, 2002 Kern Sibbald and John Walker
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of
18    the License, or (at your option) any later version.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public
26    License along with this program; if not, write to the Free
27    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28    MA 02111-1307, USA.
29
30  */
31
32 #include "bacula.h"
33 #include "stored.h"
34 #include "findlib/find.h"
35 #include "cats/cats.h"
36
37 static void do_scan(char *fname);
38 static void print_ls_output(char *fname, struct stat *statp);
39
40
41 static DEVICE *dev = NULL;
42 static B_DB *db;
43 static JCR *jcr;
44
45 static void usage()
46 {
47    fprintf(stderr,
48 "Usage: bscan [-d debug_level] <bacula-archive>\n"
49 "       -dnn            set debug level to nn\n"
50 "       -?              print this message\n\n");
51    exit(1);
52 }
53
54 static void my_free_jcr(JCR *jcr)
55 {
56    return;
57 }
58
59 int main (int argc, char *argv[])
60 {
61    int ch;
62
63    my_name_is(argc, argv, "bscan");
64    init_msg(NULL, NULL);
65
66
67    while ((ch = getopt(argc, argv, "d:?")) != -1) {
68       switch (ch) {
69          case 'd':                    /* debug level */
70             debug_level = atoi(optarg);
71             if (debug_level <= 0)
72                debug_level = 1; 
73             break;
74
75          case '?':
76          default:
77             usage();
78
79       }  
80    }
81    argc -= optind;
82    argv += optind;
83
84    if (argc != 1) {
85       Pmsg0(0, "Wrong number of arguments: \n");
86       usage();
87    }
88
89    jcr = new_jcr(sizeof(JCR), my_free_jcr);
90    jcr->VolSessionId = 1;
91    jcr->VolSessionTime = (uint32_t)time(NULL);
92    jcr->NumVolumes = 1;
93
94    /* *** FIXME **** need to put in corect db, user, and password */
95    if ((db=db_init_database("bacula", "bacula", "")) == NULL) {
96       Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
97    }
98    if (!db_open_database(db)) {
99       Emsg0(M_ABORT, 0, db_strerror(db));
100    }
101    Dmsg0(200, "Database opened\n");
102
103
104    do_scan(argv[0]);
105
106    free_jcr(jcr);
107    return 0;
108 }
109   
110
111 static void do_scan(char *devname)             
112 {
113    int n;     
114    char VolName[100];
115    char *p;
116    struct stat statp;
117    int type;
118    long record_file_index;
119    DEV_RECORD rec;
120    DEV_BLOCK *block;
121    char *fname;                       /* original file name */
122    char *ofile;                       /* output name with prefix */
123    char *lname;                       /* link name */
124    MEDIA_DBR mr;
125    POOL_DBR pr;
126    JOB_DBR jr;
127
128    if (strncmp(devname, "/dev/", 5) != 0) {
129       /* Try stripping file part */
130       p = devname + strlen(devname);
131       while (p >= devname && *p != '/') {
132          p--;
133       }
134       if (*p == '/') {
135          strcpy(VolName, p+1);
136          *p = 0;
137       }
138    }
139
140    dev = init_dev(NULL, devname);
141    if (!dev || !open_device(dev)) {
142       Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
143    }
144    Dmsg0(90, "Device opened for read.\n");
145
146    fname = (char *)get_pool_memory(PM_FNAME);
147    ofile = (char *)get_pool_memory(PM_FNAME);
148    lname = (char *)get_pool_memory(PM_FNAME);
149
150    block = new_block(dev);
151
152    strcpy(jcr->VolumeName, VolName);
153
154    if (!acquire_device_for_read(jcr, dev, block)) {
155       Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
156    }
157
158    memset(&rec, 0, sizeof(rec));
159    rec.data = (char *)get_memory(70000);
160
161    memset(&mr, 0, sizeof(mr));
162    memset(&pr, 0, sizeof(pr));
163
164    for ( ;; ) {
165       if (!read_record(dev, block, &rec)) {
166          uint32_t status;
167          if (dev->state & ST_EOT) {
168             break;
169          }
170          if (dev->state & ST_EOF) {
171             continue;                 /* try again */
172          }
173          Pmsg0(0, "Read Record got a bad record\n");
174          status_dev(dev, &status);
175          Dmsg1(20, "Device status: %x\n", status);
176          if (status & MT_EOD)
177             Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
178          else if (status & MT_EOT)
179             Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
180          else if (status & MT_EOF)
181             Emsg0(M_ABORT, 0, "Unexpected End of File\n");
182          else if (status & MT_DR_OPEN)
183             Emsg0(M_ABORT, 0, "Tape Door is Open\n");
184          else if (!(status & MT_ONLINE))
185             Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
186          else
187             Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n", n, dev_name(dev), strerror(errno));
188       }
189
190
191       /* This is no longer used */
192       if (rec.VolSessionId == 0 && rec.VolSessionTime == 0) {
193          Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
194          break;                       /* END OF FILE */
195       }
196
197       /* 
198        * Check for Start or End of Session Record 
199        *
200        */
201       if (rec.FileIndex < 0) {
202          SESSION_LABEL label, elabel;
203
204          if (debug_level > 1) {
205             dump_label_record(dev, &rec, 1);
206          }
207          switch (rec.FileIndex) {
208             case PRE_LABEL:
209                Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
210                return;
211                break;
212             case VOL_LABEL:
213                unser_volume_label(dev, &rec);
214                strcpy(mr.VolumeName, dev->VolHdr.VolName);
215                if (!db_get_media_record(db, &mr)) {
216                   Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
217                      mr.VolumeName);
218                   continue;
219                }
220                if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
221                   Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
222                      mr.MediaType, dev->VolHdr.MediaType);
223                   continue;
224                }
225                strcpy(pr.Name, dev->VolHdr.PoolName);
226                if (!db_get_pool_record(db, &pr)) {
227                   Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
228                      pr.Name);
229                   continue;
230                }
231                if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
232                   Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
233                      pr.PoolType, dev->VolHdr.PoolType);
234                   continue;
235                }
236                Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
237                break;
238             case SOS_LABEL:
239                unser_session_label(&label, &rec);
240                memset(&jr, 0, sizeof(jr));
241                jr.JobId = label.JobId;
242                if (!db_get_job_record(db, &jr)) {
243                   Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
244                      jr.JobId);
245                   continue;
246                }
247                if (rec.VolSessionId != jr.VolSessionId) {
248                   Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
249                      jr.VolSessionId, rec.VolSessionId);
250                   continue;
251                }
252                if (rec.VolSessionTime != jr.VolSessionTime) {
253                   Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
254                      jr.VolSessionTime, rec.VolSessionTime);
255                   continue;
256                }
257                if (jr.PoolId != pr.PoolId) {
258                   Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
259                      jr.PoolId, pr.PoolId);
260                   continue;
261                }
262                break;
263             case EOS_LABEL:
264                unser_session_label(&elabel, &rec);
265                if (elabel.JobId != label.JobId) {
266                   Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
267                      label.JobId, elabel.JobId);
268                   continue;
269                }
270                if (elabel.JobFiles != jr.JobFiles) {
271                   Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
272                      jr.JobFiles, elabel.JobFiles);
273                   continue;
274                }                                 
275                if (elabel.JobBytes != jr.JobBytes) {
276                   Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
277                      jr.JobBytes, elabel.JobBytes);
278                   continue;
279                }                                 
280                Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
281                break;
282             case EOM_LABEL:
283                break;
284             default:
285                break;
286          }
287          continue;
288       }
289
290       /* File Attributes stream */
291       if (rec.Stream == STREAM_UNIX_ATTRIBUTES) {
292          char *ap, *lp;
293
294          if (sizeof_pool_memory(fname) < rec.data_len) {
295             fname = (char *)realloc_pool_memory(fname, rec.data_len + 1);
296          }
297          if (sizeof_pool_memory(lname) < rec.data_len) {
298             ofile = (char *)realloc_pool_memory(ofile, rec.data_len + 1);
299          }
300          *fname = 0;
301          *lname = 0;
302
303          /*              
304           * An Attributes record consists of:
305           *    File_index
306           *    Type   (FT_types)
307           *    Filename
308           *    Attributes
309           *    Link name (if file linked i.e. FT_LNK)
310           *
311           */
312          sscanf(rec.data, "%ld %d %s", &record_file_index, &type, fname);
313          if (record_file_index != rec.FileIndex)
314             Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
315                rec.FileIndex, record_file_index);
316          ap = rec.data;
317          /* Skip to attributes */
318          while (*ap++ != 0)
319             ;
320          /* Skip to Link name */
321          if (type == FT_LNK) {
322             lp = ap;
323             while (*lp++ != 0) {
324                ;
325             }
326             strcat(lname, lp);        /* "save" link name */
327          } else {
328             *lname = 0;
329          }
330
331
332          decode_stat(ap, &statp);
333 /*       Dmsg1(000, "Restoring: %s\n", ofile); */
334
335          if (debug_level > 1) {
336             print_ls_output(fname, &statp);   
337          }
338
339       /* Data stream and extracting */
340       } else if (rec.Stream == STREAM_FILE_DATA) {
341
342       } else if (rec.Stream != STREAM_MD5_SIGNATURE) {
343          Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec.Stream, rec.data);
344       }
345    }
346
347    release_device(jcr, dev, block);
348
349    free_pool_memory(fname);
350    free_pool_memory(ofile);
351    free_pool_memory(lname);
352    term_dev(dev);
353    free_block(block);
354    return;
355 }
356
357 extern char *getuser(uid_t uid);
358 extern char *getgroup(gid_t gid);
359
360 static void print_ls_output(char *fname, struct stat *statp)
361 {
362    char buf[1000]; 
363    char *p, *f;
364    int n;
365
366    p = encode_mode(statp->st_mode, buf);
367    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
368    p += n;
369    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
370    p += n;
371    n = sprintf(p, "%8lld  ", (uint64_t)statp->st_size);
372    p += n;
373    p = encode_time(statp->st_ctime, p);
374    *p++ = ' ';
375    *p++ = ' ';
376    for (f=fname; *f; )
377       *p++ = *f++;
378    *p++ = '\n';
379    *p = 0;
380    fputs(buf, stdout);
381 }