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#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 */
77typedef struct file_media *FILE_MEDIA;
78
79struct file_media {
80    struct media	m;
81    int			fd;
82    int			regular_file;
83};
84
85struct file_media_globals {
86    long		exists;
87    long		kind;
88};
89
90typedef struct file_media_iterator *FILE_MEDIA_ITERATOR;
91
92struct file_media_iterator {
93    struct media_iterator   m;
94    long		    style;
95    long		    index;
96};
97
98
99/*
100 * Global Constants
101 */
102int potential_block_sizes[] = {
103    1, 512, 1024, 2048, 4096, 8192, 16834,
104    0
105};
106
107enum {
108    kSCSI_Disks = 0,
109    kATA_Devices = 1,
110    kSCSI_CDs = 2,
111    kMaxStyle = 2
112};
113
114
115/*
116 * Global Variables
117 */
118static long file_inited = 0;
119static struct file_media_globals file_info;
120
121/*
122 * Forward declarations
123 */
124int compute_block_size(int fd);
125void file_init(void);
126FILE_MEDIA new_file_media(void);
127long read_file_media(MEDIA m, long long offset, uint32_t count, void *address);
128long write_file_media(MEDIA m, long long offset, uint32_t count, void *address);
129long close_file_media(MEDIA m);
130long os_reload_file_media(MEDIA m);
131FILE_MEDIA_ITERATOR new_file_iterator(void);
132void reset_file_iterator(MEDIA_ITERATOR m);
133char *step_file_iterator(MEDIA_ITERATOR m);
134void delete_file_iterator(MEDIA_ITERATOR m);
135
136
137/*
138 * Routines
139 */
140void
141file_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
152FILE_MEDIA
153new_file_media(void)
154{
155    return (FILE_MEDIA) new_media(sizeof(struct file_media));
156}
157
158
159int
160compute_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
201MEDIA
202open_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
251long
252read_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
296long
297write_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
332long
333close_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
350long
351os_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
407FILE_MEDIA_ITERATOR
408new_file_iterator(void)
409{
410    return (FILE_MEDIA_ITERATOR) new_media_iterator(sizeof(struct file_media_iterator));
411}
412
413
414MEDIA_ITERATOR
415create_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
438void
439reset_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
454char *
455step_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
574void
575delete_file_iterator(MEDIA_ITERATOR m)
576{
577    return;
578}
579