#include #include #include #include #include #include #include #include #include #include #include #include #include "bigfile.h" #define EXT_HEADER "header" #define EXT_ATTR "attr" #define EXT_ATTR_V2 "attr-v2" #define EXT_DATA "%06X" #define FILEID_ATTR -2 #define FILEID_ATTR_V2 -3 #define FILEID_HEADER -1 #define RAISE(ex, errormsg, ...) { __raise__(errormsg, __FILE__, __LINE__, ##__VA_ARGS__); goto ex; } #define RAISEIF(condition, ex, errormsg, ...) { if(condition) RAISE(ex, errormsg, ##__VA_ARGS__); } static char * ERRORSTR = NULL; static size_t CHUNK_BYTES = 64 * 1024 * 1024; /* Internal AttrSet API */ struct BigAttrSet { /* All members are readonly */ int dirty; char * attrbuf; size_t bufused; size_t bufsize; BigAttr * attrlist; size_t listused; size_t listsize; }; static BigAttrSet * attrset_create(); static void attrset_free(BigAttrSet * attrset); static int attrset_read_attr_set_v1(BigAttrSet * attrset, const char * basename); static int attrset_read_attr_set_v2(BigAttrSet * attrset, const char * basename); static int attrset_write_attr_set_v2(BigAttrSet * attrset, const char * basename); static BigAttr * attrset_lookup_attr(BigAttrSet * attrset, const char * attrname); static int attrset_remove_attr(BigAttrSet * attrset, const char * attrname); static BigAttr * attrset_list_attrs(BigAttrSet * attrset, size_t * count); static int attrset_set_attr(BigAttrSet * attrset, const char * attrname, const void * data, const char * dtype, int nmemb); static int attrset_get_attr(BigAttrSet * attrset, const char * attrname, void * data, const char * dtype, int nmemb); /* Internal dtype API */ static int dtype_convert_simple(void * dst, const char * dstdtype, const void * src, const char * srcdtype, size_t nmemb); /*Check dtype is valid*/ int dtype_isvalid(const char * dtype); /* postfix detection, 1 if postfix is a postfix of str */ static int endswith(const char * str, const char * postfix) { const char * p = str + strlen(str) - 1; const char * q = postfix + strlen(postfix) - 1; for(; p >= str && q >= postfix; p --, q--) { if(*p != *q) return 0; } return 1; } /* global settings */ int big_file_set_buffer_size(size_t bytes) { CHUNK_BYTES = bytes; return 0; } /* Error handling */ char * big_file_get_error_message() { return ERRORSTR; } void big_file_clear_error_message() { if(ERRORSTR) { free(ERRORSTR); ERRORSTR = NULL; } } static char * _path_join(const char * part1, const char * part2) { size_t l1 = strlen(part1); size_t l2 = strlen(part2); char * result = malloc(l1 + l2 + 10); if(l1 == 0) { sprintf(result, "%s", part2); } else if(part1[l1 - 1] == '/') { sprintf(result, "%s%s", part1, part2); } else { sprintf(result, "%s/%s", part1, part2); } return result; } static int _big_file_path_is_block(const char * basename) { FILE * fheader = _big_file_open_a_file(basename, FILEID_HEADER, "r"); if(fheader == NULL) return 0; fclose(fheader); return 1; } static void __raise__(const char * msg, const char * file, const int line, ...) { char * mymsg; if(!msg) { if(ERRORSTR) { mymsg = strdup(ERRORSTR); } else { mymsg = strdup("UNKNOWN ERROR"); } } else { mymsg = strdup(msg); } if(ERRORSTR) free(ERRORSTR); ERRORSTR = malloc(strlen(mymsg) + 512); char * fmtstr = alloca(strlen(mymsg) + 512); sprintf(fmtstr, "%s @(%s:%d)", mymsg, file, line); free(mymsg); va_list va; va_start(va, line); vsprintf(ERRORSTR, fmtstr, va); va_end(va); } void big_file_set_error_message(char * msg) { if(ERRORSTR) free(ERRORSTR); ERRORSTR = strdup(msg); } /* BigFile */ int big_file_open(BigFile * bf, const char * basename) { memset(bf, 0, sizeof(bf[0])); struct stat st; RAISEIF(0 != stat(basename, &st), ex_stat, "Big File `%s' does not exist (%s)", basename, strerror(errno)); bf->basename = strdup(basename); return 0; ex_stat: return -1; } int big_file_create(BigFile * bf, const char * basename) { memset(bf, 0, sizeof(bf[0])); bf->basename = strdup(basename); RAISEIF(0 != _big_file_mksubdir_r(basename, ""), ex_subdir, NULL); return 0; ex_subdir: return -1; } struct bblist { struct bblist * next; char * blockname; }; static int (filter)(const struct dirent * ent) { if(ent->d_name[0] == '.') return 0; return 1; } static struct bblist * listbigfile_r(const char * basename, char * blockname, struct bblist * bblist) { struct dirent **namelist; struct stat st; int n; int i; char * current; current = _path_join(basename, blockname); n = scandir(current, &namelist, filter, alphasort); free(current); if (n < 0) return bblist; for(i = 0; i < n ; i ++) { char * blockname1 = _path_join(blockname, namelist[i]->d_name); char * fullpath1 = _path_join(basename, blockname1); BigBlock bb = {0}; if(_big_file_path_is_block(fullpath1)) { struct bblist * n = malloc(sizeof(struct bblist) + strlen(blockname1) + 1); n->next = bblist; n->blockname = (char*) &n[1]; strcpy(n->blockname, blockname1); bblist = n; } else { big_file_clear_error_message(); bblist = listbigfile_r(basename, blockname1, bblist); } free(fullpath1); free(blockname1); free(namelist[i]); } free(namelist); return bblist; } int big_file_list(BigFile * bf, char *** blocknames, int * Nblocks) { struct bblist * bblist = listbigfile_r(bf->basename, "", NULL); struct bblist * p; int N = 0; int i; for(p = bblist; p; p = p->next) { N ++; } *Nblocks = N; *blocknames = malloc(sizeof(char*) * N); for(i = 0; i < N; i ++) { (*blocknames)[i] = strdup(bblist->blockname); p = bblist; bblist = bblist->next; free(p); } return 0; } int big_file_open_block(BigFile * bf, BigBlock * block, const char * blockname) { char * basename = _path_join(bf->basename, blockname); int rt = big_block_open(block, basename); free(basename); return rt; } int big_file_create_block(BigFile * bf, BigBlock * block, const char * blockname, const char * dtype, int nmemb, int Nfile, const size_t fsize[]) { RAISEIF(0 != _big_file_mksubdir_r(bf->basename, blockname), ex_subdir, NULL); char * basename = _path_join(bf->basename, blockname); int rt = big_block_create(block, basename, dtype, nmemb, Nfile, fsize); free(basename); return rt; ex_subdir: return -1; } int big_file_close(BigFile * bf) { free(bf->basename); bf->basename = NULL; return 0; } static void sysvsum(unsigned int * sum, void * buf, size_t size); void big_file_checksum(unsigned int * sum, void * buf, size_t size) { sysvsum(sum, buf, size); } /* Bigblock */ int big_block_open(BigBlock * bb, const char * basename) { memset(bb, 0, sizeof(bb[0])); if(basename == NULL) basename = "/."; bb->basename = strdup(basename); bb->dirty = 0; bb->attrset = attrset_create(); /* COMPATIBLE WITH V1 ATTR FILES */ RAISEIF(0 != attrset_read_attr_set_v1(bb->attrset, bb->basename), ex_readattr, NULL); RAISEIF(0 != attrset_read_attr_set_v2(bb->attrset, bb->basename), ex_readattr, NULL); if (!endswith(bb->basename, "/.") && 0 != strcmp(bb->basename, ".")) { FILE * fheader = _big_file_open_a_file(bb->basename, FILEID_HEADER, "r"); RAISEIF (!fheader, ex_open, NULL); RAISEIF( (1 != fscanf(fheader, " DTYPE: %s", bb->dtype)) || (1 != fscanf(fheader, " NMEMB: %d", &(bb->nmemb))) || (1 != fscanf(fheader, " NFILE: %d", &(bb->Nfile))), ex_fscanf, "Failed to read header of block `%s' (%s)", bb->basename, strerror(errno)); RAISEIF(bb->Nfile < 0 || bb->Nfile >= INT_MAX-1, ex_fscanf, "Unreasonable value for Nfile in header of block `%s' (%d)",bb->basename,bb->Nfile); RAISEIF(bb->nmemb < 0, ex_fscanf, "Unreasonable value for nmemb in header of block `%s' (%d)",bb->basename,bb->nmemb); RAISEIF(!dtype_isvalid(bb->dtype), ex_fscanf, "Unreasonable value for dtype in header of block `%s' (%s)",bb->basename,bb->dtype); bb->fsize = calloc(bb->Nfile, sizeof(size_t)); RAISEIF(!bb->fsize, ex_fsize, "Failed to alloc memory of block `%s'", bb->basename); bb->foffset = calloc(bb->Nfile + 1, sizeof(size_t)); RAISEIF(!bb->foffset, ex_foffset, "Failed to alloc memory of block `%s'", bb->basename); bb->fchecksum = calloc(bb->Nfile, sizeof(int)); RAISEIF(!bb->fchecksum, ex_fchecksum, "Failed to alloc memory `%s'", bb->basename); int i; for(i = 0; i < bb->Nfile; i ++) { int fid; size_t size; unsigned int cksum; unsigned int sysv; RAISEIF(4 != fscanf(fheader, " " EXT_DATA ": %td : %u : %u", &fid, &size, &cksum, &sysv), ex_fscanf1, "Failed to readin physical file layout `%s' %d (%s)", bb->basename, fid, strerror(errno)); RAISEIF(fid < 0 || fid >= bb->Nfile, ex_fscanf1, "Non-existent file referenced: `%s' (%d)", bb->basename, fid); bb->fsize[fid] = size; bb->fchecksum[fid] = cksum; } bb->foffset[0] = 0; for(i = 0; i < bb->Nfile; i ++) { bb->foffset[i + 1] = bb->foffset[i] + bb->fsize[i]; } bb->size = bb->foffset[bb->Nfile]; fclose(fheader); return 0; ex_fscanf1: free(bb->fchecksum); ex_fchecksum: free(bb->foffset); ex_foffset: free(bb->fsize); ex_fsize: ex_fscanf: fclose(fheader); ex_open: return -1; } else { /* The meta block of a big file, has on extra files. */ bb->Nfile = 0; strcpy(bb->dtype, "####"); return 0; } ex_readattr: return -1; } int _big_block_create_internal(BigBlock * bb, const char * basename, const char * dtype, int nmemb, int Nfile, const size_t fsize[]) { memset(bb, 0, sizeof(bb[0])); if(basename == NULL) basename = "/."; bb->basename = strdup(basename); bb->attrset = attrset_create(); bb->attrset->dirty = 1; if (!endswith(bb->basename, "/.") && 0 != strcmp(bb->basename, ".")) { if(dtype == NULL) { dtype = "i8"; Nfile = 0; fsize = NULL; } /* always use normalized dtype in files. */ dtype_normalize(bb->dtype, dtype); bb->Nfile = Nfile; bb->nmemb = nmemb; bb->fsize = calloc(bb->Nfile, sizeof(size_t)); RAISEIF(!bb->fsize, ex_fsize, "No memory"); bb->fchecksum = calloc(bb->Nfile, sizeof(int)); RAISEIF(!bb->fchecksum, ex_fchecksum, "No memory"); bb->foffset = calloc(bb->Nfile + 1, sizeof(size_t)); RAISEIF(!bb->foffset, ex_foffset, "No memory"); int i; bb->foffset[0] = 0; for(i = 0; i < bb->Nfile; i ++) { bb->fsize[i] = fsize[i]; bb->foffset[i + 1] = bb->foffset[i] + bb->fsize[i]; bb->fchecksum[i] = 0; } bb->size = bb->foffset[bb->Nfile]; bb->dirty = 1; RAISEIF(0 != big_block_flush(bb), ex_flush, NULL); bb->dirty = 0; return 0; ex_flush: attrset_free(bb->attrset); ex_fileio: free(bb->foffset); ex_foffset: free(bb->fchecksum); ex_fchecksum: free(bb->fsize); ex_fsize: return -1; } else { /* special meta block of a file */ bb->Nfile = 0; /* never flush the header */ bb->dirty = 0; RAISEIF(0 != big_block_flush(bb), ex_flush2, NULL); bb->dirty = 0; return 0; ex_flush2: attrset_free(bb->attrset); return -1; } } static void big_block_close_internal(BigBlock * block); int big_block_create(BigBlock * bb, const char * basename, const char * dtype, int nmemb, int Nfile, const size_t fsize[]) { int rt = _big_block_create_internal(bb, basename, dtype, nmemb, Nfile, fsize); int i; RAISEIF(rt != 0, ex_internal, NULL); /* now truncate all files */ for(i = 0; i < bb->Nfile; i ++) { FILE * fp = _big_file_open_a_file(bb->basename, i, "w"); RAISEIF(fp == NULL, ex_fileio, NULL); fclose(fp); } ex_internal: return rt; ex_fileio: big_block_close_internal(bb); return -1; } int big_block_clear_checksum(BigBlock * bb) { memset(bb->fchecksum, 0, bb->Nfile * sizeof(int)); return 0; } void big_block_set_dirty(BigBlock * block, int value) { block->dirty = value; } int big_block_flush(BigBlock * block) { FILE * fheader = NULL; if(block->dirty) { int i; fheader = _big_file_open_a_file(block->basename, FILEID_HEADER, "w+"); RAISEIF(fheader == NULL, ex_fileio, NULL); RAISEIF( (0 > fprintf(fheader, "DTYPE: %s\n", block->dtype)) || (0 > fprintf(fheader, "NMEMB: %d\n", block->nmemb)) || (0 > fprintf(fheader, "NFILE: %d\n", block->Nfile)), ex_fprintf, "Writing file header"); for(i = 0; i < block->Nfile; i ++) { unsigned int s = block->fchecksum[i]; unsigned int r = (s & 0xffff) + ((s & 0xffffffff) >> 16); unsigned int checksum = (r & 0xffff) + (r >> 16); RAISEIF(0 > fprintf(fheader, EXT_DATA ": %td : %u : %u\n", i, block->fsize[i], block->fchecksum[i], checksum), ex_fprintf, "Writing file information to header"); } fclose(fheader); block->dirty = 0; } if(block->attrset->dirty) { RAISEIF(0 != attrset_write_attr_set_v2(block->attrset, block->basename), ex_write_attr, NULL); block->attrset->dirty = 0; } return 0; ex_fprintf: fclose(fheader); ex_write_attr: ex_fileio: return -1; } static void big_block_close_internal(BigBlock * block) { attrset_free(block->attrset); free(block->basename); free(block->fchecksum); free(block->fsize); free(block->foffset); memset(block, 0, sizeof(BigBlock)); } int big_block_close(BigBlock * block) { int rt = 0; RAISEIF(0 != big_block_flush(block), ex_flush, NULL); finalize: big_block_close_internal(block); return rt; ex_flush: rt = -1; goto finalize; } BigAttr * big_block_lookup_attr(BigBlock * block, const char * attrname) { return attrset_lookup_attr(block->attrset, attrname); } int big_block_remove_attr(BigBlock * block, const char * attrname) { return attrset_remove_attr(block->attrset, attrname); } BigAttr * big_block_list_attrs(BigBlock * block, size_t * count) { return attrset_list_attrs(block->attrset, count); } int big_block_set_attr(BigBlock * block, const char * attrname, const void * data, const char * dtype, int nmemb) { return attrset_set_attr(block->attrset, attrname, data, dtype, nmemb); } int big_block_get_attr(BigBlock * block, const char * attrname, void * data, const char * dtype, int nmemb) { return attrset_get_attr(block->attrset, attrname, data, dtype, nmemb); } /* * * seek ptr to offset, on bb * * offset: 0 : Start * -1 : End * -2 : End - 1 * * returns 0 * * 0 4 5 10 140 * */ int big_block_seek(BigBlock * bb, BigBlockPtr * ptr, ptrdiff_t offset) { /* handle 0 sized files */ if(bb->size == 0 && offset == 0) { ptr->fileid = 0; ptr->roffset = 0; ptr->aoffset = 0; return 0; } /* handle negatives */ if(offset < 0) offset += bb->foffset[bb->Nfile]; RAISEIF(offset > bb->size, ex_eof, /* over the end of file */ /* note that we allow seeking at the end of file */ "Over the end of file"); { int left = 0; int right = bb->Nfile; while(right > left + 1) { int mid = ((right - left) >> 1) + left; if(bb->foffset[mid] <= offset) { left = mid; } else { right = mid; } } ptr->fileid = left; ptr->roffset = offset - bb->foffset[left]; ptr->aoffset = offset; return 0; } ex_eof: return -1; } int big_block_seek_rel(BigBlock * bb, BigBlockPtr * ptr, ptrdiff_t rel) { ptrdiff_t abs = bb->foffset[ptr->fileid] + ptr->roffset + rel; return big_block_seek(bb, ptr, abs); } int big_block_eof(BigBlock * bb, BigBlockPtr * ptr) { ptrdiff_t abs = bb->foffset[ptr->fileid] + ptr->roffset; return abs >= bb->size; } /* * this function will alloc memory in array and read from offset start * size of rows from the block. * free(array->data) after using it. * * at most size rows are read, array.dims[0] has the number that's read. * * if dtype is NULL use the dtype of the block. * otherwise cast the array to the dtype * */ int big_block_read_simple(BigBlock * bb, ptrdiff_t start, ptrdiff_t size, BigArray * array, const char * dtype) { BigBlockPtr ptr = {0}; if(dtype == NULL) { dtype = bb->dtype; } void * buffer; size_t dims[2]; RAISEIF(0 != big_block_seek(bb, &ptr, start), ex_seek, "failed to seek" ); if(start + size > bb->size){ size = bb->size - start; } RAISEIF(size < 0, ex_seek, "failed to seek"); buffer = malloc(size * dtype_itemsize(dtype) * bb->nmemb); dims[0] = size; dims[1] = bb->nmemb; big_array_init(array, buffer, dtype, 2, dims, NULL); RAISEIF(0 != big_block_read(bb, &ptr, array), ex_read, "failed to read"); return 0; ex_read: free(buffer); array->data = NULL; ex_seek: return -1; } int big_block_read(BigBlock * bb, BigBlockPtr * ptr, BigArray * array) { char * chunkbuf = malloc(CHUNK_BYTES); int felsize = dtype_itemsize(bb->dtype) * bb->nmemb; size_t CHUNK_SIZE = CHUNK_BYTES / felsize; BigArray chunk_array = {0}; size_t dims[2]; dims[0] = CHUNK_SIZE; dims[1] = bb->nmemb; BigArrayIter chunk_iter; BigArrayIter array_iter; FILE * fp = NULL; ptrdiff_t toread = 0; RAISEIF(chunkbuf == NULL, ex_malloc, "Not enough memory for chunkbuf"); big_array_init(&chunk_array, chunkbuf, bb->dtype, 2, dims, NULL); big_array_iter_init(&array_iter, array); toread = array->size / bb->nmemb; ptrdiff_t abs = bb->foffset[ptr->fileid] + ptr->roffset + toread; RAISEIF(abs > bb->size, ex_eof, "Reading beyond the block `%s` at (%d:%td)", bb->basename, ptr->fileid, ptr->roffset * felsize); while(toread > 0 && ! big_block_eof(bb, ptr)) { size_t chunk_size = CHUNK_SIZE; /* remaining items in the file */ if(chunk_size > bb->fsize[ptr->fileid] - ptr->roffset) { chunk_size = bb->fsize[ptr->fileid] - ptr->roffset; } /* remaining items to read */ if(chunk_size > toread) { chunk_size = toread; } RAISEIF(chunk_size == 0, ex_insuf, "Insufficient number of items in file `%s' at (%d:%td)", bb->basename, ptr->fileid, ptr->roffset * felsize); /* read to the beginning of chunk */ big_array_iter_init(&chunk_iter, &chunk_array); fp = _big_file_open_a_file(bb->basename, ptr->fileid, "r"); RAISEIF(fp == NULL, ex_open, NULL); RAISEIF(0 > fseek(fp, ptr->roffset * felsize, SEEK_SET), ex_seek, "Failed to seek in block `%s' at (%d:%td) (%s)", bb->basename, ptr->fileid, ptr->roffset * felsize, strerror(errno)); RAISEIF(chunk_size != fread(chunkbuf, felsize, chunk_size, fp), ex_read, "Failed to read in block `%s' at (%d:%td) (%s)", bb->basename, ptr->fileid, ptr->roffset * felsize, strerror(errno)); fclose(fp); fp = NULL; /* now translate the data from chunkbuf to mptr */ RAISEIF(0 != _dtype_convert(&array_iter, &chunk_iter, chunk_size * bb->nmemb), ex_convert, NULL); toread -= chunk_size; RAISEIF(0 != big_block_seek_rel(bb, ptr, chunk_size), ex_blockseek, NULL); } free(chunkbuf); return 0; ex_read: ex_seek: fclose(fp); ex_insuf: ex_convert: ex_blockseek: ex_open: ex_eof: free(chunkbuf); ex_malloc: return -1; } int big_block_write(BigBlock * bb, BigBlockPtr * ptr, BigArray * array) { if(array->size == 0) return 0; /* the file header is modified */ bb->dirty = 1; char * chunkbuf = malloc(CHUNK_BYTES); int felsize = dtype_itemsize(bb->dtype) * bb->nmemb; size_t CHUNK_SIZE = CHUNK_BYTES / felsize; BigArray chunk_array = {0}; size_t dims[2]; dims[0] = CHUNK_SIZE; dims[1] = bb->nmemb; BigArrayIter chunk_iter; BigArrayIter array_iter; ptrdiff_t towrite = 0; FILE * fp; RAISEIF(chunkbuf == NULL, ex_malloc, "not enough memory for chunkbuf of size %d bytes", CHUNK_BYTES); big_array_init(&chunk_array, chunkbuf, bb->dtype, 2, dims, NULL); big_array_iter_init(&array_iter, array); towrite = array->size / bb->nmemb; ptrdiff_t abs = bb->foffset[ptr->fileid] + ptr->roffset + towrite; RAISEIF(abs > bb->size, ex_eof, "Writing beyond the block `%s` at (%d:%td)", bb->basename, ptr->fileid, ptr->roffset * felsize); while(towrite > 0 && ! big_block_eof(bb, ptr)) { size_t chunk_size = CHUNK_SIZE; /* remaining items in the file */ if(chunk_size > bb->fsize[ptr->fileid] - ptr->roffset) { chunk_size = bb->fsize[ptr->fileid] - ptr->roffset; } /* remaining items to read */ if(chunk_size > towrite) { chunk_size = towrite; } /* write from the beginning of chunk */ big_array_iter_init(&chunk_iter, &chunk_array); /* now translate the data to format in the file*/ RAISEIF(0 != _dtype_convert(&chunk_iter, &array_iter, chunk_size * bb->nmemb), ex_convert, NULL); sysvsum(&bb->fchecksum[ptr->fileid], chunkbuf, chunk_size * felsize); fp = _big_file_open_a_file(bb->basename, ptr->fileid, "r+"); RAISEIF(fp == NULL, ex_open, NULL); RAISEIF(0 > fseek(fp, ptr->roffset * felsize, SEEK_SET), ex_seek, "Failed to seek in block `%s' at (%d:%td) (%s)", bb->basename, ptr->fileid, ptr->roffset * felsize, strerror(errno)); RAISEIF(chunk_size != fwrite(chunkbuf, felsize, chunk_size, fp), ex_write, "Failed to write in block `%s' at (%d:%td) (%s)", bb->basename, ptr->fileid, ptr->roffset * felsize, strerror(errno)); fclose(fp); towrite -= chunk_size; RAISEIF(0 != big_block_seek_rel(bb, ptr, chunk_size), ex_blockseek, NULL); } free(chunkbuf); return 0; ex_write: ex_seek: fclose(fp); ex_convert: ex_open: ex_blockseek: ex_eof: free(chunkbuf); ex_malloc: return -1; } /** * dtype stuff * */ #define MACHINE_ENDIANNESS MACHINE_ENDIAN_F() static char MACHINE_ENDIAN_F(void) { uint32_t i = 0x01234567; return ((*((uint8_t*)(&i))) == 0x67)?'<':'>'; } int dtype_normalize(char * dst, const char * src) { /* normalize a dtype, so that * dst[0] is the endian-ness * dst[1] is the type kind char * dtype[2:] is the width * */ memset(dst, 0, 8); switch(src[0]) { case '<': case '>': case '|': case '=': strcpy(dst, src); break; default: dst[0] = '='; strcpy(dst + 1, src); } if(dst[0] == '=') { dst[0] = MACHINE_ENDIANNESS; } if(dst[0] == '|') { dst[0] = MACHINE_ENDIANNESS; } return 0; } /*Check that the passed dtype is valid. * Returns 1 if valid, 0 if invalid*/ int dtype_isvalid(const char * dtype) { if(!dtype) return 0; switch(dtype[0]) { case '<': case '>': case '|': case '=': break; default: return 0; } switch(dtype[1]) { case 'S': case 'b': case 'i': case 'f': case 'u': case 'c': break; default: return 0; } int width = atoi(&dtype[2]); if(width > 16 || width <= 0) return 0; return 1; } int dtype_itemsize(const char * dtype) { char ndtype[8]; dtype_normalize(ndtype, dtype); return atoi(&ndtype[2]); } int big_array_init(BigArray * array, void * buf, const char * dtype, int ndim, const size_t dims[], const ptrdiff_t strides[]) { memset(array, 0, sizeof(array[0])); dtype_normalize(array->dtype, dtype); array->data = buf; array->ndim = ndim; int i; memset(array->dims, 0, sizeof(ptrdiff_t) * 32); memset(array->strides, 0, sizeof(ptrdiff_t) * 32); array->size = 1; for(i = 0; i < ndim; i ++) { array->dims[i] = dims[i]; array->size *= dims[i]; } if(strides != NULL) { for(i = 0; i < ndim; i ++) { array->strides[i] = strides[i]; } } else { array->strides[ndim - 1] = dtype_itemsize(dtype); for(i = ndim - 2; i >= 0; i --) { array->strides[i] = array->strides[i + 1] * array->dims[i + 1]; } } return 0; } int big_array_iter_init(BigArrayIter * iter, BigArray * array) { memset(iter, 0, sizeof(iter[0])); iter->array = array; memset(iter->pos, 0, sizeof(ptrdiff_t) * 32); iter->dataptr = array->data; /* see if the iter is contiguous */ size_t elsize = dtype_itemsize(array->dtype); int i = 0; ptrdiff_t stride_contiguous = elsize; iter->contiguous = 1; for(i = array->ndim - 1; i >= 0; i --) { if(array->strides[i] != stride_contiguous) { iter->contiguous = 0; break; } stride_contiguous *= array->dims[i]; } return 0; } void big_array_iter_advance(BigArrayIter * iter) { BigArray * array = iter->array; if(iter->contiguous) { iter->dataptr = (char*) iter->dataptr + array->strides[array->ndim - 1]; return; } int k; iter->pos[array->ndim - 1] ++; iter->dataptr = ((char*) iter->dataptr) + array->strides[array->ndim - 1]; for(k = array->ndim - 1; k >= 0; k --) { if(iter->pos[k] == array->dims[k]) { iter->dataptr = ((char*) iter->dataptr) - array->strides[k] * iter->pos[k]; iter->pos[k] = 0; if(k > 0) { iter->pos[k - 1] ++; iter->dataptr = ((char*) iter->dataptr) + array->strides[k - 1]; } } else { break; } } } typedef struct {double r; double i;} cplx128_t; typedef struct {float r; float i;} cplx64_t; typedef union { char *S1; char *b1; int64_t *i8; uint64_t *u8; double *f8; int32_t *i4; uint32_t *u4; float *f4; cplx128_t * c16; cplx64_t * c8; void * v; } variant_t; /* format data in dtype to a string in buffer */ void dtype_format(char * buffer, const char * dtype, const void * data, const char * fmt) { char ndtype[8]; char ndtype2[8]; variant_t p; /* handle the endianness stuff in case it is not machine */ char converted[128]; dtype_normalize(ndtype2, dtype); ndtype2[0] = '='; dtype_normalize(ndtype, ndtype2); dtype_convert_simple(converted, ndtype, data, dtype, 1); p.v = converted; #define FORMAT1(dtype, defaultfmt) \ if(0 == strcmp(ndtype + 1, # dtype)) { \ if(fmt == NULL) fmt = defaultfmt; \ sprintf(buffer, fmt, *p.dtype); \ } else #define FORMAT2(dtype, defaultfmt) \ if(0 == strcmp(ndtype + 1, # dtype)) { \ if(fmt == NULL) fmt = defaultfmt; \ sprintf(buffer, fmt, p.dtype->r, p.dtype->i); \ } else FORMAT1(S1, "%c") FORMAT1(b1, "%d") FORMAT1(i8, "%ld") FORMAT1(i4, "%d") FORMAT1(u8, "%lu") FORMAT1(u4, "%u") FORMAT1(f8, "%g") FORMAT1(f4, "%g") FORMAT2(c8, "%g+%gI") FORMAT2(c16, "%g+%gI") { sprintf(buffer, "<%s>", ndtype); } } /* parse data in dtype to a string in buffer */ void dtype_parse(const char * buffer, const char * dtype, void * data, const char * fmt) { char ndtype[8]; char ndtype2[8]; variant_t p; /* handle the endianness stuff in case it is not machine */ char converted[128]; dtype_normalize(ndtype2, dtype); ndtype2[0] = '='; dtype_normalize(ndtype, ndtype2); p.v = converted; #define PARSE1(dtype, defaultfmt) \ if(0 == strcmp(ndtype + 1, # dtype)) { \ if(fmt == NULL) fmt = defaultfmt; \ sscanf(buffer, fmt, p.dtype); \ } else #define PARSE2(dtype, defaultfmt) \ if(0 == strcmp(ndtype + 1, # dtype)) { \ if(fmt == NULL) fmt = defaultfmt; \ sscanf(buffer, fmt, &p.dtype->r, &p.dtype->i); \ } else PARSE1(S1, "%c") PARSE1(i8, "%ld") PARSE1(i4, "%d") PARSE1(u8, "%lu") PARSE1(u4, "%u") PARSE1(f8, "%lf") PARSE1(f4, "%f") PARSE2(c8, "%f + %f I") PARSE2(c16, "%lf + %lf I") abort(); /* FIXME: shall not abort */ dtype_convert_simple(data, dtype, converted, ndtype, 1); } static int dtype_convert_simple(void * dst, const char * dstdtype, const void * src, const char * srcdtype, size_t nmemb) { BigArray dst_array, src_array; BigArrayIter dst_iter, src_iter; big_array_init(&dst_array, dst, dstdtype, 1, &nmemb, NULL); big_array_init(&src_array, (void*) src, srcdtype, 1, &nmemb, NULL); big_array_iter_init(&dst_iter, &dst_array); big_array_iter_init(&src_iter, &src_array); return _dtype_convert(&dst_iter, &src_iter, nmemb); } static int cast(BigArrayIter * dst, BigArrayIter * src, size_t nmemb); static void byte_swap(BigArrayIter * array, size_t nmemb); int _dtype_convert(BigArrayIter * dst, BigArrayIter * src, size_t nmemb) { /* cast buf2 of dtype2 into buf1 of dtype1 */ /* match src to machine endianness */ if(src->array->dtype[0] != MACHINE_ENDIANNESS) { BigArrayIter iter = *src; byte_swap(&iter, nmemb); } BigArrayIter iter1 = *dst; BigArrayIter iter2 = *src; if(0 != cast(&iter1, &iter2, nmemb)) { /* cast is not supported */ return -1; } /* match dst to machine endianness */ if(dst->array->dtype[0] != MACHINE_ENDIANNESS) { BigArrayIter iter = *dst; byte_swap(&iter, nmemb); } *dst = iter1; *src = iter2; return 0; } static void byte_swap(BigArrayIter * iter, size_t nmemb) { /* swap a buffer in-place */ int elsize = dtype_itemsize(iter->array->dtype); if(elsize == 1) return; /* need byte swap; do it now on buf2 */ /* XXX: this may still be wrong. */ ptrdiff_t i; int half = elsize >> 1; for(i = 0; i < nmemb; i ++) { int j; char * ptr = iter->dataptr; for(j = 0; j < half; j ++) { char tmp = ptr[j]; ptr[j] = ptr[elsize - j - 1]; ptr[elsize - j - 1] = tmp; } big_array_iter_advance(iter); } } #define CAST(d1, t1, d2, t2) \ if((0 == strcmp(d1, dst->array->dtype + 1)) && (0 == strcmp(d2, src->array->dtype + 1))) { \ for(i = 0; i < nmemb; i ++) { \ t1 * p1 = dst->dataptr; t2 * p2 = src->dataptr; \ * p1 = * p2; \ big_array_iter_advance(dst); big_array_iter_advance(src); \ } \ return 0; \ } #define CAST2(d1, t1, d2, t2) \ if((0 == strcmp(d1, dst->array->dtype + 1)) && (0 == strcmp(d2, src->array->dtype + 1))) { \ for(i = 0; i < nmemb; i ++) { \ t1 * p1 = dst->dataptr; t2 * p2 = src->dataptr; \ p1->r = p2->r; p1->i = p2->i; \ big_array_iter_advance(dst); big_array_iter_advance(src); \ } \ return 0; \ } static int cast(BigArrayIter * dst, BigArrayIter * src, size_t nmemb) { /* doing cast assuming native byte order */ /* convert buf2 to buf1, both are native; * dtype has no endian-ness prefix * */ ptrdiff_t i; /* same type, no need for casting. */ if (0 == strcmp(dst->array->dtype + 1, src->array->dtype + 1)) { if(dst->contiguous && src->contiguous) { /* directly copy of memory chunks; FIXME: use memmove? */ memcpy(dst->dataptr, src->dataptr, nmemb * dst->array->strides[dst->array->ndim-1]); dst->dataptr = (char*) dst->dataptr + nmemb * dst->array->strides[dst->array->ndim - 1]; src->dataptr = (char*) src->dataptr + nmemb * src->array->strides[src->array->ndim - 1]; return 0; } else { /* copy one by one, discontinuous */ size_t elsize = dtype_itemsize(dst->array->dtype); for(i = 0; i < nmemb; i ++) { void * p1 = dst->dataptr; void * p2 = src->dataptr; memcpy(p1, p2, elsize); big_array_iter_advance(dst); big_array_iter_advance(src); } return 0; } } if(0 == strcmp(dst->array->dtype + 1, "i8")) { CAST("i8", int64_t, "i4", int32_t); CAST("i8", int64_t, "u4", uint32_t); CAST("i8", int64_t, "u8", uint64_t); CAST("i8", int64_t, "f8", double); CAST("i8", int64_t, "f4", float); CAST("i8", int64_t, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "u8")) { CAST("u8", uint64_t, "u4", uint32_t); CAST("u8", uint64_t, "i4", int32_t); CAST("u8", uint64_t, "i8", int64_t); CAST("u8", uint64_t, "f8", double); CAST("u8", uint64_t, "f4", float); CAST("u8", uint64_t, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "f8")) { CAST("f8", double, "f4", float); CAST("f8", double, "i4", int32_t); CAST("f8", double, "i8", int64_t); CAST("f8", double, "u4", uint32_t); CAST("f8", double, "u8", uint64_t); CAST("f8", double, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "i4")) { CAST("i4", int32_t, "i8", int64_t); CAST("i4", int32_t, "u4", uint32_t); CAST("i4", int32_t, "u8", uint64_t); CAST("i4", int32_t, "f8", double); CAST("i4", int32_t, "f4", float); CAST("i4", int32_t, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "u4")) { CAST("u4", uint32_t, "u8", uint64_t); CAST("u4", uint32_t, "i4", int32_t); CAST("u4", uint32_t, "i8", int64_t); CAST("u4", uint32_t, "f8", double); CAST("u4", uint32_t, "f4", float); CAST("u4", uint32_t, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "f4")) { CAST("f4", float, "f8", double); CAST("f4", float, "i4", int32_t); CAST("f4", float, "i8", int64_t); CAST("f4", float, "u4", uint32_t); CAST("f4", float, "u8", uint64_t); CAST("f4", float, "b1", char); } else if(0 == strcmp(dst->array->dtype + 1, "c8")) { CAST2("c8", cplx64_t, "c16", cplx128_t); } else if(0 == strcmp(dst->array->dtype + 1, "c16")) { CAST2("c16", cplx128_t, "c8", cplx64_t); } RAISE(ex, "Unsupported conversion from %s to %s. ", src->array->dtype, dst->array->dtype); ex: return -1; } static void sysvsum(unsigned int * sum, void * buf, size_t size) { unsigned int thisrun = *sum; unsigned char * cp = buf; while(size --) thisrun += *(cp++); *sum = thisrun; } /* * Internal API for AttrSet objects; * */ static int attrset_read_attr_set_v1(BigAttrSet * attrset, const char * basename) { attrset->dirty = 0; FILE * fattr = _big_file_open_a_file(basename, FILEID_ATTR, "r"); if(fattr == NULL) { big_file_clear_error_message(); return 0; } int nmemb; int lname; char dtype[9]={0}; char * data; char * name; while(!feof(fattr)) { if(1 != fread(&nmemb, sizeof(int), 1, fattr)) break; RAISEIF( (1 != fread(&lname, sizeof(int), 1, fattr)) || (1 != fread(&dtype, 8, 1, fattr)) || (!dtype_isvalid(dtype)), ex_fread, "Failed to read from file" ) int ldata = dtype_itemsize(dtype) * nmemb; data = alloca(ldata); name = alloca(lname + 1); RAISEIF( (1 != fread(name, lname, 1, fattr)) || (1 != fread(data, ldata, 1, fattr)), ex_fread, "Failed to read from file"); name[lname] = 0; RAISEIF(0 != attrset_set_attr(attrset, name, data, dtype, nmemb), ex_set_attr, NULL); } attrset->dirty = 0; fclose(fattr); return 0; ex_set_attr: ex_fread: attrset->dirty = 0; fclose(fattr); return -1; } static int _isblank(int ch) { return ch == ' ' || ch == '\t'; } static int attrset_read_attr_set_v2(BigAttrSet * attrset, const char * basename) { attrset->dirty = 0; FILE * fattr = _big_file_open_a_file(basename, FILEID_ATTR_V2, "r"); if(fattr == NULL) { big_file_clear_error_message(); return 0; } fseek(fattr, 0, SEEK_END); long size = ftell(fattr); /*ftell may fail*/ RAISEIF(size < 0, ex_init, "ftell error: %s",strerror(errno)); char * buffer = (char*) malloc(size + 1); RAISEIF(!buffer, ex_init, "Could not allocate memory for buffer: %ld bytes",size+1); unsigned char * data = (unsigned char * ) malloc(size + 1); RAISEIF(!data, ex_data, "Could not allocate memory for data: %ld bytes",size+1); fseek(fattr, 0, SEEK_SET); RAISEIF(size != fread(buffer, 1, size, fattr), ex_read_file, "Failed to read attribute file\n"); buffer[size] = 0; /* now parse the v2 attr file.*/ long i = 0; #define ATTRV2_EXPECT(variable) while(_isblank(buffer[i])) i++; \ char * variable = buffer + i; \ while(!_isblank(buffer[i])) i++; buffer[i] = 0; i++; while(buffer[i]) { ATTRV2_EXPECT(name); ATTRV2_EXPECT(dtype); ATTRV2_EXPECT(rawlength); ATTRV2_EXPECT(rawdata); /* skip the reset of the line */ while(buffer[i] != '\n' && buffer[i]) i ++; if(buffer[i] == '\n') i++; int nmemb = atoi(rawlength); int itemsize = dtype_itemsize(dtype); RAISEIF(nmemb * itemsize * 2!= strlen(rawdata), ex_parse_attr, "NMEMB and data mismiatch: %d x %d (%s) * 2 != %d", nmemb, itemsize, dtype, strlen(rawdata)); int j, k; for(k = 0, j = 0; k < nmemb * itemsize; k ++, j += 2) { char buf[3]; buf[0] = rawdata[j]; buf[1] = rawdata[j+1]; buf[2] = 0; unsigned int byte = strtoll(buf, NULL, 16); data[k] = byte; } RAISEIF(0 != attrset_set_attr(attrset, name, data, dtype, nmemb), ex_set_attr, NULL); } fclose(fattr); free(data); free(buffer); attrset->dirty = 0; return 0; ex_read_file: ex_parse_attr: ex_set_attr: attrset->dirty = 0; free(data); ex_data: free(buffer); ex_init: fclose(fattr); return -1; } static int attrset_write_attr_set_v2(BigAttrSet * attrset, const char * basename) { static char conv[] = "0123456789ABCDEF"; attrset->dirty = 0; FILE * fattr = _big_file_open_a_file(basename, FILEID_ATTR_V2, "w"); RAISEIF(fattr == NULL, ex_open, NULL); ptrdiff_t i; for(i = 0; i < attrset->listused; i ++) { BigAttr * a = & attrset->attrlist[i]; int itemsize = dtype_itemsize(a->dtype); int ldata = itemsize * a->nmemb; char * rawdata = malloc(ldata * 2 + 1); char * textual = malloc(a->nmemb * 32 + 1); textual[0] = 0; unsigned char * adata = (unsigned char*) a->data; int j, k; for(j = 0, k = 0; k < ldata; k ++, j+=2) { rawdata[j] = conv[adata[k] / 16]; rawdata[j + 1] = conv[adata[k] % 16]; } rawdata[j] = 0; for(j = 0; j < a->nmemb; j ++) { if(a->dtype[1] != 'S') { char buf[128]; dtype_format(buf, a->dtype, &adata[j * itemsize], NULL); strcat(textual, buf); if(j != a->nmemb - 1) { strcat(textual, " "); } } else { char buf[] = {adata[j], 0}; if(buf[0] == '\n') { strcat(textual, "..."); break; } if(buf[0] == 0) { break; } strcat(textual, buf); } } int rt = fprintf(fattr, "%s %s %d %s #HUMANE [ %s ]\n", a->name, a->dtype, a->nmemb, rawdata, textual ); free(rawdata); free(textual); RAISEIF(rt <= 0, ex_write, "Failed to write to file"); } fclose(fattr); return 0; ex_write: fclose(fattr); ex_open: return -1; } static int attr_cmp(const void * p1, const void * p2) { const BigAttr * c1 = p1; const BigAttr * c2 = p2; return strcmp(c1->name, c2->name); } static BigAttr * attrset_append_attr(BigAttrSet * attrset) { while(attrset->listsize - attrset->listused < 1) { attrset->attrlist = realloc(attrset->attrlist, attrset->listsize * 2 * sizeof(BigAttr)); attrset->listsize *= 2; } BigAttr * a = & (attrset->attrlist[attrset->listused++]); memset(a, 0, sizeof(BigAttr)); return a; } static int attrset_add_attr(BigAttrSet * attrset, const char * attrname, const char * dtype, int nmemb) { size_t size = dtype_itemsize(dtype) * nmemb + strlen(attrname) + 1; while(attrset->bufsize - attrset->bufused < size) { int i; for(i = 0; i < attrset->listused; i ++) { attrset->attrlist[i].data -= (ptrdiff_t) attrset->attrbuf; attrset->attrlist[i].name -= (ptrdiff_t) attrset->attrbuf; } attrset->attrbuf = realloc(attrset->attrbuf, attrset->bufsize * 2); attrset->bufsize *= 2; for(i = 0; i < attrset->listused; i ++) { attrset->attrlist[i].data += (ptrdiff_t) attrset->attrbuf; attrset->attrlist[i].name += (ptrdiff_t) attrset->attrbuf; } } char * free = attrset->attrbuf + attrset->bufused; attrset->bufused += size; BigAttr * n = attrset_append_attr(attrset); n->nmemb = nmemb; memset(n->dtype, 0, 8); dtype_normalize(n->dtype, dtype); n->name = free; strcpy(free, attrname); free += strlen(attrname) + 1; n->data = free; qsort(attrset->attrlist, attrset->listused, sizeof(BigAttr), attr_cmp); return 0; } static BigAttr * attrset_lookup_attr(BigAttrSet * attrset, const char * attrname) { BigAttr lookup = {0}; lookup.name = (char*) attrname; BigAttr * found = bsearch(&lookup, attrset->attrlist, attrset->listused, sizeof(BigAttr), attr_cmp); return found; } static int attrset_remove_attr(BigAttrSet * attrset, const char * attrname) { BigAttr *attr = attrset_lookup_attr(attrset, attrname); if(attr) { ptrdiff_t ind = attr - attrset->attrlist; memmove(&attrset->attrlist[ind], &attrset->attrlist[ind + 1], (attrset->listused - ind - 1) * sizeof(BigAttr)); attrset->listused -= 1; } return 0; } static BigAttr * attrset_list_attrs(BigAttrSet * attrset, size_t * count) { *count = attrset->listused; return attrset->attrlist; } static int attrset_set_attr(BigAttrSet * attrset, const char * attrname, const void * data, const char * dtype, int nmemb) { BigAttr * attr; attrset->dirty = 1; attrset_remove_attr(attrset, attrname); /* add ensures the dtype has been normalized! */ RAISEIF(0 != attrset_add_attr(attrset, attrname, dtype, nmemb), ex_add, "Failed to add attr"); attr = attrset_lookup_attr(attrset, attrname); RAISEIF(attr->nmemb != nmemb, ex_mismatch, "attr nmemb mismatch"); return dtype_convert_simple(attr->data, attr->dtype, data, dtype, attr->nmemb); ex_mismatch: ex_add: return -1; } static int attrset_get_attr(BigAttrSet * attrset, const char * attrname, void * data, const char * dtype, int nmemb) { BigAttr * found = attrset_lookup_attr(attrset, attrname); RAISEIF(!found, ex_notfound, "attr not found"); RAISEIF(found->nmemb != nmemb, ex_mismatch, "attr nmemb mismatch"); return dtype_convert_simple(data, dtype, found->data, found->dtype, found->nmemb); ex_mismatch: ex_notfound: return -1; } static BigAttrSet * attrset_create() { BigAttrSet * attrset = calloc(1, sizeof(BigAttrSet)); attrset->attrbuf = malloc(128); attrset->bufsize = 128; attrset->bufused = 0; attrset->attrlist = malloc(sizeof(BigAttr) * 16); attrset->listsize = 16; attrset->listused = 0; return attrset; } static void attrset_free(BigAttrSet * attrset) { free(attrset->attrbuf); free(attrset->attrlist); free(attrset); } void big_attrset_set_dirty(BigAttrSet * attrset, int dirty) { attrset->dirty = dirty; } void * big_attrset_pack(BigAttrSet * attrset, size_t * bytes) { size_t n = 0; n += sizeof(BigAttrSet); n += attrset->bufused; n += sizeof(BigAttr) * attrset->listused; char * buf = calloc(n, 1); char * p = buf; char * attrbuf = (char*) (p + sizeof(BigAttrSet)); BigAttr * attrlist = (BigAttr *) (attrbuf + attrset->bufused); memcpy(p, attrset, sizeof(BigAttrSet)); memcpy(attrbuf, attrset->attrbuf, attrset->bufused); memcpy(attrlist, attrset->attrlist, attrset->listused * sizeof(BigAttr)); int i = 0; for(i = 0; i < attrset->listused; i ++) { attrlist[i].data -= (ptrdiff_t) attrset->attrbuf; attrlist[i].name -= (ptrdiff_t) attrset->attrbuf; } *bytes = n; return (void*) p; } BigAttrSet * big_attrset_unpack(void * p) { BigAttrSet * attrset = calloc(1, sizeof(attrset[0])); memcpy(attrset, p, sizeof(BigAttrSet)); p += sizeof(BigAttrSet); attrset->attrbuf = malloc(attrset->bufsize); attrset->attrlist = malloc(attrset->listsize * sizeof(BigAttr)); memcpy(attrset->attrbuf, p, attrset->bufused); p += attrset->bufused; memcpy(attrset->attrlist, p, attrset->listused * sizeof(BigAttr)); int i = 0; for(i = 0; i < attrset->listused; i ++) { attrset->attrlist[i].data += (ptrdiff_t) attrset->attrbuf; attrset->attrlist[i].name += (ptrdiff_t) attrset->attrbuf; } return attrset; } /* File Path */ FILE * _big_file_open_a_file(const char * basename, int fileid, char * mode) { char * filename; int unbuffered = 0; if(fileid == FILEID_HEADER) { filename = _path_join(basename, EXT_HEADER); } else if(fileid == FILEID_ATTR) { filename = _path_join(basename, EXT_ATTR); } else if(fileid == FILEID_ATTR_V2) { filename = _path_join(basename, EXT_ATTR_V2); } else { char d[128]; sprintf(d, EXT_DATA, fileid); filename = _path_join(basename, d); unbuffered = 1; } FILE * fp = fopen(filename, mode); RAISEIF(fp == NULL, ex_fopen, "Failed to open physical file `%s' with mode `%s' (%s)", filename, mode, strerror(errno)); if(unbuffered) { setbuf(fp, NULL); } ex_fopen: free(filename); return fp; } /* make subdir rel to pathname, recursively making parents */ int _big_file_mksubdir_r(const char * pathname, const char * subdir) { char * subdirname = strdup(subdir); char * p; int mkdirret; char * mydirname; for(p = subdirname; *p; p ++) { if(*p != '/') continue; *p = 0; mydirname = _path_join(pathname, subdirname); mkdirret = mkdir(mydirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); RAISEIF(mkdirret !=0 && errno != EEXIST, ex_mkdir, "Failed to create directory structure at `%s' (%s)", mydirname, strerror(errno) ); free(mydirname); *p = '/'; } mydirname = _path_join(pathname, subdirname); mkdirret = mkdir(mydirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); struct stat buf; RAISEIF((mkdirret !=0 && errno != EEXIST) || stat(mydirname, &buf), ex_mkdir, "Failed to create directory structure at `%s' (%s)", mydirname, strerror(errno) ); free(subdirname); free(mydirname); return 0; ex_mkdir: free(subdirname); free(mydirname); return -1; }