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