1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2013-2014 Planets Communications B.V.
6 Copyright (C) 2013-2021 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, MM
25 */
26 /**
27 * @file
28 * Windows File API device abstraction.
29 */
30
31 #include "include/bareos.h"
32 #include "lib/berrno.h"
33 #include "stored/device_control_record.h"
34 #include "stored/stored.h"
35 #include "win32_file_device.h"
36
37 namespace storagedaemon {
38
39 /**
40 * (Un)mount the device (For a FILE device)
41 */
do_mount(DeviceControlRecord * dcr,bool mount,int dotimeout)42 static bool do_mount(DeviceControlRecord* dcr, bool mount, int dotimeout)
43 {
44 DeviceResource* device_resource = dcr->dev->device_resource;
45 PoolMem ocmd(PM_FNAME);
46 POOLMEM* results;
47 DIR* dp;
48 char* icmd;
49 struct dirent *entry, *result;
50 int status, tries, name_max, count;
51 BErrNo be;
52
53 if (mount) {
54 icmd = device_resource->mount_command;
55 } else {
56 icmd = device_resource->unmount_command;
57 }
58
59 dcr->dev->EditMountCodes(ocmd, icmd);
60
61 Dmsg2(100, "do_mount: cmd=%s mounted=%d\n", ocmd.c_str(),
62 dcr->dev->IsMounted());
63
64 if (dotimeout) {
65 /* Try at most 10 times to (un)mount the device. This should perhaps be
66 * configurable. */
67 tries = 10;
68 } else {
69 tries = 1;
70 }
71 results = GetMemory(4000);
72
73 /* If busy retry each second */
74 Dmsg1(100, "do_mount run_prog=%s\n", ocmd.c_str());
75 while ((status = RunProgramFullOutput(ocmd.c_str(),
76 dcr->dev->max_open_wait / 2, results))
77 != 0) {
78 /* Doesn't work with internationalization (This is not a problem) */
79 if (mount && fnmatch("*is already mounted on*", results, 0) == 0) { break; }
80 if (!mount && fnmatch("* not mounted*", results, 0) == 0) { break; }
81 if (tries-- > 0) {
82 /* Sometimes the device cannot be mounted because it is already mounted.
83 * Try to unmount it, then remount it */
84 if (mount) {
85 Dmsg1(400, "Trying to unmount the device %s...\n",
86 dcr->dev->print_name());
87 do_mount(dcr, 0, 0);
88 }
89 Bmicrosleep(1, 0);
90 continue;
91 }
92 Dmsg5(100, "Device %s cannot be %smounted. status=%d result=%s ERR=%s\n",
93 dcr->dev->print_name(), (mount ? "" : "un"), status, results,
94 be.bstrerror(status));
95 Mmsg(dcr->dev->errmsg, _("Device %s cannot be %smounted. ERR=%s\n"),
96 dcr->dev->print_name(), (mount ? "" : "un"), be.bstrerror(status));
97
98 /*
99 * Now, just to be sure it is not mounted, try to read the filesystem.
100 */
101 name_max = pathconf(".", _PC_NAME_MAX);
102 if (name_max < 1024) { name_max = 1024; }
103
104 if (!(dp = opendir(device_resource->mount_point))) {
105 BErrNo be;
106 dcr->dev->dev_errno = errno;
107 Dmsg3(100, "do_mount: failed to open dir %s (dev=%s), ERR=%s\n",
108 device_resource->mount_point, dcr->dev->print_name(),
109 be.bstrerror());
110 goto get_out;
111 }
112
113 entry = (struct dirent*)malloc(sizeof(struct dirent) + name_max + 1000);
114 count = 0;
115 while (1) {
116 #ifdef USE_READDIR_R
117 if ((Readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
118 #else
119 result = readdir(dp);
120 if (result == NULL) {
121 #endif
122
123 dcr->dev->dev_errno = EIO;
124 Dmsg2(129,
125 "do_mount: failed to find suitable file in dir %s (dev=%s)\n",
126 device_resource->mount_point, dcr->dev->print_name());
127 break;
128 }
129 if (!bstrcmp(result->d_name, ".") && !bstrcmp(result->d_name, "..")
130 && !bstrcmp(result->d_name, ".keep")) {
131 count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
132 break;
133 } else {
134 Dmsg2(129, "do_mount: ignoring %s in %s\n", result->d_name,
135 device_resource->mount_point);
136 }
137 }
138 free(entry);
139 closedir(dp);
140
141 Dmsg1(100,
142 "do_mount: got %d files in the mount point (not counting ., .. and "
143 ".keep)\n",
144 count);
145
146 if (count > 0) {
147 /* If we got more than ., .. and .keep */
148 /* there must be something mounted */
149 if (mount) {
150 Dmsg1(100, "Did Mount by count=%d\n", count);
151 break;
152 } else {
153 /* An unmount request. We failed to unmount - report an error */
154 FreePoolMemory(results);
155 Dmsg0(200, "== error mount=1 wanted unmount\n");
156 return false;
157 }
158 }
159 get_out:
160 FreePoolMemory(results);
161 Dmsg0(200, "============ mount=0\n");
162 return false;
163 }
164
165 FreePoolMemory(results);
166 Dmsg1(200, "============ mount=%d\n", mount);
167 return true;
168 }
169
170 /**
171 * Mount the device.
172 *
173 * If timeout, wait until the mount command returns 0.
174 * If !timeout, try to mount the device only once.
175 */
176 bool win32_file_device::MountBackend(DeviceControlRecord* dcr, int timeout)
177 {
178 bool retval = true;
179
180 if (RequiresMount() && device_resource->mount_command) {
181 retval = do_mount(dcr, true, timeout);
182 }
183
184 return retval;
185 }
186
187 /**
188 * Unmount the device
189 *
190 * If timeout, wait until the unmount command returns 0.
191 * If !timeout, try to unmount the device only once.
192 */
193 bool win32_file_device::UnmountBackend(DeviceControlRecord* dcr, int timeout)
194 {
195 bool retval = true;
196
197 if (RequiresMount() && device_resource->unmount_command) {
198 retval = do_mount(dcr, false, timeout);
199 }
200
201 return retval;
202 }
203
204 int win32_file_device::d_open(const char* pathname, int flags, int mode)
205 {
206 return ::open(pathname, flags, mode);
207 }
208
209 ssize_t win32_file_device::d_read(int fd, void* buffer, size_t count)
210 {
211 return ::read(fd, buffer, count);
212 }
213
214 ssize_t win32_file_device::d_write(int fd, const void* buffer, size_t count)
215 {
216 return ::write(fd, buffer, count);
217 }
218
219 int win32_file_device::d_close(int fd) { return ::close(fd); }
220
221 int win32_file_device::d_ioctl(int fd, ioctl_req_t request, char* op)
222 {
223 return -1;
224 }
225
226 boffset_t win32_file_device::d_lseek(DeviceControlRecord* dcr,
227 boffset_t offset,
228 int whence)
229 {
230 return ::_lseeki64(fd, (__int64)offset, whence);
231 }
232
233 bool win32_file_device::d_truncate(DeviceControlRecord* dcr)
234 {
235 struct stat st;
236
237 if (ftruncate(fd, 0) != 0) {
238 BErrNo be;
239
240 Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(),
241 be.bstrerror());
242 return false;
243 }
244
245 /*
246 * Check for a successful ftruncate() and issue a work-around for devices
247 * (mostly cheap NAS) that don't support truncation.
248 * Workaround supplied by Martin Schmid as a solution to bug #1011.
249 * 1. close file
250 * 2. delete file
251 * 3. open new file with same mode
252 * 4. change ownership to original
253 */
254 if (fstat(fd, &st) != 0) {
255 BErrNo be;
256
257 Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), print_name(),
258 be.bstrerror());
259 return false;
260 }
261
262 if (st.st_size != 0) { /* ftruncate() didn't work */
263 PoolMem archive_name(PM_FNAME);
264
265 PmStrcpy(archive_name, archive_device_string);
266 if (!IsPathSeparator(
267 archive_name.c_str()[strlen(archive_name.c_str()) - 1])) {
268 PmStrcat(archive_name, "/");
269 }
270 PmStrcat(archive_name, dcr->VolumeName);
271
272 Mmsg2(errmsg,
273 _("Device %s doesn't support ftruncate(). Recreating file %s.\n"),
274 print_name(), archive_name.c_str());
275
276 /*
277 * Close file and blow it away
278 */
279 ::close(fd);
280 SecureErase(dcr->jcr, archive_name.c_str());
281
282 /*
283 * Recreate the file -- of course, empty
284 */
285 oflags = O_CREAT | O_RDWR | O_BINARY;
286 if ((fd = ::open(archive_name.c_str(), oflags, st.st_mode)) < 0) {
287 BErrNo be;
288
289 dev_errno = errno;
290 Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(),
291 be.bstrerror());
292 Emsg0(M_FATAL, 0, errmsg);
293
294 return false;
295 }
296
297 /*
298 * Reset proper owner
299 */
300 chown(archive_name.c_str(), st.st_uid, st.st_gid);
301 }
302
303 return true;
304 }
305
306 win32_file_device::win32_file_device() {}
307
308 } /* namespace storagedaemon */
309