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