fastboot: allow format on devices with small buffers
Formatting large partitions on devices with small transfer buffers did not work before since format used a strange path through the code to send data. It now uses the normal path. Also cleaned up a bit. FS code now lives in a separate file and the custom path for format is gone. Change-Id: If4e01cabc2e250b7c02ca7ce8c268e51d49e1529
This commit is contained in:
parent
9341262285
commit
0d4a4cb8aa
6 changed files with 149 additions and 209 deletions
|
|
@ -18,7 +18,7 @@ include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
|
||||||
$(LOCAL_PATH)/../../extras/ext4_utils
|
$(LOCAL_PATH)/../../extras/ext4_utils
|
||||||
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c
|
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c
|
||||||
LOCAL_MODULE := fastboot
|
LOCAL_MODULE := fastboot
|
||||||
LOCAL_MODULE_TAGS := debug
|
LOCAL_MODULE_TAGS := debug
|
||||||
LOCAL_CFLAGS += -std=gnu99
|
LOCAL_CFLAGS += -std=gnu99
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fastboot.h"
|
#include "fastboot.h"
|
||||||
#include "make_ext4fs.h"
|
#include "fs.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -51,9 +51,8 @@
|
||||||
#define OP_COMMAND 2
|
#define OP_COMMAND 2
|
||||||
#define OP_QUERY 3
|
#define OP_QUERY 3
|
||||||
#define OP_NOTICE 4
|
#define OP_NOTICE 4
|
||||||
#define OP_FORMAT 5
|
#define OP_DOWNLOAD_SPARSE 5
|
||||||
#define OP_DOWNLOAD_SPARSE 6
|
#define OP_WAIT_FOR_DISCONNECT 6
|
||||||
#define OP_WAIT_FOR_DISCONNECT 7
|
|
||||||
|
|
||||||
typedef struct Action Action;
|
typedef struct Action Action;
|
||||||
|
|
||||||
|
|
@ -79,14 +78,7 @@ static Action *action_list = 0;
|
||||||
static Action *action_last = 0;
|
static Action *action_last = 0;
|
||||||
|
|
||||||
|
|
||||||
struct image_data {
|
|
||||||
long long partition_size;
|
|
||||||
long long image_size; // real size of image file
|
|
||||||
void *buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
void generate_ext4_image(struct image_data *image);
|
|
||||||
void cleanup_image(struct image_data *image);
|
|
||||||
|
|
||||||
int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
|
int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
|
@ -102,24 +94,6 @@ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
|
||||||
return fb_command_response(usb, cmd, response);
|
return fb_command_response(usb, cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct generator {
|
|
||||||
char *fs_type;
|
|
||||||
|
|
||||||
/* generate image and return it as image->buffer.
|
|
||||||
* size of the buffer returned as image->image_size.
|
|
||||||
*
|
|
||||||
* image->partition_size specifies what is the size of the
|
|
||||||
* file partition we generate image for.
|
|
||||||
*/
|
|
||||||
void (*generate)(struct image_data *image);
|
|
||||||
|
|
||||||
/* it cleans the buffer allocated during image creation.
|
|
||||||
* this function probably does free() or munmap().
|
|
||||||
*/
|
|
||||||
void (*cleanup)(struct image_data *image);
|
|
||||||
} generators[] = {
|
|
||||||
{ "ext4", generate_ext4_image, cleanup_image }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Return true if this partition is supported by the fastboot format command.
|
/* Return true if this partition is supported by the fastboot format command.
|
||||||
* It is also used to determine if we should first erase a partition before
|
* It is also used to determine if we should first erase a partition before
|
||||||
|
|
@ -130,8 +104,7 @@ struct generator {
|
||||||
*/
|
*/
|
||||||
int fb_format_supported(usb_handle *usb, const char *partition)
|
int fb_format_supported(usb_handle *usb, const char *partition)
|
||||||
{
|
{
|
||||||
char response[FB_RESPONSE_SZ+1];
|
char response[FB_RESPONSE_SZ + 1] = {0,};
|
||||||
struct generator *generator = NULL;
|
|
||||||
int status;
|
int status;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
|
@ -140,18 +113,7 @@ int fb_format_supported(usb_handle *usb, const char *partition)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(generators); i++) {
|
return !!fs_get_generator(response);
|
||||||
if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
|
|
||||||
generator = &generators[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generator) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cb_default(Action *a, int status, char *resp)
|
static int cb_default(Action *a, int status, char *resp)
|
||||||
|
|
@ -205,163 +167,6 @@ void fb_queue_erase(const char *ptn)
|
||||||
a->msg = mkmsg("erasing '%s'", ptn);
|
a->msg = mkmsg("erasing '%s'", ptn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loads file content into buffer. Returns NULL on error. */
|
|
||||||
static void *load_buffer(int fd, off_t size)
|
|
||||||
{
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
#ifdef USE_MINGW
|
|
||||||
ssize_t count = 0;
|
|
||||||
|
|
||||||
// mmap is more efficient but mingw does not support it.
|
|
||||||
// In this case we read whole image into memory buffer.
|
|
||||||
buffer = malloc(size);
|
|
||||||
if (!buffer) {
|
|
||||||
perror("malloc");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
while(count < size) {
|
|
||||||
ssize_t actually_read = read(fd, (char*)buffer+count, size-count);
|
|
||||||
|
|
||||||
if (actually_read == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (actually_read < 0) {
|
|
||||||
if (errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
perror("read");
|
|
||||||
free(buffer);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
count += actually_read;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
if (buffer == MAP_FAILED) {
|
|
||||||
perror("mmap");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_image(struct image_data *image)
|
|
||||||
{
|
|
||||||
#ifdef USE_MINGW
|
|
||||||
free(image->buffer);
|
|
||||||
#else
|
|
||||||
munmap(image->buffer, image->image_size);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void generate_ext4_image(struct image_data *image)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
fd = fileno(tmpfile());
|
|
||||||
make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL);
|
|
||||||
|
|
||||||
fstat(fd, &st);
|
|
||||||
image->image_size = st.st_size;
|
|
||||||
image->buffer = load_buffer(fd, st.st_size);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported)
|
|
||||||
{
|
|
||||||
const char *partition = a->cmd;
|
|
||||||
char response[FB_RESPONSE_SZ+1];
|
|
||||||
int status = 0;
|
|
||||||
struct image_data image;
|
|
||||||
struct generator *generator = NULL;
|
|
||||||
int fd;
|
|
||||||
unsigned i;
|
|
||||||
char cmd[CMD_SIZE];
|
|
||||||
|
|
||||||
status = fb_getvar(usb, response, "partition-type:%s", partition);
|
|
||||||
if (status) {
|
|
||||||
if (skip_if_not_supported) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Erase successful, but not automatically formatting.\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"Can't determine partition type.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fprintf(stderr,"FAILED (%s)\n", fb_get_error());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(generators); i++) {
|
|
||||||
if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
|
|
||||||
generator = &generators[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!generator) {
|
|
||||||
if (skip_if_not_supported) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Erase successful, but not automatically formatting.\n");
|
|
||||||
fprintf(stderr,
|
|
||||||
"File system type %s not supported.\n", response);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n",
|
|
||||||
response);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = fb_getvar(usb, response, "partition-size:%s", partition);
|
|
||||||
if (status) {
|
|
||||||
if (skip_if_not_supported) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Erase successful, but not automatically formatting.\n");
|
|
||||||
fprintf(stderr, "Unable to get partition size\n.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
fprintf(stderr,"FAILED (%s)\n", fb_get_error());
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
image.partition_size = strtoll(response, (char **)NULL, 16);
|
|
||||||
|
|
||||||
generator->generate(&image);
|
|
||||||
if (!image.buffer) {
|
|
||||||
fprintf(stderr,"Cannot generate image.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Following piece of code is similar to fb_queue_flash() but executes
|
|
||||||
// actions directly without queuing
|
|
||||||
fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024);
|
|
||||||
status = fb_download_data(usb, image.buffer, image.image_size);
|
|
||||||
if (status) goto cleanup;
|
|
||||||
|
|
||||||
fprintf(stderr, "writing '%s'...\n", partition);
|
|
||||||
snprintf(cmd, sizeof(cmd), "flash:%s", partition);
|
|
||||||
status = fb_command(usb, cmd);
|
|
||||||
if (status) goto cleanup;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
generator->cleanup(&image);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fb_queue_format(const char *partition, int skip_if_not_supported)
|
|
||||||
{
|
|
||||||
Action *a;
|
|
||||||
|
|
||||||
a = queue_action(OP_FORMAT, partition);
|
|
||||||
a->data = (void*)skip_if_not_supported;
|
|
||||||
a->msg = mkmsg("formatting '%s' partition", partition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fb_queue_flash(const char *ptn, void *data, unsigned sz)
|
void fb_queue_flash(const char *ptn, void *data, unsigned sz)
|
||||||
{
|
{
|
||||||
Action *a;
|
Action *a;
|
||||||
|
|
@ -589,10 +394,6 @@ int fb_execute_queue(usb_handle *usb)
|
||||||
if (status) break;
|
if (status) break;
|
||||||
} else if (a->op == OP_NOTICE) {
|
} else if (a->op == OP_NOTICE) {
|
||||||
fprintf(stderr,"%s\n",(char*)a->data);
|
fprintf(stderr,"%s\n",(char*)a->data);
|
||||||
} else if (a->op == OP_FORMAT) {
|
|
||||||
status = fb_format(a, usb, (int)a->data);
|
|
||||||
status = a->func(a, status, status ? fb_get_error() : "");
|
|
||||||
if (status) break;
|
|
||||||
} else if (a->op == OP_DOWNLOAD_SPARSE) {
|
} else if (a->op == OP_DOWNLOAD_SPARSE) {
|
||||||
status = fb_download_data_sparse(usb, a->data);
|
status = fb_download_data_sparse(usb, a->data);
|
||||||
status = a->func(a, status, status ? fb_get_error() : "");
|
status = a->func(a, status, status ? fb_get_error() : "");
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
#include <zipfile/zipfile.h>
|
#include <zipfile/zipfile.h>
|
||||||
|
|
||||||
#include "fastboot.h"
|
#include "fastboot.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
#ifndef O_BINARY
|
#ifndef O_BINARY
|
||||||
#define O_BINARY 0
|
#define O_BINARY 0
|
||||||
|
|
@ -622,10 +623,13 @@ static int load_buf_fd(usb_handle *usb, int fd,
|
||||||
void *data;
|
void *data;
|
||||||
int64_t limit;
|
int64_t limit;
|
||||||
|
|
||||||
|
|
||||||
sz64 = file_size(fd);
|
sz64 = file_size(fd);
|
||||||
if (sz64 < 0) {
|
if (sz64 < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
limit = get_sparse_limit(usb, sz64);
|
limit = get_sparse_limit(usb, sz64);
|
||||||
if (limit) {
|
if (limit) {
|
||||||
struct sparse_file **s = load_sparse_files(fd, limit);
|
struct sparse_file **s = load_sparse_files(fd, limit);
|
||||||
|
|
@ -872,6 +876,73 @@ static int64_t parse_num(const char *arg)
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fb_perform_format(const char *partition, int skip_if_not_supported)
|
||||||
|
{
|
||||||
|
char pType[FB_RESPONSE_SZ + 1], pSize[FB_RESPONSE_SZ + 1];
|
||||||
|
unsigned int limit = INT_MAX;
|
||||||
|
struct fastboot_buffer buf;
|
||||||
|
const char *errMsg = NULL;
|
||||||
|
const struct fs_generator *gen;
|
||||||
|
uint64_t pSz;
|
||||||
|
int status;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (target_sparse_limit > 0 && target_sparse_limit < limit)
|
||||||
|
limit = target_sparse_limit;
|
||||||
|
if (sparse_limit > 0 && sparse_limit < limit)
|
||||||
|
limit = sparse_limit;
|
||||||
|
|
||||||
|
status = fb_getvar(usb, pType, "partition-type:%s", partition);
|
||||||
|
if (status) {
|
||||||
|
errMsg = "Can't determine partition type.\n";
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = fb_getvar(usb, pSize, "partition-size:%s", partition);
|
||||||
|
if (status) {
|
||||||
|
errMsg = "Unable to get partition size\n";
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
gen = fs_get_generator(pType);
|
||||||
|
if (!gen) {
|
||||||
|
if (skip_if_not_supported) {
|
||||||
|
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
|
||||||
|
fprintf(stderr, "File system type %s not supported.\n", pType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pSz = strtoll(pSize, (char **)NULL, 16);
|
||||||
|
|
||||||
|
fd = fileno(tmpfile());
|
||||||
|
if (fs_generator_generate(gen, fd, pSz)) {
|
||||||
|
close(fd);
|
||||||
|
fprintf(stderr, "Cannot generate image.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_buf_fd(usb, fd, &buf)) {
|
||||||
|
fprintf(stderr, "Cannot read image.\n");
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flash_buf(partition, &buf);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
failed:
|
||||||
|
if (skip_if_not_supported) {
|
||||||
|
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
|
||||||
|
if (errMsg)
|
||||||
|
fprintf(stderr, "%s", errMsg);
|
||||||
|
}
|
||||||
|
fprintf(stderr,"FAILED (%s)\n", fb_get_error());
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int wants_wipe = 0;
|
int wants_wipe = 0;
|
||||||
|
|
@ -1000,7 +1071,7 @@ int main(int argc, char **argv)
|
||||||
if (erase_first && needs_erase(argv[1])) {
|
if (erase_first && needs_erase(argv[1])) {
|
||||||
fb_queue_erase(argv[1]);
|
fb_queue_erase(argv[1]);
|
||||||
}
|
}
|
||||||
fb_queue_format(argv[1], 0);
|
fb_perform_format(argv[1], 0);
|
||||||
skip(2);
|
skip(2);
|
||||||
} else if(!strcmp(*argv, "signature")) {
|
} else if(!strcmp(*argv, "signature")) {
|
||||||
require(2);
|
require(2);
|
||||||
|
|
@ -1088,9 +1159,9 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
if (wants_wipe) {
|
if (wants_wipe) {
|
||||||
fb_queue_erase("userdata");
|
fb_queue_erase("userdata");
|
||||||
fb_queue_format("userdata", 1);
|
fb_perform_format("userdata", 1);
|
||||||
fb_queue_erase("cache");
|
fb_queue_erase("cache");
|
||||||
fb_queue_format("cache", 1);
|
fb_perform_format("cache", 1);
|
||||||
}
|
}
|
||||||
if (wants_reboot) {
|
if (wants_reboot) {
|
||||||
fb_queue_reboot();
|
fb_queue_reboot();
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ int fb_format_supported(usb_handle *usb, const char *partition);
|
||||||
void fb_queue_flash(const char *ptn, void *data, unsigned sz);
|
void fb_queue_flash(const char *ptn, void *data, unsigned sz);
|
||||||
void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
|
void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
|
||||||
void fb_queue_erase(const char *ptn);
|
void fb_queue_erase(const char *ptn);
|
||||||
void fb_queue_format(const char *ptn, int skip_if_not_supported);
|
void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz);
|
||||||
void fb_queue_require(const char *prod, const char *var, int invert,
|
void fb_queue_require(const char *prod, const char *var, int invert,
|
||||||
unsigned nvalues, const char **value);
|
unsigned nvalues, const char **value);
|
||||||
void fb_queue_display(const char *var, const char *prettyname);
|
void fb_queue_display(const char *var, const char *prettyname);
|
||||||
|
|
|
||||||
56
fastboot/fs.c
Normal file
56
fastboot/fs.c
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "fastboot.h"
|
||||||
|
#include "make_ext4fs.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sparse/sparse.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef USE_MINGW
|
||||||
|
#include <fcntl.h>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int generate_ext4_image(int fd, long long partSize)
|
||||||
|
{
|
||||||
|
make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_generator {
|
||||||
|
|
||||||
|
char *fs_type; //must match what fastboot reports for partition type
|
||||||
|
int (*generate)(int fd, long long partSize); //returns 0 or error value
|
||||||
|
|
||||||
|
} generators[] = {
|
||||||
|
|
||||||
|
{ "ext4", generate_ext4_image}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct fs_generator* fs_get_generator(const char* name)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(generators) / sizeof(*generators); i++)
|
||||||
|
if (!strcmp(generators[i].fs_type, name))
|
||||||
|
return generators + i;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
|
||||||
|
{
|
||||||
|
return gen->generate(tmpFileNo, partSize);
|
||||||
|
}
|
||||||
12
fastboot/fs.h
Normal file
12
fastboot/fs.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _FS_H_
|
||||||
|
#define _FH_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct fs_generator;
|
||||||
|
|
||||||
|
const struct fs_generator* fs_get_generator(const char* name);
|
||||||
|
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue