New -osirrox option sparse= controls extraction into sparse files

This commit is contained in:
2020-11-03 09:27:41 +01:00
parent 66fe150831
commit 05de7ec5ee
13 changed files with 464 additions and 82 deletions

View File

@ -533,6 +533,278 @@ ex:;
}
/* If defined the position accounting will be done by lseek() and used to
* verify the position accounting in struct Xorriso_sparse_statE.
* # def ine Xorriso_check_sparsE yes
*/
struct Xorriso_sparse_statE {
int use_lseek;
off_t cur_pos;
off_t after_last_written;
int warnings;
};
#ifdef Xorriso_check_sparsE
static int Xorriso_sparse_warn(struct XorrisO *xorriso,
struct Xorriso_sparse_statE *sparse_state,
int occasion, char *msg, int flag)
{
if(sparse_state->warnings & (1 << occasion))
return(1);
sparse_state->warnings|= 1 << occasion;
Xorriso_msgs_submit(xorriso, 0, msg, 0, "SORRY", 0);
return(1);
}
#endif /* Xorriso_check_sparsE */
static int Xorriso_sparse_init(struct XorrisO *xorriso,
struct Xorriso_sparse_statE **sparse_state,
int write_fd, int flag)
{
struct Xorriso_sparse_statE *o= NULL;
off_t cur_pos;
struct stat stbuf;
int ret;
*sparse_state= NULL;
/* Check whether sparse writing is disabled */
if(xorriso->sparse_min_gap <= 0)
{ret= 0; goto ex;}
/* Analyze write_fd */
ret= fstat(write_fd, &stbuf);
if(ret == -1)
{ret= 0; goto ex;}
if(!S_ISREG(stbuf.st_mode))
{ret= 0; goto ex;}
cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR);
if(cur_pos < stbuf.st_size)
{ret= 0; goto ex;}
Xorriso_alloc_meM(o, struct Xorriso_sparse_statE, 1);
/* Initialize sparse_state */
o->use_lseek= 1;
o->cur_pos= o->after_last_written= cur_pos;
o->warnings= 0;
ret= 1;
ex:;
if(ret >= 1)
*sparse_state= o;
else
Xorriso_free_meM(o);
return(ret);
}
static int Xorriso_sparse_zeroize(struct XorrisO *xorriso,
struct Xorriso_sparse_statE *sparse_state,
int write_fd, off_t start, off_t count,
int flag)
{
int ret, buf_size= 32 * 1024, buf_fill, wret;
off_t todo, seek_ret;
char *buf= NULL;
if(count <= 0)
{ret= 2; goto ex;}
Xorriso_alloc_meM(buf, char, buf_size);
seek_ret= lseek(write_fd, start, SEEK_SET);
if(seek_ret == -1)
{ret= -1; goto ex;}
sparse_state->cur_pos= seek_ret;
for(todo= count; todo > 0; ) {
if(buf_size < todo)
buf_fill= buf_size;
else
buf_fill= todo;
wret= write(write_fd, buf, buf_fill);
if(wret <= 0)
{ret= wret; goto ex;}
todo-= wret;
sparse_state->cur_pos+= wret;
}
ret= 1;
ex:;
Xorriso_free_meM(buf);
return(ret);
}
/* @param flag bit0= this is the last buffer of the stream
*/
static int Xorriso_sparse_write(struct XorrisO *xorriso,
struct Xorriso_sparse_statE *sparse_state,
int write_fd, char *buf, int count,
int flag)
{
int wret, i, ret;
off_t cur_pos= -1, seek_ret;
static char zero[32]= {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
if(sparse_state == NULL)
goto do_write;
if(!sparse_state->use_lseek)
goto do_write;
if(flag & 1)
goto do_write;
#ifdef Xorriso_check_sparsE
cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR);
if(cur_pos == -1)
goto do_write;
if(cur_pos != sparse_state->cur_pos) {
Xorriso_sparse_warn(xorriso, sparse_state, 0,
"cur_pos deviation in Xorriso_sparse_write:intro", 0);
sparse_state->cur_pos= cur_pos;
}
#else
cur_pos= sparse_state->cur_pos;
#endif
/* Check for all zeros */
if(count % 32)
goto do_write;
for(i= 0; i < count; i+= 32)
if(memcmp(buf + i, zero, 32))
break;
if(i < count)
goto do_write;
/* Omit write() until next non-zero buffer or end of writing */
#ifdef Xorriso_check_sparsE
/* Only for debugging: Do real lseek() instead of write() */
seek_ret= lseek(write_fd, cur_pos + count, SEEK_SET);
if(seek_ret == -1)
return(-1);
#endif
sparse_state->cur_pos= cur_pos + count;
return(count);
do_write:
if(sparse_state != NULL) {
/* Check whether the gap since after_last_written is too small.
If so: fill the whole gap by writing zeros.
*/
if(sparse_state->after_last_written < cur_pos) {
if(xorriso->sparse_min_gap > cur_pos - sparse_state->after_last_written) {
ret= Xorriso_sparse_zeroize(xorriso, sparse_state, write_fd,
sparse_state->after_last_written,
cur_pos - sparse_state->after_last_written, 0);
if(ret < 0)
return(ret);
if(ret == 0) {
seek_ret= lseek(write_fd, cur_pos, SEEK_SET);
if(seek_ret == -1)
return(-1);
sparse_state->cur_pos= seek_ret;
}
}
}
}
if(sparse_state != NULL) {
#ifdef Xorriso_check_sparsE
cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR);
if(cur_pos != sparse_state->cur_pos) {
Xorriso_sparse_warn(xorriso, sparse_state, 1,
"cur_pos deviation in Xorriso_sparse_write:do_write", 0);
sparse_state->cur_pos= cur_pos;
}
#else
/* lseek() has been delayed until now */
if(sparse_state->after_last_written != sparse_state->cur_pos) {
seek_ret= lseek(write_fd, sparse_state->cur_pos, SEEK_SET);
if(seek_ret == -1)
return(-1);
}
#endif /* ! Xorriso_check_sparsE */
}
wret= write(write_fd, buf, count);
if(sparse_state != NULL && wret > 0 && cur_pos >= 0)
sparse_state->cur_pos= sparse_state->after_last_written= cur_pos + wret;
return(wret);
}
static int Xorriso_sparse_finish(struct XorrisO *xorriso,
struct Xorriso_sparse_statE **sparse_state,
int write_fd, int flag)
{
int ret;
off_t cur_pos;
struct Xorriso_sparse_statE *o;
if(sparse_state == NULL)
return(0);
o= *sparse_state;
if(o == NULL)
return(1);
if(write_fd == -1)
{ret= 1; goto ex;}
#ifdef Xorriso_check_sparsE
cur_pos= lseek(write_fd, (off_t) 0, SEEK_CUR);
if(cur_pos == -1)
{ret= -1; goto ex;}
if(cur_pos != o->cur_pos) {
Xorriso_sparse_warn(xorriso, o, 2,
"cur_pos deviation in Xorriso_sparse_finish", 0);
o->cur_pos= cur_pos;
}
#else
cur_pos= o->cur_pos;
#endif /* ! Xorriso_check_sparsE */
if(o->after_last_written < cur_pos) {
/* Check whether the gap since after_last_written is too small.
If so: fill the whole gap by writing zeros, else: write a last zero byte.
*/
if(xorriso->sparse_min_gap > cur_pos - o->after_last_written)
ret= Xorriso_sparse_zeroize(xorriso, o, write_fd, o->after_last_written,
cur_pos - o->after_last_written, 0);
else
ret= Xorriso_sparse_zeroize(xorriso, o, write_fd, cur_pos - 1, (off_t) 1,
0);
if(ret <= 0)
goto ex;
}
ret= 1;
ex:;
Xorriso_free_meM(o);
*sparse_state= NULL;
return(ret);
}
/* @param flag bit0= Minimal transfer: access permissions only
bit1= *_offset and bytes are valid for writing to regular file
bit2= This is not a parameter. Do not report if ignored
@ -567,7 +839,8 @@ int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node,
off_t catsize;
char disk_md5[16], iso_md5[16];
void *ctx= NULL;
int use_md5= 0, i;
int use_md5= 0, i, sparse_ret= 3;
struct Xorriso_sparse_statE *sparse_state= NULL;
Xorriso_alloc_meM(buf, char, buf_size);
Xorriso_alloc_meM(temp_path, char, SfileadrL);
@ -679,6 +952,9 @@ int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node,
goto cannot_restore;
}
}
if(ISO_NODE_IS_FILE(node))
Xorriso_sparse_init(xorriso, &sparse_state, write_fd, 0);
while(todo>0) {
wanted= buf_size;
if(wanted>todo)
@ -731,7 +1007,11 @@ int Xorriso_tree_restore_node(struct XorrisO *xorriso, IsoNode *node,
ret= read_count - img_offset;
}
wret= write(write_fd, buf_pt, ret);
if(sparse_state == NULL)
wret= write(write_fd, buf_pt, ret);
else
wret= Xorriso_sparse_write(xorriso, sparse_state, write_fd, buf_pt, ret,
0);
if(wret>=0) {
todo-= wret;
xorriso->pacifier_byte_count+= wret;
@ -774,8 +1054,11 @@ bad_md5:;
}
}
}
if(write_fd != -1)
if(write_fd != -1) {
sparse_ret= Xorriso_sparse_finish(xorriso, &sparse_state, write_fd, 0);
close(write_fd);
}
write_fd= -1;
if(todo > 0 && xorriso->extract_error_mode == 2) {
unlink(open_path_pt);
@ -798,6 +1081,12 @@ bad_md5:;
}
ret= -(todo > 0);
l_errno= 0;
if(sparse_ret <= 0) {
strcpy(xorriso->info_text, "Could not finalize sparse extraction of ");
Text_shellsafe(disk_path, xorriso->info_text, 1 | 2);
Xorriso_msgs_submit(xorriso, 0, xorriso->info_text,
sparse_ret < 0 ? errno : 0, "FAILURE", 0);
}
} else if(LIBISO_ISLNK(node)) {
what= "symbolic link";
@ -898,6 +1187,8 @@ ex:;
Xorriso_iso_file_close(xorriso, &data_stream, 0);
if(ctx != NULL)
Xorriso_md5_end(xorriso, &ctx, disk_md5, 0);
if(sparse_state != NULL)
Xorriso_sparse_finish(xorriso, &sparse_state, -1, 0);
Xorriso_process_msg_queues(xorriso,0);
return(ret);
}