2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
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.
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 Bacula® is a registered trademark of Kern Sibbald.
18 * file_dev.c -- low level operations on file devices
20 * written by, Kern Sibbald, MM
21 * separated from dev.c February 2014
28 /* Imported functions */
29 const char *mode_to_str(int mode);
32 /* default primitives are designed for file */
33 int DEVICE::d_open(const char *pathname, int flags)
35 return ::open(pathname, flags);
38 int DEVICE::d_close(int fd)
43 int DEVICE::d_ioctl(int fd, ioctl_req_t request, char *mt_com)
45 return ::ioctl(fd, request, mt_com);
48 ssize_t DEVICE::d_read(int fd, void *buffer, size_t count)
50 return ::read(fd, buffer, count);
53 ssize_t DEVICE::d_write(int fd, const void *buffer, size_t count)
55 return ::write(fd, buffer, count);
58 /* Rewind file device */
59 bool DEVICE::rewind(DCR *dcr)
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 */
70 if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
73 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
74 print_name(), be.bstrerror());
82 * Reposition the device to file, block
83 * Returns: false on failure
86 bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock)
90 Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
91 Emsg0(M_FATAL, 0, errmsg);
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) {
100 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
101 print_name(), be.bstrerror());
111 /* Seek to specified place */
112 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
116 #if defined(HAVE_WIN32)
117 return ::_lseeki64(m_fd, (__int64)offset, whence);
119 return ::lseek(m_fd, offset, whence);
126 * Open a file device. For Aligned type we open both Volumes
128 void DEVICE::open_file_device(DCR *dcr, int omode)
130 POOL_MEM archive_name(PM_FNAME);
131 POOL_MEM aligned_name(PM_FNAME);
133 get_autochanger_loaded_slot(dcr);
136 * Handle opening of File Archive (not a tape)
139 pm_strcpy(archive_name, dev_name);
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".
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"),
154 if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
155 pm_strcat(archive_name, "/");
157 pm_strcat(archive_name, getVolCatName());
160 mount(1); /* do mount if required */
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) {
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);
180 Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd);
185 * Truncate a volume. If this is aligned disk, we
186 * truncate both volumes.
188 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
193 Dmsg1(100, "truncate %s\n", print_name());
198 /* maybe we should rewind and write and eof ???? */
199 return true; /* we don't really truncate tapes */
201 Dmsg1(100, "Truncate fd=%d\n", dev->m_fd);
202 if (ftruncate(dev->m_fd, 0) != 0) {
204 Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"),
205 print_name(), be.bstrerror());
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.
215 * 3. open new file with same mode
216 * 4. change ownership to original
219 if (fstat(dev->m_fd, &st) != 0) {
221 Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"),
222 print_name(), be.bstrerror());
226 if (st.st_size != 0) { /* ftruncate() didn't work */
227 POOL_MEM archive_name(PM_FNAME);
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, "/");
233 pm_strcat(archive_name, dcr->VolumeName);
235 Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
236 print_name(), archive_name.c_str());
238 /* Close file and blow it away */
240 ::unlink(archive_name.c_str());
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) {
247 Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(),
249 Dmsg1(40, "reopen failed: %s", errmsg);
250 Emsg0(M_FATAL, 0, errmsg);
254 /* Reset proper owner */
255 chown(archive_name.c_str(), st.st_uid, st.st_gid);
264 * (Un)mount the device (either a FILE or DVD device)
266 bool DEVICE::do_file_mount(int mount, int dotimeout)
268 POOL_MEM ocmd(PM_FNAME);
272 struct dirent *entry, *result;
273 int status, tries, name_max, count;
278 icmd = device->mount_command;
280 icmd = device->unmount_command;
283 clear_freespace_ok();
284 edit_mount_codes(ocmd, icmd);
286 Dmsg2(100, "do_file_mount: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
289 /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
294 results = get_memory(4000);
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) {
303 if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
307 /* Sometimes the device cannot be mounted because it is already mounted.
308 * Try to unmount it, then remount it */
310 Dmsg1(400, "Trying to unmount the device %s...\n", print_name());
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));
322 * Now, just to be sure it is not mounted, try to read the filesystem.
324 name_max = pathconf(".", _PC_NAME_MAX);
325 if (name_max < 1024) {
329 if (!(dp = opendir(device->mount_point))) {
332 Dmsg3(100, "do_file_mount: failed to open dir %s (dev=%s), ERR=%s\n",
333 device->mount_point, print_name(), be.bstrerror());
337 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
340 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
342 Dmsg2(129, "do_file_mount: failed to find suitable file in dir %s (dev=%s)\n",
343 device->mount_point, print_name());
346 if ((strcmp(result->d_name, ".")) && (strcmp(result->d_name, "..")) && (strcmp(result->d_name, ".keep"))) {
347 count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
350 Dmsg2(129, "do_file_mount: ignoring %s in %s\n", result->d_name, device->mount_point);
356 Dmsg1(100, "do_file_mount: got %d files in the mount point (not counting ., .. and .keep)\n", count);
359 /* If we got more than ., .. and .keep */
360 /* there must be something mounted */
362 Dmsg1(100, "Did Mount by count=%d\n", count);
365 /* An unmount request. We failed to unmount - report an error */
367 free_pool_memory(results);
368 Dmsg0(200, "== error mount=1 wanted unmount\n");
374 free_pool_memory(results);
375 Dmsg0(200, "============ mount=0\n");
380 set_mounted(mount); /* set/clear mounted flag */
381 free_pool_memory(results);
382 Dmsg1(200, "============ mount=%d\n", mount);