mirror of
https://github.com/urbit/shrub.git
synced 2024-12-24 20:47:27 +03:00
630 lines
15 KiB
C
630 lines
15 KiB
C
|
/* vere/foil.c
|
||
|
**
|
||
|
** This file is in the public domain.
|
||
|
*/
|
||
|
|
||
|
#include "all.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <unistd.h>
|
||
|
#include <setjmp.h>
|
||
|
#include <gmp.h>
|
||
|
#include <dirent.h>
|
||
|
#include <stdint.h>
|
||
|
#include <uv.h>
|
||
|
#include <termios.h>
|
||
|
#include <term.h>
|
||
|
#include <errno.h>
|
||
|
#include <libgen.h>
|
||
|
#include <ftw.h>
|
||
|
|
||
|
#include "vere/vere.h"
|
||
|
|
||
|
/* assumptions:
|
||
|
** all measurements are in chubs (double-words, c3_d, uint64_t).
|
||
|
** little-endian addressing is ASSUMED.
|
||
|
**
|
||
|
** framing:
|
||
|
** the last two chubs of a frame:
|
||
|
**
|
||
|
** {
|
||
|
** 64-bit frame length
|
||
|
** {
|
||
|
** (high 32 bits) mug of frame
|
||
|
** (low 32 bits) mug of current address
|
||
|
** }
|
||
|
** }
|
||
|
**
|
||
|
** we can scan for one of these frames with very low probability
|
||
|
** of a false positive. we always write to and read from the end
|
||
|
** of a file. a frame position points to its end.
|
||
|
**
|
||
|
** protocol:
|
||
|
** once the callback is called, all results are fully fsynced.
|
||
|
** all callbacks are optional and can be passed 0.
|
||
|
*/
|
||
|
|
||
|
/* _foil_fail(): fail with error.
|
||
|
*/
|
||
|
static void
|
||
|
_foil_fail(const c3_c* why_c, c3_i err_i)
|
||
|
{
|
||
|
if ( err_i ) {
|
||
|
fprintf(stderr, "%s: error: %s\r\n", why_c, uv_strerror(err_i));
|
||
|
c3_assert(0);
|
||
|
} else {
|
||
|
fprintf(stderr, "%s: file error\r\n", why_c);
|
||
|
}
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* _foil_close(): close file, blockingly.
|
||
|
*/
|
||
|
static void
|
||
|
_foil_close(uv_file fil_f)
|
||
|
{
|
||
|
c3_i err_i;
|
||
|
uv_fs_t ruq_u;
|
||
|
|
||
|
if ( 0 != (err_i = uv_fs_close(u3L, &ruq_u, fil_f, 0)) ) {
|
||
|
_foil_fail("uv_fs_close", err_i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* _foil_path(): allocate path.
|
||
|
*/
|
||
|
static c3_c*
|
||
|
_foil_path(u3_dire* dir_u,
|
||
|
const c3_c* nam_c)
|
||
|
{
|
||
|
c3_w len_w = strlen(dir_u->pax_c);
|
||
|
c3_c* pax_c;
|
||
|
|
||
|
pax_c = c3_malloc(1 + len_w + 1 + strlen(nam_c));
|
||
|
strcpy(pax_c, dir_u->pax_c);
|
||
|
pax_c[len_w] = '/';
|
||
|
strcpy(pax_c + len_w + 1, nam_c);
|
||
|
|
||
|
return pax_c;
|
||
|
}
|
||
|
|
||
|
/* u3_foil_folder(): load directory, blockingly. null if nonexistent.
|
||
|
*/
|
||
|
u3_dire*
|
||
|
u3_foil_folder(const c3_c* pax_c)
|
||
|
{
|
||
|
u3_dire* dir_u;
|
||
|
uv_fs_t ruq_u;
|
||
|
uv_dirent_t den_u;
|
||
|
c3_i err_i;
|
||
|
|
||
|
/* open directory, synchronously
|
||
|
*/
|
||
|
{
|
||
|
err_i = uv_fs_scandir(u3L, &ruq_u, pax_c, 0, 0);
|
||
|
|
||
|
if ( err_i < 0 ) {
|
||
|
if ( UV_ENOENT != err_i ) {
|
||
|
_foil_fail(pax_c, err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
if ( 0 != (err_i = uv_fs_mkdir(u3L, &ruq_u, pax_c, 0700, 0)) ) {
|
||
|
_foil_fail(pax_c, err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
return u3_foil_folder(pax_c);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
dir_u = c3_malloc(sizeof *dir_u);
|
||
|
dir_u->all_u = 0;
|
||
|
dir_u->pax_c = c3_malloc(1 + strlen(pax_c));
|
||
|
strcpy(dir_u->pax_c, pax_c);
|
||
|
}
|
||
|
|
||
|
/* create entries for all files
|
||
|
*/
|
||
|
while ( UV_EOF != uv_fs_scandir_next(&ruq_u, &den_u) ) {
|
||
|
if ( UV_DIRENT_FILE == den_u.type ) {
|
||
|
u3_dent* det_u = c3_malloc(sizeof(*det_u));
|
||
|
|
||
|
det_u->nam_c = c3_malloc(1 + strlen(den_u.name));
|
||
|
strcpy(det_u->nam_c, den_u.name);
|
||
|
|
||
|
det_u->nex_u = dir_u->all_u;
|
||
|
dir_u->all_u = det_u;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* clean up request
|
||
|
*/
|
||
|
{
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
}
|
||
|
|
||
|
/* open directory file for reading, to fsync
|
||
|
*/
|
||
|
{
|
||
|
if ( 0 > (err_i = uv_fs_open(u3L,
|
||
|
&ruq_u,
|
||
|
pax_c,
|
||
|
O_RDONLY,
|
||
|
0600,
|
||
|
0)) )
|
||
|
{
|
||
|
_foil_fail("open directory", err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
dir_u->fil_u = ruq_u.result;
|
||
|
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
}
|
||
|
return dir_u;
|
||
|
}
|
||
|
|
||
|
/* u3_foil_create(): create a new, empty, open file, not syncing.
|
||
|
*/
|
||
|
struct _foil_create_request {
|
||
|
uv_fs_t ruq_u;
|
||
|
void (*fun_f)(void*, u3_foil*);
|
||
|
void* vod_p;
|
||
|
u3_dire* dir_u;
|
||
|
c3_c* nam_c;
|
||
|
c3_c* pax_c;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
_foil_create_cb(uv_fs_t* ruq_u)
|
||
|
{
|
||
|
struct _foil_create_request* req_u = (void *)ruq_u;
|
||
|
u3_foil* fol_u;
|
||
|
|
||
|
fol_u = c3_malloc(sizeof(*fol_u));
|
||
|
fol_u->fil_u = ruq_u->result;
|
||
|
fol_u->dir_u = req_u->dir_u;
|
||
|
fol_u->nam_c = req_u->nam_c;
|
||
|
fol_u->end_d = 0;
|
||
|
|
||
|
req_u->fun_f(req_u->vod_p, fol_u);
|
||
|
|
||
|
c3_free(req_u->pax_c);
|
||
|
uv_fs_req_cleanup(ruq_u);
|
||
|
c3_free(req_u);
|
||
|
}
|
||
|
void
|
||
|
u3_foil_create(void (*fun_f)(void*, // context pointer
|
||
|
u3_foil*),// file object
|
||
|
void* vod_p, // context pointer
|
||
|
u3_dire* dir_u, // directory
|
||
|
const c3_c* nam_c) // name of new file
|
||
|
{
|
||
|
c3_c* pax_c;
|
||
|
c3_i err_i;
|
||
|
|
||
|
/* construct full path
|
||
|
*/
|
||
|
pax_c = _foil_path(dir_u, nam_c);
|
||
|
|
||
|
/* perform create
|
||
|
*/
|
||
|
{
|
||
|
struct _foil_create_request* req_u;
|
||
|
|
||
|
req_u = c3_malloc(sizeof(*req_u));
|
||
|
|
||
|
req_u->fun_f = fun_f;
|
||
|
req_u->vod_p = vod_p;
|
||
|
req_u->dir_u = dir_u;
|
||
|
req_u->nam_c = c3_malloc(1 + strlen(nam_c));
|
||
|
strcpy(req_u->nam_c, nam_c);
|
||
|
req_u->pax_c = pax_c;
|
||
|
|
||
|
if ( 0 != (err_i = uv_fs_open(u3L,
|
||
|
&req_u->ruq_u,
|
||
|
pax_c,
|
||
|
O_CREAT | O_WRONLY,
|
||
|
0600,
|
||
|
_foil_create_cb)) )
|
||
|
{
|
||
|
_foil_fail("uv_fs_open", err_i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* u3_foil_absorb(): open logfile, truncating to last good frame; blocking.
|
||
|
*/
|
||
|
u3_foil*
|
||
|
u3_foil_absorb(u3_dire* dir_u, // directory
|
||
|
c3_c* nam_c) // filename
|
||
|
{
|
||
|
u3_foil* fol_u;
|
||
|
uv_fs_t ruq_u;
|
||
|
c3_i err_i;
|
||
|
|
||
|
/* open file and create wrapper
|
||
|
*/
|
||
|
{
|
||
|
c3_c* pax_c = _foil_path(dir_u, nam_c);
|
||
|
|
||
|
if ( 0 > (err_i = uv_fs_open(u3L,
|
||
|
&ruq_u,
|
||
|
pax_c,
|
||
|
O_RDWR | O_CREAT,
|
||
|
0600,
|
||
|
0)) )
|
||
|
{
|
||
|
_foil_fail(pax_c, err_i);
|
||
|
c3_free(pax_c);
|
||
|
return 0;
|
||
|
}
|
||
|
c3_free(pax_c);
|
||
|
|
||
|
fol_u = c3_malloc(sizeof(*fol_u));
|
||
|
fol_u->dir_u = dir_u;
|
||
|
fol_u->fil_u = ruq_u.result;
|
||
|
fol_u->nam_c = c3_malloc(1 + strlen(nam_c));
|
||
|
strcpy(fol_u->nam_c, nam_c);
|
||
|
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
}
|
||
|
|
||
|
/* measure file
|
||
|
*/
|
||
|
{
|
||
|
if ( 0 != (err_i = uv_fs_fstat(u3L, &ruq_u, fol_u->fil_u, 0)) ) {
|
||
|
_foil_fail("uv_fs_fstat", err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
if ( 0 != (7 & ruq_u.statbuf.st_size) ) {
|
||
|
_foil_fail("logfile size corrupt", 0);
|
||
|
return 0;
|
||
|
}
|
||
|
fol_u->end_d = (ruq_u.statbuf.st_size >> 3ULL);
|
||
|
}
|
||
|
|
||
|
/* XX: scan for good frame.
|
||
|
*/
|
||
|
return fol_u;
|
||
|
}
|
||
|
|
||
|
/* u3_foil_delete(): delete a file; free descriptor.
|
||
|
*/
|
||
|
struct _foil_delete_request {
|
||
|
uv_fs_t ruq_u;
|
||
|
void (*fun_f)(void*);
|
||
|
void* vod_p;
|
||
|
u3_foil* fol_u;
|
||
|
c3_c* pax_c;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
_foil_delete_cb(uv_fs_t* ruq_u)
|
||
|
{
|
||
|
struct _foil_delete_request* req_u = (void *)ruq_u;
|
||
|
|
||
|
if ( req_u->fun_f ) {
|
||
|
req_u->fun_f(req_u->vod_p);
|
||
|
}
|
||
|
|
||
|
c3_free(req_u->pax_c);
|
||
|
c3_free(req_u->fol_u->nam_c);
|
||
|
c3_free(req_u->fol_u);
|
||
|
|
||
|
uv_fs_req_cleanup(ruq_u);
|
||
|
c3_free(req_u);
|
||
|
}
|
||
|
void
|
||
|
u3_foil_delete(void (*fun_f)(void*), // context pointer
|
||
|
void* vod_p, // context pointer
|
||
|
u3_foil* fol_u) // file to delete
|
||
|
{
|
||
|
c3_i err_i;
|
||
|
c3_c* pax_c;
|
||
|
|
||
|
/* construct full path
|
||
|
*/
|
||
|
pax_c = _foil_path(fol_u->dir_u, fol_u->nam_c);
|
||
|
|
||
|
/* perform delete
|
||
|
*/
|
||
|
{
|
||
|
struct _foil_delete_request* req_u;
|
||
|
|
||
|
req_u = c3_malloc(sizeof(*req_u));
|
||
|
|
||
|
req_u->fun_f = fun_f;
|
||
|
req_u->vod_p = vod_p;
|
||
|
req_u->fol_u = fol_u;
|
||
|
req_u->pax_c = pax_c;
|
||
|
|
||
|
if ( 0 != (err_i = uv_fs_unlink(u3L,
|
||
|
&req_u->ruq_u,
|
||
|
pax_c,
|
||
|
_foil_delete_cb)) )
|
||
|
{
|
||
|
_foil_fail("uv_fs_unlink", err_i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* u3_foil_append(): write a frame at the end of a file, freeing the buffer.
|
||
|
*/
|
||
|
struct _foil_append_request {
|
||
|
uv_fs_t ruq_u;
|
||
|
void (*fun_f)(void*);
|
||
|
void* vod_p;
|
||
|
u3_foil* fol_u;
|
||
|
c3_d* fam_d;
|
||
|
c3_d* buf_d;
|
||
|
};
|
||
|
static void
|
||
|
_foil_append_cb_2(uv_fs_t* ruq_u)
|
||
|
{
|
||
|
struct _foil_append_request* req_u = (void*) ruq_u;
|
||
|
|
||
|
req_u->fun_f(req_u->vod_p);
|
||
|
uv_fs_req_cleanup(ruq_u);
|
||
|
c3_free(req_u);
|
||
|
}
|
||
|
static void
|
||
|
_foil_append_cb_1(uv_fs_t* ruq_u)
|
||
|
{
|
||
|
struct _foil_append_request* req_u = (void*) ruq_u;
|
||
|
|
||
|
uv_fs_req_cleanup(ruq_u);
|
||
|
c3_free(req_u->buf_d);
|
||
|
|
||
|
uv_fs_fsync(u3L, &req_u->ruq_u,
|
||
|
req_u->fol_u->fil_u,
|
||
|
_foil_append_cb_2);
|
||
|
}
|
||
|
void
|
||
|
u3_foil_append(void (*fun_f)(void*), // context pointer
|
||
|
void* vod_p, // context pointer
|
||
|
u3_foil* fol_u, // file
|
||
|
c3_d* buf_d, // buffer to write from
|
||
|
c3_d len_d) // length in chubs
|
||
|
{
|
||
|
c3_d pos_d = fol_u->end_d;
|
||
|
struct _foil_append_request* req_u;
|
||
|
c3_i err_i;
|
||
|
|
||
|
/* set up request
|
||
|
*/
|
||
|
{
|
||
|
req_u = c3_malloc(sizeof(*req_u));
|
||
|
req_u->fun_f = fun_f;
|
||
|
req_u->vod_p = vod_p;
|
||
|
req_u->fol_u = fol_u;
|
||
|
req_u->buf_d = buf_d;
|
||
|
req_u->fam_d = c3_malloc(16);
|
||
|
}
|
||
|
|
||
|
/* framing
|
||
|
*/
|
||
|
{
|
||
|
c3_w top_w, bot_w;
|
||
|
|
||
|
fol_u->end_d = pos_d + len_d + 2;
|
||
|
|
||
|
/* XX: assumes "little-endian won", 32-bit frame length.
|
||
|
*/
|
||
|
top_w = u3r_mug_words((c3_w *)(void *) buf_d, (2 * len_d));
|
||
|
bot_w = (req_u->fol_u->end_d & 0xffffffff);
|
||
|
bot_w = u3r_mug_words(&bot_w, 1);
|
||
|
|
||
|
req_u->fam_d[0] = len_d;
|
||
|
req_u->fam_d[1] = ((c3_d)top_w) << 32ULL | ((c3_d) bot_w);
|
||
|
}
|
||
|
|
||
|
/* do it
|
||
|
*/
|
||
|
{
|
||
|
uv_buf_t buf_u[2];
|
||
|
|
||
|
buf_u[0] = uv_buf_init((void *)buf_d, (len_d * 8));
|
||
|
buf_u[1] = uv_buf_init((void *)req_u->fam_d, 16);
|
||
|
|
||
|
if ( 0 != (err_i = uv_fs_write(u3L,
|
||
|
&req_u->ruq_u,
|
||
|
fol_u->fil_u,
|
||
|
buf_u,
|
||
|
2,
|
||
|
(8ULL * pos_d),
|
||
|
_foil_append_cb_1)) )
|
||
|
{
|
||
|
_foil_fail("uv_fs_write", err_i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* u3_foil_reveal(): read the frame before a position, blocking.
|
||
|
*/
|
||
|
c3_d*
|
||
|
u3_foil_reveal(u3_foil* fol_u, // file from
|
||
|
c3_d* sop_d, // end position/prev end
|
||
|
c3_d* len_d) // length return
|
||
|
{
|
||
|
c3_d pos_d = *sop_d;
|
||
|
c3_d fam_d[2];
|
||
|
c3_l mug_l;
|
||
|
uv_fs_t ruq_u;
|
||
|
c3_i err_i;
|
||
|
|
||
|
c3_assert(pos_d >= 2);
|
||
|
c3_assert(pos_d <= fol_u->end_d);
|
||
|
|
||
|
/* read frame data
|
||
|
*/
|
||
|
{
|
||
|
uv_buf_t buf_u = uv_buf_init((void *)fam_d, 16);
|
||
|
|
||
|
fam_d[0] = fam_d[1] = 0;
|
||
|
if ( 0 > (err_i = uv_fs_read(u3L,
|
||
|
&ruq_u,
|
||
|
fol_u->fil_u,
|
||
|
&buf_u, 1,
|
||
|
(8ULL * (pos_d - 2ULL)),
|
||
|
0)) )
|
||
|
{
|
||
|
_foil_fail("uv_fs_read", err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
}
|
||
|
|
||
|
/* validate frame
|
||
|
*/
|
||
|
{
|
||
|
c3_w top_w, bot_w;
|
||
|
c3_l chk_l;
|
||
|
|
||
|
*len_d = fam_d[0];
|
||
|
if ( *len_d > (pos_d - 2ULL) ) {
|
||
|
_foil_fail("corrupt frame a", 0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
top_w = fam_d[1] >> 32ULL;
|
||
|
mug_l = top_w;
|
||
|
|
||
|
bot_w = fam_d[1] & 0xffffffff;
|
||
|
chk_l = (pos_d & 0xffffffff);
|
||
|
chk_l = u3r_mug_words(&chk_l, 1);
|
||
|
if ( bot_w != chk_l ) {
|
||
|
_foil_fail("corrupt frame b", 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* read frame
|
||
|
*/
|
||
|
{
|
||
|
c3_d* buf_d = c3_malloc(8 * *len_d);
|
||
|
uv_buf_t buf_u = uv_buf_init((void *)buf_d, 8 * *len_d);
|
||
|
c3_l gum_l;
|
||
|
|
||
|
if ( 0 > (err_i = uv_fs_read(u3L,
|
||
|
&ruq_u,
|
||
|
fol_u->fil_u,
|
||
|
&buf_u, 1,
|
||
|
(8ULL * (pos_d - (*len_d + 2ULL))),
|
||
|
0) ) )
|
||
|
{
|
||
|
_foil_fail("uv_fs_read", err_i);
|
||
|
return 0;
|
||
|
}
|
||
|
uv_fs_req_cleanup(&ruq_u);
|
||
|
|
||
|
gum_l = u3r_mug_words((c3_w *)(void *) buf_d, (2 * *len_d));
|
||
|
if ( mug_l != gum_l ) {
|
||
|
_foil_fail("corrupt frame c", 0);
|
||
|
return 0;
|
||
|
}
|
||
|
*sop_d = (pos_d - (*len_d + 2ULL));
|
||
|
return buf_d;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* u3_foil_invent(): create a new file with one frame, freeing buffer; sync.
|
||
|
*/
|
||
|
struct _foil_invent_request {
|
||
|
uv_fs_t ruq_u;
|
||
|
void (*fun_f)(void*, u3_foil*);
|
||
|
u3_foil* fol_u;
|
||
|
void* vod_p;
|
||
|
c3_d* buf_d;
|
||
|
c3_d len_d;
|
||
|
c3_d num_d;
|
||
|
#if 0
|
||
|
struct timeval bef_u;
|
||
|
#endif
|
||
|
};
|
||
|
static void
|
||
|
_foil_invent_cb_2a(void* req_p)
|
||
|
{
|
||
|
struct _foil_invent_request* req_u = req_p;
|
||
|
|
||
|
if ( 1 == req_u->num_d ) {
|
||
|
#if 0
|
||
|
{
|
||
|
struct timeval aft_u, gap_u;
|
||
|
c3_w mls_w;
|
||
|
|
||
|
gettimeofday(&aft_u, 0);
|
||
|
timersub(&aft_u, &req_u->bef_u, &gap_u);
|
||
|
mls_w = (gap_u.tv_sec * 1000) + (gap_u.tv_usec / 1000);
|
||
|
|
||
|
fprintf(stderr, "invent ms: %d\r\n", mls_w);
|
||
|
}
|
||
|
#endif
|
||
|
req_u->fun_f(req_u->vod_p, req_u->fol_u);
|
||
|
_foil_close(req_u->fol_u->fil_u);
|
||
|
|
||
|
c3_free(req_u);
|
||
|
}
|
||
|
else {
|
||
|
req_u->num_d++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_foil_invent_cb_2b(uv_fs_t* ruq_u)
|
||
|
{
|
||
|
struct _foil_invent_request* req_u = (void *)ruq_u;
|
||
|
|
||
|
uv_fs_req_cleanup(ruq_u);
|
||
|
_foil_invent_cb_2a(req_u);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_foil_invent_cb_1(void* req_p,
|
||
|
u3_foil* fol_u)
|
||
|
{
|
||
|
struct _foil_invent_request* req_u = req_p;
|
||
|
|
||
|
req_u->fol_u = fol_u;
|
||
|
|
||
|
/* fsync the parent directory, since we just created a file.
|
||
|
*/
|
||
|
uv_fs_fsync(u3L, &req_u->ruq_u,
|
||
|
req_u->fol_u->dir_u->fil_u,
|
||
|
_foil_invent_cb_2b);
|
||
|
|
||
|
u3_foil_append(_foil_invent_cb_2a,
|
||
|
req_u,
|
||
|
fol_u,
|
||
|
req_u->buf_d,
|
||
|
req_u->len_d);
|
||
|
}
|
||
|
void
|
||
|
u3_foil_invent(void (*fun_f)(void*, // context pointer
|
||
|
u3_foil*), // new file
|
||
|
void* vod_p, // context pointer
|
||
|
u3_dire* dir_u, // directory
|
||
|
c3_c* nam_c, // filename
|
||
|
c3_d* buf_d, // buffer (to free)
|
||
|
c3_d len_d) // length
|
||
|
{
|
||
|
struct _foil_invent_request* req_u;
|
||
|
|
||
|
req_u = malloc(sizeof(*req_u));
|
||
|
req_u->fun_f = fun_f;
|
||
|
req_u->fol_u = 0;
|
||
|
req_u->vod_p = vod_p;
|
||
|
req_u->buf_d = buf_d;
|
||
|
req_u->len_d = len_d;
|
||
|
req_u->num_d = 0;
|
||
|
#if 0
|
||
|
gettimeofday(&req_u->bef_u, 0);
|
||
|
#endif
|
||
|
|
||
|
u3_foil_create(_foil_invent_cb_1, req_u, dir_u, nam_c);
|
||
|
}
|