/src/netcdf-c/libsrc/memio.c
| Line | Count | Source | 
| 1 |  | /* | 
| 2 |  |  *  Copyright 2018, University Corporation for Atmospheric Research | 
| 3 |  |  *  See netcdf/COPYRIGHT file for copying and redistribution conditions. | 
| 4 |  |  */ | 
| 5 |  |  | 
| 6 |  | #include "config.h" | 
| 7 |  | #include <assert.h> | 
| 8 |  | #include <stdlib.h> | 
| 9 |  | #include <stdio.h> | 
| 10 |  | #include <errno.h> | 
| 11 |  | #include <string.h> | 
| 12 |  | #include <sys/types.h> | 
| 13 |  | #ifdef HAVE_UNISTD_H | 
| 14 |  | #include <unistd.h> | 
| 15 |  | #endif | 
| 16 |  | #ifdef HAVE_FCNTL_H | 
| 17 |  | #include <fcntl.h> | 
| 18 |  | #endif | 
| 19 |  | #ifdef HAVE_DIRENT_H | 
| 20 |  | #include <dirent.h> | 
| 21 |  | #endif | 
| 22 |  | #ifdef _WIN32 | 
| 23 |  | #include <windows.h> | 
| 24 |  | #include <winbase.h> | 
| 25 |  | #include <io.h> | 
| 26 |  | #endif | 
| 27 |  |  | 
| 28 |  | #include "ncdispatch.h" | 
| 29 |  | #include "nc3internal.h" | 
| 30 |  | #include "netcdf_mem.h" | 
| 31 |  | #include "ncpathmgr.h" | 
| 32 |  | #include "ncrc.h" | 
| 33 |  | #include "ncbytes.h" | 
| 34 |  | #include "ncutil.h" | 
| 35 |  |  | 
| 36 |  | #undef DEBUG | 
| 37 |  |  | 
| 38 |  | #ifndef HAVE_SSIZE_T | 
| 39 |  | typedef int ssize_t; | 
| 40 |  | #endif | 
| 41 |  |  | 
| 42 |  | #ifdef DEBUG | 
| 43 |  | #include <stdio.h> | 
| 44 |  | #endif | 
| 45 |  |  | 
| 46 |  | #ifndef SEEK_SET | 
| 47 |  | #define SEEK_SET 0 | 
| 48 |  | #define SEEK_CUR 1 | 
| 49 |  | #define SEEK_END 2 | 
| 50 |  | #endif | 
| 51 |  |  | 
| 52 |  | /* Define the mode flags for create: let umask rule */ | 
| 53 |  | #define OPENMODE 0666 | 
| 54 |  |  | 
| 55 |  | #include "ncio.h" | 
| 56 |  | #include "fbits.h" | 
| 57 |  | #include "rnd.h" | 
| 58 |  |  | 
| 59 |  | /* #define INSTRUMENT 1 */ | 
| 60 |  | #if INSTRUMENT /* debugging */ | 
| 61 |  | #undef NDEBUG | 
| 62 |  | #include <stdio.h> | 
| 63 |  | #include "instr.h" | 
| 64 |  | #endif | 
| 65 |  |  | 
| 66 |  | #ifndef MEMIO_MAXBLOCKSIZE | 
| 67 |  | #define MEMIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */ | 
| 68 |  | #endif | 
| 69 |  |  | 
| 70 |  | #undef MIN  /* system may define MIN somewhere and complain */ | 
| 71 |  | #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn)) | 
| 72 |  |  | 
| 73 |  | #if !defined(NDEBUG) && !defined(X_INT_MAX) | 
| 74 |  | #define  X_INT_MAX 2147483647 | 
| 75 |  | #endif | 
| 76 |  |  | 
| 77 |  | #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */ | 
| 78 |  | #define  X_ALIGN 4 | 
| 79 |  | #else | 
| 80 |  | #undef X_ALIGN | 
| 81 |  | #endif | 
| 82 |  |  | 
| 83 |  | #define REALLOCBUG | 
| 84 |  | #ifdef REALLOCBUG | 
| 85 |  | /* There is some kind of realloc bug that I cannot solve yet */ | 
| 86 | 0 | #define reallocx(m,new,old) realloc(m,new) | 
| 87 |  | #else | 
| 88 |  | static void* | 
| 89 |  | reallocx(void* mem, size_t newsize, size_t oldsize) | 
| 90 |  | { | 
| 91 |  |     void* m = malloc(newsize); | 
| 92 |  |     if(m != NULL) { | 
| 93 |  |         memcpy(m,mem,oldsize); | 
| 94 |  |   free(mem); | 
| 95 |  |     } | 
| 96 |  |     return m; | 
| 97 |  | } | 
| 98 |  | #endif | 
| 99 |  |  | 
| 100 |  | /* Private data for memio */ | 
| 101 |  |  | 
| 102 |  | typedef struct NCMEMIO { | 
| 103 |  |     int locked; /* => we cannot realloc or free*/ | 
| 104 |  |     int modified; /* => we realloc'd memory at least once */ | 
| 105 |  |     int persist; /* => save to a file; triggered by NC_PERSIST*/ | 
| 106 |  |     char* memory; | 
| 107 |  |     size_t alloc; | 
| 108 |  |     size_t size; | 
| 109 |  |     size_t pos; | 
| 110 |  |     /* Convenience flags */ | 
| 111 |  |     int diskless; | 
| 112 |  |     int inmemory; /* assert(inmemory iff !diskless */ | 
| 113 |  | } NCMEMIO; | 
| 114 |  |  | 
| 115 |  | /* Forward */ | 
| 116 |  | static int memio_rel(ncio *const nciop, off_t offset, int rflags); | 
| 117 |  | static int memio_get(ncio *const nciop, off_t offset, size_t extent, int rflags, void **const vpp); | 
| 118 |  | static int memio_move(ncio *const nciop, off_t to, off_t from, size_t nbytes, int rflags); | 
| 119 |  | static int memio_sync(ncio *const nciop); | 
| 120 |  | static int memio_filesize(ncio* nciop, off_t* filesizep); | 
| 121 |  | static int memio_pad_length(ncio* nciop, off_t length); | 
| 122 |  | static int memio_close(ncio* nciop, int); | 
| 123 |  | static int readfile(const char* path, NC_memio*); | 
| 124 |  | static int writefile(const char* path, NCMEMIO*); | 
| 125 |  | static int fileiswriteable(const char* path); | 
| 126 |  | static int fileexists(const char* path); | 
| 127 |  |  | 
| 128 |  | /* Mnemonic */ | 
| 129 |  | #define DOOPEN 1 | 
| 130 |  |  | 
| 131 |  | static size_t pagesize = 0; | 
| 132 |  |  | 
| 133 |  | /*! Create a new ncio struct to hold info about the file. */ | 
| 134 |  | static int | 
| 135 |  | memio_new(const char* path, int ioflags, size_t initialsize, ncio** nciopp, NCMEMIO** memiop) | 
| 136 | 291 | { | 
| 137 | 291 |     int status = NC_NOERR; | 
| 138 | 291 |     ncio* nciop = NULL; | 
| 139 | 291 |     NCMEMIO* memio = NULL; | 
| 140 | 291 |     size_t minsize = initialsize; | 
| 141 |  |  | 
| 142 |  |     /* Unlike netcdf-4, INMEMORY and DISKLESS share code */ | 
| 143 | 291 |     if(fIsSet(ioflags,NC_DISKLESS)) | 
| 144 | 0 |   fSet(ioflags,NC_INMEMORY);     | 
| 145 |  |  | 
| 146 |  |     /* use asserts because this is an internal function */ | 
| 147 | 291 |     assert(fIsSet(ioflags,NC_INMEMORY)); | 
| 148 | 291 |     assert(memiop != NULL && nciopp != NULL); | 
| 149 | 291 |     assert(path != NULL); | 
| 150 |  |  | 
| 151 | 291 |     if(pagesize == 0) { | 
| 152 |  | #if defined (_WIN32) || defined(_WIN64) | 
| 153 |  |       SYSTEM_INFO info; | 
| 154 |  |       GetSystemInfo (&info); | 
| 155 |  |       pagesize = info.dwPageSize; | 
| 156 |  | #elif defined HAVE_SYSCONF | 
| 157 |  |       long pgval = -1; | 
| 158 | 1 |       pgval = sysconf(_SC_PAGE_SIZE); | 
| 159 | 1 |       if(pgval < 0) { | 
| 160 | 0 |           status = NC_EIO; | 
| 161 | 0 |           goto fail; | 
| 162 | 0 |       } | 
| 163 | 1 |       pagesize = (size_t)pgval; | 
| 164 |  | #elif defined HAVE_GETPAGESIZE | 
| 165 |  |       pagesize = (size_t)getpagesize(); | 
| 166 |  | #else | 
| 167 |  |       pagesize = 4096; /* good guess */ | 
| 168 |  | #endif | 
| 169 | 1 |     } | 
| 170 |  |  | 
| 171 | 291 |     errno = 0; | 
| 172 |  |  | 
| 173 |  |     /* Always force the allocated size to be a multiple of pagesize */ | 
| 174 | 291 |     if(initialsize == 0) initialsize = pagesize; | 
| 175 | 291 |     if((initialsize % pagesize) != 0) | 
| 176 | 277 |   initialsize += (pagesize - (initialsize % pagesize)); | 
| 177 |  |  | 
| 178 | 291 |     nciop = (ncio* )calloc(1,sizeof(ncio)); | 
| 179 | 291 |     if(nciop == NULL) {status = NC_ENOMEM; goto fail;} | 
| 180 |  |  | 
| 181 | 291 |     nciop->ioflags = ioflags; | 
| 182 | 291 |     *((int*)&nciop->fd) = -1; /* caller will fix */ | 
| 183 |  |  | 
| 184 | 291 |     *((ncio_relfunc**)&nciop->rel) = memio_rel; | 
| 185 | 291 |     *((ncio_getfunc**)&nciop->get) = memio_get; | 
| 186 | 291 |     *((ncio_movefunc**)&nciop->move) = memio_move; | 
| 187 | 291 |     *((ncio_syncfunc**)&nciop->sync) = memio_sync; | 
| 188 | 291 |     *((ncio_filesizefunc**)&nciop->filesize) = memio_filesize; | 
| 189 | 291 |     *((ncio_pad_lengthfunc**)&nciop->pad_length) = memio_pad_length; | 
| 190 | 291 |     *((ncio_closefunc**)&nciop->close) = memio_close; | 
| 191 |  |  | 
| 192 | 291 |     memio = (NCMEMIO*)calloc(1,sizeof(NCMEMIO)); | 
| 193 | 291 |     if(memio == NULL) {status = NC_ENOMEM; goto fail;} | 
| 194 | 291 |     *((void* *)&nciop->pvt) = memio; | 
| 195 |  |  | 
| 196 | 291 |     *((char**)&nciop->path) = strdup(path); | 
| 197 | 291 |     if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;} | 
| 198 |  |  | 
| 199 | 291 |     if(memiop && memio) *memiop = memio; else free(memio); | 
| 200 | 291 |     if(nciopp && nciop) *nciopp = nciop; | 
| 201 | 0 |     else { | 
| 202 | 0 |         if(nciop->path != NULL) free((char*)nciop->path); | 
| 203 |  |   /* Fix 38699 */ | 
| 204 | 0 |   nciop->path = NULL; | 
| 205 | 0 |         free(nciop); | 
| 206 | 0 |     } | 
| 207 | 291 |     memio->alloc = initialsize; | 
| 208 | 291 |     memio->pos = 0; | 
| 209 | 291 |     memio->size = minsize; | 
| 210 | 291 |     memio->memory = NULL; /* filled in by caller */ | 
| 211 |  |  | 
| 212 | 291 |     if(fIsSet(ioflags,NC_DISKLESS)) | 
| 213 | 0 |   memio->diskless = 1; | 
| 214 | 291 |     if(fIsSet(ioflags,NC_INMEMORY)) | 
| 215 | 291 |   memio->inmemory = 1; | 
| 216 | 291 |     if(fIsSet(ioflags,NC_PERSIST)) | 
| 217 | 0 |   memio->persist = 1; | 
| 218 |  |  | 
| 219 | 291 | done: | 
| 220 | 291 |     return status; | 
| 221 |  |  | 
| 222 | 0 | fail: | 
| 223 | 0 |     if(memio != NULL) free(memio); | 
| 224 | 0 |     if(nciop != NULL) { | 
| 225 | 0 |         if(nciop->path != NULL) free((char*)nciop->path); | 
| 226 |  |   /* Fix 38699 */ | 
| 227 | 0 |   nciop->path = NULL; | 
| 228 | 0 |         free(nciop); | 
| 229 | 0 |     } | 
| 230 | 0 |     goto done; | 
| 231 | 291 | } | 
| 232 |  |  | 
| 233 |  | /* Create a file, and the ncio struct to go with it. | 
| 234 |  |  | 
| 235 |  |    path - path of file to create. | 
| 236 |  |    ioflags - flags from nc_create | 
| 237 |  |    initialsz - From the netcdf man page: "The argument | 
| 238 |  |                initialsize sets the initial size of the file at creation time." | 
| 239 |  |    igeto - | 
| 240 |  |    igetsz - | 
| 241 |  |    sizehintp - the size of a page of data for buffered reads and writes. | 
| 242 |  |    parameters - arbitrary data | 
| 243 |  |    nciopp - pointer to a pointer that will get location of newly | 
| 244 |  |    created and inited ncio struct. | 
| 245 |  |    mempp - pointer to pointer to the initial memory read. | 
| 246 |  | */ | 
| 247 |  | int | 
| 248 |  | memio_create(const char* path, int ioflags, | 
| 249 |  |     size_t initialsz, | 
| 250 |  |     off_t igeto, size_t igetsz, size_t* sizehintp, | 
| 251 |  |     void* parameters /*ignored*/, | 
| 252 |  |     ncio* *nciopp, void** const mempp) | 
| 253 | 0 | { | 
| 254 | 0 |     ncio* nciop; | 
| 255 | 0 |     int fd; | 
| 256 | 0 |     int status; | 
| 257 | 0 |     NCMEMIO* memio = NULL; | 
| 258 |  | 
 | 
| 259 | 0 |     if(path == NULL ||* path == 0) | 
| 260 | 0 |         return NC_EINVAL; | 
| 261 |  |      | 
| 262 | 0 |     status = memio_new(path, ioflags, initialsz, &nciop, &memio); | 
| 263 | 0 |     if(status != NC_NOERR) | 
| 264 | 0 |         return status; | 
| 265 |  |  | 
| 266 | 0 |     if(memio->persist) { | 
| 267 |  |   /* Verify the file is writeable or does not exist*/ | 
| 268 | 0 |   if(fileexists(path) && !fileiswriteable(path)) | 
| 269 | 0 |       {status = EPERM; goto unwind_open;}   | 
| 270 | 0 |     } | 
| 271 |  |  | 
| 272 |  |     /* Allocate the memory for this file */ | 
| 273 | 0 |     memio->memory = (char*)malloc((size_t)memio->alloc); | 
| 274 | 0 |     if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;} | 
| 275 | 0 |     memio->locked = 0; | 
| 276 |  | 
 | 
