xref: /openbsd-src/sbin/pdisk/file_media.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 
44 #include "file_media.h"
45 #include "errors.h"
46 
47 
48 /*
49  * Defines
50  */
51 #define loff_t off_t
52 #define llseek lseek
53 #define LOFF_MAX LLONG_MAX
54 
55 
56 /*
57  * Types
58  */
59 typedef struct file_media *FILE_MEDIA;
60 
61 struct file_media {
62     struct media	m;
63     int			fd;
64     int			regular_file;
65 };
66 
67 struct file_media_globals {
68     long		exists;
69     long		kind;
70 };
71 
72 typedef struct file_media_iterator *FILE_MEDIA_ITERATOR;
73 
74 struct file_media_iterator {
75     struct media_iterator   m;
76     long		    style;
77     long		    index;
78 };
79 
80 
81 /*
82  * Global Constants
83  */
84 int potential_block_sizes[] = {
85     1, 512, 1024, 2048,
86     0
87 };
88 
89 enum {
90     kSCSI_Disks = 0,
91     kATA_Devices = 1,
92     kSCSI_CDs = 2,
93     kMaxStyle = 2
94 };
95 
96 
97 /*
98  * Global Variables
99  */
100 static long file_inited = 0;
101 static struct file_media_globals file_info;
102 
103 /*
104  * Forward declarations
105  */
106 int compute_block_size(int fd);
107 void file_init(void);
108 FILE_MEDIA new_file_media(void);
109 long read_file_media(MEDIA m, long long offset, unsigned long count, void *address);
110 long write_file_media(MEDIA m, long long offset, unsigned long count, void *address);
111 long close_file_media(MEDIA m);
112 long os_reload_file_media(MEDIA m);
113 FILE_MEDIA_ITERATOR new_file_iterator(void);
114 void reset_file_iterator(MEDIA_ITERATOR m);
115 char *step_file_iterator(MEDIA_ITERATOR m);
116 void delete_file_iterator(MEDIA_ITERATOR m);
117 
118 
119 /*
120  * Routines
121  */
122 void
123 file_init(void)
124 {
125     if (file_inited != 0) {
126 	return;
127     }
128     file_inited = 1;
129 
130     file_info.kind = allocate_media_kind();
131 }
132 
133 
134 FILE_MEDIA
135 new_file_media(void)
136 {
137     return (FILE_MEDIA) new_media(sizeof(struct file_media));
138 }
139 
140 
141 int
142 compute_block_size(int fd)
143 {
144     int size;
145     int max_size;
146     loff_t x;
147     long t;
148     int i;
149     char *buffer;
150 
151     max_size = 0;
152     for (i = 0; ; i++) {
153     	size = potential_block_sizes[i];
154     	if (size == 0) {
155 	    break;
156     	}
157     	if (max_size < size) {
158 	    max_size = size;
159     	}
160     }
161 
162     buffer = malloc(max_size);
163     if (buffer != 0) {
164 	for (i = 0; ; i++) {
165 	    size = potential_block_sizes[i];
166 	    if (size == 0) {
167 		break;
168 	    }
169 	    if ((x = llseek(fd, (loff_t)0, SEEK_SET)) < 0) {
170 		error(errno, "Can't seek on file");
171 		break;
172 	    }
173 	    if ((t = read(fd, buffer, size)) == size) {
174 		free(buffer);
175 		return size;
176 	    }
177 	}
178     }
179     return 0;
180 }
181 
182 
183 MEDIA
184 open_file_as_media(char *file, int oflag)
185 {
186     FILE_MEDIA	a;
187     int			fd;
188     loff_t off;
189     struct stat info;
190 
191     if (file_inited == 0) {
192 	    file_init();
193     }
194 
195     a = 0;
196     fd = open(file, oflag);
197     if (fd >= 0) {
198 	a = new_file_media();
199 	if (a != 0) {
200 	    a->m.kind = file_info.kind;
201 	    a->m.grain = compute_block_size(fd);
202 	    off = llseek(fd, (loff_t)0, SEEK_END);	/* seek to end of media */
203 	    //printf("file size = %Ld\n", off);
204 	    a->m.size_in_bytes = (long long) off;
205 	    a->m.do_read = read_file_media;
206 	    a->m.do_write = write_file_media;
207 	    a->m.do_close = close_file_media;
208 	    a->m.do_os_reload = os_reload_file_media;
209 	    a->fd = fd;
210 	    a->regular_file = 0;
211 	    if (fstat(fd, &info) < 0) {
212 		error(errno, "can't stat file '%s'", file);
213 	    } else {
214 		a->regular_file = S_ISREG(info.st_mode);
215 	    }
216 	} else {
217 	    close(fd);
218 	}
219     }
220     return (MEDIA) a;
221 }
222 
223 
224 long
225 read_file_media(MEDIA m, long long offset, unsigned long count, void *address)
226 {
227     FILE_MEDIA a;
228     long rtn_value;
229     loff_t off;
230     int t;
231 
232     a = (FILE_MEDIA) m;
233     rtn_value = 0;
234     if (a == 0) {
235 	/* no media */
236 	fprintf(stderr,"no media\n");
237     } else if (a->m.kind != file_info.kind) {
238 	/* wrong kind - XXX need to error here - this is an internal problem */
239 	fprintf(stderr,"wrong kind\n");
240     } else if (count <= 0 || count % a->m.grain != 0) {
241 	/* can't handle size */
242 	fprintf(stderr,"bad size\n");
243     } else if (offset < 0 || offset % a->m.grain != 0) {
244 	/* can't handle offset */
245 	fprintf(stderr,"bad offset\n");
246     } else if (offset + count > a->m.size_in_bytes && a->m.size_in_bytes != (long long) 0) {
247 	/* check for offset (and offset+count) too large */
248 	fprintf(stderr,"offset+count too large\n");
249     } else if (offset + count > (long long) LOFF_MAX) {
250 	/* check for offset (and offset+count) too large */
251 	fprintf(stderr,"offset+count too large 2\n");
252     } else {
253 	/* do the read */
254 	off = offset;
255 	if ((off = llseek(a->fd, off, SEEK_SET)) >= 0) {
256 	    if ((t = read(a->fd, address, count)) == count) {
257 		rtn_value = 1;
258 	    } else {
259 		fprintf(stderr,"read failed\n");
260 	    }
261 	} else {
262 	    fprintf(stderr,"lseek failed\n");
263 	}
264     }
265     return rtn_value;
266 }
267 
268 
269 long
270 write_file_media(MEDIA m, long long offset, unsigned long count, void *address)
271 {
272     FILE_MEDIA a;
273     long rtn_value;
274     loff_t off;
275     int t;
276 
277     a = (FILE_MEDIA) m;
278     rtn_value = 0;
279     if (a == 0) {
280 	/* no media */
281     } else if (a->m.kind != file_info.kind) {
282 	/* wrong kind - XXX need to error here - this is an internal problem */
283     } else if (count <= 0 || count % a->m.grain != 0) {
284 	/* can't handle size */
285     } else if (offset < 0 || offset % a->m.grain != 0) {
286 	/* can't handle offset */
287     } else if (offset + count > (long long) LOFF_MAX) {
288 	/* check for offset (and offset+count) too large */
289     } else {
290 	/* do the write  */
291 	off = offset;
292 	if ((off = llseek(a->fd, off, SEEK_SET)) >= 0) {
293 	    if ((t = write(a->fd, address, count)) == count) {
294 		if (off + count > a->m.size_in_bytes) {
295 			a->m.size_in_bytes = off + count;
296 		}
297 		rtn_value = 1;
298 	    }
299 	}
300     }
301     return rtn_value;
302 }
303 
304 
305 long
306 close_file_media(MEDIA m)
307 {
308     FILE_MEDIA a;
309 
310     a = (FILE_MEDIA) m;
311     if (a == 0) {
312 	return 0;
313     } else if (a->m.kind != file_info.kind) {
314 	/* XXX need to error here - this is an internal problem */
315 	return 0;
316     }
317 
318     close(a->fd);
319     return 1;
320 }
321 
322 
323 long
324 os_reload_file_media(MEDIA m)
325 {
326     FILE_MEDIA a;
327     long rtn_value;
328 
329     a = (FILE_MEDIA) m;
330     rtn_value = 0;
331     if (a == 0) {
332 	/* no media */
333     } else if (a->m.kind != file_info.kind) {
334 	/* wrong kind - XXX need to error here - this is an internal problem */
335     } else if (a->regular_file) {
336 	/* okay - nothing to do */
337 	rtn_value = 1;
338     } else {
339 	rtn_value = 1;
340     }
341     return rtn_value;
342 }
343 
344 
345 FILE_MEDIA_ITERATOR
346 new_file_iterator(void)
347 {
348     return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator));
349 }
350 
351 
352 MEDIA_ITERATOR
353 create_file_iterator(void)
354 {
355     FILE_MEDIA_ITERATOR a;
356 
357     if (file_inited == 0) {
358 	file_init();
359     }
360 
361     a = new_file_iterator();
362     if (a != 0) {
363 	a->m.kind = file_info.kind;
364 	a->m.state = kInit;
365 	a->m.do_reset = reset_file_iterator;
366 	a->m.do_step = step_file_iterator;
367 	a->m.do_delete = delete_file_iterator;
368 	a->style = 0;
369 	a->index = 0;
370     }
371 
372     return (MEDIA_ITERATOR) a;
373 }
374 
375 
376 void
377 reset_file_iterator(MEDIA_ITERATOR m)
378 {
379     FILE_MEDIA_ITERATOR a;
380 
381     a = (FILE_MEDIA_ITERATOR) m;
382     if (a == 0) {
383 	/* no media */
384     } else if (a->m.kind != file_info.kind) {
385 	/* wrong kind - XXX need to error here - this is an internal problem */
386     } else if (a->m.state != kInit) {
387 	a->m.state = kReset;
388     }
389 }
390 
391 
392 char *
393 step_file_iterator(MEDIA_ITERATOR m)
394 {
395     FILE_MEDIA_ITERATOR a;
396     char *result;
397     struct stat info;
398     int	fd;
399     int bump;
400     int value;
401 
402     a = (FILE_MEDIA_ITERATOR) m;
403     if (a == 0) {
404 	/* no media */
405     } else if (a->m.kind != file_info.kind) {
406 	/* wrong kind - XXX need to error here - this is an internal problem */
407     } else {
408 	switch (a->m.state) {
409 	case kInit:
410 	    a->m.state = kReset;
411 	    /* fall through to reset */
412 	case kReset:
413 	    a->style = 0 /* first style */;
414 	    a->index = 0 /* first index */;
415 	    a->m.state = kIterating;
416 	    /* fall through to iterate */
417 	case kIterating:
418 	    while (1) {
419 		if (a->style > kMaxStyle) {
420 		    break;
421 		}
422 #ifndef notdef
423 		/* if old version of mklinux then skip CD drive */
424 		if (a->style == kSCSI_Disks && a->index == 3) {
425 		    a->index += 1;
426 		}
427 #endif
428 		/* generate result */
429 		result = (char *) malloc(20);
430 		if (result != NULL) {
431 		    /*
432 		     * for DR3 we should actually iterate through:
433 		     *
434 		     *    /dev/sd[a...]    # first missing is end of list
435 		     *    /dev/hd[a...]    # may be holes in sequence
436 		     *    /dev/scd[0...]   # first missing is end of list
437 		     *
438 		     * and stop in each group when either a stat of
439 		     * the name fails or if an open fails for
440 		     * particular reasons.
441 		     */
442 		    bump = 0;
443 		    value = (int) a->index;
444 		    switch (a->style) {
445 		    case kSCSI_Disks:
446 			if (value < 26) {
447 			    snprintf(result, 20, "/dev/sd%c", 'a'+value);
448 			} else if (value < 676) {
449 			    snprintf(result, 20, "/dev/sd%c%c",
450 				    'a' + value / 26,
451 				    'a' + value % 26);
452 			} else {
453 			    bump = -1;
454 			}
455 			break;
456 		    case kATA_Devices:
457 			if (value < 26) {
458 			    snprintf(result, 20, "/dev/hd%c", 'a'+value);
459 			} else {
460 			    bump = -1;
461 			}
462 			break;
463 		    case kSCSI_CDs:
464 			if (value < 10) {
465 			    snprintf(result, 20, "/dev/scd%c", '0'+value);
466 			} else {
467 			    bump = -1;
468 			}
469 			break;
470 		    }
471 		    if (bump != 0) {
472 			// already set don't even check
473 		    } else if (stat(result, &info) < 0) {
474 			bump = 1;
475 		    } else if ((fd = open(result, O_RDONLY)) >= 0) {
476 			close(fd);
477 		    } else if (errno == ENXIO || errno == ENODEV) {
478 			if (a->style == kATA_Devices) {
479 			    bump = -1;
480 			} else {
481 			    bump = 1;
482 			}
483 		    }
484 		    if (bump) {
485 			if (bump > 0) {
486 			    a->style += 1; /* next style */
487 			    a->index = 0; /* first index again */
488 			} else {
489 			    a->index += 1; /* next index */
490 			}
491 			free(result);
492 			continue;
493 		    }
494 		}
495 
496 		a->index += 1; /* next index */
497 		return result;
498 	    }
499 	    a->m.state = kEnd;
500 	    /* fall through to end */
501 	case kEnd:
502 	default:
503 	    break;
504 	}
505     }
506     return 0 /* no entry */;
507 }
508 
509 
510 void
511 delete_file_iterator(MEDIA_ITERATOR m)
512 {
513     return;
514 }
515