1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2014-2014 Planets Communications B.V.
5 Copyright (C) 2014-2014 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Marco van Wieringen, February 2014
24 */
25 /**
26 * @file
27 * Object Storage API device abstraction.
28 */
29
30 #include "include/bareos.h"
31
32 #ifdef HAVE_DROPLET
33 #include "stored/stored.h"
34 #include "object_store_device.h"
35
36 namespace storagedaemon {
37
38 /**
39 * Options that can be specified for this device type.
40 */
41 enum device_option_type
42 {
43 argument_none = 0,
44 argument_profile,
45 argument_bucket
46 };
47
48 struct device_option {
49 const char* name;
50 enum device_option_type type;
51 int compare_size;
52 };
53
54 static device_option device_options[] = {{"profile=", argument_profile, 8},
55 {"bucket=", argument_bucket, 7},
56 {NULL, argument_none}};
57
58 static int droplet_reference_count = 0;
59 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
60
61 /**
62 * Generic log function that glues libdroplet with BAREOS.
63 */
ObjectStoreLogfunc(dpl_ctx_t * ctx,dpl_log_level_t level,const char * message)64 static void ObjectStoreLogfunc(dpl_ctx_t* ctx,
65 dpl_log_level_t level,
66 const char* message)
67 {
68 switch (level) {
69 case DPL_DEBUG:
70 Dmsg1(100, "%s\n", message);
71 break;
72 case DPL_INFO:
73 Emsg1(M_INFO, 0, "%s\n", message);
74 break;
75 case DPL_WARNING:
76 Emsg1(M_WARNING, 0, "%s\n", message);
77 break;
78 case DPL_ERROR:
79 Emsg1(M_ERROR, 0, "%s\n", message);
80 break;
81 }
82 }
83
84 /**
85 * Map the droplet errno's to system ones.
86 */
DropletErrnoToSystemErrno(dpl_status_t status)87 static inline int DropletErrnoToSystemErrno(dpl_status_t status)
88 {
89 switch (status) {
90 case DPL_ENOENT:
91 errno = ENOENT;
92 break;
93 case DPL_EIO:
94 errno = EIO;
95 break;
96 case DPL_ENAMETOOLONG:
97 errno = ENAMETOOLONG;
98 break;
99 case DPL_EEXIST:
100 errno = EEXIST;
101 break;
102 case DPL_EPERM:
103 errno = EPERM;
104 break;
105 default:
106 break;
107 }
108
109 return -1;
110 }
111
112 /**
113 * Open a volume using libdroplet.
114 */
d_open(const char * pathname,int flags,int mode)115 int object_store_device::d_open(const char* pathname, int flags, int mode)
116 {
117 dpl_status_t status;
118 dpl_vfile_flag_t dpl_flags;
119 dpl_option_t dpl_options;
120
121 #if 1
122 Mmsg1(errmsg,
123 _("Object Storage devices are not yet supported, please disable %s\n"),
124 dev_name);
125 return -1;
126 #endif
127
128 /*
129 * Initialize the droplet library when its not done previously.
130 */
131 P(mutex);
132 if (droplet_reference_count == 0) {
133 status = dpl_init();
134 if (status != DPL_SUCCESS) {
135 V(mutex);
136 return -1;
137 }
138
139 dpl_set_log_func(ObjectStoreLogfunc);
140 droplet_reference_count++;
141 }
142 V(mutex);
143
144 if (!object_configstring_) {
145 int len;
146 char *bp, *next_option;
147 bool done;
148
149 if (!dev_options) {
150 Mmsg0(errmsg, _("No device options configured\n"));
151 Emsg0(M_FATAL, 0, errmsg);
152 return -1;
153 }
154
155 object_configstring_ = strdup(dev_options);
156
157 bp = object_configstring_;
158 while (bp) {
159 next_option = strchr(bp, ',');
160 if (next_option) { *next_option++ = '\0'; }
161
162 done = false;
163 for (int i = 0; !done && device_options[i].name; i++) {
164 /*
165 * Try to find a matching device option.
166 */
167 if (bstrncasecmp(bp, device_options[i].name,
168 device_options[i].compare_size)) {
169 switch (device_options[i].type) {
170 case argument_profile:
171 profile_ = bp + device_options[i].compare_size;
172 done = true;
173 break;
174 case argument_bucket:
175 object_bucketname_ = bp + device_options[i].compare_size;
176 done = true;
177 break;
178 default:
179 break;
180 }
181 }
182 }
183
184 if (!done) {
185 Mmsg1(errmsg, _("Unable to parse device option: %s\n"), bp);
186 Emsg0(M_FATAL, 0, errmsg);
187 goto bail_out;
188 }
189
190 bp = next_option;
191 }
192
193 if (!profile_) {
194 Mmsg0(errmsg, _("No droplet profile configured\n"));
195 Emsg0(M_FATAL, 0, errmsg);
196 goto bail_out;
197 }
198
199 /*
200 * Strip any .profile prefix from the libdroplet profile name.
201 */
202 len = strlen(profile_);
203 if (len > 8 && Bstrcasecmp(profile_ + (len - 8), ".profile")) {
204 profile_[len - 8] = '\0';
205 }
206 }
207
208 /*
209 * See if we need to setup a new context for this device.
210 */
211 if (!ctx_) {
212 char* bp;
213
214 /*
215 * See if this is a path.
216 */
217 bp = strrchr(object_configstring_, '/');
218 if (!bp) {
219 /*
220 * Only a profile name.
221 */
222 ctx_ = dpl_ctx_new(NULL, object_configstring_);
223 } else {
224 if (bp == object_configstring_) {
225 /*
226 * Profile in root of filesystem
227 */
228 ctx_ = dpl_ctx_new("/", bp + 1);
229 } else {
230 /*
231 * Profile somewhere else.
232 */
233 *bp++ = '\0';
234 ctx_ = dpl_ctx_new(object_configstring_, bp);
235 }
236 }
237
238 /*
239 * If we failed to allocate a new context fail the open.
240 */
241 if (!ctx_) {
242 Mmsg1(errmsg, _("Failed to create a new context using config %s\n"),
243 dev_options);
244 return -1;
245 }
246
247 /*
248 * Login if that is needed for this backend.
249 */
250 status = dpl_login(ctx_);
251 switch (status) {
252 case DPL_SUCCESS:
253 break;
254 case DPL_ENOTSUPP:
255 /*
256 * Backend doesn't support login which is fine.
257 */
258 break;
259 default:
260 Mmsg2(errmsg,
261 _("Failed to login for voume %s using dpl_login(): ERR=%s.\n"),
262 getVolCatName(), dpl_status_str(status));
263 return -1;
264 }
265
266 /*
267 * If a bucketname was defined set it in the context.
268 */
269 if (object_bucketname_) { ctx_->cur_bucket = object_bucketname_; }
270 }
271
272 /*
273 * See if we don't have a file open already.
274 */
275 if (vfd_) {
276 dpl_close(vfd_);
277 vfd_ = NULL;
278 }
279
280 /*
281 * Create some options for libdroplet.
282 *
283 * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into
284 * no need to let the library allocate memory we
285 * need to free after copying the data.
286 */
287 memset(&dpl_options, 0, sizeof(dpl_options));
288 dpl_options.mask |= DPL_OPTION_NOALLOC;
289
290 if (flags & O_CREAT) {
291 dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR;
292 status = dpl_open(ctx_, /* context */
293 getVolCatName(), /* locator */
294 dpl_flags, /* flags */
295 &dpl_options, /* options */
296 NULL, /* condition */
297 NULL, /* metadata */
298 NULL, /* sysmd */
299 NULL, /* query_params */
300 NULL, /* stream_status */
301 &vfd_);
302 } else {
303 dpl_flags = DPL_VFILE_FLAG_RDWR;
304 status = dpl_open(ctx_, /* context */
305 getVolCatName(), /* locator */
306 dpl_flags, /* flags */
307 &dpl_options, /* options */
308 NULL, /* condition */
309 NULL, /* metadata */
310 NULL, /* sysmd */
311 NULL, /* query_params */
312 NULL, /* stream_status */
313 &vfd_);
314 }
315
316 switch (status) {
317 case DPL_SUCCESS:
318 offset_ = 0;
319 return 0;
320 default:
321 Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"),
322 getVolCatName(), dpl_status_str(status));
323 vfd_ = NULL;
324 return DropletErrnoToSystemErrno(status);
325 }
326
327 bail_out:
328 return -1;
329 }
330
331 /**
332 * Read data from a volume using libdroplet.
333 */
d_read(int fd,void * buffer,size_t count)334 ssize_t object_store_device::d_read(int fd, void* buffer, size_t count)
335 {
336 if (vfd_) {
337 unsigned int buflen;
338 dpl_status_t status;
339
340 buflen = count;
341 status = dpl_pread(vfd_, count, offset_, (char**)&buffer, &buflen);
342
343 switch (status) {
344 case DPL_SUCCESS:
345 offset_ += buflen;
346 return buflen;
347 default:
348 Mmsg2(errmsg, _("Failed to read %s using dpl_read(): ERR=%s.\n"),
349 getVolCatName(), dpl_status_str(status));
350 return DropletErrnoToSystemErrno(status);
351 }
352 } else {
353 errno = EBADF;
354 return -1;
355 }
356 }
357
358 /**
359 * Write data to a volume using libdroplet.
360 */
d_write(int fd,const void * buffer,size_t count)361 ssize_t object_store_device::d_write(int fd, const void* buffer, size_t count)
362 {
363 if (vfd_) {
364 dpl_status_t status;
365
366 status = dpl_pwrite(vfd_, (char*)buffer, count, offset_);
367 switch (status) {
368 case DPL_SUCCESS:
369 offset_ += count;
370 return count;
371 default:
372 Mmsg2(errmsg, _("Failed to write %s using dpl_write(): ERR=%s.\n"),
373 getVolCatName(), dpl_status_str(status));
374 return DropletErrnoToSystemErrno(status);
375 }
376 } else {
377 errno = EBADF;
378 return -1;
379 }
380 }
381
d_close(int fd)382 int object_store_device::d_close(int fd)
383 {
384 if (vfd_) {
385 dpl_status_t status;
386
387 status = dpl_close(vfd_);
388 switch (status) {
389 case DPL_SUCCESS:
390 vfd_ = NULL;
391 return 0;
392 default:
393 vfd_ = NULL;
394 return DropletErrnoToSystemErrno(status);
395 }
396 } else {
397 errno = EBADF;
398 return -1;
399 }
400 }
401
d_ioctl(int fd,ioctl_req_t request,char * op)402 int object_store_device::d_ioctl(int fd, ioctl_req_t request, char* op)
403 {
404 return -1;
405 }
406
407 /**
408 * Open a directory on the object store and find out size information for a
409 * file.
410 */
ObjectStoreGetFileSize(dpl_ctx_t * ctx,const char * filename)411 static inline size_t ObjectStoreGetFileSize(dpl_ctx_t* ctx,
412 const char* filename)
413 {
414 void* dir_hdl;
415 dpl_status_t status;
416 dpl_dirent_t dirent;
417 size_t filesize = -1;
418
419 status = dpl_opendir(ctx, ".", &dir_hdl);
420 switch (status) {
421 case DPL_SUCCESS:
422 break;
423 default:
424 return -1;
425 }
426
427 while (!dpl_eof(dir_hdl)) {
428 if (Bstrcasecmp(dirent.name, filename)) {
429 filesize = dirent.size;
430 break;
431 }
432 }
433
434 dpl_closedir(dir_hdl);
435
436 return filesize;
437 }
438
d_lseek(DeviceControlRecord * dcr,boffset_t offset,int whence)439 boffset_t object_store_device::d_lseek(DeviceControlRecord* dcr,
440 boffset_t offset,
441 int whence)
442 {
443 switch (whence) {
444 case SEEK_SET:
445 offset_ = offset;
446 break;
447 case SEEK_CUR:
448 offset_ += offset;
449 break;
450 case SEEK_END: {
451 size_t filesize;
452
453 filesize = ObjectStoreGetFileSize(ctx_, getVolCatName());
454 if (filesize >= 0) {
455 offset_ = filesize + offset;
456 } else {
457 return -1;
458 }
459 break;
460 }
461 default:
462 return -1;
463 }
464
465 return offset_;
466 }
467
d_truncate(DeviceControlRecord * dcr)468 bool object_store_device::d_truncate(DeviceControlRecord* dcr)
469 {
470 /*
471 * libdroplet doesn't have a truncate function so unlink the volume and create
472 * a new empty one.
473 */
474 if (vfd_) {
475 dpl_status_t status;
476 dpl_vfile_flag_t dpl_flags;
477 dpl_option_t dpl_options;
478
479 status = dpl_close(vfd_);
480 switch (status) {
481 case DPL_SUCCESS:
482 vfd_ = NULL;
483 break;
484 default:
485 Mmsg2(errmsg, _("Failed to close %s using dpl_close(): ERR=%s.\n"),
486 getVolCatName(), dpl_status_str(status));
487 return false;
488 }
489
490 status = dpl_unlink(ctx_, getVolCatName());
491 switch (status) {
492 case DPL_SUCCESS:
493 break;
494 default:
495 Mmsg2(errmsg, _("Failed to unlink %s using dpl_unlink(): ERR=%s.\n"),
496 getVolCatName(), dpl_status_str(status));
497 return false;
498 }
499
500 /*
501 * Create some options for libdroplet.
502 *
503 * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into
504 * no need to let the library allocate memory we
505 * need to free after copying the data.
506 */
507 memset(&dpl_options, 0, sizeof(dpl_options));
508 dpl_options.mask |= DPL_OPTION_NOALLOC;
509
510 dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR;
511 status = dpl_open(ctx_, /* context */
512 getVolCatName(), /* locator */
513 dpl_flags, /* flags */
514 &dpl_options, /* options */
515 NULL, /* condition */
516 NULL, /* metadata */
517 NULL, /* sysmd */
518 NULL, /* query_params */
519 NULL, /* stream_status */
520 &vfd_);
521
522 switch (status) {
523 case DPL_SUCCESS:
524 break;
525 default:
526 Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"),
527 getVolCatName(), dpl_status_str(status));
528 return false;
529 }
530 }
531
532 return true;
533 }
534
~object_store_device()535 object_store_device::~object_store_device()
536 {
537 if (ctx_) {
538 dpl_ctx_free(ctx_);
539 ctx_ = NULL;
540 }
541
542 if (object_configstring_) { free(object_configstring_); }
543
544 P(mutex);
545 droplet_reference_count--;
546 if (droplet_reference_count == 0) { dpl_free(); }
547 V(mutex);
548 }
549
object_store_device()550 object_store_device::object_store_device()
551 {
552 object_configstring_ = NULL;
553 object_bucketname_ = NULL;
554 ctx_ = NULL;
555 }
556
557 #ifdef HAVE_DYNAMIC_SD_BACKENDS
backend_instantiate(JobControlRecord * jcr,int device_type)558 extern "C" Device* backend_instantiate(JobControlRecord* jcr, int device_type)
559 {
560 Device* dev = NULL;
561
562 switch (device_type) {
563 case B_OBJECT_STORE_DEV:
564 dev = new object_store_device;
565 break;
566 default:
567 Jmsg(jcr, M_FATAL, 0, _("Request for unknown devicetype: %d\n"),
568 device_type);
569 break;
570 }
571
572 return dev;
573 }
574
flush_backend(void)575 extern "C" void flush_backend(void) {}
576 #endif
577 } /* namespace storagedaemon */
578 #endif /* HAVE_DROPLET */
579