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