]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/file_dev.c
Avoid segfault in dump_block() when the block_len is invalid
[bacula/bacula] / bacula / src / stored / file_dev.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *   file_dev.c  -- low level operations on file devices
23  *
24  *     written by, Kern Sibbald, MM
25  *     separated from dev.c February 2014
26  *
27  */
28
29 #include "bacula.h"
30 #include "stored.h"
31
32 /* Imported functions */
33 const char *mode_to_str(int mode);
34
35
36 /* default primitives are designed for file */
37 int DEVICE::d_open(const char *pathname, int flags)
38 {
39    return ::open(pathname, flags);
40 }
41
42 int DEVICE::d_close(int fd)
43 {
44    return ::close(fd);
45 }
46
47 int DEVICE::d_ioctl(int fd, ioctl_req_t request, char *mt_com)
48 {
49 #ifdef HAVE_WIN32
50    return -1;
51 #else
52    return ::ioctl(fd, request, mt_com);
53 #endif
54 }
55
56 ssize_t DEVICE::d_read(int fd, void *buffer, size_t count)
57 {
58    return ::read(fd, buffer, count);
59 }
60
61 ssize_t DEVICE::d_write(int fd, const void *buffer, size_t count)
62 {
63    return ::write(fd, buffer, count);
64 }
65
66 /* Rewind file device */
67 bool DEVICE::rewind(DCR *dcr)
68 {
69    Dmsg3(400, "rewind res=%d fd=%d %s\n", num_reserved(), m_fd, print_name());
70    state &= ~(ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
71    block_num = file = 0;
72    file_size = 0;
73    file_addr = 0;
74    if (m_fd < 0) {
75       return false;
76    }
77    if (is_file()) {
78       if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
79          berrno be;
80          dev_errno = errno;
81          Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
82             print_name(), be.bstrerror());
83          return false;
84       }
85    }
86    return true;
87 }
88
89 /*
90  * Reposition the device to file, block
91  * Returns: false on failure
92  *          true  on success
93  */
94 bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock)
95 {
96    if (!is_open()) {
97       dev_errno = EBADF;
98       Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
99       Emsg0(M_FATAL, 0, errmsg);
100       return false;
101    }
102
103    boffset_t pos = (((boffset_t)rfile)<<32) | rblock;
104    Dmsg1(100, "===== lseek to %d\n", (int)pos);
105    if (lseek(dcr, pos, SEEK_SET) == (boffset_t)-1) {
106       berrno be;
107       dev_errno = errno;
108       Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
109          print_name(), be.bstrerror());
110       return false;
111    }
112    file = rfile;
113    block_num = rblock;
114    file_addr = pos;
115    return true;
116 }
117
118
119 /* Seek to specified place */
120 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
121 {
122    switch (dev_type) {
123    case B_FILE_DEV:
124 #if defined(HAVE_WIN32)
125       return ::_lseeki64(m_fd, (__int64)offset, whence);
126 #else
127       return ::lseek(m_fd, offset, whence);
128 #endif
129    }
130    return -1;
131 }
132
133 /*
134  * Open a file device.
135  */
136 void DEVICE::open_file_device(DCR *dcr, int omode)
137 {
138    POOL_MEM archive_name(PM_FNAME);
139    struct stat sp;
140
141    get_autochanger_loaded_slot(dcr);
142
143    /*
144     * Handle opening of File Archive (not a tape)
145     */
146
147    pm_strcpy(archive_name, dev_name);
148    /*
149     * If this is a virtual autochanger (i.e. changer_res != NULL)
150     *  we simply use the device name, assuming it has been
151     *  appropriately setup by the "autochanger".
152     */
153    if (!device->changer_res || device->changer_command[0] == 0 ||
154         strcmp(device->changer_command, "/dev/null") == 0) {
155       if (VolCatInfo.VolCatName[0] == 0) {
156          Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
157             print_name());
158          clear_opened();
159          return;
160       }
161
162       /* If not /dev/null concatenate VolumeName */
163       if (!is_null()) {
164          if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
165             pm_strcat(archive_name, "/");
166          }
167          pm_strcat(archive_name, getVolCatName());
168       }
169    }
170
171    mount(1);                          /* do mount if required */
172
173    openmode = omode;
174    set_mode(omode);
175    /* If creating file, give 0640 permissions */
176    Dmsg3(100, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode),
177          archive_name.c_str(), mode);
178    /* Use system open() */
179    if ((m_fd = ::open(archive_name.c_str(), mode, 0640)) < 0) {
180       berrno be;
181       dev_errno = errno;
182       Mmsg3(errmsg, _("Could not open(%s,%s,0640): ERR=%s\n"),
183             archive_name.c_str(), mode_to_str(omode), be.bstrerror());
184       Dmsg1(40, "open failed: %s", errmsg);
185    } else {
186       Dmsg2(40, "Did open(%s,%s,0640)\n", archive_name.c_str(), mode_to_str(omode));
187    }
188    if (m_fd >= 0) {
189       dev_errno = 0;
190       file = 0;
191       file_addr = 0;
192
193       /* Refresh the underline device id */
194       if (fstat(m_fd, &sp) == 0) {
195          devno = sp.st_dev;
196       }
197    }
198    Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd);
199 }
200
201
202 /*
203  * Truncate a volume.
204  */
205 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
206 {
207    struct stat st;
208    DEVICE *dev = this;
209
210    Dmsg1(100, "truncate %s\n", print_name());
211    switch (dev_type) {
212    case B_VTL_DEV:
213    case B_VTAPE_DEV:
214    case B_TAPE_DEV:
215       /* maybe we should rewind and write and eof ???? */
216       return true;                    /* we don't really truncate tapes */
217    case B_FILE_DEV:
218       /* Do truncate for 1 or 2 devices */
219       for ( ;; ) {
220          Dmsg1(100, "Truncate fd=%d\n", dev->m_fd);
221          if (ftruncate(dev->m_fd, 0) != 0) {
222             berrno be;
223             Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"),
224                   print_name(), be.bstrerror());
225             return false;
226          }
227
228          /*
229           * Check for a successful ftruncate() and issue a work-around for devices
230           * (mostly cheap NAS) that don't support truncation.
231           * Workaround supplied by Martin Schmid as a solution to bug #1011.
232           * 1. close file
233           * 2. delete file
234           * 3. open new file with same mode
235           * 4. change ownership to original
236           */
237
238          if (fstat(dev->m_fd, &st) != 0) {
239             berrno be;
240             Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"),
241                   print_name(), be.bstrerror());
242             return false;
243          }
244
245          if (st.st_size != 0) {             /* ftruncate() didn't work */
246             POOL_MEM archive_name(PM_FNAME);
247
248             pm_strcpy(archive_name, dev_name);
249             if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
250                pm_strcat(archive_name, "/");
251             }
252             pm_strcat(archive_name, dcr->VolumeName);
253
254             Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
255                   print_name(), archive_name.c_str());
256
257             /* Close file and blow it away */
258             ::close(dev->m_fd);
259             ::unlink(archive_name.c_str());
260
261             /* Recreate the file -- of course, empty */
262             dev->set_mode(CREATE_READ_WRITE);
263             if ((dev->m_fd = ::open(archive_name.c_str(), mode, st.st_mode)) < 0) {
264                berrno be;
265                dev_errno = errno;
266                Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(),
267                      be.bstrerror());
268                Dmsg1(40, "reopen failed: %s", errmsg);
269                Emsg0(M_FATAL, 0, errmsg);
270                return false;
271             }
272
273             /* Reset proper owner */
274             chown(archive_name.c_str(), st.st_uid, st.st_gid);
275          }
276          break;
277       }
278       return true;
279    }
280    return false;
281 }
282
283
284 /*
285  * (Un)mount the device (either a FILE or DVD device)
286  */
287 bool DEVICE::mount_file(int mount, int dotimeout)
288 {
289    POOL_MEM ocmd(PM_FNAME);
290    POOLMEM *results;
291    DIR* dp;
292    char *icmd;
293    struct dirent *entry, *result;
294    int status, tries, name_max, count;
295    berrno be;
296
297    Dsm_check(200);
298    if (mount) {
299       icmd = device->mount_command;
300    } else {
301       icmd = device->unmount_command;
302    }
303
304    clear_freespace_ok();
305    edit_mount_codes(ocmd, icmd);
306
307    Dmsg2(100, "mount_file: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
308
309    if (dotimeout) {
310       /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
311       tries = 10;
312    } else {
313       tries = 1;
314    }
315    results = get_memory(4000);
316
317    /* If busy retry each second */
318    Dmsg1(100, "mount_file run_prog=%s\n", ocmd.c_str());
319    while ((status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results)) != 0) {
320       /* Doesn't work with internationalization (This is not a problem) */
321       if (mount && fnmatch("*is already mounted on*", results, 0) == 0) {
322          break;
323       }
324       if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
325          break;
326       }
327       if (tries-- > 0) {
328          /* Sometimes the device cannot be mounted because it is already mounted.
329           * Try to unmount it, then remount it */
330          if (mount) {
331             Dmsg1(400, "Trying to unmount the device %s...\n", print_name());
332             mount_file(0, 0);
333          }
334          bmicrosleep(1, 0);
335          continue;
336       }
337       Dmsg5(100, "Device %s cannot be %smounted. stat=%d result=%s ERR=%s\n", print_name(),
338            (mount ? "" : "un"), status, results, be.bstrerror(status));
339       Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"),
340            print_name(), (mount ? "" : "un"), be.bstrerror(status));
341
342       /*
343        * Now, just to be sure it is not mounted, try to read the filesystem.
344        */
345       name_max = pathconf(".", _PC_NAME_MAX);
346       if (name_max < 1024) {
347          name_max = 1024;
348       }
349
350       if (!(dp = opendir(device->mount_point))) {
351          berrno be;
352          dev_errno = errno;
353          Dmsg3(100, "mount_file: failed to open dir %s (dev=%s), ERR=%s\n",
354                device->mount_point, print_name(), be.bstrerror());
355          goto get_out;
356       }
357
358       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
359       count = 0;
360       while (1) {
361          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
362             dev_errno = EIO;
363             Dmsg2(129, "mount_file: failed to find suitable file in dir %s (dev=%s)\n",
364                   device->mount_point, print_name());
365             break;
366          }
367          if ((strcmp(result->d_name, ".")) && (strcmp(result->d_name, "..")) && (strcmp(result->d_name, ".keep"))) {
368             count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
369             break;
370          } else {
371             Dmsg2(129, "mount_file: ignoring %s in %s\n", result->d_name, device->mount_point);
372          }
373       }
374       free(entry);
375       closedir(dp);
376
377       Dmsg1(100, "mount_file: got %d files in the mount point (not counting ., .. and .keep)\n", count);
378
379       if (count > 0) {
380          /* If we got more than ., .. and .keep */
381          /*   there must be something mounted */
382          if (mount) {
383             Dmsg1(100, "Did Mount by count=%d\n", count);
384             break;
385          } else {
386             /* An unmount request. We failed to unmount - report an error */
387             set_mounted(true);
388             free_pool_memory(results);
389             Dmsg0(200, "== error mount=1 wanted unmount\n");
390             return false;
391          }
392       }
393 get_out:
394       set_mounted(false);
395       free_pool_memory(results);
396       Dmsg0(200, "============ mount=0\n");
397       Dsm_check(200);
398       return false;
399    }
400
401    set_mounted(mount);              /* set/clear mounted flag */
402    free_pool_memory(results);
403    /* Do not check free space when unmounting */
404    Dmsg1(200, "============ mount=%d\n", mount);
405    return true;
406 }