1 /***************************************************************************
2 * *
3 * LIBDSK: General floppy and diskimage access library *
4 * Copyright (C) 2001,2007 John Elliott <seasip.webmaster@gmail.com> *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Library General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Library General Public *
17 * License along with this library; if not, write to the Free *
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
19 * MA 02111-1307, USA *
20 * *
21 ***************************************************************************/
22
23 /* This driver implements access to a flat file, like drvposix, but with the
24 * sides laid out in the order specified by the disk geometry. What you end
25 * up with is a logical filesystem image, hence the name. */
26
27 #include <stdio.h>
28 #include "libdsk.h"
29 #include "drvi.h"
30 #include "drvlogi.h"
31
32
33 /* This struct contains function pointers to the driver's functions, and the
34 * size of its DSK_DRIVER subclass */
35
36 DRV_CLASS dc_logical =
37 {
38 sizeof(LOGICAL_DSK_DRIVER),
39 "logical",
40 "Raw file logical sector order",
41 logical_open, /* open */
42 logical_creat, /* create new */
43 logical_close, /* close */
44 logical_read, /* read sector, working from physical address */
45 logical_write, /* write sector, working from physical address */
46 logical_format, /* format track, physical */
47 NULL, /* get geometry */
48 NULL, /* sector ID */
49 logical_xseek, /* seek to track */
50 logical_status, /* drive status */
51 };
52
logical_open(DSK_DRIVER * self,const char * filename)53 dsk_err_t logical_open(DSK_DRIVER *self, const char *filename)
54 {
55 LOGICAL_DSK_DRIVER *lpxself;
56
57 /* Sanity check: Is this meant for our driver? */
58 if (self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
59 lpxself = (LOGICAL_DSK_DRIVER *)self;
60
61 lpxself->lpx_fp = fopen(filename, "r+b");
62 if (!lpxself->lpx_fp)
63 {
64 lpxself->lpx_readonly = 1;
65 lpxself->lpx_fp = fopen(filename, "rb");
66 }
67 if (!lpxself->lpx_fp) return DSK_ERR_NOTME;
68 /* v0.9.5: Record exact size, so we can tell if we're writing off the end
69 * of the file. Under Windows, writing off the end of the file fills the
70 * gaps with random data, which can cause mess to appear in the directory;
71 * and under UNIX, the entire directory is filled with zeroes. */
72 if (fseek(lpxself->lpx_fp, 0, SEEK_END)) return DSK_ERR_SYSERR;
73 lpxself->lpx_filesize = ftell(lpxself->lpx_fp);
74
75 return DSK_ERR_OK;
76 }
77
78
logical_creat(DSK_DRIVER * self,const char * filename)79 dsk_err_t logical_creat(DSK_DRIVER *self, const char *filename)
80 {
81 LOGICAL_DSK_DRIVER *lpxself;
82
83 /* Sanity check: Is this meant for our driver? */
84 if (self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
85 lpxself = (LOGICAL_DSK_DRIVER *)self;
86
87 lpxself->lpx_fp = fopen(filename, "w+b");
88 lpxself->lpx_readonly = 0;
89 if (!lpxself->lpx_fp) return DSK_ERR_SYSERR;
90 lpxself->lpx_filesize = 0;
91 return DSK_ERR_OK;
92 }
93
94
logical_close(DSK_DRIVER * self)95 dsk_err_t logical_close(DSK_DRIVER *self)
96 {
97 LOGICAL_DSK_DRIVER *lpxself;
98
99 if (self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
100 lpxself = (LOGICAL_DSK_DRIVER *)self;
101
102 if (lpxself->lpx_fp)
103 {
104 if (fclose(lpxself->lpx_fp) == EOF) return DSK_ERR_SYSERR;
105 lpxself->lpx_fp = NULL;
106 }
107 return DSK_ERR_OK;
108 }
109
110
logical_read(DSK_DRIVER * self,const DSK_GEOMETRY * geom,void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)111 dsk_err_t logical_read(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
112 void *buf, dsk_pcyl_t cylinder,
113 dsk_phead_t head, dsk_psect_t sector)
114 {
115 LOGICAL_DSK_DRIVER *lpxself;
116 dsk_lsect_t offset;
117 dsk_err_t err;
118
119 if (!buf || !self || !geom || self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
120 lpxself = (LOGICAL_DSK_DRIVER *)self;
121
122 if (!lpxself->lpx_fp) return DSK_ERR_NOTRDY;
123
124 err = dg_ps2ls(geom, cylinder, head, sector, &offset);
125 if (err) return err;
126 offset *= geom->dg_secsize;
127
128 if (fseek(lpxself->lpx_fp, offset, SEEK_SET)) return DSK_ERR_SYSERR;
129
130 if (fread(buf, 1, geom->dg_secsize, lpxself->lpx_fp) < geom->dg_secsize)
131 {
132 return DSK_ERR_NOADDR;
133 }
134 return DSK_ERR_OK;
135 }
136
137
seekto(LOGICAL_DSK_DRIVER * self,unsigned long offset)138 static dsk_err_t seekto(LOGICAL_DSK_DRIVER *self, unsigned long offset)
139 {
140 /* 0.9.5: Fill any "holes" in the file with 0xE5. Otherwise, UNIX would
141 * fill them with zeroes and Windows would fill them with whatever
142 * happened to be lying around */
143 if (self->lpx_filesize < offset)
144 {
145 if (fseek(self->lpx_fp, self->lpx_filesize, SEEK_SET)) return DSK_ERR_SYSERR;
146 while (self->lpx_filesize < offset)
147 {
148 if (fputc(0xE5, self->lpx_fp) == EOF) return DSK_ERR_SYSERR;
149 ++self->lpx_filesize;
150 }
151 }
152 if (fseek(self->lpx_fp, offset, SEEK_SET)) return DSK_ERR_SYSERR;
153 return DSK_ERR_OK;
154 }
155
logical_write(DSK_DRIVER * self,const DSK_GEOMETRY * geom,const void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)156 dsk_err_t logical_write(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
157 const void *buf, dsk_pcyl_t cylinder,
158 dsk_phead_t head, dsk_psect_t sector)
159 {
160 LOGICAL_DSK_DRIVER *lpxself;
161 dsk_lsect_t offset;
162 dsk_err_t err;
163
164 if (!buf || !self || !geom || self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
165 lpxself = (LOGICAL_DSK_DRIVER *)self;
166
167 if (!lpxself->lpx_fp) return DSK_ERR_NOTRDY;
168 if (lpxself->lpx_readonly) return DSK_ERR_RDONLY;
169
170 err = dg_ps2ls(geom, cylinder, head, sector, &offset);
171 if (err) return err;
172 offset *= geom->dg_secsize;
173
174 err = seekto(lpxself, offset);
175 if (err) return err;
176
177 if (fwrite(buf, 1, geom->dg_secsize, lpxself->lpx_fp) < geom->dg_secsize)
178 {
179 return DSK_ERR_NOADDR;
180 }
181 if (lpxself->lpx_filesize < offset + geom->dg_secsize)
182 lpxself->lpx_filesize = offset + geom->dg_secsize;
183 return DSK_ERR_OK;
184 }
185
186
logical_format(DSK_DRIVER * self,DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,const DSK_FORMAT * format,unsigned char filler)187 dsk_err_t logical_format(DSK_DRIVER *self, DSK_GEOMETRY *geom,
188 dsk_pcyl_t cylinder, dsk_phead_t head,
189 const DSK_FORMAT *format, unsigned char filler)
190 {
191 /*
192 * Note that we completely ignore the "format" parameter, since raw LOGICAL
193 * images don't hold track headers.
194 */
195 LOGICAL_DSK_DRIVER *lpxself;
196 dsk_lsect_t offset;
197 unsigned long trklen;
198 dsk_err_t err;
199
200 (void)format;
201 if (!self || !geom || self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
202 lpxself = (LOGICAL_DSK_DRIVER *)self;
203
204 if (!lpxself->lpx_fp) return DSK_ERR_NOTRDY;
205 if (lpxself->lpx_readonly) return DSK_ERR_RDONLY;
206
207 trklen = geom->dg_sectors * geom->dg_secsize;
208
209 err = dg_ps2ls(geom, cylinder, head, geom->dg_secbase, &offset);
210 if (err) return err;
211 offset *= geom->dg_secsize;
212
213 err = seekto(lpxself, offset);
214 if (err) return err;
215 if (lpxself->lpx_filesize < offset + trklen)
216 lpxself->lpx_filesize = offset + trklen;
217
218 while (trklen--)
219 if (fputc(filler, lpxself->lpx_fp) == EOF) return DSK_ERR_SYSERR;
220
221 return DSK_ERR_OK;
222 }
223
224
225
logical_xseek(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head)226 dsk_err_t logical_xseek(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
227 dsk_pcyl_t cylinder, dsk_phead_t head)
228 {
229 LOGICAL_DSK_DRIVER *lpxself;
230 dsk_err_t err;
231 dsk_lsect_t offset;
232
233 if (!self || !geom || self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
234 lpxself = (LOGICAL_DSK_DRIVER *)self;
235
236 if (!lpxself->lpx_fp) return DSK_ERR_NOTRDY;
237
238 if (cylinder >= geom->dg_cylinders || head >= geom->dg_heads)
239 return DSK_ERR_SEEKFAIL;
240
241 err = dg_ps2ls(geom, cylinder, head, geom->dg_secbase, &offset);
242 if (err) return err;
243 offset *= geom->dg_secsize;
244
245 if (fseek(lpxself->lpx_fp, offset, SEEK_SET)) return DSK_ERR_SEEKFAIL;
246
247 return DSK_ERR_OK;
248 }
249
logical_status(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_phead_t head,unsigned char * result)250 dsk_err_t logical_status(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
251 dsk_phead_t head, unsigned char *result)
252 {
253 LOGICAL_DSK_DRIVER *lpxself;
254
255 if (!self || !geom || self->dr_class != &dc_logical) return DSK_ERR_BADPTR;
256 lpxself = (LOGICAL_DSK_DRIVER *)self;
257
258 if (!lpxself->lpx_fp) *result &= ~DSK_ST3_READY;
259 if (lpxself->lpx_readonly) *result |= DSK_ST3_RO;
260 return DSK_ERR_OK;
261 }
262
263