]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bscan.c
Add jcr to BSOCK and BDB SpoolAttr NoAttributs finish up multiple simultaneous jobs...
[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 static BSR *bsr;
45
46 static void usage()
47 {
48    fprintf(stderr,
49 "Usage: bscan [-d debug_level] <bacula-archive>\n"
50 "       -dnn            set debug level to nn\n"
51 "       -?              print this message\n\n");
52    exit(1);
53 }
54
55 static void my_free_jcr(JCR *jcr)
56 {
57    return;
58 }
59
60 int main (int argc, char *argv[])
61 {
62    int ch;
63
64    my_name_is(argc, argv, "bscan");
65    init_msg(NULL, NULL);
66
67
68    while ((ch = getopt(argc, argv, "b:d:?")) != -1) {
69       switch (ch) {
70          case 'b':
71             bsr = parse_bsr(NULL, optarg);
72             break;
73          case 'd':                    /* debug level */
74             debug_level = atoi(optarg);
75             if (debug_level <= 0)
76                debug_level = 1; 
77             break;
78
79          case '?':
80          default:
81             usage();
82
83       }  
84    }
85    argc -= optind;
86    argv += optind;
87
88    if (argc != 1) {
89       Pmsg0(0, "Wrong number of arguments: \n");
90       usage();
91    }
92
93    jcr = new_jcr(sizeof(JCR), my_free_jcr);
94    jcr->VolSessionId = 1;
95    jcr->VolSessionTime = (uint32_t)time(NULL);
96    jcr->NumVolumes = 1;
97    jcr->bsr = bsr;
98    strcpy(jcr->Job, "bscan");
99    jcr->dev_name = get_pool_memory(PM_FNAME);
100    strcpy(jcr->dev_name, argv[0]);
101
102    /* *** FIXME **** need to put in corect db, user, and password */
103    if ((db=db_init_database(NULL, "bacula", "bacula", "")) == NULL) {
104       Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
105    }
106    if (!db_open_database(db)) {
107       Emsg0(M_ABORT, 0, db_strerror(db));
108    }
109    Dmsg0(200, "Database opened\n");
110
111
112    do_scan(argv[0]);
113
114    free_jcr(jcr);
115    return 0;
116 }
117   
118
119 static void do_scan(char *devname)             
120 {
121    char VolName[100];
122    char *p;
123    struct stat statp;
124    int type;
125    long record_file_index;
126    DEV_RECORD *rec;
127    DEV_BLOCK *block;
128    POOLMEM *fname;                       /* original file name */
129    POOLMEM *ofile;                       /* output name with prefix */
130    POOLMEM *lname;                       /* link name */
131    MEDIA_DBR mr;
132    POOL_DBR pr;
133    JOB_DBR jr;
134
135    if (strncmp(devname, "/dev/", 5) != 0) {
136       /* Try stripping file part */
137       p = devname + strlen(devname);
138       while (p >= devname && *p != '/') {
139          p--;
140       }
141       if (*p == '/') {
142          strcpy(VolName, p+1);
143          *p = 0;
144       }
145    }
146    strcpy(jcr->VolumeName, VolName);
147
148    dev = init_dev(NULL, devname);
149    if (!dev || !open_device(dev)) {
150       Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
151    }
152    Dmsg0(90, "Device opened for read.\n");
153
154    fname = get_pool_memory(PM_FNAME);
155    ofile = get_pool_memory(PM_FNAME);
156    lname = get_pool_memory(PM_FNAME);
157
158    block = new_block(dev);
159    
160    create_vol_list(jcr);
161
162    if (!acquire_device_for_read(jcr, dev, block)) {
163       Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
164    }
165
166    rec = new_record();
167    free_pool_memory(rec->data);
168    rec->data = get_memory(70000);
169
170    memset(&mr, 0, sizeof(mr));
171    memset(&pr, 0, sizeof(pr));
172
173    for ( ;; ) {
174       if (!read_block_from_device(dev, block)) {
175          uint32_t status;
176          if (dev->state & ST_EOT) {
177             if (!mount_next_read_volume(jcr, dev, block)) {
178                break;
179             }
180             continue;
181          }
182          if (dev->state & ST_EOF) {
183             continue;                 /* try again */
184          }
185          if (dev->state & ST_SHORT) {
186             continue;
187          }
188          Pmsg0(0, "Read Record got a bad record\n");
189          status_dev(dev, &status);
190          Dmsg1(20, "Device status: %x\n", status);
191          if (status & MT_EOD)
192             Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
193          else if (status & MT_EOT)
194             Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
195          else if (status & MT_EOF)
196             Emsg0(M_ABORT, 0, "Unexpected End of File\n");
197          else if (status & MT_DR_OPEN)
198             Emsg0(M_ABORT, 0, "Tape Door is Open\n");
199          else if (!(status & MT_ONLINE))
200             Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
201          else
202             Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n", 
203                status, dev_name(dev), strerror(errno));
204       }
205
206       for (rec->state=0; !is_block_empty(rec); ) {
207             SESSION_LABEL label, elabel;
208          if (!read_record_from_block(block, rec)) {
209             break;
210          }
211
212
213          /* This is no longer used */
214          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
215             Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
216             break;                       /* END OF FILE */
217          }
218
219          /* 
220           * Check for Start or End of Session Record 
221           *
222           */
223          if (rec->FileIndex < 0) {
224
225             if (debug_level > 1) {
226                dump_label_record(dev, rec, 1);
227             }
228             switch (rec->FileIndex) {
229                case PRE_LABEL:
230                   Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
231                   return;
232                   break;
233                case VOL_LABEL:
234                   unser_volume_label(dev, rec);
235                   strcpy(mr.VolumeName, dev->VolHdr.VolName);
236                   if (!db_get_media_record(db, &mr)) {
237                      Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
238                         mr.VolumeName);
239                      continue;
240                   }
241                   if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
242                      Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
243                         mr.MediaType, dev->VolHdr.MediaType);
244                      continue;
245                   }
246                   strcpy(pr.Name, dev->VolHdr.PoolName);
247                   if (!db_get_pool_record(db, &pr)) {
248                      Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
249                         pr.Name);
250                      continue;
251                   }
252                   if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
253                      Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
254                         pr.PoolType, dev->VolHdr.PoolType);
255                      continue;
256                   }
257                   Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
258                   break;
259                case SOS_LABEL:
260                   unser_session_label(&label, rec);
261                   memset(&jr, 0, sizeof(jr));
262                   jr.JobId = label.JobId;
263                   if (!db_get_job_record(db, &jr)) {
264                      Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
265                         jr.JobId);
266                      continue;
267                   }
268                   if (rec->VolSessionId != jr.VolSessionId) {
269                      Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
270                         jr.VolSessionId, rec->VolSessionId);
271                      continue;
272                   }
273                   if (rec->VolSessionTime != jr.VolSessionTime) {
274                      Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
275                         jr.VolSessionTime, rec->VolSessionTime);
276                      continue;
277                   }
278                   if (jr.PoolId != pr.PoolId) {
279                      Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
280                         jr.PoolId, pr.PoolId);
281                      continue;
282                   }
283                   break;
284                case EOS_LABEL:
285                   unser_session_label(&elabel, rec);
286                   if (elabel.JobId != label.JobId) {
287                      Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
288                         label.JobId, elabel.JobId);
289                      continue;
290                   }
291                   if (elabel.JobFiles != jr.JobFiles) {
292                      Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
293                         jr.JobFiles, elabel.JobFiles);
294                      continue;
295                   }                                 
296                   if (elabel.JobBytes != jr.JobBytes) {
297                      Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
298                         jr.JobBytes, elabel.JobBytes);
299                      continue;
300                   }                                 
301                   Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
302                   break;
303                case EOM_LABEL:
304                   break;
305                default:
306                   break;
307             }
308             continue;
309          }
310
311          /* Is this the file we want? */
312          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &label)) {
313             rec->remainder = 0;
314             continue;
315          }
316          if (is_partial_record(rec)) {
317             break;
318          }
319
320          /* File Attributes stream */
321          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
322             char *ap, *lp, *fp;
323
324             if (sizeof_pool_memory(fname) < rec->data_len) {
325                fname = realloc_pool_memory(fname, rec->data_len + 1);
326             }
327             if (sizeof_pool_memory(lname) < rec->data_len) {
328                lname = realloc_pool_memory(lname, rec->data_len + 1);
329             }
330             *fname = 0;
331             *lname = 0;
332
333             /*              
334              * An Attributes record consists of:
335              *    File_index
336              *    Type   (FT_types)
337              *    Filename
338              *    Attributes
339              *    Link name (if file linked i.e. FT_LNK)
340              *
341              */
342             sscanf(rec->data, "%ld %d", &record_file_index, &type);
343             if (record_file_index != rec->FileIndex)
344                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
345                   rec->FileIndex, record_file_index);
346             ap = rec->data;
347             while (*ap++ != ' ')         /* skip record file index */
348                ;
349             while (*ap++ != ' ')         /* skip type */
350                ;
351             /* Save filename and position to attributes */
352             fp = fname;
353             while (*ap != 0) {
354                *fp++  = *ap++;
355             }
356             *fp = *ap++;                 /* terminate filename & point to attribs */
357
358             /* Skip through attributes to link name */
359             if (type == FT_LNK) {
360                lp = ap;
361                while (*lp++ != 0) {
362                   ;
363                }
364                strcat(lname, lp);        /* "save" link name */
365             } else {
366                *lname = 0;
367             }
368
369             decode_stat(ap, &statp);
370
371             if (debug_level > 1) {
372                print_ls_output(fname, &statp);   
373             }
374
375          /* Data stream and extracting */
376          } else if (rec->Stream == STREAM_FILE_DATA) {
377
378          } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
379             Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
380          }
381       }
382    }
383
384    release_device(jcr, dev, block);
385
386    free_pool_memory(fname);
387    free_pool_memory(ofile);
388    free_pool_memory(lname);
389    term_dev(dev);
390    free_block(block);
391    free_record(rec);
392    return;
393 }
394
395 extern char *getuser(uid_t uid);
396 extern char *getgroup(gid_t gid);
397
398 static void print_ls_output(char *fname, struct stat *statp)
399 {
400    char buf[1000]; 
401    char *p, *f;
402    int n;
403
404    p = encode_mode(statp->st_mode, buf);
405    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
406    p += n;
407    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
408    p += n;
409    n = sprintf(p, "%8lld  ", (uint64_t)statp->st_size);
410    p += n;
411    p = encode_time(statp->st_ctime, p);
412    *p++ = ' ';
413    *p++ = ' ';
414    for (f=fname; *f; )
415       *p++ = *f++;
416    *p++ = '\n';
417    *p = 0;
418    fputs(buf, stdout);
419 }