summaryrefslogtreecommitdiff
path: root/include/sqfs/data_writer.h
blob: 4623493ab449608510f7f71ef79b31ef0b19b2da (plain)
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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
 * data_writer.h - This file is part of libsquashfs
 *
 * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#ifndef SFQS_DATA_WRITER_H
#define SFQS_DATA_WRITER_H

#include "sqfs/predef.h"

/**
 * @file data_writer.h
 *
 * @brief Contains declarations for the data block processor.
 */

/**
 * @struct sqfs_data_writer_t
 *
 * @brief Abstracts generating of file data and fragment blocks.
 *
 * This data structure provides a simple begin/append/end interface
 * to generate file data blocks (see @ref sqfs_data_writer_begin_file,
 * @ref sqfs_data_writer_append and @ref sqfs_data_writer_end respectively).
 *
 * Internally it takes care of partitioning data in the correct block sizes,
 * adding tail-ens to fragment blocks, compressing the data, deduplicating data
 * and finally writing it to disk.
 */

/**
 * @struct sqfs_block_hooks_t
 *
 * @brief A set of hooks for tapping into the data writer.
 *
 * This structure can be registered with an @ref sqfs_data_writer_t and
 * contains function pointers that will be called during various stages
 * when writing data to disk.
 *
 * The callbacks can not only be used for accounting but may also write extra
 * data to the output file or make modifications to the blocks before they are
 * writtien.
 *
 * The callbacks can be individually set to NULL to disable them.
 */
struct sqfs_block_hooks_t {
	/**
	 * @brief Set this to the size of the struct.
	 *
	 * This is required for future expandabillity while maintaining ABI
	 * compatibillity. At the current time, the implementation of
	 * @ref sqfs_data_writer_set_hooks rejects any hook struct where this
	 * isn't the exact size. If new hooks are added in the future, the
	 * struct grows and the future implementation can tell by the size
	 * whether the application uses the new version or the old one.
	 */
	size_t size;

	/**
	 * @brief Gets called before writing a block to disk.
	 *
	 * If this is not NULL, it gets called before a block is written to
	 * disk. If the block has the @ref SQFS_BLK_ALLIGN flag set, the
	 * function is called before padding the file.
	 *
	 * The function may modify the block itself or write data to the file.
	 * which is taken into account when padding the file.
	 *
	 * @param user A user pointer.
	 * @param block The block that is about to be written.
	 * @param file The file that the block will be written to.
	 */
	void (*pre_block_write)(void *user, sqfs_block_t *block,
				sqfs_file_t *file);

	/**
	 * @brief Gets called after writing a block to disk.
	 *
	 * If this is not NULL, it gets called after a block is written to
	 * disk. If the block has the @ref SQFS_BLK_ALLIGN flag set, the
	 * function is called before padding the file.
	 *
	 * Modifying the block is rather pointless, but the function may
	 * write data to the file which is taken into account when padding
	 * the file.
	 *
	 * @param user A user pointer.
	 * @param block The block that is about to be written.
	 * @param file The file that the block was written to.
	 */
	void (*post_block_write)(void *user, const sqfs_block_t *block,
				 sqfs_file_t *file);

	/**
	 * @brief Gets called before storing a fragment in a fragment block.
	 *
	 * The function can modify the block before it is stored.
	 *
	 * @param user A user pointer.
	 * @param block The data chunk that is about to be merged into the
	 *              fragment block.
	 */
	void (*pre_fragment_store)(void *user, sqfs_block_t *block);

	/**
	 * @brief Gets called if block deduplication managed to get
	 *        rid of the data blocks of a file.
	 *
	 * @param user A user pointer.
	 * @param count The number of blocks that have been erased.
	 * @param bytes The number of bytes that have been erased. Includes
	 *              potential padding before and after the end.
	 */
	void (*notify_blocks_erased)(void *user, size_t count,
				     sqfs_u64 bytes);

	/**
	 * @brief Gets called before throwing away a fragment that turned out
	 *        to be a duplicate.
	 *
	 * @param user A user pointer.
	 * @param block The data chunk that is about to be merged into the
	 *              fragment block.
	 */
	void (*notify_fragment_discard)(void *user, const sqfs_block_t *block);

	/**
	 * @brief Gets called before writing a block of padding bytes to disk.
	 *
	 * @param user A user pointer.
	 * @param block The padding bytes that are about to be written.
	 * @param count The number of padding bytes in the block.
	 */
	void (*prepare_padding)(void *user, sqfs_u8 *block, size_t count);
};

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Create a data block writer.
 *
 * @memberof sqfs_data_writer_t
 *
 * @param max_block_size The maximum size of a data block. Required for the
 *                       internal scratch buffer used for compressing data.
 * @param cmp A pointer to a compressor. If multiple worker threads are used,
 *            the deep copy function of the compressor is used to create
 *            several instances that don't interfere with each other.
 * @param num_workers The number of worker threads to create.
 * @param max_backlog The maximum number of blocks currently in flight. When
 *                    trying to add more, enqueueing blocks until the in-flight
 *                    block count drops below the threshold.
 * @param devblksz File can optionally be allgined to device block size. This
 *                 specifies the desired allignment.
 * @param file The output file to write the finished blocks to.
 *
 * @return A pointer to a data writer object on success, NULL on allocation
 *         failure or on failure to create and initialize the worker threads.
 */
