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