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