/
chunks_delete.c
219 lines (180 loc) · 5.07 KB
/
chunks_delete.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include "bsdtar_platform.h"
#include <stdlib.h>
#include <string.h>
#include "chunks_internal.h"
#include "rwhashtab.h"
#include "storage.h"
#include "warnp.h"
#include "chunks.h"
struct chunks_delete_internal {
RWHASHTAB * HT; /* Hash table of struct chunkdata. */
struct chunkdata * dir; /* On-disk directory entries. */
char * path; /* Path to cache directory. */
STORAGE_D * S; /* Storage layer cookie. */
struct chunkstats stats_total; /* All archives, w/ multiplicity. */
struct chunkstats stats_unique; /* All archives, w/o multiplicity. */
struct chunkstats stats_extra; /* Extra (non-chunked) data. */
struct chunkstats stats_tape; /* This archive, w/ multiplicity. */
struct chunkstats stats_freed; /* Chunks being deleted. */
struct chunkstats stats_tapee; /* Extra data in this archive. */
};
/**
* chunks_delete_start(cachepath, S):
* Start a delete transaction using the cache directory ${cachepath} and the
* storage layer cookie ${S}.
*/
CHUNKS_D *
chunks_delete_start(const char * cachepath, STORAGE_D * S)
{
struct chunks_delete_internal * C;
/* Allocate memory. */
if ((C = malloc(sizeof(struct chunks_delete_internal))) == NULL)
goto err0;
/* Record the storage cookie that we're using. */
C->S = S;
/* Create a copy of the path. */
if ((C->path = strdup(cachepath)) == NULL)
goto err1;
/* Read the existing chunk directory. */
if ((C->HT = chunks_directory_read(cachepath, &C->dir,
&C->stats_unique, &C->stats_total, &C->stats_extra)) == NULL)
goto err2;
/* Zero "new chunks" and "this tape" statistics. */
chunks_stats_zero(&C->stats_tape);
chunks_stats_zero(&C->stats_freed);
chunks_stats_zero(&C->stats_tapee);
/* Success! */
return (C);
err2:
free(C->path);
err1:
free(C);
err0:
/* Failure! */
return (NULL);
}
/**
* chunks_delete_chunk(C, hash):
* Delete the chunk with HMAC ${hash} as part of the delete transaction
* associated with the cookie ${C}. Note that chunks are actually
* removed from disk once they have been "deleted" by the same number of
* transactions as they have been "written" by.
*/
int
chunks_delete_chunk(CHUNKS_D * C, const uint8_t * hash)
{
struct chunkdata * ch;
/* If the chunk is not in ${C}->HT, error out. */
if ((ch = rwhashtab_read(C->HT, hash)) == NULL) {
warn0("Chunk is missing or directory is corrupt");
goto err0;
}
/* Update statistics. */
chunks_stats_add(&C->stats_total, ch->len, ch->zlen, -1);
chunks_stats_add(&C->stats_tape, ch->len, ch->zlen, 1);
ch->ncopies -= 1;
/* If the chunk is not marked as CHDATA_CTAPE... */
if ((ch->flags & CHDATA_CTAPE) == 0) {
/* ... add that flag... */
ch->flags |= CHDATA_CTAPE;
/* ... decrement the reference counter... */
ch->nrefs -= 1;
/* ... and delete the chunk if the refcount is now zero. */
if (ch->nrefs == 0) {
chunks_stats_add(&C->stats_unique, ch->len,
ch->zlen, -1);
chunks_stats_add(&C->stats_freed, ch->len,
ch->zlen, 1);
if (storage_delete_file(C->S, 'c', hash))
goto err0;
}
}
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/**
* chunks_delete_extrastats(C, len):
* Notify the chunk layer that non-chunked data of length ${len} has been
* deleted directly via the storage layer; this information is used when
* displaying archive statistics.
*/
void
chunks_delete_extrastats(CHUNKS_D * C, size_t len)
{
chunks_stats_add(&C->stats_extra, len, len, -1);
chunks_stats_add(&C->stats_tapee, len, len, 1);
}
/**
* chunks_delete_printstats(stream, C):
* Print statistics for the delete transaction associated with the cookie
* ${C} to ${stream}.
*/
int
chunks_delete_printstats(FILE * stream, CHUNKS_D * C)
{
/* Print header. */
if (chunks_stats_printheader(stream))
goto err0;
/* Print the statistics we have. */
if (chunks_stats_print(stream, &C->stats_total, "All archives",
&C->stats_extra))
goto err0;
if (chunks_stats_print(stream, &C->stats_unique, " (unique data)",
&C->stats_extra))
goto err0;
if (chunks_stats_print(stream, &C->stats_tape, "This archive",
&C->stats_tapee))
goto err0;
if (chunks_stats_print(stream, &C->stats_freed, "Deleted data",
&C->stats_tapee))
goto err0;
/* Success! */
return (0);
err0:
/* Failure! */
return (-1);
}
/**
* chunks_delete_end(C):
* Finish the delete transaction associated with the cookie ${C}.
*/
int
chunks_delete_end(CHUNKS_D * C)
{
/* Write the new chunk directory. */
if (chunks_directory_write(C->path, C->HT, &C->stats_extra))
goto err1;
/* Free the chunk hash table. */
chunks_directory_free(C->HT, C->dir);
/* Free memory. */
free(C->path);
free(C);
/* Success! */
return (0);
err1:
chunks_directory_free(C->HT, C->dir);
free(C->path);
free(C);
/* Failure! */
return (-1);
}
/**
* chunks_delete_free(C):
* Terminate the delete transaction associated with the cookie ${C}.
* (See chunks_write_free for details of what "terminate" means.)
*/
void
chunks_delete_free(CHUNKS_D * C)
{
/* Behave consistently with free(NULL). */
if (C == NULL)
return;
/* Free the chunk hash table. */
chunks_directory_free(C->HT, C->dir);
/* Free memory. */
free(C->path);
free(C);
}