2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * file_dev.c -- low level operations on file devices
23 * written by, Kern Sibbald, MM
24 * separated from dev.c February 2014
31 /* Imported functions */
32 const char *mode_to_str(int mode);
35 /* default primitives are designed for file */
36 int DEVICE::d_open(const char *pathname, int flags)
38 return ::open(pathname, flags);
41 int DEVICE::d_close(int fd)
46 int DEVICE::d_ioctl(int fd, ioctl_req_t request, char *mt_com)
51 return ::ioctl(fd, request, mt_com);
55 ssize_t DEVICE::d_read(int fd, void *buffer, size_t count)
57 return ::read(fd, buffer, count);
60 ssize_t DEVICE::d_write(int fd, const void *buffer, size_t count)
62 return ::write(fd, buffer, count);
65 /* Rewind file device */
66 bool DEVICE::rewind(DCR *dcr)
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 */
77 if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
80 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
81 print_name(), be.bstrerror());
89 * Reposition the device to file, block
90 * Returns: false on failure
93 bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock)
97 Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
98 Emsg0(M_FATAL, 0, errmsg);
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) {
107 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
108 print_name(), be.bstrerror());
118 /* Seek to specified place */
119 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
123 #if defined(HAVE_WIN32)
124 return ::_lseeki64(m_fd, (__int64)offset, whence);
126 return ::lseek(m_fd, offset, whence);
133 * Open a file device.
135 void DEVICE::open_file_device(DCR *dcr, int omode)
137 POOL_MEM archive_name(PM_FNAME);
140 get_autochanger_loaded_slot(dcr);
143 * Handle opening of File Archive (not a tape)
146 pm_strcpy(archive_name, dev_name);
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".
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"),
158 pm_strcpy(dcr->jcr->errmsg, errmsg);
164 /* If not /dev/null concatenate VolumeName */
166 if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
167 pm_strcat(archive_name, "/");
169 pm_strcat(archive_name, getVolCatName());
173 mount(1); /* do mount if required */
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) {
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);
188 Dmsg2(40, "Did open(%s,%s,0640)\n", archive_name.c_str(), mode_to_str(omode));
195 /* Refresh the underline device id */
196 if (fstat(m_fd, &sp) == 0) {
201 pm_strcpy(dcr->jcr->errmsg, errmsg);
204 Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd);
211 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
216 Dmsg1(100, "truncate %s\n", print_name());
221 /* maybe we should rewind and write and eof ???? */
222 return true; /* we don't really truncate tapes */
224 /* Do truncate for 1 or 2 devices */
226 Dmsg1(100, "Truncate fd=%d\n", dev->m_fd);
227 if (ftruncate(dev->m_fd, 0) != 0) {
229 Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"),
230 print_name(), be.bstrerror());
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.
240 * 3. open new file with same mode
241 * 4. change ownership to original
244 if (fstat(dev->m_fd, &st) != 0) {
246 Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"),
247 print_name(), be.bstrerror());
251 if (st.st_size != 0) { /* ftruncate() didn't work */
252 POOL_MEM archive_name(PM_FNAME);
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, "/");
258 pm_strcat(archive_name, dcr->VolumeName);
260 Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
261 print_name(), archive_name.c_str());
263 /* Close file and blow it away */
265 ::unlink(archive_name.c_str());
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) {
272 Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(),
274 Dmsg1(40, "reopen failed: %s", errmsg);
275 Emsg0(M_FATAL, 0, errmsg);
279 /* Reset proper owner */
280 chown(archive_name.c_str(), st.st_uid, st.st_gid);
291 * (Un)mount the device (either a FILE or DVD device)
293 bool DEVICE::mount_file(int mount, int dotimeout)
295 POOL_MEM ocmd(PM_FNAME);
299 struct dirent *entry, *result;
300 int status, tries, name_max, count;
305 icmd = device->mount_command;
307 icmd = device->unmount_command;
310 clear_freespace_ok();
311 edit_mount_codes(ocmd, icmd);
313 Dmsg2(100, "mount_file: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
316 /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
321 results = get_memory(4000);
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) {
330 if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
334 /* Sometimes the device cannot be mounted because it is already mounted.
335 * Try to unmount it, then remount it */
337 Dmsg1(400, "Trying to unmount the device %s...\n", print_name());
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));
349 * Now, just to be sure it is not mounted, try to read the filesystem.
351 name_max = pathconf(".", _PC_NAME_MAX);
352 if (name_max < 1024) {
356 if (!(dp = opendir(device->mount_point))) {
359 Dmsg3(100, "mount_file: failed to open dir %s (dev=%s), ERR=%s\n",
360 device->mount_point, print_name(), be.bstrerror());
364 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
367 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
369 Dmsg2(129, "mount_file: failed to find suitable file in dir %s (dev=%s)\n",
370 device->mount_point, print_name());
373 if ((strcmp(result->d_name, ".")) && (strcmp(result->d_name, "..")) && (strcmp(result->d_name, ".keep"))) {
374 count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
377 Dmsg2(129, "mount_file: ignoring %s in %s\n", result->d_name, device->mount_point);
383 Dmsg1(100, "mount_file: got %d files in the mount point (not counting ., .. and .keep)\n", count);
386 /* If we got more than ., .. and .keep */
387 /* there must be something mounted */
389 Dmsg1(100, "Did Mount by count=%d\n", count);
392 /* An unmount request. We failed to unmount - report an error */
394 free_pool_memory(results);
395 Dmsg0(200, "== error mount=1 wanted unmount\n");
401 free_pool_memory(results);
402 Dmsg0(200, "============ mount=0\n");
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);