PageRenderTime 63ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/amanda/tags/amanda260p2/device-src/s3-device.c

#
C | 1184 lines | 794 code | 187 blank | 203 comment | 177 complexity | bd3e4d304aeb0e72137fce939a4d6a7b MD5 | raw file
  1. /*
  2. * Copyright (c) 2005 Zmanda, Inc. All Rights Reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU Lesser General Public License version 2.1 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This library is distributed in the hope that it will be useful, but
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. * License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public License
  14. * along with this library; if not, write to the Free Software Foundation,
  15. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  16. *
  17. * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
  18. * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
  19. */
  20. /* An S3 device uses Amazon's S3 service (http://www.amazon.com/s3) to store
  21. * data. It stores data in keys named with a user-specified prefix, inside a
  22. * user-specified bucket. Data is stored in the form of numbered (large)
  23. * blocks.
  24. */
  25. #include <string.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <unistd.h>
  29. #include <dirent.h>
  30. #include <regex.h>
  31. #include <time.h>
  32. #include "util.h"
  33. #include "amanda.h"
  34. #include "conffile.h"
  35. #include "device.h"
  36. #include "s3-device.h"
  37. #include <curl/curl.h>
  38. #ifdef HAVE_OPENSSL_HMAC_H
  39. # include <openssl/hmac.h>
  40. #else
  41. # ifdef HAVE_CRYPTO_HMAC_H
  42. # include <crypto/hmac.h>
  43. # else
  44. # ifdef HAVE_HMAC_H
  45. # include <hmac.h>
  46. # endif
  47. # endif
  48. #endif
  49. /*
  50. * Constants and static data
  51. */
  52. /* Maximum key length as specified in the S3 documentation
  53. * (*excluding* null terminator) */
  54. #define S3_MAX_KEY_LENGTH 1024
  55. #define S3_DEVICE_MIN_BLOCK_SIZE 1024
  56. #define S3_DEVICE_MAX_BLOCK_SIZE (10*1024*1024)
  57. /* This goes in lieu of file number for metadata. */
  58. #define SPECIAL_INFIX "special-"
  59. /* pointer to the class of our parent */
  60. static DeviceClass *parent_class = NULL;
  61. /*
  62. * prototypes
  63. */
  64. /*
  65. * utility functions */
  66. /* Given file and block numbers, return an S3 key.
  67. *
  68. * @param self: the S3Device object
  69. * @param file: the file number
  70. * @param block: the block within that file
  71. * @returns: a newly allocated string containing an S3 key.
  72. */
  73. static char *
  74. file_and_block_to_key(S3Device *self,
  75. int file,
  76. guint64 block);
  77. /* Given the name of a special file (such as 'tapestart'), generate
  78. * the S3 key to use for that file.
  79. *
  80. * @param self: the S3Device object
  81. * @param special_name: name of the special file
  82. * @param file: a file number to include; omitted if -1
  83. * @returns: a newly alocated string containing an S3 key.
  84. */
  85. static char *
  86. special_file_to_key(S3Device *self,
  87. char *special_name,
  88. int file);
  89. /* Write an amanda header file to S3.
  90. *
  91. * @param self: the S3Device object
  92. * @param label: the volume label
  93. * @param timestamp: the volume timestamp
  94. */
  95. static gboolean
  96. write_amanda_header(S3Device *self,
  97. char *label,
  98. char * timestamp);
  99. /* "Fast forward" this device to the end by looking up the largest file number
  100. * present and setting the current file number one greater.
  101. *
  102. * @param self: the S3Device object
  103. */
  104. static gboolean
  105. seek_to_end(S3Device *self);
  106. /* Find the number of the last file that contains any data (even just a header).
  107. *
  108. * @param self: the S3Device object
  109. * @returns: the last file, or -1 in event of an error
  110. */
  111. static int
  112. find_last_file(S3Device *self);
  113. /* Delete all blocks in the given file, including the filestart block
  114. *
  115. * @param self: the S3Device object
  116. * @param file: the file to delete
  117. */
  118. static gboolean
  119. delete_file(S3Device *self,
  120. int file);
  121. /* Set up self->s3 as best as possible. Unless SILENT is TRUE,
  122. * any problems will generate warnings (with g_warning). Regardless,
  123. * the return value is TRUE iff self->s3 is useable.
  124. *
  125. * @param self: the S3Device object
  126. * @param silent: silence warnings
  127. * @returns: TRUE if the handle is set up
  128. */
  129. static gboolean
  130. setup_handle(S3Device * self,
  131. gboolean ignore_problems);
  132. /*
  133. * class mechanics */
  134. static void
  135. s3_device_init(S3Device * o);
  136. static void
  137. s3_device_class_init(S3DeviceClass * c);
  138. static void
  139. s3_device_finalize(GObject * o);
  140. static Device*
  141. s3_device_factory(char * device_type,
  142. char * device_name);
  143. /*
  144. * virtual functions */
  145. static gboolean
  146. s3_device_open_device(Device *pself,
  147. char *device_name);
  148. static ReadLabelStatusFlags s3_device_read_label(Device * self);
  149. static gboolean
  150. s3_device_start(Device * self,
  151. DeviceAccessMode mode,
  152. char * label,
  153. char * timestamp);
  154. static gboolean
  155. s3_device_start_file(Device * self,
  156. const dumpfile_t * jobInfo);
  157. static gboolean
  158. s3_device_write_block(Device * self,
  159. guint size,
  160. gpointer data,
  161. gboolean last);
  162. static gboolean
  163. s3_device_finish_file(Device * self);
  164. static dumpfile_t*
  165. s3_device_seek_file(Device *pself,
  166. guint file);
  167. static gboolean
  168. s3_device_seek_block(Device *pself,
  169. guint64 block);
  170. static int
  171. s3_device_read_block(Device * pself,
  172. gpointer data,
  173. int *size_req);
  174. static gboolean
  175. s3_device_recycle_file(Device *pself,
  176. guint file);
  177. static gboolean s3_device_property_set(Device * p_self, DevicePropertyId id,
  178. GValue * val);
  179. static gboolean s3_device_property_get(Device * p_self, DevicePropertyId id,
  180. GValue * val);
  181. /*
  182. * Private functions
  183. */
  184. /* {{{ file_and_block_to_key */
  185. static char *
  186. file_and_block_to_key(S3Device *self,
  187. int file,
  188. guint64 block)
  189. {
  190. char *s3_key = g_strdup_printf("%sf%08x-b%016llx.data",
  191. self->prefix, file, (long long unsigned int)block);
  192. g_assert(strlen(s3_key) <= S3_MAX_KEY_LENGTH);
  193. return s3_key;
  194. }
  195. /* }}} */
  196. /* {{{ special_file_to_key */
  197. static char *
  198. special_file_to_key(S3Device *self,
  199. char *special_name,
  200. int file)
  201. {
  202. if (file == -1)
  203. return g_strdup_printf("%s" SPECIAL_INFIX "%s", self->prefix, special_name);
  204. else
  205. return g_strdup_printf("%sf%08x-%s", self->prefix, file, special_name);
  206. }
  207. /* }}} */
  208. /* {{{ write_amanda_header */
  209. static gboolean
  210. write_amanda_header(S3Device *self,
  211. char *label,
  212. char * timestamp)
  213. {
  214. char * amanda_header = NULL;
  215. char * key = NULL;
  216. int header_size;
  217. gboolean header_fits, result;
  218. dumpfile_t * dumpinfo = NULL;
  219. /* build the header */
  220. dumpinfo = make_tapestart_header(DEVICE(self), label, timestamp);
  221. amanda_header = device_build_amanda_header(DEVICE(self), dumpinfo,
  222. &header_size, &header_fits);
  223. if (!header_fits) {
  224. fprintf(stderr,
  225. _("Amanda tapestart header won't fit in a single block!\n"));
  226. g_free(amanda_header);
  227. return FALSE;
  228. }
  229. /* write out the header and flush the uploads. */
  230. key = special_file_to_key(self, "tapestart", -1);
  231. result = s3_upload(self->s3, self->bucket, key, amanda_header, header_size);
  232. g_free(amanda_header);
  233. g_free(key);
  234. if (!result) {
  235. fprintf(stderr, _("While writing amanda header: %s\n"),
  236. s3_strerror(self->s3));
  237. }
  238. return result;
  239. }
  240. /* }}} */
  241. /* {{{ seek_to_end */
  242. static gboolean
  243. seek_to_end(S3Device *self) {
  244. int last_file;
  245. Device *pself = DEVICE(self);
  246. last_file = find_last_file(self);
  247. if (last_file < 0)
  248. return FALSE;
  249. pself->file = last_file;
  250. return TRUE;
  251. }
  252. /* }}} */
  253. /* Convert an object name into a file number, assuming the given prefix
  254. * length. Returns -1 if the object name is invalid, or 0 if the object name
  255. * is a "special" key. */
  256. static int key_to_file(guint prefix_len, const char * key) {
  257. int file;
  258. int i;
  259. /* skip the prefix */
  260. g_return_val_if_fail(strlen(key) > prefix_len, -1);
  261. key += prefix_len;
  262. if (strncmp(key, SPECIAL_INFIX, strlen(SPECIAL_INFIX)) == 0) {
  263. return 0;
  264. }
  265. /* check that key starts with 'f' */
  266. g_return_val_if_fail(key[0] == 'f', -1);
  267. key++;
  268. /* check that key is of the form "%08x-" */
  269. for (i = 0; i < 8; i++) {
  270. if (!(key[i] >= '0' && key[i] <= '9') &&
  271. !(key[i] >= 'a' && key[i] <= 'f') &&
  272. !(key[i] >= 'A' && key[i] <= 'F')) break;
  273. }
  274. if (key[i] != '-') return -1;
  275. if (i < 8) return -1;
  276. /* convert the file number */
  277. errno = 0;
  278. file = strtoul(key, NULL, 16);
  279. if (errno != 0) {
  280. g_warning(_("unparseable file number '%s'"), key);
  281. return -1;
  282. }
  283. return file;
  284. }
  285. /* {{{ find_last_file */
  286. /* Find the number of the last file that contains any data (even just a header).
  287. * Returns -1 in event of an error
  288. */
  289. static int
  290. find_last_file(S3Device *self) {
  291. gboolean result;
  292. GSList *keys;
  293. unsigned int prefix_len = strlen(self->prefix);
  294. int last_file = 0;
  295. /* list all keys matching C{PREFIX*-*}, stripping the C{-*} */
  296. result = s3_list_keys(self->s3, self->bucket, self->prefix, "-", &keys);
  297. if (!result) {
  298. fprintf(stderr, _("While listing S3 keys: %s\n"),
  299. s3_strerror(self->s3));
  300. return -1;
  301. }
  302. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  303. int file = key_to_file(prefix_len, keys->data);
  304. /* and if it's the last, keep it */
  305. if (file > last_file)
  306. last_file = file;
  307. }
  308. return last_file;
  309. }
  310. /* }}} */
  311. /* {{{ find_next_file */
  312. /* Find the number of the file following the requested one, if any.
  313. * Returns 0 if there is no such file or -1 in event of an error
  314. */
  315. static int
  316. find_next_file(S3Device *self, int last_file) {
  317. gboolean result;
  318. GSList *keys;
  319. unsigned int prefix_len = strlen(self->prefix);
  320. int next_file = 0;
  321. /* list all keys matching C{PREFIX*-*}, stripping the C{-*} */
  322. result = s3_list_keys(self->s3, self->bucket, self->prefix, "-", &keys);
  323. if (!result) {
  324. fprintf(stderr, _("While listing S3 keys: %s\n"),
  325. s3_strerror(self->s3));
  326. return -1;
  327. }
  328. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  329. int file;
  330. file = key_to_file(prefix_len, (char*)keys->data);
  331. if (file < 0) {
  332. /* Set this in case we don't find a next file; this is not a
  333. * hard error, so if we can find a next file we'll return that
  334. * instead. */
  335. next_file = -1;
  336. }
  337. if (file < next_file && file > last_file) {
  338. next_file = file;
  339. }
  340. }
  341. return last_file;
  342. }
  343. /* }}} */
  344. /* {{{ delete_file */
  345. static gboolean
  346. delete_file(S3Device *self,
  347. int file)
  348. {
  349. gboolean result;
  350. GSList *keys;
  351. char *my_prefix = g_strdup_printf("%sf%08x-", self->prefix, file);
  352. result = s3_list_keys(self->s3, self->bucket, my_prefix, NULL, &keys);
  353. if (!result) {
  354. fprintf(stderr, _("While listing S3 keys: %s\n"),
  355. s3_strerror(self->s3));
  356. return FALSE;
  357. }
  358. /* this will likely be a *lot* of keys */
  359. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  360. if (self->verbose) g_debug(_("Deleting %s"), (char*)keys->data);
  361. if (!s3_delete(self->s3, self->bucket, keys->data)) {
  362. fprintf(stderr, _("While deleting key '%s': %s\n"),
  363. (char*)keys->data, s3_strerror(self->s3));
  364. g_slist_free(keys);
  365. return FALSE;
  366. }
  367. }
  368. return TRUE;
  369. }
  370. /* }}} */
  371. /*
  372. * Class mechanics
  373. */
  374. /* {{{ s3_device_register */
  375. void
  376. s3_device_register(void)
  377. {
  378. static const char * device_prefix_list[] = { "s3", NULL };
  379. g_assert(s3_init());
  380. register_device(s3_device_factory, device_prefix_list);
  381. }
  382. /* }}} */
  383. /* {{{ s3_device_get_type */
  384. GType
  385. s3_device_get_type(void)
  386. {
  387. static GType type = 0;
  388. if G_UNLIKELY(type == 0) {
  389. static const GTypeInfo info = {
  390. sizeof (S3DeviceClass),
  391. (GBaseInitFunc) NULL,
  392. (GBaseFinalizeFunc) NULL,
  393. (GClassInitFunc) s3_device_class_init,
  394. (GClassFinalizeFunc) NULL,
  395. NULL /* class_data */,
  396. sizeof (S3Device),
  397. 0 /* n_preallocs */,
  398. (GInstanceInitFunc) s3_device_init,
  399. NULL
  400. };
  401. type = g_type_register_static (TYPE_DEVICE, "S3Device", &info,
  402. (GTypeFlags)0);
  403. }
  404. return type;
  405. }
  406. /* }}} */
  407. /* {{{ s3_device_init */
  408. static void
  409. s3_device_init(S3Device * self)
  410. {
  411. Device * o;
  412. DeviceProperty prop;
  413. GValue response;
  414. self->initializing = TRUE;
  415. /* Register property values */
  416. o = (Device*)(self);
  417. bzero(&response, sizeof(response));
  418. prop.base = &device_property_concurrency;
  419. prop.access = PROPERTY_ACCESS_GET_MASK;
  420. g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
  421. g_value_set_enum(&response, CONCURRENCY_PARADIGM_SHARED_READ);
  422. device_add_property(o, &prop, &response);
  423. g_value_unset(&response);
  424. prop.base = &device_property_streaming;
  425. g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
  426. g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
  427. device_add_property(o, &prop, &response);
  428. g_value_unset(&response);
  429. prop.base = &device_property_block_size;
  430. g_value_init(&response, G_TYPE_INT);
  431. g_value_set_int(&response, -1); /* indicates a variable block size; see below */
  432. device_add_property(o, &prop, &response);
  433. g_value_unset(&response);
  434. prop.base = &device_property_min_block_size;
  435. g_value_init(&response, G_TYPE_UINT);
  436. g_value_set_uint(&response, S3_DEVICE_MIN_BLOCK_SIZE);
  437. device_add_property(o, &prop, &response);
  438. prop.base = &device_property_max_block_size;
  439. g_value_set_uint(&response, S3_DEVICE_MAX_BLOCK_SIZE);
  440. device_add_property(o, &prop, &response);
  441. g_value_unset(&response);
  442. prop.base = &device_property_appendable;
  443. g_value_init(&response, G_TYPE_BOOLEAN);
  444. g_value_set_boolean(&response, TRUE);
  445. device_add_property(o, &prop, &response);
  446. prop.base = &device_property_partial_deletion;
  447. g_value_set_boolean(&response, TRUE);
  448. device_add_property(o, &prop, &response);
  449. g_value_unset(&response);
  450. prop.base = &device_property_canonical_name;
  451. g_value_init(&response, G_TYPE_STRING);
  452. g_value_set_static_string(&response, "s3:");
  453. device_add_property(o, &prop, &response);
  454. g_value_unset(&response);
  455. prop.base = &device_property_medium_access_type;
  456. g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
  457. g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
  458. device_add_property(o, &prop, &response);
  459. g_value_unset(&response);
  460. prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START;
  461. prop.base = &device_property_s3_secret_key;
  462. device_add_property(o, &prop, NULL);
  463. prop.base = &device_property_s3_access_key;
  464. device_add_property(o, &prop, NULL);
  465. #ifdef WANT_DEVPAY
  466. prop.base = &device_property_s3_user_token;
  467. device_add_property(o, &prop, NULL);
  468. #endif
  469. }
  470. /* }}} */
  471. /* {{{ s3_device_class_init */
  472. static void
  473. s3_device_class_init(S3DeviceClass * c G_GNUC_UNUSED)
  474. {
  475. GObjectClass *g_object_class = (GObjectClass*) c;
  476. DeviceClass *device_class = (DeviceClass *)c;
  477. parent_class = g_type_class_ref (TYPE_DEVICE);
  478. device_class->open_device = s3_device_open_device;
  479. device_class->read_label = s3_device_read_label;
  480. device_class->start = s3_device_start;
  481. device_class->start_file = s3_device_start_file;
  482. device_class->write_block = s3_device_write_block;
  483. device_class->finish_file = s3_device_finish_file;
  484. device_class->seek_file = s3_device_seek_file;
  485. device_class->seek_block = s3_device_seek_block;
  486. device_class->read_block = s3_device_read_block;
  487. device_class->recycle_file = s3_device_recycle_file;
  488. device_class->property_set = s3_device_property_set;
  489. device_class->property_get = s3_device_property_get;
  490. g_object_class->finalize = s3_device_finalize;
  491. }
  492. /* }}} */
  493. /* {{{ s3_device_factory */
  494. static Device*
  495. s3_device_factory(char * device_type,
  496. char * device_name)
  497. {
  498. Device *rval;
  499. S3Device * s3_rval;
  500. g_assert(0 == strcmp(device_type, "s3"));
  501. rval = DEVICE(g_object_new(TYPE_S3_DEVICE, NULL));
  502. s3_rval = (S3Device*)rval;
  503. if (!device_open_device(rval, device_name)) {
  504. g_object_unref(rval);
  505. return NULL;
  506. } else {
  507. s3_rval->initializing = FALSE;
  508. return rval;
  509. }
  510. }
  511. /* }}} */
  512. /*
  513. * Virtual function overrides
  514. */
  515. /* {{{ s3_device_open_device */
  516. static gboolean
  517. s3_device_open_device(Device *pself,
  518. char *device_name)
  519. {
  520. S3Device *self = S3_DEVICE(pself);
  521. char * name_colon;
  522. g_return_val_if_fail(self != NULL, FALSE);
  523. /* Device name may be bucket/prefix, to support multiple volumes in a
  524. * single bucket. */
  525. name_colon = index(device_name, '/');
  526. if (name_colon == NULL) {
  527. self->bucket = g_strdup(device_name);
  528. self->prefix = g_strdup("");
  529. } else {
  530. self->bucket = g_strndup(device_name, name_colon - device_name);
  531. self->prefix = g_strdup(name_colon + 1);
  532. }
  533. if (self->bucket == NULL || self->bucket[0] == '\0') {
  534. fprintf(stderr, _("Empty bucket name in device %s.\n"), device_name);
  535. amfree(self->bucket);
  536. amfree(self->prefix);
  537. return FALSE;
  538. }
  539. g_debug(_("S3 driver using bucket '%s', prefix '%s'"), self->bucket, self->prefix);
  540. /* default value */
  541. self->verbose = FALSE;
  542. if (parent_class->open_device) {
  543. parent_class->open_device(pself, device_name);
  544. }
  545. return TRUE;
  546. }
  547. /* }}} */
  548. /* {{{ s3_device_finalize */
  549. static void s3_device_finalize(GObject * obj_self) {
  550. S3Device *self = S3_DEVICE (obj_self);
  551. if(G_OBJECT_CLASS(parent_class)->finalize)
  552. (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
  553. if(self->s3) s3_free(self->s3);
  554. if(self->bucket) g_free(self->bucket);
  555. if(self->prefix) g_free(self->prefix);
  556. }
  557. /* }}} */
  558. static gboolean setup_handle(S3Device * self, G_GNUC_UNUSED gboolean silent) {
  559. if (self->s3 == NULL) {
  560. if (self->access_key == NULL) {
  561. if (!silent) fprintf(stderr, _("No S3 access key specified\n"));
  562. return FALSE;
  563. }
  564. if (self->secret_key == NULL) {
  565. if (!silent) fprintf(stderr, _("No S3 secret key specified\n"));
  566. return FALSE;
  567. }
  568. #ifdef WANT_DEVPAY
  569. if (self->user_token == NULL) {
  570. if (!silent) fprintf(stderr, _("No S3 user token specified\n"));
  571. return FALSE;
  572. }
  573. #endif
  574. self->s3 = s3_open(self->access_key, self->secret_key
  575. #ifdef WANT_DEVPAY
  576. , self->user_token
  577. #endif
  578. );
  579. if (self->s3 == NULL) {
  580. fprintf(stderr, "Internal error creating S3 handle.\n");
  581. return FALSE;
  582. }
  583. }
  584. s3_verbose(self->s3, self->verbose);
  585. return TRUE;
  586. }
  587. /* {{{ s3_device_read_label */
  588. static ReadLabelStatusFlags
  589. s3_device_read_label(Device *pself) {
  590. S3Device *self = S3_DEVICE(pself);
  591. char *key;
  592. gpointer buf;
  593. guint buf_size;
  594. dumpfile_t amanda_header;
  595. if (!setup_handle(self, self->initializing))
  596. return READ_LABEL_STATUS_DEVICE_ERROR;
  597. key = special_file_to_key(self, "tapestart", -1);
  598. if (!s3_read(self->s3, self->bucket, key, &buf, &buf_size, S3_DEVICE_MAX_BLOCK_SIZE)) {
  599. guint response_code;
  600. s3_error_code_t s3_error_code;
  601. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  602. /* if it's an expected error (not found), just return FALSE */
  603. if (response_code == 404 &&
  604. (s3_error_code == S3_ERROR_NoSuchKey || s3_error_code == S3_ERROR_NoSuchBucket)) {
  605. g_debug(_("Amanda header not found while reading tapestart header (this is expected for empty tapes)"));
  606. return READ_LABEL_STATUS_VOLUME_UNLABELED;
  607. }
  608. /* otherwise, log it and return */
  609. fprintf(stderr, _("While trying to read tapestart header: %s\n"),
  610. s3_strerror(self->s3));
  611. return READ_LABEL_STATUS_DEVICE_ERROR;
  612. }
  613. g_assert(buf != NULL);
  614. fh_init(&amanda_header);
  615. parse_file_header(buf, &amanda_header, buf_size);
  616. g_free(buf);
  617. if (amanda_header.type != F_TAPESTART) {
  618. fprintf(stderr, _("Invalid amanda header\n"));
  619. return READ_LABEL_STATUS_VOLUME_ERROR;
  620. }
  621. amfree(pself->volume_label);
  622. pself->volume_label = g_strdup(amanda_header.name);
  623. amfree(pself->volume_time);
  624. pself->volume_time = g_strdup(amanda_header.datestamp);
  625. return READ_LABEL_STATUS_SUCCESS;
  626. }
  627. /* }}} */
  628. /* {{{ s3_device_start */
  629. static gboolean
  630. s3_device_start (Device * pself, DeviceAccessMode mode,
  631. char * label, char * timestamp) {
  632. S3Device * self;
  633. int file, last_file;
  634. self = S3_DEVICE(pself);
  635. g_return_val_if_fail (self != NULL, FALSE);
  636. if (!setup_handle(self, FALSE))
  637. return FALSE;
  638. /* try creating the bucket, in case it doesn't exist */
  639. if (mode != ACCESS_READ && !s3_make_bucket(self->s3, self->bucket)) {
  640. guint response_code;
  641. s3_error_code_t s3_error_code;
  642. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  643. /* if it isn't an expected error (bucket already exists),
  644. * return FALSE */
  645. if (response_code != 409 ||
  646. s3_error_code != S3_ERROR_BucketAlreadyExists) {
  647. fprintf(stderr, _("While creating new S3 bucket: %s\n"),
  648. s3_strerror(self->s3));
  649. return FALSE;
  650. }
  651. }
  652. /* call up to the parent (Device) to set access_mode, volume_label,
  653. * and volume_time, either from the arguments (ACCESS_WRITE) or by
  654. * reading from the 0th file (otherwise)
  655. */
  656. if (parent_class->start)
  657. if (!parent_class->start((Device*)self, mode, label, timestamp))
  658. return FALSE;
  659. /* take care of any dirty work for this mode */
  660. switch (mode) {
  661. case ACCESS_READ:
  662. break;
  663. case ACCESS_WRITE:
  664. /* delete all files */
  665. last_file = find_last_file(self);
  666. if (last_file < 0) return FALSE;
  667. for (file = 0; file <= last_file; file++) {
  668. if (!delete_file(self, file)) return FALSE;
  669. }
  670. /* write a new amanda header */
  671. if (!write_amanda_header(self, label, timestamp)) {
  672. return FALSE;
  673. }
  674. break;
  675. case ACCESS_APPEND:
  676. return seek_to_end(self);
  677. break;
  678. case ACCESS_NULL:
  679. g_assert_not_reached();
  680. }
  681. g_assert(pself->access_mode == mode);
  682. return TRUE;
  683. }
  684. /* }}} */
  685. static gboolean s3_device_property_get(Device * p_self, DevicePropertyId id,
  686. GValue * val) {
  687. S3Device * self;
  688. const DevicePropertyBase * base;
  689. self = S3_DEVICE(p_self);
  690. g_return_val_if_fail(self != NULL, FALSE);
  691. base = device_property_get_by_id(id);
  692. g_return_val_if_fail(self != NULL, FALSE);
  693. g_value_unset_init(val, base->type);
  694. if (id == PROPERTY_S3_SECRET_KEY) {
  695. if (self->secret_key != NULL) {
  696. g_value_set_string(val, self->secret_key);
  697. return TRUE;
  698. } else {
  699. return FALSE;
  700. }
  701. } else if (id == PROPERTY_S3_ACCESS_KEY) {
  702. if (self->access_key != NULL) {
  703. g_value_set_string(val, self->access_key);
  704. return TRUE;
  705. } else {
  706. return FALSE;
  707. }
  708. }
  709. #ifdef WANT_DEVPAY
  710. else if (id == PROPERTY_S3_USER_TOKEN) {
  711. if (self->user_token != NULL) {
  712. g_value_set_string(val, self->user_token);
  713. return TRUE;
  714. } else {
  715. return FALSE;
  716. }
  717. }
  718. #endif /* WANT_DEVPAY */
  719. else if (id == PROPERTY_VERBOSE) {
  720. g_value_set_boolean(val, self->verbose);
  721. return TRUE;
  722. } else {
  723. /* chain up */
  724. if (parent_class->property_get) {
  725. return (parent_class->property_get)(p_self, id, val);
  726. } else {
  727. return FALSE;
  728. }
  729. }
  730. g_assert_not_reached();
  731. }
  732. static gboolean s3_device_property_set(Device * p_self, DevicePropertyId id,
  733. GValue * val) {
  734. S3Device * self;
  735. const DevicePropertyBase * base;
  736. self = S3_DEVICE(p_self);
  737. g_return_val_if_fail(self != NULL, FALSE);
  738. base = device_property_get_by_id(id);
  739. g_return_val_if_fail(self != NULL, FALSE);
  740. g_return_val_if_fail(G_VALUE_HOLDS(val, base->type), FALSE);
  741. if (id == PROPERTY_S3_SECRET_KEY) {
  742. if (p_self->access_mode != ACCESS_NULL)
  743. return FALSE;
  744. amfree(self->secret_key);
  745. self->secret_key = g_value_dup_string(val);
  746. device_clear_volume_details(p_self);
  747. return TRUE;
  748. } else if (id == PROPERTY_S3_ACCESS_KEY) {
  749. if (p_self->access_mode != ACCESS_NULL)
  750. return FALSE;
  751. amfree(self->access_key);
  752. self->access_key = g_value_dup_string(val);
  753. device_clear_volume_details(p_self);
  754. return TRUE;
  755. }
  756. #ifdef WANT_DEVPAY
  757. else if (id == PROPERTY_S3_USER_TOKEN) {
  758. if (p_self->access_mode != ACCESS_NULL)
  759. return FALSE;
  760. amfree(self->user_token);
  761. self->user_token = g_value_dup_string(val);
  762. device_clear_volume_details(p_self);
  763. return TRUE;
  764. }
  765. #endif /* WANT_DEVPAY */
  766. else if (id == PROPERTY_VERBOSE) {
  767. self->verbose = g_value_get_boolean(val);
  768. /* Our S3 handle may not yet have been instantiated; if so, it will
  769. * get the proper verbose setting when it is created */
  770. if (self->s3)
  771. s3_verbose(self->s3, self->verbose);
  772. return TRUE;
  773. } else {
  774. if (parent_class->property_set) {
  775. return (parent_class->property_set)(p_self, id, val);
  776. } else {
  777. return FALSE;
  778. }
  779. }
  780. g_assert_not_reached();
  781. }
  782. /* functions for writing */
  783. /* {{{ s3_device_start_file */
  784. static gboolean
  785. s3_device_start_file (Device *pself, const dumpfile_t *jobInfo) {
  786. S3Device *self = S3_DEVICE(pself);
  787. char *amanda_header;
  788. int header_size;
  789. gboolean header_fits, result;
  790. char *key;
  791. g_return_val_if_fail (self != NULL, FALSE);
  792. /* Build the amanda header. */
  793. amanda_header = device_build_amanda_header(pself, jobInfo,
  794. &header_size, &header_fits);
  795. g_return_val_if_fail(amanda_header != NULL, FALSE);
  796. g_return_val_if_fail(header_fits, FALSE);
  797. /* set the file and block numbers correctly */
  798. pself->file = (pself->file > 0)? pself->file+1 : 1;
  799. pself->block = 0;
  800. pself->in_file = TRUE;
  801. /* write it out as a special block (not the 0th) */
  802. key = special_file_to_key(self, "filestart", pself->file);
  803. result = s3_upload(self->s3, self->bucket, key, amanda_header, header_size);
  804. g_free(amanda_header);
  805. g_free(key);
  806. if (!result) {
  807. fprintf(stderr, _("While writing filestart header: %s\n"),
  808. s3_strerror(self->s3));
  809. return FALSE;
  810. }
  811. return TRUE;
  812. }
  813. /* }}} */
  814. /* {{{ s3_device_write_block */
  815. static gboolean
  816. s3_device_write_block (Device * pself, guint size, gpointer data,
  817. gboolean last_block) {
  818. gboolean result;
  819. char *filename;
  820. S3Device * self = S3_DEVICE(pself);;
  821. g_assert (self != NULL);
  822. g_assert (data != NULL);
  823. filename = file_and_block_to_key(self, pself->file, pself->block);
  824. result = s3_upload(self->s3, self->bucket, filename, data, size);
  825. g_free(filename);
  826. if (!result) {
  827. fprintf(stderr, _("While writing data block to S3: %s\n"),
  828. s3_strerror(self->s3));
  829. return FALSE;
  830. }
  831. pself->block++;
  832. /* if this is the last block, finish the file */
  833. if (last_block) {
  834. return s3_device_finish_file(pself);
  835. }
  836. return TRUE;
  837. }
  838. /* }}} */
  839. /* {{{ s3_device_finish_file */
  840. static gboolean
  841. s3_device_finish_file (Device * pself) {
  842. /* we're not in a file anymore */
  843. pself->in_file = FALSE;
  844. return TRUE;
  845. }
  846. /* }}} */
  847. /* {{{ s3_device_recycle_file */
  848. static gboolean
  849. s3_device_recycle_file(Device *pself, guint file) {
  850. S3Device *self = S3_DEVICE(pself);
  851. return delete_file(self, file);
  852. }
  853. /* }}} */
  854. /* functions for reading */
  855. /* {{{ s3_device_seek_file */
  856. static dumpfile_t*
  857. s3_device_seek_file(Device *pself, guint file) {
  858. S3Device *self = S3_DEVICE(pself);
  859. gboolean result;
  860. char *key;
  861. gpointer buf;
  862. guint buf_size;
  863. dumpfile_t *amanda_header;
  864. pself->file = file;
  865. pself->block = 0;
  866. pself->in_file = TRUE;
  867. /* read it in */
  868. key = special_file_to_key(self, "filestart", pself->file);
  869. result = s3_read(self->s3, self->bucket, key, &buf, &buf_size, S3_DEVICE_MAX_BLOCK_SIZE);
  870. g_free(key);
  871. if (!result) {
  872. guint response_code;
  873. s3_error_code_t s3_error_code;
  874. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  875. /* if it's an expected error (not found), check what to do. */
  876. if (response_code == 404 && s3_error_code == S3_ERROR_NoSuchKey) {
  877. int next_file;
  878. pself->file = -1;
  879. pself->in_file = FALSE;
  880. next_file = find_next_file(self, pself->file);
  881. if (next_file > 0) {
  882. /* Note short-circut of dispatcher. */
  883. return s3_device_seek_file(pself, next_file);
  884. } else if (next_file == 0) {
  885. /* No next file. Check if we are one past the end. */
  886. key = special_file_to_key(self, "filestart", pself->file - 1);
  887. result = s3_read(self->s3, self->bucket, key, &buf, &buf_size,
  888. S3_DEVICE_MAX_BLOCK_SIZE);
  889. g_free(key);
  890. if (result) {
  891. return make_tapeend_header();
  892. } else {
  893. return NULL;
  894. }
  895. }
  896. } else {
  897. /* An error occured finding out if we are the last file. */
  898. return NULL;
  899. }
  900. }
  901. /* and make a dumpfile_t out of it */
  902. g_assert(buf != NULL);
  903. amanda_header = g_new(dumpfile_t, 1);
  904. fh_init(amanda_header);
  905. parse_file_header(buf, amanda_header, buf_size);
  906. g_free(buf);
  907. switch (amanda_header->type) {
  908. case F_DUMPFILE:
  909. case F_CONT_DUMPFILE:
  910. case F_SPLIT_DUMPFILE:
  911. return amanda_header;
  912. default:
  913. fprintf(stderr,
  914. _("Invalid amanda header while reading file header\n"));
  915. g_free(amanda_header);
  916. return NULL;
  917. }
  918. }
  919. /* }}} */
  920. /* {{{ s3_device_seek_block */
  921. static gboolean
  922. s3_device_seek_block(Device *pself, guint64 block) {
  923. pself->block = block;
  924. return TRUE;
  925. }
  926. /* }}} */
  927. /* {{{ s3_device_read_block */
  928. static int
  929. s3_device_read_block (Device * pself, gpointer data, int *size_req) {
  930. S3Device * self = S3_DEVICE(pself);
  931. char *key;
  932. gpointer buf;
  933. gboolean result;
  934. guint buf_size;
  935. g_assert (self != NULL);
  936. /* get the file*/
  937. key = file_and_block_to_key(self, pself->file, pself->block);
  938. g_assert(key != NULL);
  939. if (self->cached_key && (0 == strcmp(key, self->cached_key))) {
  940. /* use the cached copy and clear the cache */
  941. buf = self->cached_buf;
  942. buf_size = self->cached_size;
  943. self->cached_buf = NULL;
  944. g_free(self->cached_key);
  945. self->cached_key = NULL;
  946. } else {
  947. /* clear the cache and actually download the file */
  948. if (self->cached_buf) {
  949. g_free(self->cached_buf);
  950. self->cached_buf = NULL;
  951. }
  952. if (self->cached_key) {
  953. g_free(self->cached_key);
  954. self->cached_key = NULL;
  955. }
  956. result = s3_read(self->s3, self->bucket, key, &buf, &buf_size, S3_DEVICE_MAX_BLOCK_SIZE);
  957. if (!result) {
  958. guint response_code;
  959. s3_error_code_t s3_error_code;
  960. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  961. g_free(key);
  962. key = NULL;
  963. /* if it's an expected error (not found), just return -1 */
  964. if (response_code == 404 && s3_error_code == S3_ERROR_NoSuchKey) {
  965. pself->is_eof = TRUE;
  966. pself->in_file = FALSE;
  967. return -1;
  968. }
  969. /* otherwise, log it and return FALSE */
  970. fprintf(stderr, _("While reading data block from S3: %s\n"),
  971. s3_strerror(self->s3));
  972. return -1;
  973. }
  974. }
  975. /* INVARIANT: cache is NULL */
  976. g_assert(self->cached_buf == NULL);
  977. g_assert(self->cached_key == NULL);
  978. /* now see how the caller wants to deal with that */
  979. if (data == NULL || *size_req < 0 || buf_size > (guint)*size_req) {
  980. /* A size query or short buffer -- load the cache and return the size*/
  981. self->cached_buf = buf;
  982. self->cached_key = key;
  983. self->cached_size = buf_size;
  984. *size_req = buf_size;
  985. return 0;
  986. } else {
  987. /* ok, all checks are passed -- copy the data */
  988. *size_req = buf_size;
  989. g_memmove(data, buf, buf_size);
  990. g_free(key);
  991. g_free(buf);
  992. /* move on to the next block */
  993. pself->block++;
  994. return buf_size;
  995. }
  996. }
  997. /* }}} */