1 /*
2 * Copyright 1996, University Corporation for Atmospheric Research
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5 /* $Id: winceio.c,v 1.2 2010/05/04 17:30:04 dmh Exp $ */
6 /* Dennis Heimbigner 2010-3-04 */
7
8
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <assert.h>
14 #include <stdlib.h>
15 #include <stdio.h> /* DEBUG */
16
17 #ifndef _WIN32_WCE
18 #include <errno.h>
19 #else
20 #define EPERM NC_EPERM
21 #define ENOMEM NC_ENOMEM
22 #define EINVAL NC_EINVAL
23 #define EIO NC_EINVAL
24 #define EEXIST NC_EEXIST
25 #endif
26
27 #ifndef NC_NOERR
28 #define NC_NOERR 0
29 #endif
30
31 #include <string.h>
32 #include <stdio.h>
33
34 #include "ncio.h"
35 #include "fbits.h"
36 #include "rnd.h"
37
38 #if !defined(NDEBUG) && !defined(X_INT_MAX)
39 #define X_INT_MAX 2147483647
40 #endif
41 #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
42 #define X_ALIGN 4
43 #endif
44
45 #define ALWAYS_NC_SHARE 0 /* DEBUG */
46
47 #define DEFAULTBLKSIZE 32768
48
49 static FILE* descriptors[1024];
50 static int fdmax = 1; /* never use zero */
51
52 /* Begin OS */
53
54 /*
55 * What is the preferred I/O block size?
56 * (This becomes the default *sizehint == ncp->chunk in the higher layers.)
57 * TODO: What is the the best answer here?
58 */
59 static size_t
blksize(int fd)60 blksize(int fd)
61 {
62 return (size_t) DEFAULTBLKSIZE;
63 }
64
65 /*
66 * Sortof like ftruncate, except won't make the
67 * file shorter.
68 */
69 static int
fgrow(FILE * f,const off_t len)70 fgrow(FILE* f, const off_t len)
71 {
72 int status = NC_NOERR;
73 long pos = ftell(f);
74 long size;
75 pos = ftell(f);
76 status = fseek(f,0,SEEK_END);
77 if(ferror(f)) return EIO;
78 size = ftell(f);
79 status = fseek(f,pos,SEEK_SET);
80 if(ferror(f)) return EIO;
81 if(len < size) return NC_NOERR;
82 else {
83 const long dumb = 0;
84 status = fseek(f, len-sizeof(dumb), SEEK_SET);
85 if(ferror(f)) return EIO;
86 fwrite((const void *)&dumb, 1, sizeof(dumb), f);
87 if(ferror(f)) return EIO;
88 status = fseek(f, pos, SEEK_SET);
89 if(ferror(f)) return EIO;
90 }
91 return NC_NOERR;
92 }
93
94
95 /*
96 * Sortof like ftruncate, except won't make the file shorter. Differs
97 * from fgrow by only writing one byte at designated seek position, if
98 * needed.
99 */
100 static int
fgrow2(FILE * f,const off_t len)101 fgrow2(FILE* f, const off_t len)
102 {
103 int status = NC_NOERR;
104 long pos = ftell(f);
105 long size;
106 pos = ftell(f);
107 status = fseek(f,0,SEEK_END);
108 if(ferror(f)) return EIO;
109 size = ftell(f);
110 status = fseek(f,pos,SEEK_SET);
111 if(ferror(f)) return EIO;
112 if(len < size) return NC_NOERR;
113 else {
114 const char dumb = 0;
115 status = fseek(f, len-sizeof(dumb), SEEK_SET);
116 if(ferror(f)) return EIO;
117 fwrite((const void *)&dumb, 1, sizeof(dumb), f);
118 if(ferror(f)) return EIO;
119 status = fseek(f, pos, SEEK_SET);
120 if(ferror(f)) return EIO;
121 }
122 return NC_NOERR;
123 }
124 /* End OS */
125 /* Begin ffio */
126
127 static int
fileio_pgout(ncio * const nciop,off_t const offset,const size_t extent,const void * const vp,off_t * posp)128 fileio_pgout(ncio *const nciop,
129 off_t const offset, const size_t extent,
130 const void *const vp, off_t *posp)
131 {
132 int status = NC_NOERR;
133 FILE* f = descriptors[nciop->fd];
134
135 #ifdef X_ALIGN
136 assert(offset % X_ALIGN == 0);
137 assert(extent % X_ALIGN == 0);
138 #endif
139
140 if(*posp != offset)
141 {
142 status = fseek(f, offset, SEEK_SET);
143 if(ferror(f)) return EIO;
144 *posp = offset;
145 }
146 fwrite(vp,1,extent,f);
147 if(ferror(f)) return EIO;
148 *posp += extent;
149 return NC_NOERR;
150 }
151
152 static int
fileio_pgin(ncio * const nciop,off_t const offset,const size_t extent,void * const vp,size_t * nreadp,off_t * posp)153 fileio_pgin(ncio *const nciop,
154 off_t const offset, const size_t extent,
155 void *const vp, size_t *nreadp, off_t *posp)
156 {
157 int status = NC_NOERR;
158 ssize_t nread;
159 int count;
160
161 FILE* f = descriptors[nciop->fd];
162
163 #ifdef X_ALIGN
164 assert(offset % X_ALIGN == 0);
165 assert(extent % X_ALIGN == 0);
166 #endif
167
168 if(*posp != offset)
169 {
170 status = fseek(f, offset, SEEK_SET);
171 if(ferror(f)) return EIO;
172 *posp = offset;
173 }
174
175 nread = fread(vp,1,extent,f);
176 if(ferror(f)) return EIO;
177 *nreadp = nread;
178 *posp += nread;
179 return NC_NOERR;
180 }
181
182 /* */
183
184 typedef struct ncio_ffio {
185 off_t pos;
186 /* buffer */
187 off_t bf_offset;
188 size_t bf_extent;
189 size_t bf_cnt;
190 void *bf_base;
191 } ncio_ffio;
192
193 static int
ncio_fileio_rel(ncio * const nciop,off_t offset,int rflags)194 ncio_fileio_rel(ncio *const nciop, off_t offset, int rflags)
195 {
196 int status = NC_NOERR;
197 FILE* f = descriptors[nciop->fd];
198
199 ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
200
201 assert(ffp->bf_offset <= offset);
202 assert(ffp->bf_cnt != 0);
203 assert(ffp->bf_cnt <= ffp->bf_extent);
204 #ifdef X_ALIGN
205 assert(offset < ffp->bf_offset + X_ALIGN);
206 assert(ffp->bf_cnt % X_ALIGN == 0 );
207 #endif
208
209 if(fIsSet(rflags, RGN_MODIFIED))
210 {
211 if(!fIsSet(nciop->ioflags, NC_WRITE))
212 return EPERM; /* attempt to write readonly file */
213
214 status = fileio_pgout(nciop, ffp->bf_offset,
215 ffp->bf_cnt,
216 ffp->bf_base, &ffp->pos);
217 /* if error, invalidate buffer anyway */
218 }
219 ffp->bf_offset = OFF_NONE;
220 ffp->bf_cnt = 0;
221 return status;
222 }
223
224
225 static int
ncio_fileio_get(ncio * const nciop,off_t offset,size_t extent,int rflags,void ** const vpp)226 ncio_fileio_get(ncio *const nciop,
227 off_t offset, size_t extent,
228 int rflags,
229 void **const vpp)
230 {
231 ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
232 int status = NC_NOERR;
233 FILE* f = descriptors[nciop->fd];
234 #ifdef X_ALIGN
235 size_t rem;
236 #endif
237
238 if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
239 return EPERM; /* attempt to write readonly file */
240
241 assert(extent != 0);
242 assert(extent < X_INT_MAX); /* sanity check */
243
244 assert(ffp->bf_cnt == 0);
245
246 #ifdef X_ALIGN
247 /* round to seekable boundaries */
248 rem = offset % X_ALIGN;
249 if(rem != 0)
250 {
251 offset -= rem;
252 extent += rem;
253 }
254
255 {
256 const size_t rndup = extent % X_ALIGN;
257 if(rndup != 0)
258 extent += X_ALIGN - rndup;
259 }
260
261 assert(offset % X_ALIGN == 0);
262 assert(extent % X_ALIGN == 0);
263 #endif
264
265 if(ffp->bf_extent < extent)
266 {
267 if(ffp->bf_base != NULL)
268 {
269 free(ffp->bf_base);
270 ffp->bf_base = NULL;
271 ffp->bf_extent = 0;
272 }
273 assert(ffp->bf_extent == 0);
274 ffp->bf_base = malloc(extent);
275 if(ffp->bf_base == NULL)
276 return ENOMEM;
277 ffp->bf_extent = extent;
278 }
279
280 status = fileio_pgin(nciop, offset,
281 extent,
282 ffp->bf_base,
283 &ffp->bf_cnt, &ffp->pos);
284 if(status != NC_NOERR)
285 return status;
286
287 ffp->bf_offset = offset;
288
289 if(ffp->bf_cnt < extent)
290 {
291 (void) memset((char *)ffp->bf_base + ffp->bf_cnt, 0,
292 extent - ffp->bf_cnt);
293 ffp->bf_cnt = extent;
294 }
295
296
297 #ifdef X_ALIGN
298 *vpp = (char *)ffp->bf_base + rem;
299 #else
300 *vpp = (char *)ffp->bf_base;
301 #endif
302 return NC_NOERR;
303 }
304
305
306 static int
ncio_fileio_move(ncio * const nciop,off_t to,off_t from,size_t nbytes,int rflags)307 ncio_fileio_move(ncio *const nciop, off_t to, off_t from,
308 size_t nbytes, int rflags)
309 {
310 int status = NC_NOERR;
311 off_t lower = from;
312 off_t upper = to;
313 char *base;
314 size_t diff = upper - lower;
315 size_t extent = diff + nbytes;
316 FILE* f = descriptors[nciop->fd];
317
318 rflags &= RGN_NOLOCK; /* filter unwanted flags */
319
320 if(to == from)
321 return NC_NOERR; /* NOOP */
322
323 if(to > from)
324 {
325 /* growing */
326 lower = from;
327 upper = to;
328 }
329 else
330 {
331 /* shrinking */
332 lower = to;
333 upper = from;
334 }
335
336 diff = upper - lower;
337 extent = diff + nbytes;
338
339 status = ncio_fileio_get(nciop, lower, extent, RGN_WRITE|rflags,
340 (void **)&base);
341
342 if(status != NC_NOERR)
343 return status;
344
345 if(to > from)
346 (void) memmove(base + diff, base, nbytes);
347 else
348 (void) memmove(base, base + diff, nbytes);
349
350 (void) ncio_fileio_rel(nciop, lower, RGN_MODIFIED);
351
352 return status;
353 }
354
355 static int
ncio_fileio_sync(ncio * const nciop)356 ncio_fileio_sync(ncio *const nciop)
357 {
358 FILE* f = descriptors[nciop->fd];
359 fflush(f);
360 return NC_NOERR;
361 }
362
363 static void
ncio_fileio_free(void * const pvt)364 ncio_fileio_free(void *const pvt)
365 {
366 ncio_ffio *ffp = (ncio_ffio *)pvt;
367 if(ffp == NULL)
368 return;
369
370 if(ffp->bf_base != NULL)
371 {
372 free(ffp->bf_base);
373 ffp->bf_base = NULL;
374 ffp->bf_offset = OFF_NONE;
375 ffp->bf_extent = 0;
376 ffp->bf_cnt = 0;
377 }
378 }
379
380
381 static int
ncio_fileio_init2(ncio * const nciop,size_t * sizehintp)382 ncio_fileio_init2(ncio *const nciop, size_t *sizehintp)
383 {
384 FILE* f = descriptors[nciop->fd];
385 ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
386
387 assert(f != NULL);
388
389 ffp->bf_extent = *sizehintp;
390
391 assert(ffp->bf_base == NULL);
392
393 /* this is separate allocation because it may grow */
394 ffp->bf_base = malloc(ffp->bf_extent);
395 if(ffp->bf_base == NULL)
396 {
397 ffp->bf_extent = 0;
398 return ENOMEM;
399 }
400 /* else */
401 return NC_NOERR;
402 }
403
404
405 static void
ncio_fileio_init(ncio * const nciop)406 ncio_fileio_init(ncio *const nciop)
407 {
408 ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
409 p
410 *((ncio_relfunc **)&nciop->rel) = ncio_fileio_rel; /* cast away const */
411 *((ncio_getfunc **)&nciop->get) = ncio_fileio_get; /* cast away const */
412 *((ncio_movefunc **)&nciop->move) = ncio_fileio_move; /* cast away const */
413 *((ncio_syncfunc **)&nciop->sync) = ncio_fileio_sync; /* cast away const */
414 *((ncio_freefunc **)&nciop->free) = ncio_fileio_free; /* cast away const */
415
416 ffp->pos = -1;
417 ffp->bf_offset = OFF_NONE;
418 ffp->bf_extent = 0;
419 ffp->bf_cnt = 0;
420 ffp->bf_base = NULL;
421 }
422
423 /* */
424
425 static void
ncio_free(ncio * nciop)426 ncio_free(ncio *nciop)
427 {
428 if(nciop == NULL)
429 return;
430
431 if(nciop->free != NULL)
432 nciop->free(nciop->pvt);
433
434 free(nciop);
435 }
436
437 static ncio *
ncio_new(const char * path,int ioflags)438 ncio_new(const char *path, int ioflags)
439 {
440 size_t sz_ncio = M_RNDUP(sizeof(ncio));
441 size_t sz_path = M_RNDUP(strlen(path) +1);
442 size_t sz_ncio_pvt;
443 ncio *nciop;
444
445 #if ALWAYS_NC_SHARE /* DEBUG */
446 fSet(ioflags, NC_SHARE);
447 #endif
448
449 if(fIsSet(ioflags, NC_SHARE))
450 fprintf(stderr, "NC_SHARE not implemented for fileio\n");
451
452 sz_ncio_pvt = sizeof(ncio_ffio);
453
454 nciop = (ncio *) malloc(sz_ncio + sz_path + sz_ncio_pvt);
455 if(nciop == NULL)
456 return NULL;
457
458 nciop->ioflags = ioflags;
459 *((int *)&nciop->fd) = -1; /* cast away const */
460
461 nciop->path = (char *) ((char *)nciop + sz_ncio);
462 (void) strcpy((char *)nciop->path, path); /* cast away const */
463
464 /* cast away const */
465 *((void **)&nciop->pvt) = (void *)(nciop->path + sz_path);
466
467 ncio_fileio_init(nciop);
468
469 return nciop;
470 }
471
472 /* Public below this point */
473
474 /* TODO: Is this reasonable for this platform? */
475 static const size_t NCIO_MINBLOCKSIZE = 0x100;
476 static const size_t NCIO_MAXBLOCKSIZE = 0x100000;
477
478 int
ncio_create(const char * path,int ioflags,size_t initialsz,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const igetvpp)479 ncio_create(const char *path, int ioflags,
480 size_t initialsz,
481 off_t igeto, size_t igetsz, size_t *sizehintp,
482 void* parameters,
483 ncio **nciopp, void **const igetvpp)
484 {
485 ncio *nciop;
486 #ifdef _WIN32_WCE
487 char* oflags = "bw+"; /*==?(O_RDWR|O_CREAT|O_TRUNC) && binary*/
488 #else
489 char* oflags = "w+"; /*==?(O_RDWR|O_CREAT|O_TRUNC);*/
490 #endif
491 FILE* f;
492 int i,fd;
493 int status = NC_NOERR;
494
495 if(initialsz < (size_t)igeto + igetsz)
496 initialsz = (size_t)igeto + igetsz;
497
498 fSet(ioflags, NC_WRITE);
499
500 if(path == NULL || *path == 0)
501 return EINVAL;
502
503 nciop = ncio_new(path, ioflags);
504 if(nciop == NULL)
505 return ENOMEM;
506
507 if(fIsSet(ioflags, NC_NOCLOBBER)) {
508 /* Since we do not have use of the O_EXCL flag,
509 we need to fake it */
510 #ifdef WINCE
511 f = fopen(path,"rb");
512 #else
513 f = fopen(path,"r");
514 #endif
515 if(f != NULL) { /* do not overwrite */
516 (void)fclose(f);
517 return EEXIST;
518 }
519 }
520
521 f = fopen(path, oflags);
522 if(f == NULL)
523 {
524 status = errno;
525 goto unwind_new;
526 }
527
528 /* Locate an open pseudo file descriptor */
529 fd = -1;
530 for(i=1;i<fdmax;i++) {if(descriptors[i] == NULL) {fd=i;break;}}
531 if(fd < 0) {fd = fdmax; fdmax++;}
532 descriptors[fd] = f;
533
534 *((int *)&nciop->fd) = fd; /* cast away const */
535 if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
536 {
537 /* Use default */
538 *sizehintp = blksize(fd);
539 }
540 else
541 {
542 *sizehintp = M_RNDUP(*sizehintp);
543 }
544
545 status = ncio_fileio_init2(nciop, sizehintp);
546 if(status != NC_NOERR)
547 goto unwind_open;
548
549 if(initialsz != 0)
550 {
551 status = fgrow(f, (off_t)initialsz);
552 if(status != NC_NOERR)
553 goto unwind_open;
554 }
555
556 if(igetsz != 0)
557 {
558 status = nciop->get(nciop,
559 igeto, igetsz,
560 RGN_WRITE,
561 igetvpp);
562 if(status != NC_NOERR)
563 goto unwind_open;
564 }
565
566 *nciopp = nciop;
567 return NC_NOERR;
568
569 unwind_open:
570 (void) fclose(descriptors[fd]);
571 descriptors[fd] = NULL;
572 /* ?? unlink */
573 /*FALLTHRU*/
574 unwind_new:
575 ncio_free(nciop);
576 return status;
577 }
578
579
580 int
ncio_open(const char * path,int ioflags,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const igetvpp)581 ncio_open(const char *path,
582 int ioflags,
583 off_t igeto, size_t igetsz, size_t *sizehintp,
584 void* parameters,
585 ncio **nciopp, void **const igetvpp)
586 {
587 ncio *nciop;
588 char* oflags = fIsSet(ioflags, NC_WRITE) ? "r+"
589 #ifdef WINCE
590 : "rb";
591 #else
592 : "r";
593 #endif
594 FILE* f;
595 int i,fd;
596 int status = NC_NOERR;
597
598 if(path == NULL || *path == 0)
599 return EINVAL;
600
601 nciop = ncio_new(path, ioflags);
602 if(nciop == NULL)
603 return ENOMEM;
604
605 f = fopen(path, oflags);
606
607 if(f == NULL)
608 {
609 status = errno;
610 goto unwind_new;
611 }
612
613 /* Locate an open pseudo file descriptor */
614 fd = -1;
615 for(i=1;i<fdmax;i++) {if(descriptors[i] == NULL) {fd=i;break;}}
616 if(fd < 0) {fd = fdmax; fdmax++;}
617 descriptors[fd] = f;
618
619 *((int *)&nciop->fd) = fd; /* cast away const */
620
621 if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
622 {
623 /* Use default */
624 *sizehintp = blksize(fd);
625 }
626 else
627 {
628 *sizehintp = M_RNDUP(*sizehintp);
629 }
630
631 status = ncio_fileio_init2(nciop, sizehintp);
632 if(status != NC_NOERR)
633 goto unwind_open;
634
635 if(igetsz != 0)
636 {
637 status = nciop->get(nciop,
638 igeto, igetsz,
639 0,
640 igetvpp);
641 if(status != NC_NOERR)
642 goto unwind_open;
643 }
644
645 *nciopp = nciop;
646 return NC_NOERR;
647
648 unwind_open:
649 (void) fclose(descriptors[fd]);
650 descriptors[fd] = NULL;
651 /*FALLTHRU*/
652 unwind_new:
653 ncio_free(nciop);
654 return status;
655 }
656
657
658 /*
659 * Get file size in bytes.
660 * Is use of fstatus = fseek() really necessary, or could we use standard fstat() call
661 * and get st_size member?
662 */
663 int
ncio_filesize(ncio * nciop,off_t * filesizep)664 ncio_filesize(ncio *nciop, off_t *filesizep)
665 {
666 int status = NC_NOERR;
667 off_t filesize, current, reset;
668 FILE* f;
669
670 if(nciop == NULL)
671 return EINVAL;
672
673 f = descriptors[nciop->fd];
674
675 current = ftell(f);
676 status = fseek(f, 0, SEEK_END); /* get size */
677 if(ferror(f)) return EIO;
678 *filesizep = ftell(f);
679 status = fseek(f, current, SEEK_SET); /* reset */
680 if(ferror(f)) return EIO;
681 return NC_NOERR;
682 }
683
684
685 /*
686 * Sync any changes to disk, then extend file so its size is length.
687 * This is only intended to be called before close, if the file is
688 * open for writing and the actual size does not match the calculated
689 * size, perhaps as the result of having been previously written in
690 * NOFILL mode.
691 */
692 int
ncio_pad_length(ncio * nciop,off_t length)693 ncio_pad_length(ncio *nciop, off_t length)
694 {
695 int status = NC_NOERR;
696 FILE* f;
697
698 if(nciop == NULL)
699 return EINVAL;
700
701 f = descriptors[nciop->fd];
702
703 if(!fIsSet(nciop->ioflags, NC_WRITE))
704 return EPERM; /* attempt to write readonly file */
705
706 status = nciop->sync(nciop);
707 if(status != NC_NOERR)
708 return status;
709
710 status = fgrow2(f, length);
711 if(status != NC_NOERR)
712 return errno;
713 return NC_NOERR;
714 }
715
716
717 int
ncio_close(ncio * nciop,int doUnlink)718 ncio_close(ncio *nciop, int doUnlink)
719 {
720 int status = NC_NOERR;
721 FILE* f;
722
723 if(nciop == NULL)
724 return EINVAL;
725
726 f = descriptors[nciop->fd];
727
728 nciop->sync(nciop);
729
730 (void) fclose(f);
731
732 descriptors[nciop->fd] = NULL;
733
734 if(doUnlink)
735 (void) unlink(nciop->path);
736
737 ncio_free(nciop);
738
739 return status;
740 }
741