PageRenderTime 62ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
C | 1447 lines | 983 code | 258 blank | 206 comment | 155 complexity | b19d6abae724e2ff1508a64690c96a47 MD5 | raw file
  1. /*
  2. * Copyright (c) 2005-2008 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., 465 S Mathlida Ave, Suite 300
  18. * Sunnyvale, CA 94086, 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.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. * Type checking and casting macros
  51. */
  52. #define TYPE_S3_DEVICE (s3_device_get_type())
  53. #define S3_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), s3_device_get_type(), S3Device)
  54. #define S3_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), s3_device_get_type(), S3Device const)
  55. #define S3_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), s3_device_get_type(), S3DeviceClass)
  56. #define IS_S3_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), s3_device_get_type ())
  57. #define S3_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), s3_device_get_type(), S3DeviceClass)
  58. static GType s3_device_get_type (void);
  59. /*
  60. * Main object structure
  61. */
  62. typedef struct _S3MetadataFile S3MetadataFile;
  63. typedef struct _S3Device S3Device;
  64. struct _S3Device {
  65. Device __parent__;
  66. /* The "easy" curl handle we use to access Amazon S3 */
  67. S3Handle *s3;
  68. /* S3 access information */
  69. char *bucket;
  70. char *prefix;
  71. /* The S3 access information. */
  72. char *secret_key;
  73. char *access_key;
  74. char *user_token;
  75. gboolean is_devpay;
  76. char *bucket_location;
  77. /* a cache for unsuccessful reads (where we get the file but the caller
  78. * doesn't have space for it or doesn't want it), where we expect the
  79. * next call will request the same file.
  80. */
  81. char *cached_buf;
  82. char *cached_key;
  83. int cached_size;
  84. /* Produce verbose output? */
  85. gboolean verbose;
  86. /* Use SSL? */
  87. gboolean use_ssl;
  88. };
  89. /*
  90. * Class definition
  91. */
  92. typedef struct _S3DeviceClass S3DeviceClass;
  93. struct _S3DeviceClass {
  94. DeviceClass __parent__;
  95. };
  96. /*
  97. * Constants and static data
  98. */
  99. #define S3_DEVICE_NAME "s3"
  100. #define DEVPAY_DEVICE_NAME "s3zmanda"
  101. /* Maximum key length as specified in the S3 documentation
  102. * (*excluding* null terminator) */
  103. #define S3_MAX_KEY_LENGTH 1024
  104. /* Note: for compatability, min can only be decreased and max increased */
  105. #define S3_DEVICE_MIN_BLOCK_SIZE 1024
  106. #define S3_DEVICE_MAX_BLOCK_SIZE (100*1024*1024)
  107. #define S3_DEVICE_DEFAULT_BLOCK_SIZE (10*1024*1024)
  108. /* This goes in lieu of file number for metadata. */
  109. #define SPECIAL_INFIX "special-"
  110. /* pointer to the class of our parent */
  111. static DeviceClass *parent_class = NULL;
  112. /*
  113. * device-specific properties
  114. */
  115. /* Authentication information for Amazon S3. Both of these are strings. */
  116. static DevicePropertyBase device_property_s3_access_key;
  117. static DevicePropertyBase device_property_s3_secret_key;
  118. #define PROPERTY_S3_SECRET_KEY (device_property_s3_secret_key.ID)
  119. #define PROPERTY_S3_ACCESS_KEY (device_property_s3_access_key.ID)
  120. /* Same, but for S3 with DevPay. */
  121. static DevicePropertyBase device_property_s3_user_token;
  122. #define PROPERTY_S3_USER_TOKEN (device_property_s3_user_token.ID)
  123. /* Location constraint for new buckets created on Amazon S3. */
  124. static DevicePropertyBase device_property_s3_bucket_location;
  125. #define PROPERTY_S3_BUCKET_LOCATION (device_property_s3_bucket_location.ID)
  126. /* Whether to use SSL with Amazon S3. */
  127. static DevicePropertyBase device_property_s3_ssl;
  128. #define PROPERTY_S3_SSL (device_property_s3_ssl.ID)
  129. /*
  130. * prototypes
  131. */
  132. void s3_device_register(void);
  133. /*
  134. * utility functions */
  135. /* Given file and block numbers, return an S3 key.
  136. *
  137. * @param self: the S3Device object
  138. * @param file: the file number
  139. * @param block: the block within that file
  140. * @returns: a newly allocated string containing an S3 key.
  141. */
  142. static char *
  143. file_and_block_to_key(S3Device *self,
  144. int file,
  145. guint64 block);
  146. /* Given the name of a special file (such as 'tapestart'), generate
  147. * the S3 key to use for that file.
  148. *
  149. * @param self: the S3Device object
  150. * @param special_name: name of the special file
  151. * @param file: a file number to include; omitted if -1
  152. * @returns: a newly alocated string containing an S3 key.
  153. */
  154. static char *
  155. special_file_to_key(S3Device *self,
  156. char *special_name,
  157. int file);
  158. /* Write an amanda header file to S3.
  159. *
  160. * @param self: the S3Device object
  161. * @param label: the volume label
  162. * @param timestamp: the volume timestamp
  163. */
  164. static gboolean
  165. write_amanda_header(S3Device *self,
  166. char *label,
  167. char * timestamp);
  168. /* "Fast forward" this device to the end by looking up the largest file number
  169. * present and setting the current file number one greater.
  170. *
  171. * @param self: the S3Device object
  172. */
  173. static gboolean
  174. seek_to_end(S3Device *self);
  175. /* Find the number of the last file that contains any data (even just a header).
  176. *
  177. * @param self: the S3Device object
  178. * @returns: the last file, or -1 in event of an error
  179. */
  180. static int
  181. find_last_file(S3Device *self);
  182. /* Delete all blocks in the given file, including the filestart block
  183. *
  184. * @param self: the S3Device object
  185. * @param file: the file to delete
  186. */
  187. static gboolean
  188. delete_file(S3Device *self,
  189. int file);
  190. /* Set up self->s3 as best as possible.
  191. *
  192. * The return value is TRUE iff self->s3 is useable.
  193. *
  194. * @param self: the S3Device object
  195. * @returns: TRUE if the handle is set up
  196. */
  197. static gboolean
  198. setup_handle(S3Device * self);
  199. /*
  200. * class mechanics */
  201. static void
  202. s3_device_init(S3Device * o);
  203. static void
  204. s3_device_class_init(S3DeviceClass * c);
  205. static void
  206. s3_device_finalize(GObject * o);
  207. static Device*
  208. s3_device_factory(char * device_name, char * device_type, char * device_node);
  209. /*
  210. * Property{Get,Set}Fns */
  211. static gboolean s3_device_set_access_key_fn(Device *self,
  212. DevicePropertyBase *base, GValue *val,
  213. PropertySurety surety, PropertySource source);
  214. static gboolean s3_device_set_secret_key_fn(Device *self,
  215. DevicePropertyBase *base, GValue *val,
  216. PropertySurety surety, PropertySource source);
  217. static gboolean s3_device_set_user_token_fn(Device *self,
  218. DevicePropertyBase *base, GValue *val,
  219. PropertySurety surety, PropertySource source);
  220. static gboolean s3_device_set_bucket_location_fn(Device *self,
  221. DevicePropertyBase *base, GValue *val,
  222. PropertySurety surety, PropertySource source);
  223. static gboolean s3_device_set_verbose_fn(Device *self,
  224. DevicePropertyBase *base, GValue *val,
  225. PropertySurety surety, PropertySource source);
  226. static gboolean s3_device_set_ssl_fn(Device *self,
  227. DevicePropertyBase *base, GValue *val,
  228. PropertySurety surety, PropertySource source);
  229. /*
  230. * virtual functions */
  231. static void
  232. s3_device_open_device(Device *pself, char *device_name,
  233. char * device_type, char * device_node);
  234. static DeviceStatusFlags s3_device_read_label(Device * self);
  235. static gboolean
  236. s3_device_start(Device * self,
  237. DeviceAccessMode mode,
  238. char * label,
  239. char * timestamp);
  240. static gboolean
  241. s3_device_finish(Device * self);
  242. static gboolean
  243. s3_device_start_file(Device * self,
  244. dumpfile_t * jobInfo);
  245. static gboolean
  246. s3_device_write_block(Device * self,
  247. guint size,
  248. gpointer data);
  249. static gboolean
  250. s3_device_finish_file(Device * self);
  251. static dumpfile_t*
  252. s3_device_seek_file(Device *pself,
  253. guint file);
  254. static gboolean
  255. s3_device_seek_block(Device *pself,
  256. guint64 block);
  257. static int
  258. s3_device_read_block(Device * pself,
  259. gpointer data,
  260. int *size_req);
  261. static gboolean
  262. s3_device_recycle_file(Device *pself,
  263. guint file);
  264. /*
  265. * Private functions
  266. */
  267. static char *
  268. file_and_block_to_key(S3Device *self,
  269. int file,
  270. guint64 block)
  271. {
  272. char *s3_key = g_strdup_printf("%sf%08x-b%016llx.data",
  273. self->prefix, file, (long long unsigned int)block);
  274. g_assert(strlen(s3_key) <= S3_MAX_KEY_LENGTH);
  275. return s3_key;
  276. }
  277. static char *
  278. special_file_to_key(S3Device *self,
  279. char *special_name,
  280. int file)
  281. {
  282. if (file == -1)
  283. return g_strdup_printf("%s" SPECIAL_INFIX "%s", self->prefix, special_name);
  284. else
  285. return g_strdup_printf("%sf%08x-%s", self->prefix, file, special_name);
  286. }
  287. static gboolean
  288. write_amanda_header(S3Device *self,
  289. char *label,
  290. char * timestamp)
  291. {
  292. CurlBuffer amanda_header = {NULL, 0, 0, 0};
  293. char * key = NULL;
  294. gboolean header_fits, result;
  295. dumpfile_t * dumpinfo = NULL;
  296. Device *d_self = DEVICE(self);
  297. /* build the header */
  298. dumpinfo = make_tapestart_header(DEVICE(self), label, timestamp);
  299. amanda_header.buffer = device_build_amanda_header(DEVICE(self), dumpinfo,
  300. /* casting guint* to int* */
  301. (int*) &amanda_header.buffer_len, &header_fits);
  302. if (!header_fits) {
  303. device_set_error(d_self,
  304. stralloc(_("Amanda tapestart header won't fit in a single block!")),
  305. DEVICE_STATUS_DEVICE_ERROR);
  306. g_free(amanda_header.buffer);
  307. return FALSE;
  308. }
  309. /* write out the header and flush the uploads. */
  310. key = special_file_to_key(self, "tapestart", -1);
  311. result = s3_upload(self->s3, self->bucket, key, S3_BUFFER_READ_FUNCS,
  312. &amanda_header, NULL, NULL);
  313. g_free(amanda_header.buffer);
  314. g_free(key);
  315. if (!result) {
  316. device_set_error(d_self,
  317. vstrallocf(_("While writing amanda header: %s"), s3_strerror(self->s3)),
  318. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  319. }
  320. return result;
  321. }
  322. static gboolean
  323. seek_to_end(S3Device *self) {
  324. int last_file;
  325. Device *pself = DEVICE(self);
  326. last_file = find_last_file(self);
  327. if (last_file < 0)
  328. return FALSE;
  329. pself->file = last_file;
  330. return TRUE;
  331. }
  332. /* Convert an object name into a file number, assuming the given prefix
  333. * length. Returns -1 if the object name is invalid, or 0 if the object name
  334. * is a "special" key. */
  335. static int key_to_file(guint prefix_len, const char * key) {
  336. int file;
  337. int i;
  338. /* skip the prefix */
  339. if (strlen(key) <= prefix_len)
  340. return -1;
  341. key += prefix_len;
  342. if (strncmp(key, SPECIAL_INFIX, strlen(SPECIAL_INFIX)) == 0) {
  343. return 0;
  344. }
  345. /* check that key starts with 'f' */
  346. if (key[0] != 'f')
  347. return -1;
  348. key++;
  349. /* check that key is of the form "%08x-" */
  350. for (i = 0; i < 8; i++) {
  351. if (!(key[i] >= '0' && key[i] <= '9') &&
  352. !(key[i] >= 'a' && key[i] <= 'f') &&
  353. !(key[i] >= 'A' && key[i] <= 'F')) break;
  354. }
  355. if (key[i] != '-') return -1;
  356. if (i < 8) return -1;
  357. /* convert the file number */
  358. errno = 0;
  359. file = strtoul(key, NULL, 16);
  360. if (errno != 0) {
  361. g_warning(_("unparseable file number '%s'"), key);
  362. return -1;
  363. }
  364. return file;
  365. }
  366. /* Find the number of the last file that contains any data (even just a header).
  367. * Returns -1 in event of an error
  368. */
  369. static int
  370. find_last_file(S3Device *self) {
  371. gboolean result;
  372. GSList *keys;
  373. unsigned int prefix_len = strlen(self->prefix);
  374. int last_file = 0;
  375. Device *d_self = DEVICE(self);
  376. /* list all keys matching C{PREFIX*-*}, stripping the C{-*} */
  377. result = s3_list_keys(self->s3, self->bucket, self->prefix, "-", &keys);
  378. if (!result) {
  379. device_set_error(d_self,
  380. vstrallocf(_("While listing S3 keys: %s"), s3_strerror(self->s3)),
  381. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  382. return -1;
  383. }
  384. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  385. int file = key_to_file(prefix_len, keys->data);
  386. /* and if it's the last, keep it */
  387. if (file > last_file)
  388. last_file = file;
  389. }
  390. return last_file;
  391. }
  392. /* Find the number of the file following the requested one, if any.
  393. * Returns 0 if there is no such file or -1 in event of an error
  394. */
  395. static int
  396. find_next_file(S3Device *self, int last_file) {
  397. gboolean result;
  398. GSList *keys;
  399. unsigned int prefix_len = strlen(self->prefix);
  400. int next_file = 0;
  401. Device *d_self = DEVICE(self);
  402. /* list all keys matching C{PREFIX*-*}, stripping the C{-*} */
  403. result = s3_list_keys(self->s3, self->bucket, self->prefix, "-", &keys);
  404. if (!result) {
  405. device_set_error(d_self,
  406. vstrallocf(_("While listing S3 keys: %s"), s3_strerror(self->s3)),
  407. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  408. return -1;
  409. }
  410. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  411. int file;
  412. file = key_to_file(prefix_len, (char*)keys->data);
  413. if (file < 0) {
  414. /* Set this in case we don't find a next file; this is not a
  415. * hard error, so if we can find a next file we'll return that
  416. * instead. */
  417. next_file = -1;
  418. }
  419. if (file < next_file && file > last_file) {
  420. next_file = file;
  421. }
  422. }
  423. return last_file;
  424. }
  425. static gboolean
  426. delete_file(S3Device *self,
  427. int file)
  428. {
  429. gboolean result;
  430. GSList *keys;
  431. char *my_prefix = g_strdup_printf("%sf%08x-", self->prefix, file);
  432. Device *d_self = DEVICE(self);
  433. result = s3_list_keys(self->s3, self->bucket, my_prefix, NULL, &keys);
  434. if (!result) {
  435. device_set_error(d_self,
  436. vstrallocf(_("While listing S3 keys: %s"), s3_strerror(self->s3)),
  437. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  438. return FALSE;
  439. }
  440. /* this will likely be a *lot* of keys */
  441. for (; keys; keys = g_slist_remove(keys, keys->data)) {
  442. if (self->verbose) g_debug(_("Deleting %s"), (char*)keys->data);
  443. if (!s3_delete(self->s3, self->bucket, keys->data)) {
  444. device_set_error(d_self,
  445. vstrallocf(_("While deleting key '%s': %s"),
  446. (char*)keys->data, s3_strerror(self->s3)),
  447. DEVICE_STATUS_DEVICE_ERROR);
  448. g_slist_free(keys);
  449. return FALSE;
  450. }
  451. }
  452. return TRUE;
  453. }
  454. /*
  455. * Class mechanics
  456. */
  457. void
  458. s3_device_register(void)
  459. {
  460. static const char * device_prefix_list[] = { S3_DEVICE_NAME, DEVPAY_DEVICE_NAME, NULL };
  461. g_assert(s3_init());
  462. /* set up our properties */
  463. device_property_fill_and_register(&device_property_s3_secret_key,
  464. G_TYPE_STRING, "s3_secret_key",
  465. "Secret access key to authenticate with Amazon S3");
  466. device_property_fill_and_register(&device_property_s3_access_key,
  467. G_TYPE_STRING, "s3_access_key",
  468. "Access key ID to authenticate with Amazon S3");
  469. device_property_fill_and_register(&device_property_s3_user_token,
  470. G_TYPE_STRING, "s3_user_token",
  471. "User token for authentication Amazon devpay requests");
  472. device_property_fill_and_register(&device_property_s3_bucket_location,
  473. G_TYPE_STRING, "s3_bucket_location",
  474. "Location constraint for buckets on Amazon S3");
  475. device_property_fill_and_register(&device_property_s3_ssl,
  476. G_TYPE_BOOLEAN, "s3_ssl",
  477. "Whether to use SSL with Amazon S3");
  478. /* register the device itself */
  479. register_device(s3_device_factory, device_prefix_list);
  480. }
  481. static GType
  482. s3_device_get_type(void)
  483. {
  484. static GType type = 0;
  485. if G_UNLIKELY(type == 0) {
  486. static const GTypeInfo info = {
  487. sizeof (S3DeviceClass),
  488. (GBaseInitFunc) NULL,
  489. (GBaseFinalizeFunc) NULL,
  490. (GClassInitFunc) s3_device_class_init,
  491. (GClassFinalizeFunc) NULL,
  492. NULL /* class_data */,
  493. sizeof (S3Device),
  494. 0 /* n_preallocs */,
  495. (GInstanceInitFunc) s3_device_init,
  496. NULL
  497. };
  498. type = g_type_register_static (TYPE_DEVICE, "S3Device", &info,
  499. (GTypeFlags)0);
  500. }
  501. return type;
  502. }
  503. static void
  504. s3_device_init(S3Device * self)
  505. {
  506. Device * dself = DEVICE(self);
  507. GValue response;
  508. /* Register property values
  509. * Note: Some aren't added until s3_device_open_device()
  510. */
  511. bzero(&response, sizeof(response));
  512. g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
  513. g_value_set_enum(&response, CONCURRENCY_PARADIGM_SHARED_READ);
  514. device_set_simple_property(dself, PROPERTY_CONCURRENCY,
  515. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  516. g_value_unset(&response);
  517. g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
  518. g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
  519. device_set_simple_property(dself, PROPERTY_STREAMING,
  520. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  521. g_value_unset(&response);
  522. g_value_init(&response, G_TYPE_BOOLEAN);
  523. g_value_set_boolean(&response, TRUE);
  524. device_set_simple_property(dself, PROPERTY_APPENDABLE,
  525. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  526. g_value_unset(&response);
  527. g_value_init(&response, G_TYPE_BOOLEAN);
  528. g_value_set_boolean(&response, TRUE);
  529. device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
  530. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  531. g_value_unset(&response);
  532. g_value_init(&response, G_TYPE_BOOLEAN);
  533. g_value_set_boolean(&response, FALSE);
  534. device_set_simple_property(dself, PROPERTY_COMPRESSION,
  535. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  536. g_value_unset(&response);
  537. g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
  538. g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
  539. device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
  540. &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
  541. g_value_unset(&response);
  542. }
  543. static void
  544. s3_device_class_init(S3DeviceClass * c G_GNUC_UNUSED)
  545. {
  546. GObjectClass *g_object_class = (GObjectClass*) c;
  547. DeviceClass *device_class = (DeviceClass *)c;
  548. parent_class = g_type_class_ref (TYPE_DEVICE);
  549. device_class->open_device = s3_device_open_device;
  550. device_class->read_label = s3_device_read_label;
  551. device_class->start = s3_device_start;
  552. device_class->finish = s3_device_finish;
  553. device_class->start_file = s3_device_start_file;
  554. device_class->write_block = s3_device_write_block;
  555. device_class->finish_file = s3_device_finish_file;
  556. device_class->seek_file = s3_device_seek_file;
  557. device_class->seek_block = s3_device_seek_block;
  558. device_class->read_block = s3_device_read_block;
  559. device_class->recycle_file = s3_device_recycle_file;
  560. g_object_class->finalize = s3_device_finalize;
  561. device_class_register_property(device_class, PROPERTY_S3_ACCESS_KEY,
  562. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  563. device_simple_property_get_fn,
  564. s3_device_set_access_key_fn);
  565. device_class_register_property(device_class, PROPERTY_S3_SECRET_KEY,
  566. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  567. device_simple_property_get_fn,
  568. s3_device_set_secret_key_fn);
  569. device_class_register_property(device_class, PROPERTY_S3_USER_TOKEN,
  570. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  571. device_simple_property_get_fn,
  572. s3_device_set_user_token_fn);
  573. device_class_register_property(device_class, PROPERTY_S3_BUCKET_LOCATION,
  574. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  575. device_simple_property_get_fn,
  576. s3_device_set_bucket_location_fn);
  577. device_class_register_property(device_class, PROPERTY_VERBOSE,
  578. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  579. device_simple_property_get_fn,
  580. s3_device_set_verbose_fn);
  581. device_class_register_property(device_class, PROPERTY_S3_SSL,
  582. PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
  583. device_simple_property_get_fn,
  584. s3_device_set_ssl_fn);
  585. device_class_register_property(device_class, PROPERTY_COMPRESSION,
  586. PROPERTY_ACCESS_GET_MASK,
  587. device_simple_property_get_fn,
  588. NULL);
  589. }
  590. static gboolean
  591. s3_device_set_access_key_fn(Device *p_self, DevicePropertyBase *base,
  592. GValue *val, PropertySurety surety, PropertySource source)
  593. {
  594. S3Device *self = S3_DEVICE(p_self);
  595. amfree(self->access_key);
  596. self->access_key = g_value_dup_string(val);
  597. device_clear_volume_details(p_self);
  598. return device_simple_property_set_fn(p_self, base, val, surety, source);
  599. }
  600. static gboolean
  601. s3_device_set_secret_key_fn(Device *p_self, DevicePropertyBase *base,
  602. GValue *val, PropertySurety surety, PropertySource source)
  603. {
  604. S3Device *self = S3_DEVICE(p_self);
  605. amfree(self->secret_key);
  606. self->secret_key = g_value_dup_string(val);
  607. device_clear_volume_details(p_self);
  608. return device_simple_property_set_fn(p_self, base, val, surety, source);
  609. }
  610. static gboolean
  611. s3_device_set_user_token_fn(Device *p_self, DevicePropertyBase *base,
  612. GValue *val, PropertySurety surety, PropertySource source)
  613. {
  614. S3Device *self = S3_DEVICE(p_self);
  615. if (!self->is_devpay) {
  616. device_set_error(p_self, stralloc(_(
  617. "Can't set a user token unless DevPay is in use")),
  618. DEVICE_STATUS_DEVICE_ERROR);
  619. return FALSE;
  620. }
  621. amfree(self->user_token);
  622. self->user_token = g_value_dup_string(val);
  623. device_clear_volume_details(p_self);
  624. return device_simple_property_set_fn(p_self, base, val, surety, source);
  625. }
  626. static gboolean
  627. s3_device_set_bucket_location_fn(Device *p_self, DevicePropertyBase *base,
  628. GValue *val, PropertySurety surety, PropertySource source)
  629. {
  630. S3Device *self = S3_DEVICE(p_self);
  631. if (self->use_ssl && !s3_curl_location_compat()) {
  632. device_set_error(p_self, stralloc(_(
  633. "Location constraint given for Amazon S3 bucket, "
  634. "but libcurl is too old support wildcard certificates.")),
  635. DEVICE_STATUS_DEVICE_ERROR);
  636. return FALSE;
  637. }
  638. if (!s3_bucket_location_compat(self->bucket)) {
  639. device_set_error(p_self, g_strdup_printf(_(
  640. "Location constraint given for Amazon S3 bucket, "
  641. "but the bucket name (%s) is not usable as a subdomain."),
  642. self->bucket),
  643. DEVICE_STATUS_DEVICE_ERROR);
  644. return FALSE;
  645. }
  646. amfree(self->bucket_location);
  647. self->bucket_location = g_value_dup_string(val);
  648. device_clear_volume_details(p_self);
  649. return device_simple_property_set_fn(p_self, base, val, surety, source);
  650. }
  651. static gboolean
  652. s3_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base,
  653. GValue *val, PropertySurety surety, PropertySource source)
  654. {
  655. S3Device *self = S3_DEVICE(p_self);
  656. self->verbose = g_value_get_boolean(val);
  657. /* Our S3 handle may not yet have been instantiated; if so, it will
  658. * get the proper verbose setting when it is created */
  659. if (self->s3)
  660. s3_verbose(self->s3, self->verbose);
  661. return device_simple_property_set_fn(p_self, base, val, surety, source);
  662. }
  663. static gboolean
  664. s3_device_set_ssl_fn(Device *p_self, DevicePropertyBase *base,
  665. GValue *val, PropertySurety surety, PropertySource source)
  666. {
  667. S3Device *self = S3_DEVICE(p_self);
  668. gboolean new_val;
  669. new_val = g_value_get_boolean(val);
  670. /* Our S3 handle may not yet have been instantiated; if so, it will
  671. * get the proper use_ssl setting when it is created */
  672. if (self->s3 && !s3_use_ssl(self->s3, new_val)) {
  673. device_set_error(p_self, g_strdup_printf(_(
  674. "Error setting S3 SSL/TLS use "
  675. "(tried to enable SSL/TLS for S3, but curl doesn't support it?)")),
  676. DEVICE_STATUS_DEVICE_ERROR);
  677. return FALSE;
  678. }
  679. self->use_ssl = new_val;
  680. return device_simple_property_set_fn(p_self, base, val, surety, source);
  681. }
  682. static Device*
  683. s3_device_factory(char * device_name, char * device_type, char * device_node)
  684. {
  685. Device *rval;
  686. S3Device * s3_rval;
  687. g_assert(0 == strcmp(device_type, S3_DEVICE_NAME) ||
  688. 0 == strcmp(device_type, DEVPAY_DEVICE_NAME));
  689. rval = DEVICE(g_object_new(TYPE_S3_DEVICE, NULL));
  690. s3_rval = (S3Device*)rval;
  691. device_open_device(rval, device_name, device_type, device_node);
  692. return rval;
  693. }
  694. /*
  695. * Virtual function overrides
  696. */
  697. static void
  698. s3_device_open_device(Device *pself, char *device_name,
  699. char * device_type, char * device_node)
  700. {
  701. S3Device *self = S3_DEVICE(pself);
  702. char * name_colon;
  703. GValue tmp_value;
  704. pself->min_block_size = S3_DEVICE_MIN_BLOCK_SIZE;
  705. pself->max_block_size = S3_DEVICE_MAX_BLOCK_SIZE;
  706. pself->block_size = S3_DEVICE_DEFAULT_BLOCK_SIZE;
  707. /* Device name may be bucket/prefix, to support multiple volumes in a
  708. * single bucket. */
  709. name_colon = strchr(device_node, '/');
  710. if (name_colon == NULL) {
  711. self->bucket = g_strdup(device_node);
  712. self->prefix = g_strdup("");
  713. } else {
  714. self->bucket = g_strndup(device_node, name_colon - device_node);
  715. self->prefix = g_strdup(name_colon + 1);
  716. }
  717. self->is_devpay = !strcmp(device_type, DEVPAY_DEVICE_NAME);
  718. if (self->bucket == NULL || self->bucket[0] == '\0') {
  719. device_set_error(pself,
  720. vstrallocf(_("Empty bucket name in device %s"), device_name),
  721. DEVICE_STATUS_DEVICE_ERROR);
  722. amfree(self->bucket);
  723. amfree(self->prefix);
  724. return;
  725. }
  726. g_debug(_("S3 driver using bucket '%s', prefix '%s'"), self->bucket, self->prefix);
  727. /* default values */
  728. self->verbose = FALSE;
  729. /* use SSL if available */
  730. self->use_ssl = s3_curl_supports_ssl();
  731. bzero(&tmp_value, sizeof(GValue));
  732. g_value_init(&tmp_value, G_TYPE_BOOLEAN);
  733. g_value_set_boolean(&tmp_value, self->use_ssl);
  734. device_set_simple_property(pself, device_property_s3_ssl.ID,
  735. &tmp_value, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
  736. if (parent_class->open_device) {
  737. parent_class->open_device(pself, device_name, device_type, device_node);
  738. }
  739. }
  740. static void s3_device_finalize(GObject * obj_self) {
  741. S3Device *self = S3_DEVICE (obj_self);
  742. if(G_OBJECT_CLASS(parent_class)->finalize)
  743. (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
  744. if(self->s3) s3_free(self->s3);
  745. if(self->bucket) g_free(self->bucket);
  746. if(self->prefix) g_free(self->prefix);
  747. if(self->access_key) g_free(self->access_key);
  748. if(self->secret_key) g_free(self->secret_key);
  749. if(self->user_token) g_free(self->user_token);
  750. if(self->bucket_location) g_free(self->bucket_location);
  751. }
  752. static gboolean setup_handle(S3Device * self) {
  753. Device *d_self = DEVICE(self);
  754. if (self->s3 == NULL) {
  755. if (self->access_key == NULL || self->access_key[0] == '\0') {
  756. device_set_error(d_self,
  757. stralloc(_("No Amazon access key specified")),
  758. DEVICE_STATUS_DEVICE_ERROR);
  759. return FALSE;
  760. }
  761. if (self->secret_key == NULL || self->secret_key[0] == '\0') {
  762. device_set_error(d_self,
  763. stralloc(_("No Amazon secret key specified")),
  764. DEVICE_STATUS_DEVICE_ERROR);
  765. return FALSE;
  766. }
  767. if (self->is_devpay && self->user_token == NULL) {
  768. device_set_error(d_self,
  769. stralloc(_("No Amazon user token specified")),
  770. DEVICE_STATUS_DEVICE_ERROR);
  771. return FALSE;
  772. }
  773. self->s3 = s3_open(self->access_key, self->secret_key, self->user_token,
  774. self->bucket_location);
  775. if (self->s3 == NULL) {
  776. device_set_error(d_self,
  777. stralloc(_("Internal error creating S3 handle")),
  778. DEVICE_STATUS_DEVICE_ERROR);
  779. return FALSE;
  780. }
  781. }
  782. s3_verbose(self->s3, self->verbose);
  783. if (!s3_use_ssl(self->s3, self->use_ssl)) {
  784. device_set_error(d_self, g_strdup_printf(_(
  785. "Error setting S3 SSL/TLS use "
  786. "(tried to enable SSL/TLS for S3, but curl doesn't support it?)")),
  787. DEVICE_STATUS_DEVICE_ERROR);
  788. return FALSE;
  789. }
  790. return TRUE;
  791. }
  792. static DeviceStatusFlags
  793. s3_device_read_label(Device *pself) {
  794. S3Device *self = S3_DEVICE(pself);
  795. char *key;
  796. CurlBuffer buf = {NULL, 0, 0, S3_DEVICE_MAX_BLOCK_SIZE};
  797. dumpfile_t *amanda_header;
  798. /* note that this may be called from s3_device_start, when
  799. * self->access_mode is not ACCESS_NULL */
  800. amfree(pself->volume_label);
  801. amfree(pself->volume_time);
  802. amfree(pself->volume_header);
  803. if (device_in_error(self)) return pself->status;
  804. if (!setup_handle(self)) {
  805. /* setup_handle already set our error message */
  806. return pself->status;
  807. }
  808. key = special_file_to_key(self, "tapestart", -1);
  809. if (!s3_read(self->s3, self->bucket, key, S3_BUFFER_WRITE_FUNCS, &buf, NULL, NULL)) {
  810. guint response_code;
  811. s3_error_code_t s3_error_code;
  812. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  813. /* if it's an expected error (not found), just return FALSE */
  814. if (response_code == 404 &&
  815. (s3_error_code == S3_ERROR_NoSuchKey || s3_error_code == S3_ERROR_NoSuchBucket)) {
  816. g_debug(_("Amanda header not found while reading tapestart header (this is expected for empty tapes)"));
  817. device_set_error(pself,
  818. stralloc(_("Amanda header not found -- unlabeled volume?")),
  819. DEVICE_STATUS_DEVICE_ERROR
  820. | DEVICE_STATUS_VOLUME_ERROR
  821. | DEVICE_STATUS_VOLUME_UNLABELED);
  822. return pself->status;
  823. }
  824. /* otherwise, log it and return */
  825. device_set_error(pself,
  826. vstrallocf(_("While trying to read tapestart header: %s"), s3_strerror(self->s3)),
  827. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  828. return pself->status;
  829. }
  830. g_assert(buf.buffer != NULL);
  831. amanda_header = g_new(dumpfile_t, 1);
  832. parse_file_header(buf.buffer, amanda_header, buf.buffer_pos);
  833. pself->volume_header = amanda_header;
  834. g_free(buf.buffer);
  835. if (amanda_header->type != F_TAPESTART) {
  836. device_set_error(pself, stralloc(_("Invalid amanda header")), DEVICE_STATUS_VOLUME_ERROR);
  837. return pself->status;
  838. }
  839. pself->volume_label = g_strdup(amanda_header->name);
  840. pself->volume_time = g_strdup(amanda_header->datestamp);
  841. /* pself->volume_header is already set */
  842. device_set_error(pself, NULL, DEVICE_STATUS_SUCCESS);
  843. return pself->status;
  844. }
  845. static gboolean
  846. s3_device_start (Device * pself, DeviceAccessMode mode,
  847. char * label, char * timestamp) {
  848. S3Device * self;
  849. int file, last_file;
  850. self = S3_DEVICE(pself);
  851. if (device_in_error(self)) return FALSE;
  852. if (!setup_handle(self)) {
  853. /* setup_handle already set our error message */
  854. return FALSE;
  855. }
  856. pself->access_mode = mode;
  857. pself->in_file = FALSE;
  858. /* try creating the bucket, in case it doesn't exist */
  859. if (mode != ACCESS_READ && !s3_make_bucket(self->s3, self->bucket)) {
  860. guint response_code;
  861. s3_error_code_t s3_error_code;
  862. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  863. /* if it isn't an expected error (bucket already exists),
  864. * return FALSE */
  865. if (response_code != 409 ||
  866. s3_error_code != S3_ERROR_BucketAlreadyExists) {
  867. device_set_error(pself,
  868. vstrallocf(_("While creating new S3 bucket: %s"), s3_strerror(self->s3)),
  869. DEVICE_STATUS_DEVICE_ERROR);
  870. return FALSE;
  871. }
  872. }
  873. /* take care of any dirty work for this mode */
  874. switch (mode) {
  875. case ACCESS_READ:
  876. if (pself->volume_label == NULL && s3_device_read_label(pself) != DEVICE_STATUS_SUCCESS) {
  877. /* s3_device_read_label already set our error message */
  878. return FALSE;
  879. }
  880. break;
  881. case ACCESS_WRITE:
  882. /* delete all files */
  883. last_file = find_last_file(self);
  884. if (last_file < 0) return FALSE;
  885. for (file = 0; file <= last_file; file++) {
  886. if (!delete_file(self, file))
  887. /* delete_file already set our error message */
  888. return FALSE;
  889. }
  890. /* write a new amanda header */
  891. if (!write_amanda_header(self, label, timestamp)) {
  892. return FALSE;
  893. }
  894. pself->volume_label = newstralloc(pself->volume_label, label);
  895. pself->volume_time = newstralloc(pself->volume_time, timestamp);
  896. /* unset the VOLUME_UNLABELED flag, if it was set */
  897. device_set_error(pself, NULL, DEVICE_STATUS_SUCCESS);
  898. break;
  899. case ACCESS_APPEND:
  900. if (pself->volume_label == NULL && s3_device_read_label(pself) != DEVICE_STATUS_SUCCESS) {
  901. /* s3_device_read_label already set our error message */
  902. return FALSE;
  903. }
  904. return seek_to_end(self);
  905. break;
  906. case ACCESS_NULL:
  907. g_assert_not_reached();
  908. }
  909. return TRUE;
  910. }
  911. static gboolean
  912. s3_device_finish (Device * pself) {
  913. if (device_in_error(pself)) return FALSE;
  914. /* we're not in a file anymore */
  915. pself->access_mode = ACCESS_NULL;
  916. return TRUE;
  917. }
  918. /* functions for writing */
  919. static gboolean
  920. s3_device_start_file (Device *pself, dumpfile_t *jobInfo) {
  921. S3Device *self = S3_DEVICE(pself);
  922. CurlBuffer amanda_header = {NULL, 0, 0, 0};
  923. gboolean header_fits, result;
  924. char *key;
  925. if (device_in_error(self)) return FALSE;
  926. /* Set the blocksize to zero, since there's no header to skip (it's stored
  927. * in a distinct file, rather than block zero) */
  928. jobInfo->blocksize = 0;
  929. /* Build the amanda header. */
  930. amanda_header.buffer = device_build_amanda_header(pself, jobInfo,
  931. (int *) &amanda_header.buffer_len, &header_fits);
  932. if (!header_fits) {
  933. device_set_error(pself,
  934. stralloc(_("Amanda file header won't fit in a single block!")),
  935. DEVICE_STATUS_DEVICE_ERROR);
  936. return FALSE;
  937. }
  938. /* set the file and block numbers correctly */
  939. pself->file = (pself->file > 0)? pself->file+1 : 1;
  940. pself->block = 0;
  941. pself->in_file = TRUE;
  942. /* write it out as a special block (not the 0th) */
  943. key = special_file_to_key(self, "filestart", pself->file);
  944. result = s3_upload(self->s3, self->bucket, key, S3_BUFFER_READ_FUNCS,
  945. &amanda_header, NULL, NULL);
  946. g_free(amanda_header.buffer);
  947. g_free(key);
  948. if (!result) {
  949. device_set_error(pself,
  950. vstrallocf(_("While writing filestart header: %s"), s3_strerror(self->s3)),
  951. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  952. return FALSE;
  953. }
  954. return TRUE;
  955. }
  956. static gboolean
  957. s3_device_write_block (Device * pself, guint size, gpointer data) {
  958. gboolean result;
  959. char *filename;
  960. S3Device * self = S3_DEVICE(pself);
  961. CurlBuffer to_write = {data, size, 0, 0};
  962. g_assert (self != NULL);
  963. g_assert (data != NULL);
  964. if (device_in_error(self)) return FALSE;
  965. filename = file_and_block_to_key(self, pself->file, pself->block);
  966. result = s3_upload(self->s3, self->bucket, filename, S3_BUFFER_READ_FUNCS,
  967. &to_write, NULL, NULL);
  968. g_free(filename);
  969. if (!result) {
  970. device_set_error(pself,
  971. vstrallocf(_("While writing data block to S3: %s"), s3_strerror(self->s3)),
  972. DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
  973. return FALSE;
  974. }
  975. pself->block++;
  976. return TRUE;
  977. }
  978. static gboolean
  979. s3_device_finish_file (Device * pself) {
  980. if (device_in_error(pself)) return FALSE;
  981. /* we're not in a file anymore */
  982. pself->in_file = FALSE;
  983. return TRUE;
  984. }
  985. static gboolean
  986. s3_device_recycle_file(Device *pself, guint file) {
  987. S3Device *self = S3_DEVICE(pself);
  988. if (device_in_error(self)) return FALSE;
  989. return delete_file(self, file);
  990. /* delete_file already set our error message if necessary */
  991. }
  992. /* functions for reading */
  993. static dumpfile_t*
  994. s3_device_seek_file(Device *pself, guint file) {
  995. S3Device *self = S3_DEVICE(pself);
  996. gboolean result;
  997. char *key;
  998. CurlBuffer buf = {NULL, 0, 0, S3_DEVICE_MAX_BLOCK_SIZE};
  999. dumpfile_t *amanda_header;
  1000. const char *errmsg = NULL;
  1001. if (device_in_error(self)) return NULL;
  1002. pself->file = file;
  1003. pself->is_eof = FALSE;
  1004. pself->in_file = FALSE;
  1005. pself->block = 0;
  1006. /* read it in */
  1007. key = special_file_to_key(self, "filestart", pself->file);
  1008. result = s3_read(self->s3, self->bucket, key, S3_BUFFER_WRITE_FUNCS,
  1009. &buf, NULL, NULL);
  1010. g_free(key);
  1011. if (!result) {
  1012. guint response_code;
  1013. s3_error_code_t s3_error_code;
  1014. s3_error(self->s3, &errmsg, &response_code, &s3_error_code, NULL, NULL, NULL);
  1015. /* if it's an expected error (not found), check what to do. */
  1016. if (response_code == 404 && s3_error_code == S3_ERROR_NoSuchKey) {
  1017. int next_file;
  1018. next_file = find_next_file(self, pself->file);
  1019. if (next_file > 0) {
  1020. /* Note short-circut of dispatcher. */
  1021. return s3_device_seek_file(pself, next_file);
  1022. } else if (next_file == 0) {
  1023. /* No next file. Check if we are one past the end. */
  1024. key = special_file_to_key(self, "filestart", pself->file - 1);
  1025. result = s3_read(self->s3, self->bucket, key,
  1026. S3_BUFFER_WRITE_FUNCS, &buf, NULL, NULL);
  1027. g_free(key);
  1028. if (result) {
  1029. /* pself->file, etc. are already correct */
  1030. return make_tapeend_header();
  1031. } else {
  1032. device_set_error(pself,
  1033. stralloc(_("Attempt to read past tape-end file")),
  1034. DEVICE_STATUS_SUCCESS);
  1035. return NULL;
  1036. }
  1037. }
  1038. } else {
  1039. /* An unexpected error occured finding out if we are the last file. */
  1040. device_set_error(pself,
  1041. stralloc(errmsg),
  1042. DEVICE_STATUS_DEVICE_ERROR);
  1043. return NULL;
  1044. }
  1045. }
  1046. /* and make a dumpfile_t out of it */
  1047. g_assert(buf.buffer != NULL);
  1048. amanda_header = g_new(dumpfile_t, 1);
  1049. fh_init(amanda_header);
  1050. parse_file_header(buf.buffer, amanda_header, buf.buffer_pos);
  1051. g_free(buf.buffer);
  1052. switch (amanda_header->type) {
  1053. case F_DUMPFILE:
  1054. case F_CONT_DUMPFILE:
  1055. case F_SPLIT_DUMPFILE:
  1056. break;
  1057. default:
  1058. device_set_error(pself,
  1059. stralloc(_("Invalid amanda header while reading file header")),
  1060. DEVICE_STATUS_VOLUME_ERROR);
  1061. g_free(amanda_header);
  1062. return NULL;
  1063. }
  1064. pself->in_file = TRUE;
  1065. return amanda_header;
  1066. }
  1067. static gboolean
  1068. s3_device_seek_block(Device *pself, guint64 block) {
  1069. if (device_in_error(pself)) return FALSE;
  1070. pself->block = block;
  1071. return TRUE;
  1072. }
  1073. typedef struct s3_read_block_data {
  1074. gpointer data;
  1075. int size_req;
  1076. int size_written;
  1077. CurlBuffer curl;
  1078. } s3_read_block_data;
  1079. /* wrapper around s3_buffer_write_func to write as much data as possible to
  1080. * the user's buffer, and switch to a dynamically allocated buffer if that
  1081. * isn't large enough */
  1082. static size_t
  1083. s3_read_block_write_func(void *ptr, size_t size, size_t nmemb, void *stream)
  1084. {
  1085. s3_read_block_data *dat = stream;
  1086. guint new_bytes, bytes_needed;
  1087. /* if data is NULL, call through to s3_buffer_write_func */
  1088. if (!dat->data) {
  1089. return s3_buffer_write_func(ptr, size, nmemb, (void *)(&dat->curl));
  1090. }
  1091. new_bytes = (guint) size * nmemb;
  1092. bytes_needed = dat->size_written + new_bytes;
  1093. if (bytes_needed > (guint)dat->size_written) {
  1094. /* this read will overflow the user's buffer, so malloc ourselves
  1095. * a new buffer and keep reading */
  1096. dat->curl.buffer = g_malloc(bytes_needed);
  1097. dat->curl.buffer_len = bytes_needed;
  1098. dat->curl.buffer_pos = dat->size_written;
  1099. memcpy(dat->curl.buffer, dat->data, dat->size_written);
  1100. dat->data = NULL; /* signal that the user's buffer is too small */
  1101. return s3_buffer_write_func(ptr, size, nmemb, (void *)(&dat->curl));
  1102. }
  1103. memcpy(dat->data + dat->size_written, ptr, bytes_needed);
  1104. return new_bytes;
  1105. }
  1106. static int
  1107. s3_device_read_block (Device * pself, gpointer data, int *size_req) {
  1108. S3Device * self = S3_DEVICE(pself);
  1109. char *key;
  1110. s3_read_block_data dat = {NULL, 0, 0, { NULL, 0, 0, S3_DEVICE_MAX_BLOCK_SIZE} };
  1111. gboolean result;
  1112. g_assert (self != NULL);
  1113. if (device_in_error(self)) return -1;
  1114. /* get the file*/
  1115. key = file_and_block_to_key(self, pself->file, pself->block);
  1116. g_assert(key != NULL);
  1117. if (self->cached_key && (0 == strcmp(key, self->cached_key))) {
  1118. if (*size_req >= self->cached_size) {
  1119. /* use the cached copy and clear the cache */
  1120. memcpy(data, self->cached_buf, self->cached_size);
  1121. *size_req = self->cached_size;
  1122. g_free(key);
  1123. g_free(self->cached_key);
  1124. self->cached_key = NULL;
  1125. g_free(self->cached_buf);
  1126. self->cached_buf = NULL;
  1127. pself->block++;
  1128. return *size_req;
  1129. } else {
  1130. *size_req = self->cached_size;
  1131. g_free(key);
  1132. return 0;
  1133. }
  1134. }
  1135. /* clear the cache, as it's useless to us */
  1136. if (self->cached_key) {
  1137. g_free(self->cached_key);
  1138. self->cached_key = NULL;
  1139. g_free(self->cached_buf);
  1140. self->cached_buf = NULL;
  1141. }
  1142. /* set up dat for the write_func callback */
  1143. if (!data || *size_req <= 0) {
  1144. dat.data = NULL;
  1145. dat.size_req = 0;
  1146. } else {
  1147. dat.data = data;
  1148. dat.size_req = *size_req;
  1149. }
  1150. result = s3_read(self->s3, self->bucket, key, s3_read_block_write_func,
  1151. s3_buffer_reset_func, &dat, NULL, NULL);
  1152. if (!result) {
  1153. guint response_code;
  1154. s3_error_code_t s3_error_code;
  1155. s3_error(self->s3, NULL, &response_code, &s3_error_code, NULL, NULL, NULL);
  1156. g_free(key);
  1157. key = NULL;
  1158. /* if it's an expected error (not found), just return -1 */
  1159. if (response_code == 404 && s3_error_code == S3_ERROR_NoSuchKey) {
  1160. pself->is_eof = TRUE;
  1161. pself->in_file = FALSE;
  1162. device_set_error(pself,
  1163. stralloc(_("EOF")),
  1164. DEVICE_STATUS_SUCCESS);
  1165. return -1;
  1166. }
  1167. /* otherwise, log it and return FALSE */
  1168. device_set_error(pself,
  1169. vstrallocf(_("While reading data block from S3: %s"), s3_strerror(self->s3)),
  1170. DEVICE_STATUS_VOLUME_ERROR);
  1171. return -1;
  1172. }
  1173. if (dat.data == NULL) {
  1174. /* data was larger than the available space, so cache it and return
  1175. * the actual size */
  1176. self->cached_buf = dat.curl.buffer;
  1177. self->cached_size = dat.curl.buffer_pos;
  1178. self->cached_key = key;
  1179. key = NULL;
  1180. *size_req = dat.curl.buffer_pos;
  1181. return 0;
  1182. }
  1183. /* ok, the read went directly to the user's buffer, so we need only
  1184. * set and return the size */
  1185. pself->block++;
  1186. g_free(key);
  1187. *size_req = dat.size_req;
  1188. return dat.size_req;
  1189. }