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