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