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