SQFS_API
sqfs_data_writer_t *sqfs_data_writer_create(size_t max_block_size,
					    sqfs_compressor_t *cmp,
					    unsigned int num_workers,
					    size_t max_backlog,
					    size_t devblksz,
					    sqfs_file_t *file);

/**
 * @brief Destroy a data writer and free all memory used by it.
 *
 * @memberof sqfs_data_writer_t
 *
 * @param proc A pointer to a data writer object.
 */
SQFS_API void sqfs_data_writer_destroy(sqfs_data_writer_t *proc);

/**
 * @brief Start writing a file.
 *
 * @memberof sqfs_data_writer_t
 *
 * After calling this function, call @ref sqfs_data_writer_append repeatedly to
 * add data to the file. Finally call @ref sqfs_data_writer_end_file when you
 * are done. After writing all files, use @ref sqfs_data_writer_finish to wait
 * until all blocks that are still in flight are done and written to disk.
 *
 * The specified inode pointer is kept internally and updated with the
 * compressed block sizes and final destinations of the file and possible
 * fragment. You need to make sure it has enough backing-store for all blocks
 * to come. Furthermore, since there can still be blocks in-flight even after
 * calling @ref sqfs_data_writer_end_file, the data in the inode may still
 * change. The only point at which the data writer is guarnteed to not touch
 * them anymore is after @ref sqfs_data_writer_finish has returned.
 *
 * @param proc A pointer to a data writer object.
 * @param inode The regular file inode representing the file. The data writer
 *              internally updates it while writing blocks to disk.
 * @param flags A combination of @ref E_SQFS_BLK_FLAGS that can be used to
 *              micro manage how the data is processed.
 *
 * @return Zero on success, an @ref E_SQFS_ERROR value on failure.
 */
SQFS_API int sqfs_data_writer_begin_file(sqfs_data_writer_t *proc,
					 sqfs_inode_generic_t *inode,
					 sqfs_u32 flags);

/**
 * @brief Append data to the current file.
 *
 * @memberof sqfs_data_writer_t
 *
 * Call this after @ref sqfs_data_writer_begin_file to add data to a file.
 *
 * @param proc A pointer to a data writer object.
 * @param data A pointer to a buffer to read data from.
 * @param size How many bytes should be copied out of the given
 *             buffer and written to disk.
 *
 * @return Zero on success, an @ref E_SQFS_ERROR value on failure.
 */
SQFS_API int sqfs_data_writer_append(sqfs_data_writer_t *proc,
				     const void *data, size_t size);

/**
 * @brief Stop writing the current file and flush everything that is
 *        buffered internally.
 *
 * @memberof sqfs_data_writer_t
 *
 * The counter part to @ref sqfs_data_writer_begin_file.
 *
 * Even after calling this, there might still be data blocks in-flight.
 * Use @ref sqfs_data_writer_finish when you are done writing files to force
 * the remaining blocks to be processed and written to disk.
 *
 * @param proc A pointer to a data writer object.
 *
 * @return Zero on success, an @ref E_SQFS_ERROR value on failure.
 */
SQFS_API int sqfs_data_writer_end_file(sqfs_data_writer_t *proc);

/**
 * @brief Wait for the in-flight data blocks to finish and finally flush the
 *        current fragment block.
 *
 * @memberof sqfs_data_writer_t
 *
 * @param proc A pointer to a block processor object.
 *
 * @return Zero on success, an @ref E_SQFS_ERROR value on failure. The failure
 *         return value can either be an error encountered during enqueueing,
 *         processing or writing to disk.
 */
SQFS_API int sqfs_data_writer_finish(sqfs_data_writer_t *proc);

/**
 * @brief Write the completed fragment table to disk.
 *
 * @memberof sqfs_data_writer_t
 *
 * Call this after producing the inode and directory table to generate
 * the fragment table for the squashfs image.
 *
 * @param proc A pointer to a data writer object.
 * @param super A pointer to a super block to write information about the
 *              fragment table to.
 *
 * @return Zero on success, an @ref E_SQFS_ERROR value on failure.
 */
SQFS_API
int sqfs_data_writer_write_fragment_table(sqfs_data_writer_t *proc,
					  sqfs_super_t *super);

/**
 * @brief Register a set of hooks to be invoked when writing blocks to disk.
 *
 * @memberof sqfs_data_writer_t
 *
 * @param proc A pointer to a data writer object.
 * @param user_ptr A user pointer to pass to the callbacks.
 * @param hooks A structure containing the hooks.
 *
 * @return Zero on success, @ref SQFS_ERROR_UNSUPPORTED if the size field of
 *         the hooks doesn't match any size knwon to the library.
 */
SQFS_API
int sqfs_data_writer_set_hooks(sqfs_data_writer_t *proc, void *user_ptr,
			       const sqfs_block_hooks_t *hooks);

#ifdef __cplusplus
}
#endif

#endif /* SFQS_DATA_WRITER_H */