memio.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. * Copyright 1996, University Corporation for Atmospheric Research
  3. * See netcdf/COPYRIGHT file for copying and redistribution conditions.
  4. */
  5. #if defined (_WIN32) || defined (_WIN64)
  6. #include <windows.h>
  7. #include <winbase.h>
  8. #include <io.h>
  9. #define lseek64 lseek
  10. #endif
  11. #include "config.h"
  12. #include <assert.h>
  13. #include <stdlib.h>
  14. #include <errno.h>
  15. #include <string.h>
  16. #ifdef _MSC_VER /* Microsoft Compilers */
  17. #include <io.h>
  18. #else
  19. #include <unistd.h>
  20. #endif
  21. #ifdef HAVE_FCNTL_H
  22. #include <fcntl.h>
  23. #endif
  24. #include "nc.h"
  25. #undef DEBUG
  26. #ifdef DEBUG
  27. #include <stdio.h>
  28. #endif
  29. #ifndef HAVE_SSIZE_T
  30. #define ssize_t int
  31. #endif
  32. #ifndef SEEK_SET
  33. #define SEEK_SET 0
  34. #define SEEK_CUR 1
  35. #define SEEK_END 2
  36. #endif
  37. /* Define the mode flags for create: let umask rule */
  38. #define OPENMODE 0666
  39. #include "ncio.h"
  40. #include "fbits.h"
  41. #include "rnd.h"
  42. /* #define INSTRUMENT 1 */
  43. #if INSTRUMENT /* debugging */
  44. #undef NDEBUG
  45. #include <stdio.h>
  46. /*#include "instr.h"*/
  47. #endif
  48. #ifndef MEMIO_MAXBLOCKSIZE
  49. #define MEMIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
  50. #endif
  51. #undef MIN /* system may define MIN somewhere and complain */
  52. #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
  53. #if !defined(NDEBUG) && !defined(X_INT_MAX)
  54. #define X_INT_MAX 2147483647
  55. #endif
  56. #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
  57. #define X_ALIGN 4
  58. #else
  59. #undef X_ALIGN
  60. #endif
  61. /* Private data for memio */
  62. typedef struct NCMEMIO {
  63. int locked; /* => we cannot realloc */
  64. int persist; /* => save to a file; triggered by NC_WRITE */
  65. char* memory;
  66. off_t alloc;
  67. off_t size;
  68. off_t pos;
  69. } NCMEMIO;
  70. /* Forward */
  71. static int memio_rel(ncio *const nciop, off_t offset, int rflags);
  72. static int memio_get(ncio *const nciop, off_t offset, size_t extent, int rflags, void **const vpp);
  73. static int memio_move(ncio *const nciop, off_t to, off_t from, size_t nbytes, int rflags);
  74. static int memio_sync(ncio *const nciop);
  75. static int memio_filesize(ncio* nciop, off_t* filesizep);
  76. static int memio_pad_length(ncio* nciop, off_t length);
  77. static int memio_close(ncio* nciop, int);
  78. /* Mnemonic */
  79. #define DOOPEN 1
  80. static long pagesize = 0;
  81. /* Create a new ncio struct to hold info about the file. */
  82. static int
  83. memio_new(const char* path, int ioflags, off_t initialsize, ncio** nciopp, NCMEMIO** memiop)
  84. {
  85. int status = NC_NOERR;
  86. ncio* nciop = NULL;
  87. NCMEMIO* memio = NULL;
  88. int openfd = -1;
  89. if(pagesize == 0) {
  90. #if defined (_WIN32) || defined(_WIN64)
  91. SYSTEM_INFO info;
  92. GetSystemInfo (&info);
  93. pagesize = info.dwPageSize;
  94. #elif defined HAVE_SYSCONF
  95. pagesize = sysconf(_SC_PAGE_SIZE);
  96. #elif defined HAVE_GETPAGESIZE
  97. pagesize = getpagesize();
  98. #else
  99. pagesize = 4096; /* good guess */
  100. #endif
  101. }
  102. errno = 0;
  103. /* Always force the allocated size to be a multiple of pagesize */
  104. if(initialsize == 0) initialsize = pagesize;
  105. if((initialsize % pagesize) != 0)
  106. initialsize += (pagesize - (initialsize % pagesize));
  107. nciop = (ncio* )calloc(1,sizeof(ncio));
  108. if(nciop == NULL) {status = NC_ENOMEM; goto fail;}
  109. nciop->ioflags = ioflags;
  110. *((int*)&nciop->fd) = -1; /* caller will fix */
  111. *((char**)&nciop->path) = strdup(path);
  112. if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
  113. *((ncio_relfunc**)&nciop->rel) = memio_rel;
  114. *((ncio_getfunc**)&nciop->get) = memio_get;
  115. *((ncio_movefunc**)&nciop->move) = memio_move;
  116. *((ncio_syncfunc**)&nciop->sync) = memio_sync;
  117. *((ncio_filesizefunc**)&nciop->filesize) = memio_filesize;
  118. *((ncio_pad_lengthfunc**)&nciop->pad_length) = memio_pad_length;
  119. *((ncio_closefunc**)&nciop->close) = memio_close;
  120. memio = (NCMEMIO*)calloc(1,sizeof(NCMEMIO));
  121. if(memio == NULL) {status = NC_ENOMEM; goto fail;}
  122. *((void* *)&nciop->pvt) = memio;
  123. memio->alloc = initialsize;
  124. memio->memory = NULL;
  125. memio->size = 0;
  126. memio->pos = 0;
  127. memio->persist = fIsSet(ioflags,NC_WRITE);
  128. if(nciopp) *nciopp = nciop;
  129. if(memiop) *memiop = memio;
  130. done:
  131. if(openfd >= 0) close(openfd);
  132. return status;
  133. fail:
  134. if(nciop != NULL) {
  135. if(nciop->path != NULL) free((char*)nciop->path);
  136. }
  137. goto done;
  138. }
  139. /* Create a file, and the ncio struct to go with it. This function is
  140. only called from nc__create_mp.
  141. path - path of file to create.
  142. ioflags - flags from nc_create
  143. initialsz - From the netcdf man page: "The argument
  144. Iinitialsize sets the initial size of the file at creation time."
  145. igeto -
  146. igetsz -
  147. sizehintp - the size of a page of data for buffered reads and writes.
  148. nciopp - pointer to a pointer that will get location of newly
  149. created and inited ncio struct.
  150. mempp - pointer to pointer to the initial memory read.
  151. */
  152. int
  153. memio_create(const char* path, int ioflags,
  154. size_t initialsz,
  155. off_t igeto, size_t igetsz, size_t* sizehintp,
  156. ncio* *nciopp, void** const mempp)
  157. {
  158. ncio* nciop;
  159. int fd;
  160. int status;
  161. NCMEMIO* memio = NULL;
  162. int persist = (ioflags & NC_WRITE?1:0);
  163. int oflags;
  164. if(path == NULL ||* path == 0)
  165. return NC_EINVAL;
  166. /* For diskless open has, the file must be classic version 1 or 2.*/
  167. if(fIsSet(ioflags,NC_NETCDF4))
  168. return NC_EDISKLESS; /* violates constraints */
  169. status = memio_new(path, ioflags, initialsz, &nciop, &memio);
  170. if(status != NC_NOERR)
  171. return status;
  172. memio->size = 0;
  173. if(!persist) {
  174. memio->memory = (char*)malloc(memio->alloc);
  175. if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
  176. } else { /*persist */
  177. /* Open the file, but make sure we can write it if needed */
  178. oflags = (persist ? O_RDWR : O_RDONLY);
  179. #ifdef O_BINARY
  180. fSet(oflags, O_BINARY);
  181. #endif
  182. oflags |= (O_CREAT|O_TRUNC);
  183. if(fIsSet(ioflags,NC_NOCLOBBER))
  184. oflags |= O_EXCL;
  185. #ifdef vms
  186. fd = open(path, oflags, 0, "ctx=stm");
  187. #else
  188. fd = open(path, oflags, OPENMODE);
  189. #endif
  190. if(fd < 0) {status = errno; goto unwind_open;}
  191. (void)close(fd); /* will reopen at nc_close */
  192. /* malloc memory */
  193. memio->memory = (char*)malloc(memio->alloc);
  194. if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
  195. } /*!persist*/
  196. #ifdef DEBUG
  197. fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
  198. #endif
  199. fd = nc__pseudofd();
  200. *((int* )&nciop->fd) = fd;
  201. fSet(nciop->ioflags, NC_WRITE);
  202. if(igetsz != 0)
  203. {
  204. status = nciop->get(nciop,
  205. igeto, igetsz,
  206. RGN_WRITE,
  207. mempp);
  208. if(status != NC_NOERR)
  209. goto unwind_open;
  210. }
  211. /* Pick a default sizehint */
  212. if(sizehintp) *sizehintp = pagesize;
  213. *nciopp = nciop;
  214. return NC_NOERR;
  215. unwind_open:
  216. memio_close(nciop,1);
  217. return status;
  218. }
  219. /* This function opens the data file. It is only called from nc.c,
  220. from nc__open_mp and nc_delete_mp.
  221. path - path of data file.
  222. ioflags - flags passed into nc_open.
  223. igeto - looks like this function can do an initial page get, and
  224. igeto is going to be the offset for that. But it appears to be
  225. unused
  226. igetsz - the size in bytes of initial page get (a.k.a. extent). Not
  227. ever used in the library.
  228. sizehintp - the size of a page of data for buffered reads and writes.
  229. nciopp - pointer to pointer that will get address of newly created
  230. and inited ncio struct.
  231. mempp - pointer to pointer to the initial memory read.
  232. */
  233. int
  234. memio_open(const char* path,
  235. int ioflags,
  236. off_t igeto, size_t igetsz, size_t* sizehintp,
  237. ncio* *nciopp, void** const mempp)
  238. {
  239. ncio* nciop;
  240. int fd;
  241. int status;
  242. int persist = (fIsSet(ioflags,NC_WRITE)?1:0);
  243. int oflags;
  244. NCMEMIO* memio = NULL;
  245. size_t sizehint;
  246. off_t filesize;
  247. if(path == NULL ||* path == 0)
  248. return EINVAL;
  249. assert(sizehintp != NULL);
  250. sizehint = *sizehintp;
  251. /* Open the file, but make sure we can write it if needed */
  252. oflags = (persist ? O_RDWR : O_RDONLY);
  253. #ifdef O_BINARY
  254. fSet(oflags, O_BINARY);
  255. #endif
  256. oflags |= O_EXCL;
  257. #ifdef vms
  258. fd = open(path, oflags, 0, "ctx=stm");
  259. #else
  260. fd = open(path, oflags, OPENMODE);
  261. #endif
  262. #ifdef DEBUG
  263. if(fd < 0) {
  264. fprintf(stderr,"open failed: file=%s err=",path);
  265. perror("");
  266. }
  267. #endif
  268. if(fd < 0) {status = errno; goto unwind_open;}
  269. /* get current filesize = max(|file|,initialize)*/
  270. filesize = lseek(fd,0,SEEK_END);
  271. if(filesize < 0) {status = errno; goto unwind_open;}
  272. /* move pointer back to beginning of file */
  273. (void)lseek(fd,0,SEEK_SET);
  274. if(filesize < (off_t)sizehint)
  275. filesize = (off_t)sizehint;
  276. status = memio_new(path, ioflags, filesize, &nciop, &memio);
  277. if(status != NC_NOERR)
  278. return status;
  279. memio->size = filesize;
  280. memio->memory = (char*)malloc(memio->alloc);
  281. if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
  282. #ifdef DEBUG
  283. fprintf(stderr,"memio_open: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
  284. #endif
  285. /* Read the file into the memio memory */
  286. /* We need to do multiple reads because there is no
  287. guarantee that the amount read will be the full amount */
  288. {
  289. off_t red = memio->size;
  290. char* pos = memio->memory;
  291. while(red > 0) {
  292. ssize_t count = read(fd, pos, red);
  293. if(count < 0)
  294. {close(fd); status = errno; goto unwind_open;}
  295. if(count == 0)
  296. {close(fd); status = NC_ENOTNC; goto unwind_open;}
  297. red -= count;
  298. pos += count;
  299. }
  300. }
  301. (void)close(fd); /* until memio_close() */
  302. /* Use half the filesize as the blocksize */
  303. sizehint = filesize/2;
  304. fd = nc__pseudofd();
  305. *((int* )&nciop->fd) = fd;
  306. if(igetsz != 0)
  307. {
  308. status = nciop->get(nciop,
  309. igeto, igetsz,
  310. 0,
  311. mempp);
  312. if(status != NC_NOERR)
  313. goto unwind_open;
  314. }
  315. *sizehintp = sizehint;
  316. *nciopp = nciop;
  317. return NC_NOERR;
  318. unwind_open:
  319. memio_close(nciop,0);
  320. return status;
  321. }
  322. /*
  323. * Get file size in bytes.
  324. */
  325. static int
  326. memio_filesize(ncio* nciop, off_t* filesizep)
  327. {
  328. NCMEMIO* memio;
  329. if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
  330. memio = (NCMEMIO*)nciop->pvt;
  331. if(filesizep != NULL) *filesizep = memio->size;
  332. return NC_NOERR;
  333. }
  334. /*
  335. * Sync any changes to disk, then truncate or extend file so its size
  336. * is length. This is only intended to be called before close, if the
  337. * file is open for writing and the actual size does not match the
  338. * calculated size, perhaps as the result of having been previously
  339. * written in NOFILL mode.
  340. */
  341. static int
  342. memio_pad_length(ncio* nciop, off_t length)
  343. {
  344. NCMEMIO* memio;
  345. if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
  346. memio = (NCMEMIO*)nciop->pvt;
  347. if(!fIsSet(nciop->ioflags, NC_WRITE))
  348. return EPERM; /* attempt to write readonly file*/
  349. if(memio->locked > 0)
  350. return NC_EDISKLESS;
  351. if(length > memio->alloc) {
  352. /* Realloc the allocated memory to a multiple of the pagesize*/
  353. off_t newsize = length;
  354. void* newmem = NULL;
  355. /* Round to a multiple of pagesize */
  356. if((newsize % pagesize) != 0)
  357. newsize += (pagesize - (newsize % pagesize));
  358. newmem = (char*)realloc(memio->memory,newsize);
  359. if(newmem == NULL) return NC_ENOMEM;
  360. /* zero out the extra memory */
  361. memset((void*)(newmem+memio->alloc),0,(newsize - memio->alloc));
  362. #ifdef DEBUG
  363. fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n",
  364. (unsigned long)memio->memory,(unsigned long)memio->alloc,
  365. (unsigned long)newmem,(unsigned long)newsize);
  366. #endif
  367. memio->memory = newmem;
  368. memio->alloc = newsize;
  369. }
  370. memio->size = length;
  371. return NC_NOERR;
  372. }
  373. /* Write out any dirty buffers to disk and
  374. ensure that next read will get data from disk.
  375. Sync any changes, then close the open file associated with the ncio
  376. struct, and free its memory.
  377. nciop - pointer to ncio to close.
  378. doUnlink - if true, unlink file
  379. */
  380. static int
  381. memio_close(ncio* nciop, int doUnlink)
  382. {
  383. int status = NC_NOERR;
  384. NCMEMIO* memio;
  385. int fd = -1;
  386. if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
  387. memio = (NCMEMIO*)nciop->pvt;
  388. assert(memio != NULL);
  389. /* See if the user wants the contents persisted to a file */
  390. if(memio->persist) {
  391. /* Try to open the file for writing */
  392. int oflags = O_WRONLY|O_CREAT|O_TRUNC;
  393. #ifdef O_BINARY
  394. fSet(oflags, O_BINARY);
  395. #endif
  396. fd = open(nciop->path, oflags, OPENMODE);
  397. if(fd >= 0) {
  398. /* We need to do multiple writes because there is no
  399. guarantee that the amount written will be the full amount */
  400. off_t written = memio->size;
  401. char* pos = memio->memory;
  402. while(written > 0) {
  403. ssize_t count = write(fd, pos, written);
  404. if(count < 0)
  405. {status = errno; goto done;}
  406. if(count == 0)
  407. {status = NC_ENOTNC; goto done;}
  408. written -= count;
  409. pos += count;
  410. }
  411. } else
  412. status = errno;
  413. /* Free up things */
  414. if(memio->memory != NULL) free(memio->memory);
  415. }
  416. done:
  417. /* do cleanup */
  418. if(fd >= 0) (void)close(fd);
  419. if(memio != NULL) free(memio);
  420. if(nciop->path != NULL) free((char*)nciop->path);
  421. free(nciop);
  422. return status;
  423. }
  424. static int
  425. guarantee(ncio* nciop, off_t endpoint)
  426. {
  427. NCMEMIO* memio = (NCMEMIO*)nciop->pvt;
  428. if(endpoint > memio->alloc) {
  429. /* extend the allocated memory and size */
  430. int status = memio_pad_length(nciop,endpoint);
  431. if(status != NC_NOERR) return status;
  432. }
  433. if(memio->size < endpoint)
  434. memio->size = endpoint;
  435. return NC_NOERR;
  436. }
  437. /*
  438. * Request that the region (offset, extent)
  439. * be made available through *vpp.
  440. */
  441. static int
  442. memio_get(ncio* const nciop, off_t offset, size_t extent, int rflags, void** const vpp)
  443. {
  444. int status = NC_NOERR;
  445. NCMEMIO* memio;
  446. if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
  447. memio = (NCMEMIO*)nciop->pvt;
  448. status = guarantee(nciop, offset+extent);
  449. memio->locked++;
  450. if(status != NC_NOERR) return status;
  451. if(vpp) *vpp = memio->memory+offset;
  452. return NC_NOERR;
  453. }
  454. /*
  455. * Like memmove(), safely move possibly overlapping data.
  456. */
  457. static int
  458. memio_move(ncio* const nciop, off_t to, off_t from, size_t nbytes, int ignored)
  459. {
  460. int status = NC_NOERR;
  461. NCMEMIO* memio;
  462. if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
  463. memio = (NCMEMIO*)nciop->pvt;
  464. if(from < to) {
  465. /* extend if "to" is not currently allocated */
  466. status = guarantee(nciop,to+nbytes);
  467. if(status != NC_NOERR) return status;
  468. }
  469. /* check for overlap */
  470. if((to + nbytes) > from || (from + nbytes) > to) {
  471. /* Ranges overlap */
  472. #ifdef HAVE_MEMMOVE
  473. memmove((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
  474. #else
  475. off_t overlap;
  476. off_t nbytes1;
  477. if((from + nbytes) > to) {
  478. overlap = ((from + nbytes) - to); /* # bytes of overlap */
  479. nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
  480. /* move the non-overlapping part */
  481. memcpy((void*)(memio->memory+(to+overlap)),
  482. (void*)(memio->memory+(from+overlap)),
  483. nbytes1);
  484. /* move the overlapping part */
  485. memcpy((void*)(memio->memory+to),
  486. (void*)(memio->memory+from),
  487. overlap);
  488. } else { /*((to + nbytes) > from) */
  489. overlap = ((to + nbytes) - from); /* # bytes of overlap */
  490. nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
  491. /* move the non-overlapping part */
  492. memcpy((void*)(memio->memory+to),
  493. (void*)(memio->memory+from),
  494. nbytes1);
  495. /* move the overlapping part */
  496. memcpy((void*)(memio->memory+(to+nbytes1)),
  497. (void*)(memio->memory+(from+nbytes1)),
  498. overlap);
  499. }
  500. #endif
  501. } else {/* no overlap */
  502. memcpy((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
  503. }
  504. return status;
  505. }
  506. static int
  507. memio_rel(ncio* const nciop, off_t offset, int rflags)
  508. {
  509. NCMEMIO* memio;
  510. if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
  511. memio = (NCMEMIO*)nciop->pvt;
  512. memio->locked--;
  513. return NC_NOERR; /* do nothing */
  514. }
  515. /*
  516. * Write out any dirty buffers to disk and
  517. * ensure that next read will get data from disk.
  518. */
  519. static int
  520. memio_sync(ncio* const nciop)
  521. {
  522. return NC_NOERR; /* do nothing */
  523. }