| 277 |  | #ifdef DEBUG | 
| 278 |  | fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc); | 
| 279 |  | #endif | 
| 280 |  | 
 | 
| 281 | 0 |     fd = nc__pseudofd(); | 
| 282 | 0 |     *((int* )&nciop->fd) = fd; | 
| 283 |  | 
 | 
| 284 | 0 |     fSet(nciop->ioflags, NC_WRITE); /* Always writeable */ | 
| 285 |  | 
 | 
| 286 | 0 |     if(igetsz != 0) | 
| 287 | 0 |     { | 
| 288 | 0 |         status = nciop->get(nciop, | 
| 289 | 0 |                 igeto, igetsz, | 
| 290 | 0 |                 RGN_WRITE, | 
| 291 | 0 |                 mempp); | 
| 292 | 0 |         if(status != NC_NOERR) | 
| 293 | 0 |             goto unwind_open; | 
| 294 | 0 |     } | 
| 295 |  |  | 
| 296 |  |     /* Pick a default sizehint */ | 
| 297 | 0 |     if(sizehintp) *sizehintp = (size_t)pagesize; | 
| 298 |  | 
 | 
| 299 | 0 |     *nciopp = nciop; | 
| 300 | 0 |     return NC_NOERR; | 
| 301 |  |  | 
| 302 | 0 | unwind_open: | 
| 303 | 0 |     memio_close(nciop,1); | 
| 304 | 0 |     return status; | 
| 305 | 0 | } | 
| 306 |  |  | 
| 307 |  | /* This function opens the data file or inmemory data | 
| 308 |  |    path - path of data file. | 
| 309 |  |    ioflags - flags passed into nc_open. | 
| 310 |  |    igeto - looks like this function can do an initial page get, and | 
| 311 |  |    igeto is going to be the offset for that. But it appears to be | 
| 312 |  |    unused | 
| 313 |  |    igetsz - the size in bytes of initial page get (a.k.a. extent). Not | 
| 314 |  |    ever used in the library. | 
| 315 |  |    sizehintp - the size of a page of data for buffered reads and writes. | 
| 316 |  |    parameters - arbitrary data | 
| 317 |  |    nciopp - pointer to pointer that will get address of newly created | 
| 318 |  |    and inited ncio struct. | 
| 319 |  |    mempp - pointer to pointer to the initial memory read. | 
| 320 |  | */ | 
| 321 |  | int | 
| 322 |  | memio_open(const char* path, | 
| 323 |  |     int ioflags, | 
| 324 |  |     off_t igeto, size_t igetsz, size_t* sizehintp, | 
| 325 |  |     void* parameters, | 
| 326 |  |     ncio* *nciopp, void** const mempp) | 
| 327 | 291 | { | 
| 328 | 291 |     ncio* nciop = NULL; | 
| 329 | 291 |     int fd = -1; | 
| 330 | 291 |     int status = NC_NOERR; | 
| 331 | 291 |     size_t sizehint = 0; | 
| 332 | 291 |     NC_memio meminfo; /* use struct to avoid worrying about free'ing it */ | 
| 333 | 291 |     NCMEMIO* memio = NULL; | 
| 334 | 291 |     size_t initialsize; | 
| 335 |  |     /* Should be the case that diskless => inmemory but not converse */ | 
| 336 | 291 |     int diskless = (fIsSet(ioflags,NC_DISKLESS)); | 
| 337 | 291 |     int inmemory = fIsSet(ioflags,NC_INMEMORY); | 
| 338 | 291 |     int locked = 0; | 
| 339 |  |  | 
| 340 | 291 |     assert(inmemory ? !diskless : 1); | 
| 341 |  |  | 
| 342 | 291 |     if(path == NULL || strlen(path) == 0) | 
| 343 | 0 |         return NC_EINVAL; | 
| 344 |  |  | 
| 345 | 291 |     assert(sizehintp != NULL); | 
| 346 |  |  | 
| 347 | 291 |     sizehint = *sizehintp; | 
| 348 |  |  | 
| 349 | 291 |     memset(&meminfo,0,sizeof(meminfo)); | 
| 350 |  |  | 
| 351 | 291 |     if(inmemory) { /* parameters provide the memory chunk */ | 
| 352 | 291 |   NC_memio* memparams = (NC_memio*)parameters; | 
| 353 | 291 |         meminfo = *memparams; | 
| 354 | 291 |         locked = fIsSet(meminfo.flags,NC_MEMIO_LOCKED); | 
| 355 |  |   /* As a safeguard, if !locked and NC_WRITE is set, | 
| 356 |  |            then we must take control of the incoming memory */ | 
| 357 | 291 |         if(!locked && fIsSet(ioflags,NC_WRITE)) { | 
| 358 | 0 |       memparams->memory = NULL;      | 
| 359 | 0 |   }   | 
| 360 | 291 |     } else { /* read the file into a chunk of memory*/ | 
| 361 | 0 |   assert(diskless); | 
| 362 | 0 |   status = readfile(path,&meminfo); | 
| 363 | 0 |   if(status != NC_NOERR) | 
| 364 | 0 |       {goto unwind_open;} | 
| 365 | 0 |     } | 
| 366 |  |  | 
| 367 |  |     /* Fix up initial size */ | 
| 368 | 291 |     initialsize = meminfo.size; | 
| 369 |  |  | 
| 370 |  |     /* create the NCMEMIO structure */ | 
| 371 | 291 |     status = memio_new(path, ioflags, initialsize, &nciop, &memio); | 
| 372 | 291 |     if(status != NC_NOERR) | 
| 373 | 0 |   {goto unwind_open;} | 
| 374 | 291 |     memio->locked = locked; | 
| 375 |  |  | 
| 376 |  |     /* Initialize the memio memory */ | 
| 377 | 291 |     memio->memory = meminfo.memory; | 
| 378 |  |  | 
| 379 |  |     /* memio_new may have modified the allocated size, in which case, | 
| 380 |  |        reallocate the memory unless the memory is locked. */     | 
| 381 | 291 |     if(memio->alloc > meminfo.size) { | 
| 382 | 277 |   if(memio->locked) | 
| 383 | 277 |       memio->alloc = meminfo.size; /* force it back to what it was */ | 
| 384 | 0 |   else { | 
| 385 | 0 |      void* oldmem = memio->memory; | 
| 386 | 0 |      memio->memory = reallocx(oldmem,memio->alloc,meminfo.size); | 
| 387 | 0 |      if(memio->memory == NULL) | 
| 388 | 0 |          {status = NC_ENOMEM; goto unwind_open;} | 
| 389 | 0 |   } | 
| 390 | 277 |     } | 
| 391 |  |  | 
| 392 |  | #ifdef DEBUG | 
| 393 |  | fprintf(stderr,"memio_open: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc); | 
| 394 |  | #endif | 
| 395 |  |  | 
| 396 | 291 |     if(memio->persist) { | 
| 397 |  |   /* Verify the file is writeable and exists */ | 
| 398 | 0 |   if(!fileexists(path)) | 
| 399 | 0 |       {status = ENOENT; goto unwind_open;}   | 
| 400 | 0 |   if(!fileiswriteable(path)) | 
| 401 | 0 |       {status = EACCES; goto unwind_open;}   | 
| 402 | 0 |     } | 
| 403 |  |  | 
| 404 |  |     /* Use half the filesize as the blocksize ; why? */ | 
| 405 | 291 |     sizehint = (size_t)(memio->alloc/2); | 
| 406 |  |  | 
| 407 |  |     /* sizehint must be multiple of 8 */ | 
| 408 | 291 |     sizehint = (sizehint / 8) * 8; | 
| 409 | 291 |     if(sizehint < 8) sizehint = 8; | 
| 410 |  |  | 
| 411 | 291 |     fd = nc__pseudofd(); | 
| 412 | 291 |     *((int* )&nciop->fd) = fd; | 
| 413 |  |  | 
| 414 | 291 |     if(igetsz != 0) | 
| 415 | 0 |     { | 
| 416 | 0 |         status = nciop->get(nciop, | 
| 417 | 0 |                 igeto, igetsz, | 
| 418 | 0 |                 0, | 
| 419 | 0 |                 mempp); | 
| 420 | 0 |         if(status != NC_NOERR) | 
| 421 | 0 |             goto unwind_open; | 
| 422 | 0 |     } | 
| 423 |  |  | 
| 424 | 291 |     if(sizehintp) *sizehintp = sizehint; | 
| 425 | 291 |     if(nciopp) *nciopp = nciop; else {ncio_close(nciop,0);} | 
| 426 | 291 |     return NC_NOERR; | 
| 427 |  |  | 
| 428 | 0 | unwind_open: | 
| 429 | 0 |     if(fd >= 0) | 
| 430 | 0 |       close(fd); | 
| 431 | 0 |     memio_close(nciop,0); | 
| 432 | 0 |     return status; | 
| 433 | 291 | } | 
| 434 |  |  | 
| 435 |  | /* | 
| 436 |  |  *  Get file size in bytes. | 
| 437 |  |  */ | 
| 438 |  | static int | 
| 439 |  | memio_filesize(ncio* nciop, off_t* filesizep) | 
| 440 | 302 | { | 
| 441 | 302 |     NCMEMIO* memio; | 
| 442 | 302 |     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL; | 
| 443 | 302 |     memio = (NCMEMIO*)nciop->pvt; | 
| 444 | 302 |     if(filesizep != NULL) *filesizep = (off_t)memio->size; | 
| 445 | 302 |     return NC_NOERR; | 
| 446 | 302 | } | 
| 447 |  |  | 
| 448 |  | /* | 
| 449 |  |  *  Sync any changes to disk, then truncate or extend file so its size | 
| 450 |  |  *  is length.  This is only intended to be called before close, if the | 
| 451 |  |  *  file is open for writing and the actual size does not match the | 
| 452 |  |  *  calculated size, perhaps as the result of having been previously | 
| 453 |  |  *  written in NOFILL mode. | 
| 454 |  |  */ | 
| 455 |  | static int | 
| 456 |  | memio_pad_length(ncio* nciop, off_t length) | 
| 457 | 200 | { | 
| 458 | 200 |     NCMEMIO* memio; | 
| 459 | 200 |     size_t len = (size_t)length; | 
| 460 | 200 |     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL; | 
| 461 | 200 |     memio = (NCMEMIO*)nciop->pvt; | 
| 462 |  |  | 
| 463 | 200 |     if(!fIsSet(nciop->ioflags,NC_WRITE)) | 
| 464 | 200 |         return EPERM; /* attempt to write readonly file*/ | 
| 465 | 0 |     if(memio->locked) | 
| 466 | 0 |   return NC_EINMEMORY; | 
| 467 |  |  | 
| 468 | 0 |     if(len > memio->alloc) { | 
| 469 |  |         /* Realloc the allocated memory to a multiple of the pagesize*/ | 
| 470 | 0 |   size_t newsize = (size_t)len; | 
| 471 | 0 |   void* newmem = NULL; | 
| 472 |  |   /* Round to a multiple of pagesize */ | 
| 473 | 0 |   if((newsize % pagesize) != 0) | 
| 474 | 0 |       newsize += (pagesize - (newsize % pagesize)); | 
| 475 |  | 
 | 
| 476 | 0 |         newmem = (char*)reallocx(memio->memory,newsize,memio->alloc); | 
| 477 | 0 |         if(newmem == NULL) return NC_ENOMEM; | 
| 478 |  |   /* If not copy is set, then fail if the newmem address is different | 
| 479 |  |            from old address */ | 
| 480 | 0 |   if(newmem != memio->memory) { | 
| 481 | 0 |       memio->modified++; | 
| 482 | 0 |       if(memio->locked) { | 
| 483 | 0 |     free(newmem); | 
| 484 | 0 |     return NC_EINMEMORY; | 
| 485 | 0 |       } | 
| 486 | 0 |         } | 
| 487 |  |   /* zero out the extra memory */ | 
| 488 | 0 |         memset((void*)((char*)newmem+memio->alloc),0,(size_t)(newsize - memio->alloc)); | 
| 489 |  | 
 | 
| 490 |  | #ifdef DEBUG | 
| 491 |  | fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n", | 
| 492 |  | (unsigned long)memio->memory,(unsigned long)memio->alloc, | 
| 493 |  | (unsigned long)newmem,(unsigned long)newsize); | 
| 494 |  | #endif | 
| 495 | 0 |   memio->memory = newmem; | 
| 496 | 0 |   memio->alloc = newsize; | 
| 497 | 0 |   memio->modified = 1; | 
| 498 | 0 |     } | 
| 499 | 0 |     memio->size = len; | 
| 500 | 0 |     return NC_NOERR; | 
| 501 | 0 | } | 
| 502 |  |  | 
| 503 |  | /*! Write out any dirty buffers to disk. | 
| 504 |  |  | 
| 505 |  |   Write out any dirty buffers to disk and ensure that next read will get data from disk. | 
| 506 |  |   Sync any changes, then close the open file associated with the ncio struct, and free its memory. | 
| 507 |  |  | 
| 508 |  |   @param[in] nciop pointer to ncio to close. | 
| 509 |  |   @param[in] doUnlink if true, unlink file | 
| 510 |  |   @return NC_NOERR on success, error code on failure. | 
| 511 |  | */ | 
| 512 |  |  | 
| 513 |  | static int | 
| 514 |  | memio_close(ncio* nciop, int doUnlink) | 
| 515 | 291 | { | 
| 516 | 291 |     int status = NC_NOERR; | 
| 517 | 291 |     NCMEMIO* memio ; | 
| 518 |  |  | 
| 519 | 291 |     if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR; | 
| 520 |  |  | 
| 521 | 291 |     memio = (NCMEMIO*)nciop->pvt; | 
| 522 | 291 |     assert(memio != NULL); | 
| 523 |  |  | 
| 524 |  |     /* See if the user wants the contents persisted to a file */ | 
| 525 | 291 |     if(memio->persist && memio->memory != NULL) { | 
| 526 | 0 |   status = writefile(nciop->path,memio);     | 
| 527 | 0 |     } | 
| 528 |  |  | 
| 529 |  |     /* We only free the memio memory if file is not locked or has been modified */ | 
| 530 | 291 |     if(memio->memory != NULL && (!memio->locked || memio->modified)) { | 
| 531 | 0 |   free(memio->memory); | 
| 532 | 0 |   memio->memory = NULL; | 
| 533 | 0 |     } | 
| 534 |  |     /* do cleanup  */ | 
| 535 | 291 |     if(memio != NULL) free(memio); | 
| 536 | 291 |     if(nciop->path != NULL) free((char*)nciop->path); | 
| 537 |  |     /* Fix 38699 */ | 
| 538 | 291 |     nciop->path = NULL; | 
| 539 | 291 |     free(nciop); | 
| 540 | 291 |     return status; | 
| 541 | 291 | } | 
| 542 |  |  | 
| 543 |  | static int | 
| 544 |  | guarantee(ncio* nciop, off_t endpoint0) | 
| 545 | 4.99k | { | 
| 546 | 4.99k |     NCMEMIO* memio = (NCMEMIO*)nciop->pvt; | 
| 547 | 4.99k |     if(endpoint0 > memio->alloc) { | 
| 548 |  |   /* extend the allocated memory and size */ | 
| 549 | 200 |   int status = memio_pad_length(nciop,endpoint0); | 
| 550 | 200 |   if(status != NC_NOERR) return status; | 
| 551 | 200 |     } | 
| 552 | 4.79k |     if(memio->size < endpoint0) | 
| 553 | 0 |   memio->size = (size_t)endpoint0; | 
| 554 | 4.79k |     return NC_NOERR; | 
| 555 | 4.99k | } | 
| 556 |  |  | 
| 557 |  | /* | 
| 558 |  |  * Request that the region (offset, extent) | 
| 559 |  |  * be made available through *vpp. | 
| 560 |  |  */ | 
| 561 |  | static int | 
| 562 |  | memio_get(ncio* const nciop, off_t offset, size_t extent, int rflags, void** const vpp) | 
| 563 | 4.99k | { | 
| 564 | 4.99k |     int status = NC_NOERR; | 
| 565 | 4.99k |     NCMEMIO* memio; | 
| 566 | 4.99k |     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL; | 
| 567 | 4.99k |     memio = (NCMEMIO*)nciop->pvt; | 
| 568 | 4.99k |     status = guarantee(nciop, offset+(off_t)extent); | 
| 569 | 4.99k |     memio->locked++; | 
| 570 | 4.99k |     if(status != NC_NOERR) return status; | 
| 571 | 4.79k |     if(vpp) *vpp = memio->memory+offset; | 
| 572 | 4.79k |     return NC_NOERR; | 
| 573 | 4.99k | } | 
| 574 |  |  | 
| 575 |  | /* | 
| 576 |  |  * Like memmove(), safely move possibly overlapping data. | 
| 577 |  |  */ | 
| 578 |  | static int | 
| 579 |  | memio_move(ncio* const nciop, off_t to, off_t from, size_t nbytes, int ignored) | 
| 580 | 0 | { | 
| 581 | 0 |     int status = NC_NOERR; | 
| 582 | 0 |     NCMEMIO* memio; | 
| 583 |  | 
 | 
| 584 | 0 |     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL; | 
| 585 | 0 |     memio = (NCMEMIO*)nciop->pvt; | 
| 586 | 0 |     if(from < to) { | 
| 587 |  |        /* extend if "to" is not currently allocated */ | 
| 588 | 0 |        status = guarantee(nciop,to+(off_t)nbytes); | 
| 589 | 0 |        if(status != NC_NOERR) return status; | 
| 590 | 0 |     } | 
| 591 |  |     /* check for overlap */ | 
| 592 | 0 |     if((to + (off_t)nbytes) > from || (from + (off_t)nbytes) > to) { | 
| 593 |  |   /* Ranges overlap */ | 
| 594 | 0 | #ifdef HAVE_MEMMOVE | 
| 595 | 0 |         memmove((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes); | 
| 596 |  | #else | 
| 597 |  |         off_t overlap; | 
| 598 |  |   off_t nbytes1; | 
| 599 |  |         if((from + nbytes) > to) { | 
| 600 |  |       overlap = ((from + nbytes) - to); /* # bytes of overlap */ | 
| 601 |  |       nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */ | 
| 602 |  |       /* move the non-overlapping part */ | 
| 603 |  |             memcpy((void*)(memio->memory+(to+overlap)), | 
| 604 |  |                    (void*)(memio->memory+(from+overlap)), | 
| 605 |  |        nbytes1); | 
| 606 |  |       /* move the overlapping part */ | 
| 607 |  |       memcpy((void*)(memio->memory+to), | 
| 608 |  |                    (void*)(memio->memory+from), | 
| 609 |  |        overlap); | 
| 610 |  |   } else { /*((to + nbytes) > from) */ | 
| 611 |  |       overlap = ((to + nbytes) - from); /* # bytes of overlap */ | 
| 612 |  |       nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */ | 
| 613 |  |       /* move the non-overlapping part */ | 
| 614 |  |             memcpy((void*)(memio->memory+to), | 
| 615 |  |                    (void*)(memio->memory+from), | 
| 616 |  |        nbytes1); | 
| 617 |  |       /* move the overlapping part */ | 
| 618 |  |       memcpy((void*)(memio->memory+(to+nbytes1)), | 
| 619 |  |                    (void*)(memio->memory+(from+nbytes1)), | 
| 620 |  |        overlap); | 
| 621 |  |   } | 
| 622 |  | #endif | 
| 623 | 0 |     } else {/* no overlap */ | 
| 624 | 0 |   memcpy((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes); | 
| 625 | 0 |     } | 
| 626 | 0 |     return status; | 
| 627 | 0 | } | 
| 628 |  |  | 
| 629 |  | static int | 
| 630 |  | memio_rel(ncio* const nciop, off_t offset, int rflags) | 
| 631 | 4.79k | { | 
| 632 | 4.79k |     NCMEMIO* memio; | 
| 633 | 4.79k |     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL; | 
| 634 | 4.79k |     memio = (NCMEMIO*)nciop->pvt; | 
| 635 | 4.79k |     memio->locked--; | 
| 636 | 4.79k |     return NC_NOERR; /* do nothing */ | 
| 637 | 4.79k | } | 
| 638 |  |  | 
| 639 |  | /* | 
| 640 |  |  * Write out any dirty buffers to disk and | 
| 641 |  |  * ensure that next read will get data from disk. | 
| 642 |  |  */ | 
| 643 |  | static int | 
| 644 |  | memio_sync(ncio* const nciop) | 
| 645 | 291 | { | 
| 646 | 291 |     return NC_NOERR; /* do nothing */ | 
| 647 | 291 | } | 
| 648 |  |  | 
| 649 |  | /* "Hidden" Internal function to extract the  | 
| 650 |  |    the size and/or contents of the memory. | 
| 651 |  | */ | 
| 652 |  | int | 
| 653 |  | memio_extract(ncio* const nciop, size_t* sizep, void** memoryp) | 
| 654 | 0 | { | 
| 655 | 0 |     int status = NC_NOERR; | 
| 656 | 0 |     NCMEMIO* memio = NULL; | 
| 657 |  | 
 | 
| 658 | 0 |     if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR; | 
| 659 | 0 |     memio = (NCMEMIO*)nciop->pvt; | 
| 660 | 0 |     assert(memio != NULL); | 
| 661 | 0 |     if(sizep) *sizep = memio->size; | 
| 662 |  | 
 | 
| 663 | 0 |     if(memoryp && memio->memory != NULL) { | 
| 664 | 0 |   *memoryp = memio->memory; | 
| 665 | 0 |   memio->memory = NULL; /* make sure it does not get free'd */ | 
| 666 | 0 |     } | 
| 667 | 0 |     return status; | 
| 668 | 0 | } | 
| 669 |  |  | 
| 670 |  | /* Return 1 if file exists, 0 otherwise */ | 
| 671 |  | static int | 
| 672 |  | fileexists(const char* path) | 
| 673 | 0 | { | 
| 674 | 0 |     int ok; | 
| 675 |  |     /* See if the file exists at all */ | 
| 676 | 0 |     ok = NCaccess(path,ACCESS_MODE_EXISTS); | 
| 677 | 0 |     if(ok < 0) /* file does not exist */ | 
| 678 | 0 |       return 0; | 
| 679 | 0 |     return 1; | 
| 680 | 0 | } | 
| 681 |  |  | 
| 682 |  | /* Return 1 if file is writeable, return 0 otherwise; | 
| 683 |  |    assumes fileexists has been checked already */ | 
| 684 |  | static int | 
| 685 |  | fileiswriteable(const char* path) | 
| 686 | 0 | { | 
| 687 | 0 |     int ok; | 
| 688 |  |     /* if W is ok */ | 
| 689 | 0 |     ok = NCaccess(path,ACCESS_MODE_W); | 
| 690 | 0 |     if(ok < 0) | 
| 691 | 0 |   return 0; | 
| 692 | 0 |     return 1; | 
| 693 | 0 | } | 
| 694 |  |  | 
| 695 |  | #if 0 /* not used */ | 
| 696 |  | /* Return 1 if file is READABLE, return 0 otherwise; | 
| 697 |  |    assumes fileexists has been checked already */ | 
| 698 |  | static int | 
| 699 |  | fileisreadable(const char* path) | 
| 700 |  | { | 
| 701 |  |     int ok; | 
| 702 |  |     /* if RW is ok */ | 
| 703 |  |     ok = NCaccess(path,ACCESS_MODE_R); | 
| 704 |  |     if(ok < 0) | 
| 705 |  |   return 0; | 
| 706 |  |     return 1; | 
| 707 |  | } | 
| 708 |  | #endif | 
| 709 |  |  | 
| 710 |  | /* Read contents of a disk file into a memory chunk */ | 
| 711 |  | static int | 
| 712 |  | readfile(const char* path, NC_memio* memio) | 
| 713 | 0 | { | 
| 714 | 0 |     int status = NC_NOERR; | 
| 715 | 0 |     FILE* f = NULL; | 
| 716 | 0 |     NCbytes* buf = ncbytesnew(); | 
| 717 |  | 
 | 
| 718 | 0 |     if((status = NC_readfile(path,buf))) goto done; | 
| 719 | 0 |     if(memio) { | 
| 720 | 0 |   memio->size = ncbyteslength(buf); | 
| 721 | 0 |   memio->memory = ncbytesextract(buf); | 
| 722 | 0 |     } | 
| 723 |  | 
 | 
| 724 | 0 | done: | 
| 725 | 0 |     ncbytesfree(buf); | 
| 726 | 0 |     if(f != NULL) fclose(f); | 
| 727 | 0 |     return status;     | 
| 728 | 0 | } | 
| 729 |  |  | 
| 730 |  | /* write contents of a memory chunk back into a disk file */ | 
| 731 |  | static int | 
| 732 |  | writefile(const char* path, NCMEMIO* memio) | 
| 733 | 0 | { | 
| 734 | 0 |     int status = NC_NOERR; | 
| 735 |  | 
 | 
| 736 | 0 |     if(memio) { | 
| 737 | 0 |         if((status = NC_writefile(path,memio->size,memio->memory))) goto done; | 
| 738 | 0 |     } | 
| 739 | 0 | done: | 
| 740 | 0 |     return status;     | 
| 741 | 0 | } |