123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- #include "platform.h"
- #include <stddef.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include "crypto.h"
- #include "netpacket.h"
- #include "netproto.h"
- #include "storage_internal.h"
- #include "sysendian.h"
- #include "tsnetwork.h"
- #include "warnp.h"
- #include "storage.h"
- #include "storage_read_cache.h"
- struct storage_read_internal {
- NETPACKET_CONNECTION * NPC;
- struct storage_read_cache * cache;
- uint64_t machinenum;
- };
- struct read_file_internal {
- int done;
- int status;
- uint8_t * buf;
- size_t buflen;
- };
- struct read_file_cookie {
- int (* callback)(void *, int, uint8_t *, size_t);
- void * cookie;
- struct storage_read_internal * S;
- uint64_t machinenum;
- uint8_t class;
- uint8_t name[32];
- uint32_t size;
- uint8_t * buf;
- size_t buflen;
- };
- static sendpacket_callback callback_read_file_send;
- static handlepacket_callback callback_read_file_response;
- static int callback_read_file(void *, int, uint8_t *, size_t);
- /**
- * storage_read_init(machinenum):
- * Prepare for read operations. Note that since reads are non-transactional,
- * this could be a no-op aside from storing the machine number.
- */
- STORAGE_R *
- storage_read_init(uint64_t machinenum)
- {
- struct storage_read_internal * S;
- /* Allocate memory. */
- if ((S = malloc(sizeof(struct storage_read_internal))) == NULL)
- goto err0;
- /* Create the cache. */
- if ((S->cache = storage_read_cache_init()) == NULL)
- goto err1;
- /* Open netpacket connection. */
- if ((S->NPC = netpacket_open(USERAGENT)) == NULL)
- goto err2;
- /* Store machine number. */
- S->machinenum = machinenum;
- /* Success! */
- return (S);
- err2:
- storage_read_cache_free(S->cache);
- err1:
- free(S);
- err0:
- /* Failure! */
- return (NULL);
- }
- /**
- * storage_read_add_name_cache(S, class, name):
- * Add the file ${name} from class ${class} into the cache for the read cookie
- * ${S} returned from storage_read_init. The data will not be fetched yet;
- * but any future fetch will look in the cache first and will store the block
- * in the cache if it needs to be fetched.
- */
- int
- storage_read_add_name_cache(STORAGE_R * S, char class, const uint8_t name[32])
- {
- /* Pass request to the cache. */
- return (storage_read_cache_add_name(S->cache, class, name));
- }
- /**
- * storage_read_set_cache_limit(S, size):
- * Set a limit of ${size} bytes on the cache associated with read cookie ${S}.
- */
- void
- storage_read_set_cache_limit(STORAGE_R * S, size_t size)
- {
- /* Pass request to the cache. */
- storage_read_cache_set_limit(S->cache, size);
- }
- /**
- * storage_read_file(S, buf, buflen, class, name):
- * Read the file ${name} from class ${class} using the read cookie ${S}
- * returned from storage_read_init into the buffer ${buf} of length ${buflen}.
- * Return 0 on success, 1 if the file does not exist; 2 if the file is not
- * ${buflen} bytes long or is corrupt; or -1 on error.
- */
- int
- storage_read_file(STORAGE_R * S, uint8_t * buf, size_t buflen,
- char class, const uint8_t name[32])
- {
- struct read_file_internal C;
- uint8_t * cached_buf;
- size_t cached_buflen;
- /* Can we serve this from our cache? */
- storage_read_cache_find(S->cache, class, name, &cached_buf,
- &cached_buflen);
- if (cached_buf != NULL) {
- if (buflen != cached_buflen) {
- /* Bad length. */
- C.status = 2;
- goto done;
- } else {
- /* Good length, copy data out. */
- C.status = 0;
- memcpy(buf, cached_buf, buflen);
- goto done;
- }
- }
- /* Initialize structure. */
- C.buf = buf;
- C.buflen = buflen;
- C.done = 0;
- /* Issue the request and spin until completion. */
- if (storage_read_file_callback(S, C.buf, C.buflen, class, name,
- callback_read_file, &C))
- goto err0;
- if (network_spin(&C.done))
- goto err0;
- done:
- /* Return status code from server. */
- return (C.status);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * storage_read_file_alloc(S, buf, buflen, class, name):
- * Allocate a buffer and read the file ${name} from class ${class} using the
- * read cookie ${S} into it; set ${buf} to point at the buffer, and
- * ${buflen} to the length of the buffer. Return 0, 1, 2, or -1 as per
- * storage_read_file.
- */
- int
- storage_read_file_alloc(STORAGE_R * S, uint8_t ** buf,
- size_t * buflen, char class, const uint8_t name[32])
- {
- struct read_file_internal C;
- uint8_t * cached_buf;
- size_t cached_buflen;
- /* Can we serve this from our cache? */
- storage_read_cache_find(S->cache, class, name, &cached_buf,
- &cached_buflen);
- if (cached_buf != NULL) {
- /* Allocate a buffer and copy data out. */
- if ((*buf = malloc(cached_buflen)) == NULL)
- goto err0;
- memcpy(*buf, cached_buf, cached_buflen);
- *buflen = cached_buflen;
- /* Data is good. */
- C.status = 0;
- goto done;
- }
- /* Initialize structure. */
- C.buf = NULL;
- C.buflen = 0;
- C.done = 0;
- /* Issue the request and spin until completion. */
- if (storage_read_file_callback(S, C.buf, C.buflen, class, name,
- callback_read_file, &C))
- goto err0;
- if (network_spin(&C.done))
- goto err0;
- /* Store buffer and length if appropriate. */
- if (C.status == 0) {
- *buf = C.buf;
- *buflen = C.buflen;
- }
- done:
- /* Return status code. */
- return (C.status);
- err0:
- /* Failure! */
- return (-1);
- }
- /**
- * storage_read_file_callback(S, buf, buflen, class, name, callback, cookie):
- * Read the file ${name} from class ${class} using the read cookie ${S}
- * returned from storage_read_init. If ${buf} is non-NULL, then read the
- * file (which should be ${buflen} bytes in length) into ${buf}; otherwise
- * malloc a buffer. Invoke ${callback}(${cookie}, status, b, blen) when
- * complete, where ${status} is 0, 1, 2, or -1 as per storage_read_file,
- * ${b} is the buffer into which the data was read (which will be ${buf} if
- * that value was non-NULL) and ${blen} is the length of the file.
- */
- int
- storage_read_file_callback(STORAGE_R * S, uint8_t * buf, size_t buflen,
- char class, const uint8_t name[32],
- int callback(void *, int, uint8_t *, size_t), void * cookie)
- {
- struct read_file_cookie * C;
- /* Sanity-check file size if a buffer was provided. */
- if ((buf != NULL) && (buflen > 262144 - STORAGE_FILE_OVERHEAD)) {
- warn0("Programmer error: File too large");
- goto err0;
- }
- /* Bake a cookie. */
- if ((C = malloc(sizeof(struct read_file_cookie))) == NULL)
- goto err0;
- C->callback = callback;
- C->cookie = cookie;
- C->S = S;
- C->machinenum = S->machinenum;
- C->class = (uint8_t)class;
- memcpy(C->name, name, 32);
- /* Do we have a buffer? */
- if (buf != NULL) {
- C->buf = buf;
- C->buflen = buflen;
- C->size = (uint32_t)(buflen + STORAGE_FILE_OVERHEAD);
- } else {
- C->buf = NULL;
- C->buflen = 0;
- C->size = (uint32_t)(-1);
- }
- /* Ask the netpacket layer to send a request and get a response. */
- if (netpacket_op(S->NPC, callback_read_file_send, C))
- goto err0;
- /* Success! */
- return (0);
- err0:
- /* Failure! */
- return (-1);
- }
- static int
- callback_read_file_send(void * cookie, NETPACKET_CONNECTION * NPC)
- {
- struct read_file_cookie * C = cookie;
- /* Ask the server to read the file. */
- return (netpacket_read_file(NPC, C->machinenum, C->class,
- C->name, C->size, callback_read_file_response));
- }
- static int
- callback_read_file_response(void * cookie, NETPACKET_CONNECTION * NPC,
- int status, uint8_t packettype, const uint8_t * packetbuf,
- size_t packetlen)
- {
- struct read_file_cookie * C = cookie;
- uint32_t filelen;
- int sc;
- int rc;
- (void)NPC; /* UNUSED */
- /* Handle errors. */
- if (status != NETWORK_STATUS_OK) {
- netproto_printerr(status);
- goto err1;
- }
- /* Make sure we received the right type of packet. */
- if (packettype != NETPACKET_READ_FILE_RESPONSE)
- goto err2;
- /* Verify packet hmac. */
- switch (netpacket_hmac_verify(packettype, NULL,
- packetbuf, packetlen - 32, CRYPTO_KEY_AUTH_GET)) {
- case 1:
- goto err2;
- case -1:
- goto err1;
- }
- /* Make sure that the packet corresponds to the right file. */
- if ((packetbuf[1] != C->class) ||
- (memcmp(&packetbuf[2], C->name, 32)))
- goto err2;
- /* Extract status code and file length returned by server. */
- sc = packetbuf[0];
- filelen = be32dec(&packetbuf[34]);
- /* Verify packet integrity. */
- switch (sc) {
- case 0:
- if (packetlen != filelen + 70)
- goto err2;
- if (C->size != (uint32_t)(-1)) {
- if (filelen != C->size)
- goto err2;
- } else {
- if ((filelen < STORAGE_FILE_OVERHEAD) ||
- (filelen > 262144))
- goto err2;
- }
- break;
- case 1:
- case 3:
- if ((packetlen != 70) || (filelen != 0))
- goto err2;
- break;
- case 2:
- if (packetlen != 70)
- goto err2;
- break;
- default:
- goto err2;
- }
- /* Decrypt file data if appropriate. */
- if (sc == 0) {
- /* Allocate a buffer if necessary. */
- if (C->size == (uint32_t)(-1)) {
- C->buflen = filelen - STORAGE_FILE_OVERHEAD;
- if ((C->buf = malloc(C->buflen)) == NULL)
- goto err1;
- }
- switch (crypto_file_dec(&packetbuf[38], C->buflen, C->buf)) {
- case 0:
- /* Should we cache this data? */
- storage_read_cache_add_data(C->S->cache,
- (char)C->class, C->name, C->buf, C->buflen);
- break;
- case 1:
- /* File is corrupt. */
- sc = 2;
- if (C->size == (uint32_t)(-1)) {
- free(C->buf);
- C->buf = NULL;
- }
- break;
- case -1:
- if (C->size == (uint32_t)(-1))
- free(C->buf);
- goto err1;
- }
- }
- /*
- * If the user's tarsnap account balance is negative, print a warning
- * message and then pass back a generic error status code.
- */
- if (sc == 3) {
- warn0("Cannot read data from tarsnap server: "
- "Account balance is not positive.");
- warn0("Please add more money to your tarsnap account");
- sc = -1;
- }
- /* Perform the callback. */
- rc = (C->callback)(C->cookie, sc, C->buf, C->buflen);
- /* Free the cookie. */
- free(C);
- /* Return result from callback. */
- return (rc);
- err2:
- netproto_printerr(NETPROTO_STATUS_PROTERR);
- err1:
- free(C);
- /* Failure! */
- return (-1);
- }
- /* Callback for storage_read_file and storage_read_file_alloc. */
- static int
- callback_read_file(void * cookie, int sc, uint8_t * buf, size_t buflen)
- {
- struct read_file_internal * C = cookie;
- /* Record parameters. */
- C->status = sc;
- C->buf = buf;
- C->buflen = buflen;
- /* We're done. */
- C->done = 1;
- /* Success! */
- return (0);
- }
- /**
- * storage_read_free(S):
- * Close the read cookie ${S} and free any allocated memory.
- */
- void
- storage_read_free(STORAGE_R * S)
- {
- /* Behave consistently with free(NULL). */
- if (S == NULL)
- return;
- /* Close netpacket connection. */
- netpacket_close(S->NPC);
- /* Free cache. */
- storage_read_cache_free(S->cache);
- /* Free memory. */
- free(S);
- }
|