2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
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.
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.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * file_dev.c -- low level operations on file devices
24 * written by, Kern Sibbald, MM
25 * separated from dev.c February 2014
32 /* Imported functions */
33 const char *mode_to_str(int mode);
36 /* default primitives are designed for file */
37 int DEVICE::d_open(const char *pathname, int flags)
39 return ::open(pathname, flags);
42 int DEVICE::d_close(int fd)
47 int DEVICE::d_ioctl(int fd, ioctl_req_t request, char *mt_com)
52 return ::ioctl(fd, request, mt_com);
56 ssize_t DEVICE::d_read(int fd, void *buffer, size_t count)
58 return ::read(fd, buffer, count);
61 ssize_t DEVICE::d_write(int fd, const void *buffer, size_t count)
63 return ::write(fd, buffer, count);
66 /* Rewind file device */
67 bool DEVICE::rewind(DCR *dcr)
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 */
78 if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
81 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
82 print_name(), be.bstrerror());
90 * Reposition the device to file, block
91 * Returns: false on failure
94 bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock)
98 Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
99 Emsg0(M_FATAL, 0, errmsg);
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) {
108 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
109 print_name(), be.bstrerror());
119 /* Seek to specified place */
120 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
124 #if defined(HAVE_WIN32)
125 return ::_lseeki64(m_fd, (__int64)offset, whence);
127 return ::lseek(m_fd, offset, whence);
134 * Open a file device.
136 void DEVICE::open_file_device(DCR *dcr, int omode)
138 POOL_MEM archive_name(PM_FNAME);
141 get_autochanger_loaded_slot(dcr);
144 * Handle opening of File Archive (not a tape)
147 pm_strcpy(archive_name, dev_name);
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".
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"),
162 /* If not /dev/null concatenate VolumeName */
164 if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
165 pm_strcat(archive_name, "/");
167 pm_strcat(archive_name, getVolCatName());
171 mount(1); /* do mount if required */
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) {
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);
186 Dmsg2(40, "Did open(%s,%s,0640)\n", archive_name.c_str(), mode_to_str(omode));
193 /* Refresh the underline device id */
194 if (fstat(m_fd, &sp) == 0) {
198 Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd);
205 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
210 Dmsg1(100, "truncate %s\n", print_name());
215 /* maybe we should rewind and write and eof ???? */
216 return true; /* we don't really truncate tapes */
218 /* Do truncate for 1 or 2 devices */
220 Dmsg1(100, "Truncate fd=%d\n", dev->m_fd);
221 if (ftruncate(dev->m_fd, 0) != 0) {
223 Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"),
224 print_name(), be.bstrerror());
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.
234 * 3. open new file with same mode
235 * 4. change ownership to original
238 if (fstat(dev->m_fd, &st) != 0) {
240 Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"),
241 print_name(), be.bstrerror());
245 if (st.st_size != 0) { /* ftruncate() didn't work */
246 POOL_MEM archive_name(PM_FNAME);
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, "/");
252 pm_strcat(archive_name, dcr->VolumeName);
254 Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
255 print_name(), archive_name.c_str());
257 /* Close file and blow it away */
259 ::unlink(archive_name.c_str());
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) {
266 Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(),
268 Dmsg1(40, "reopen failed: %s", errmsg);
269 Emsg0(M_FATAL, 0, errmsg);
273 /* Reset proper owner */
274 chown(archive_name.c_str(), st.st_uid, st.st_gid);
285 * (Un)mount the device (either a FILE or DVD device)
287 bool DEVICE::mount_file(int mount, int dotimeout)
289 POOL_MEM ocmd(PM_FNAME);
293 struct dirent *entry, *result;
294 int status, tries, name_max, count;
299 icmd = device->mount_command;
301 icmd = device->unmount_command;
304 clear_freespace_ok();
305 edit_mount_codes(ocmd, icmd);
307 Dmsg2(100, "mount_file: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
310 /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
315 results = get_memory(4000);
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) {
324 if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
328 /* Sometimes the device cannot be mounted because it is already mounted.
329 * Try to unmount it, then remount it */
331 Dmsg1(400, "Trying to unmount the device %s...\n", print_name());
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));
343 * Now, just to be sure it is not mounted, try to read the filesystem.
345 name_max = pathconf(".", _PC_NAME_MAX);
346 if (name_max < 1024) {
350 if (!(dp = opendir(device->mount_point))) {
353 Dmsg3(100, "mount_file: failed to open dir %s (dev=%s), ERR=%s\n",
354 device->mount_point, print_name(), be.bstrerror());
358 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
361 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
363 Dmsg2(129, "mount_file: failed to find suitable file in dir %s (dev=%s)\n",
364 device->mount_point, print_name());
367 if ((strcmp(result->d_name, ".")) && (strcmp(result->d_name, "..")) && (strcmp(result->d_name, ".keep"))) {
368 count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
371 Dmsg2(129, "mount_file: ignoring %s in %s\n", result->d_name, device->mount_point);
377 Dmsg1(100, "mount_file: got %d files in the mount point (not counting ., .. and .keep)\n", count);
380 /* If we got more than ., .. and .keep */
381 /* there must be something mounted */
383 Dmsg1(100, "Did Mount by count=%d\n", count);
386 /* An unmount request. We failed to unmount - report an error */
388 free_pool_memory(results);
389 Dmsg0(200, "== error mount=1 wanted unmount\n");
395 free_pool_memory(results);
396 Dmsg0(200, "============ mount=0\n");
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);