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