xref: /netbsd-src/external/bsd/pdisk/dist/file_media.c (revision 48a628ae0434c4247b560ad8f2eb1dc06d0dd070)
1 /*
2  * file_media.c -
3  *
4  * Written by Eryk Vershen
5  */
6 
7 /*
8  * Copyright 1997,1998 by Apple Computer, Inc.
9  *              All Rights Reserved
10  *
11  * Permission to use, copy, modify, and distribute this software and
12  * its documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appears in all copies and
14  * that both the copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE.
20  *
21  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26  */
27 
28 // for printf()
29 #include <stdio.h>
30 // for malloc() & free()
31 #include <stdlib.h>
32 // for lseek(), read(), write(), close()
33 #include <unistd.h>
34 // for open()
35 #include <fcntl.h>
36 // for LONG_MAX
37 #include <limits.h>
38 // for errno
39 #include <errno.h>
40 
41 #ifdef __linux__
42 #include <sys/ioctl.h>
43 #include <linux/fs.h>
44 #include <linux/hdreg.h>
45 #include <sys/stat.h>
46 #else
47 #ifdef __unix__
48 #include <sys/ioctl.h>
49 #include <sys/stat.h>
50 #endif
51 #endif
52 
53 #include "file_media.h"
54 #include "errors.h"
55 
56 
57 /*
58  * Defines
59  */
60 #ifdef __linux__
61 #define LOFF_MAX 9223372036854775807LL
62 extern __loff_t llseek __P ((int __fd, __loff_t __offset, int __whence));
63 #elif defined(__NetBSD__) || defined(__APPLE__)
64 #define loff_t off_t
65 #define llseek lseek
66 #define LOFF_MAX LLONG_MAX
67 #else
68 #define loff_t long
69 #define llseek lseek
70 #define LOFF_MAX LONG_MAX
71 #endif
72 
73 
74 /*
75  * Types
76  */
77 typedef struct file_media *FILE_MEDIA;
78 
79 struct file_media {
80     struct media	m;
81     int			fd;
82     int			regular_file;
83 };
84 
85 struct file_media_globals {
86     long		exists;
87     long		kind;
88 };
89 
90 typedef struct file_media_iterator *FILE_MEDIA_ITERATOR;
91 
92 struct file_media_iterator {
93     struct media_iterator   m;
94     long		    style;
95     long		    index;
96 };
97 
98 
99 /*
100  * Global Constants
101  */
102 int potential_block_sizes[] = {
103     1, 512, 1024, 2048, 4096, 8192, 16834,
104     0
105 };
106 
107 enum {
108     kSCSI_Disks = 0,
109     kATA_Devices = 1,
110     kSCSI_CDs = 2,
111     kMaxStyle = 2
112 };
113 
114 
115 /*
116  * Global Variables
117  */
118 static long file_inited = 0;
119 static struct file_media_globals file_info;
120 
121 /*
122  * Forward declarations
123  */
124 int compute_block_size(int fd);
125 void file_init(void);
126 FILE_MEDIA new_file_media(void);
127 long read_file_media(MEDIA m, long long offset, uint32_t count, void *address);
128 long write_file_media(MEDIA m, long long offset, uint32_t count, void *address);
129 long close_file_media(MEDIA m);
130 long os_reload_file_media(MEDIA m);
131 FILE_MEDIA_ITERATOR new_file_iterator(void);
132 void reset_file_iterator(MEDIA_ITERATOR m);
133 char *step_file_iterator(MEDIA_ITERATOR m);
134 void delete_file_iterator(MEDIA_ITERATOR m);
135 
136 
137 /*
138  * Routines
139  */
140 void
file_init(void)141 file_init(void)
142 {
143     if (file_inited != 0) {
144 	return;
145     }
146     file_inited = 1;
147 
148     file_info.kind = allocate_media_kind();
149 }
150 
151 
152 FILE_MEDIA
new_file_media(void)153 new_file_media(void)
154 {
155     return (FILE_MEDIA) new_media(sizeof(struct file_media));
156 }
157 
158 
159 int
compute_block_size(int fd)160 compute_block_size(int fd)
161 {
162     int size;
163     int max_size;
164     loff_t x;
165     long t;
166     int i;
167     char *buffer;
168 
169     max_size = 0;
170     for (i = 0; ; i++) {
171     	size = potential_block_sizes[i];
172     	if (size == 0) {
173 	    break;
174     	}
175     	if (max_size < size) {
176 	    max_size = size;
177     	}
178     }
179 
180     buffer = malloc(max_size);
181     if (buffer != 0) {
182 	for (i = 0; ; i++) {
183 	    size = potential_block_sizes[i];
184 	    if (size == 0) {
185 		break;
186 	    }
187 	    if ((x = llseek(fd, (loff_t)0, 0)) < 0) {
188 		error(errno, "Can't seek on file");
189 		break;
190 	    }
191 	    if ((t = read(fd, buffer, size)) == size) {
192 		free(buffer);
193 		return size;
194 	    }
195 	}
196     }
197     return 0;
198 }
199 
200 
201 MEDIA
open_file_as_media(char * file,int oflag)202 open_file_as_media(char *file, int oflag)
203 {
204     FILE_MEDIA	a;
205     int			fd;
206     loff_t off;
207 #if defined(__linux__) || defined(__unix__)
208     struct stat info;
209 #endif
210 
211     if (file_inited == 0) {
212 	    file_init();
213     }
214 
215     a = 0;
216     fd = open(file, oflag);
217     if (fd >= 0) {
218 	a = new_file_media();
219 	if (a != 0) {
220 	    a->m.kind = file_info.kind;
221 	    a->m.grain = compute_block_size(fd);
222 	    off = llseek(fd, (loff_t)0, 2);	/* seek to end of media */
223 #if !defined(__linux__) && !defined(__unix__)
224 	    if (off <= 0) {
225 		off = 1; /* XXX not right? */
226 	    }
227 #endif
228 	    //printf("file size = %Ld\n", off);
229 	    a->m.size_in_bytes = (long long) off;
230 	    a->m.do_read = read_file_media;
231 	    a->m.do_write = write_file_media;
232 	    a->m.do_close = close_file_media;
233 	    a->m.do_os_reload = os_reload_file_media;
234 	    a->fd = fd;
235 	    a->regular_file = 0;
236 #if defined(__linux__) || defined(__unix__)
237 	    if (fstat(fd, &info) < 0) {
238 		error(errno, "can't stat file '%s'", file);
239 	    } else {
240 		a->regular_file = S_ISREG(info.st_mode);
241 	    }
242 #endif
243 	} else {
244 	    close(fd);
245 	}
246     }
247     return (MEDIA) a;
248 }
249 
250 
251 long
read_file_media(MEDIA m,long long offset,uint32_t count,void * address)252 read_file_media(MEDIA m, long long offset, uint32_t count, void *address)
253 {
254     FILE_MEDIA a;
255     long rtn_value;
256     loff_t off;
257     int t;
258 
259     a = (FILE_MEDIA) m;
260     rtn_value = 0;
261     if (a == 0) {
262 	/* no media */
263 	fprintf(stderr,"no media\n");
264     } else if (a->m.kind != file_info.kind) {
265 	/* wrong kind - XXX need to error here - this is an internal problem */
266 	fprintf(stderr,"wrong kind\n");
267     } else if (count <= 0 || count % a->m.grain != 0) {
268 	/* can't handle size */
269 	fprintf(stderr,"bad size\n");
270     } else if (offset < 0 || offset % a->m.grain != 0) {
271 	/* can't handle offset */
272 	fprintf(stderr,"bad offset\n");
273     } else if (offset + (long long) count > a->m.size_in_bytes && a->m.size_in_bytes != (long long) 0) {
274 	/* check for offset (and offset+count) too large */
275 	fprintf(stderr,"offset+count too large\n");
276     } else if (offset + count > (long long) LOFF_MAX) {
277 	/* check for offset (and offset+count) too large */
278 	fprintf(stderr,"offset+count too large 2\n");
279     } else {
280 	/* do the read */
281 	off = offset;
282 	if ((off = llseek(a->fd, off, 0)) >= 0) {
283 	    if ((t = read(a->fd, address, count)) == (ssize_t)count) {
284 		rtn_value = 1;
285 	    } else {
286 		fprintf(stderr,"read failed\n");
287 	    }
288 	} else {
289 	    fprintf(stderr,"lseek failed\n");
290 	}
291     }
292     return rtn_value;
293 }
294 
295 
296 long
write_file_media(MEDIA m,long long offset,uint32_t count,void * address)297 write_file_media(MEDIA m, long long offset, uint32_t count, void *address)
298 {
299     FILE_MEDIA a;
300     long rtn_value;
301     loff_t off;
302     int t;
303 
304     a = (FILE_MEDIA) m;
305     rtn_value = 0;
306     if (a == 0) {
307 	/* no media */
308     } else if (a->m.kind != file_info.kind) {
309 	/* wrong kind - XXX need to error here - this is an internal problem */
310     } else if (count <= 0 || count % a->m.grain != 0) {
311 	/* can't handle size */
312     } else if (offset < 0 || offset % a->m.grain != 0) {
313 	/* can't handle offset */
314     } else if (offset + count > (long long) LOFF_MAX) {
315 	/* check for offset (and offset+count) too large */
316     } else {
317 	/* do the write  */
318 	off = offset;
319 	if ((off = llseek(a->fd, off, 0)) >= 0) {
320 		if ((t = write(a->fd, address, count)) == (ssize_t)count) {
321 		if (off + (long long) count > a->m.size_in_bytes) {
322 			a->m.size_in_bytes = off + count;
323 		}
324 		rtn_value = 1;
325 	    }
326 	}
327     }
328     return rtn_value;
329 }
330 
331 
332 long
close_file_media(MEDIA m)333 close_file_media(MEDIA m)
334 {
335     FILE_MEDIA a;
336 
337     a = (FILE_MEDIA) m;
338     if (a == 0) {
339 	return 0;
340     } else if (a->m.kind != file_info.kind) {
341 	/* XXX need to error here - this is an internal problem */
342 	return 0;
343     }
344 
345     close(a->fd);
346     return 1;
347 }
348 
349 
350 long
os_reload_file_media(MEDIA m)351 os_reload_file_media(MEDIA m)
352 {
353     FILE_MEDIA a;
354     long rtn_value;
355 #if defined(__linux__)
356     int i;
357     int saved_errno;
358 #endif
359 
360     a = (FILE_MEDIA) m;
361     rtn_value = 0;
362     if (a == 0) {
363 	/* no media */
364     } else if (a->m.kind != file_info.kind) {
365 	/* wrong kind - XXX need to error here - this is an internal problem */
366     } else if (a->regular_file) {
367 	/* okay - nothing to do */
368 	rtn_value = 1;
369     } else {
370 #ifdef __linux__
371 	sync();
372 	sleep(2);
373 	if ((i = ioctl(a->fd, BLKRRPART)) != 0) {
374 	    saved_errno = errno;
375 	} else {
376 	    // some kernel versions (1.2.x) seem to have trouble
377 	    // rereading the partition table, but if asked to do it
378 	    // twice, the second time works. - biro@yggdrasil.com */
379 	    sync();
380 	    sleep(2);
381 	    if ((i = ioctl(a->fd, BLKRRPART)) != 0) {
382 		saved_errno = errno;
383 	    }
384 	}
385 
386 	// printf("Syncing disks.\n");
387 	sync();
388 	sleep(4);		/* for sync() */
389 
390 	if (i < 0) {
391 	    error(saved_errno, "Re-read of partition table failed");
392 	    printf("Reboot your system to ensure the "
393 		    "partition table is updated.\n");
394 	}
395 #endif
396 	rtn_value = 1;
397     }
398     return rtn_value;
399 }
400 
401 
402 #if !defined(__linux__) && !defined(__unix__)
403 #pragma mark -
404 #endif
405 
406 
407 FILE_MEDIA_ITERATOR
new_file_iterator(void)408 new_file_iterator(void)
409 {
410     return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator));
411 }
412 
413 
414 MEDIA_ITERATOR
create_file_iterator(void)415 create_file_iterator(void)
416 {
417     FILE_MEDIA_ITERATOR a;
418 
419     if (file_inited == 0) {
420 	file_init();
421     }
422 
423     a = new_file_iterator();
424     if (a != 0) {
425 	a->m.kind = file_info.kind;
426 	a->m.state = kInit;
427 	a->m.do_reset = reset_file_iterator;
428 	a->m.do_step = step_file_iterator;
429 	a->m.do_delete = delete_file_iterator;
430 	a->style = 0;
431 	a->index = 0;
432     }
433 
434     return (MEDIA_ITERATOR) a;
435 }
436 
437 
438 void
reset_file_iterator(MEDIA_ITERATOR m)439 reset_file_iterator(MEDIA_ITERATOR m)
440 {
441     FILE_MEDIA_ITERATOR a;
442 
443     a = (FILE_MEDIA_ITERATOR) m;
444     if (a == 0) {
445 	/* no media */
446     } else if (a->m.kind != file_info.kind) {
447 	/* wrong kind - XXX need to error here - this is an internal problem */
448     } else if (a->m.state != kInit) {
449 	a->m.state = kReset;
450     }
451 }
452 
453 
454 char *
step_file_iterator(MEDIA_ITERATOR m)455 step_file_iterator(MEDIA_ITERATOR m)
456 {
457     FILE_MEDIA_ITERATOR a;
458     char *result;
459     struct stat info;
460     int	fd;
461     int bump;
462     int value;
463 
464     a = (FILE_MEDIA_ITERATOR) m;
465     if (a == 0) {
466 	/* no media */
467     } else if (a->m.kind != file_info.kind) {
468 	/* wrong kind - XXX need to error here - this is an internal problem */
469     } else {
470 	switch (a->m.state) {
471 	case kInit:
472 	    a->m.state = kReset;
473 	    /* fall through to reset */
474 	case kReset:
475 	    a->style = 0 /* first style */;
476 	    a->index = 0 /* first index */;
477 	    a->m.state = kIterating;
478 	    /* fall through to iterate */
479 	case kIterating:
480 	    while (1) {
481 		if (a->style > kMaxStyle) {
482 		    break;
483 		}
484 #ifndef notdef
485 		/* if old version of mklinux then skip CD drive */
486 		if (a->style == kSCSI_Disks && a->index == 3) {
487 		    a->index += 1;
488 		}
489 #endif
490 		/* generate result */
491 		result = (char *) malloc(20);
492 		if (result != NULL) {
493 		    /*
494 		     * for DR3 we should actually iterate through:
495 		     *
496 		     *    /dev/sd[a...]    # first missing is end of list
497 		     *    /dev/hd[a...]    # may be holes in sequence
498 		     *    /dev/scd[0...]   # first missing is end of list
499 		     *
500 		     * and stop in each group when either a stat of
501 		     * the name fails or if an open fails for
502 		     * particular reasons.
503 		     */
504 		    bump = 0;
505 		    value = (int) a->index;
506 		    switch (a->style) {
507 		    case kSCSI_Disks:
508 			if (value < 26) {
509 			    snprintf(result, 20, "/dev/sd%c", 'a'+value);
510 			} else if (value < 676) {
511 			    snprintf(result, 20, "/dev/sd%c%c",
512 				    'a' + value / 26,
513 				    'a' + value % 26);
514 			} else {
515 			    bump = -1;
516 			}
517 			break;
518 		    case kATA_Devices:
519 			if (value < 26) {
520 			    snprintf(result, 20, "/dev/hd%c", 'a'+value);
521 			} else {
522 			    bump = -1;
523 			}
524 			break;
525 		    case kSCSI_CDs:
526 			if (value < 10) {
527 			    snprintf(result, 20, "/dev/scd%c", '0'+value);
528 			} else {
529 			    bump = -1;
530 			}
531 			break;
532 		    }
533 		    if (bump != 0) {
534 			// already set don't even check
535 		    } else if (stat(result, &info) < 0) {
536 			bump = 1;
537 		    } else if ((fd = open(result, O_RDONLY)) >= 0) {
538 			close(fd);
539 #if defined(__linux__) || defined(__unix__)
540 		    } else if (errno == ENXIO || errno == ENODEV) {
541 			if (a->style == kATA_Devices) {
542 			    bump = -1;
543 			} else {
544 			    bump = 1;
545 			}
546 #endif
547 		    }
548 		    if (bump) {
549 			if (bump > 0) {
550 			    a->style += 1; /* next style */
551 			    a->index = 0; /* first index again */
552 			} else {
553 			    a->index += 1; /* next index */
554 			}
555 			free(result);
556 			continue;
557 		    }
558 		}
559 
560 		a->index += 1; /* next index */
561 		return result;
562 	    }
563 	    a->m.state = kEnd;
564 	    /* fall through to end */
565 	case kEnd:
566 	default:
567 	    break;
568 	}
569     }
570     return 0 /* no entry */;
571 }
572 
573 
574 void
delete_file_iterator(MEDIA_ITERATOR m)575 delete_file_iterator(MEDIA_ITERATOR m)
576 {
577     return;
578 }
579