Add high-resolution timekeeping functions (primarily for benchmarking).
[bluesky.git] / bluesky / file.c
1 /* Blue Sky: File Systems in the Cloud
2  *
3  * Copyright (C) 2009  The Regents of the University of California
4  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
5  *
6  * TODO: Licensing
7  */
8
9 #include <stdint.h>
10 #include <glib.h>
11 #include <string.h>
12
13 #include "bluesky.h"
14
15 /* Core filesystem: handling of regular files and caching of file data. */
16
17 /* Mark a given block dirty and make sure that data is faulted in so that it
18  * can be written to. */
19 void bluesky_block_touch(BlueSkyInode *inode, uint64_t i)
20 {
21     g_return_if_fail(i < inode->blocks->len);
22     BlueSkyBlock *block = &g_array_index(inode->blocks, BlueSkyBlock, i);
23
24     switch (block->type) {
25     case BLUESKY_BLOCK_ZERO:
26         block->data = bluesky_string_new(g_malloc0(BLUESKY_BLOCK_SIZE),
27                                          BLUESKY_BLOCK_SIZE);
28         break;
29     case BLUESKY_BLOCK_REF:
30         bluesky_block_fetch(inode->fs, block);
31         g_assert(block->type == BLUESKY_BLOCK_CACHED);
32         /* Fall through */
33     case BLUESKY_BLOCK_CACHED:
34     case BLUESKY_BLOCK_DIRTY:
35         block->data = bluesky_string_dup(block->data);
36         break;
37     }
38
39     block->type = BLUESKY_BLOCK_DIRTY;
40 }
41
42 /* Set the size of a file.  This will truncate or extend the file as needed.
43  * Newly-allocated bytes are zeroed. */
44 void bluesky_file_truncate(BlueSkyInode *inode, uint64_t size)
45 {
46     g_return_if_fail(size <= BLUESKY_MAX_FILE_SIZE);
47
48     if (size == inode->size)
49         return;
50
51     uint64_t blocks = (size + BLUESKY_BLOCK_SIZE - 1) / BLUESKY_BLOCK_SIZE;
52
53     if (blocks > inode->blocks->len) {
54         /* Need to add new blocks to the end of a file.  New block structures
55          * are automatically zeroed, which initializes them to be pointers to
56          * zero blocks so we don't need to do any more work. */
57         g_array_set_size(inode->blocks, blocks);
58     } else if (blocks < inode->blocks->len) {
59         /* Delete blocks from a file.  Must reclaim memory. */
60         for (guint i = inode->blocks->len; i < blocks; i++) {
61             BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock, i);
62             g_free(b->ref);
63             bluesky_string_unref(b->data);
64         }
65         g_array_set_size(inode->blocks, blocks);
66     }
67
68     /* If the file size is being decreased, ensure that any trailing data in
69      * the last block is zeroed. */
70     if (size < inode->size) {
71         BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
72                                          blocks - 1);
73         if (b->type != BLUESKY_BLOCK_ZERO) {
74             bluesky_block_touch(inode, blocks - 1);
75             int end_offset = size % BLUESKY_BLOCK_SIZE;
76             if (end_offset > 0) {
77                 memset(&b->data->data[end_offset], 0,
78                        BLUESKY_BLOCK_SIZE - end_offset);
79             }
80         }
81     }
82
83     inode->size = size;
84     bluesky_inode_update_ctime(inode, 1);
85 }
86
87 void bluesky_file_write(BlueSkyInode *inode, uint64_t offset,
88                         const char *data, gint len)
89 {
90     g_print("Start write: %ld\n", bluesky_now_hires());
91     g_return_if_fail(inode->type == BLUESKY_REGULAR);
92     g_return_if_fail(offset < inode->size);
93     g_return_if_fail(len <= inode->size - offset);
94
95     if (len == 0)
96         return;
97
98     while (len > 0) {
99         uint64_t block_num = offset / BLUESKY_BLOCK_SIZE;
100         gint block_offset = offset % BLUESKY_BLOCK_SIZE;
101         gint bytes = MIN(BLUESKY_BLOCK_SIZE - block_offset, len);
102
103         bluesky_block_touch(inode, block_num);
104         BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
105                                          block_num);
106         memcpy(&b->data->data[block_offset], data, bytes);
107         bluesky_block_flush(inode->fs, b);
108
109         offset += bytes;
110         data += bytes;
111         len -= bytes;
112     }
113
114     bluesky_inode_update_ctime(inode, 1);
115     bluesky_inode_flush(inode->fs, inode);
116     g_print("End write: %ld\n", bluesky_now_hires());
117 }
118
119 void bluesky_file_read(BlueSkyInode *inode, uint64_t offset,
120                        char *buf, gint len)
121 {
122     g_return_if_fail(inode->type == BLUESKY_REGULAR);
123     g_return_if_fail(offset < inode->size);
124     g_return_if_fail(len <= inode->size - offset);
125
126     while (len > 0) {
127         uint64_t block_num = offset / BLUESKY_BLOCK_SIZE;
128         gint block_offset = offset % BLUESKY_BLOCK_SIZE;
129         gint bytes = MIN(BLUESKY_BLOCK_SIZE - block_offset, len);
130
131         BlueSkyBlock *b = &g_array_index(inode->blocks, BlueSkyBlock,
132                                          block_num);
133         switch (b->type) {
134         case BLUESKY_BLOCK_ZERO:
135             memset(buf, 0, bytes);
136             break;
137         case BLUESKY_BLOCK_REF:
138             bluesky_block_fetch(inode->fs, b);
139             /* Fall through */
140         case BLUESKY_BLOCK_CACHED:
141         case BLUESKY_BLOCK_DIRTY:
142             memcpy(buf, &b->data->data[block_offset], bytes);
143             break;
144         }
145
146         offset += bytes;
147         buf += bytes;
148         len -= bytes;
149     }
150 }
151
152 /* Read the given block from cloud-backed storage if the data is not already
153  * cached. */
154 void bluesky_block_fetch(BlueSkyFS *fs, BlueSkyBlock *block)
155 {
156     if (block->type != BLUESKY_BLOCK_REF)
157         return;
158
159     g_print("Fetching block from %s\n", block->ref);
160     BlueSkyRCStr *string = bluesky_store_get(fs->store, block->ref);
161
162     bluesky_string_unref(block->data);
163     block->data = bluesky_crypt_decrypt(string, fs->encryption_key);
164     block->type = BLUESKY_BLOCK_CACHED;
165     bluesky_string_unref(string);
166 }
167
168 /* Write the given block to cloud-backed storage and mark it clean. */
169 void bluesky_block_flush(BlueSkyFS *fs, BlueSkyBlock *block)
170 {
171     if (block->type != BLUESKY_BLOCK_DIRTY)
172         return;
173
174     BlueSkyRCStr *data = block->data;
175     data = bluesky_crypt_encrypt(data, fs->encryption_key);
176
177     GChecksum *csum = g_checksum_new(G_CHECKSUM_SHA256);
178     g_checksum_update(csum, data->data, data->len);
179     gchar *name = g_strdup(g_checksum_get_string(csum));
180
181     g_print("Flushing block as %s\n", name);
182     bluesky_store_put(fs->store, name, data);
183     g_free(block->ref);
184     block->ref = name;
185
186     /* block->type = BLUESKY_BLOCK_CACHED; */
187     bluesky_string_unref(block->data);
188     block->data = NULL;
189     block->type = BLUESKY_BLOCK_REF;
190
191     g_checksum_free(csum);
192     bluesky_string_unref(data);
193 }