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