OpenCBM
fs.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 1999-2004 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8 */
9 
10 #include "d64copy_int.h"
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "arch.h"
17 
18 static d64copy_settings *fs_settings;
19 
20 static FILE *the_file;
21 static char *error_map;
22 static int block_count;
23 
24 /* always use maximum size for error map */
25 #define ERROR_MAP_LENGTH D71_BLOCKS
26 
27 static int block_offset(int tr, int se)
28 {
29  int sectors = 0, i;
30  for(i = 1; i < tr; i++)
31  {
32  sectors += d64copy_sector_count(fs_settings->two_sided, i);
33  }
34  return (sectors + se) * BLOCKSIZE;
35 }
36 
37 static int read_block(unsigned char tr, unsigned char se, unsigned char *block)
38 {
39  if(fseek(the_file, block_offset(tr, se), SEEK_SET) == 0)
40  {
41  return fread(block, BLOCKSIZE, 1, the_file) != 1;
42  }
43  return 1;
44 }
45 
46 /*
47  * Variables to make sure writing the block is an atomary process
48  */
49 static int atom_execute = 0;
50 static unsigned char atom_tr;
51 static unsigned char atom_se;
52 static const unsigned char *atom_blk;
53 static int atom_size;
54 static int atom_read_status;
55 
56 static int write_block(unsigned char tr, unsigned char se, const unsigned char *blk, int size, int read_status)
57 {
58  long ofs;
59  int ret;
60 
61  atom_tr = tr;
62  atom_se = se;
63  atom_blk = blk;
64  atom_size = size;
65  atom_read_status = read_status;
66 
67  atom_execute = 1;
68 
69  ofs = block_offset(tr, se);
70  if(fseek(the_file, ofs, SEEK_SET) == 0)
71  {
72  error_map[ofs / BLOCKSIZE] = (char) ((read_status == 0) ? 1 : read_status);
73  ret = fwrite(blk, size, 1, the_file) != 1;
74  }
75  else
76  {
77  ret = 1;
78  }
79 
80  atom_execute = 0;
81 
82  return ret;
83 }
84 
85 static int open_disk(CBM_FILE fd, d64copy_settings *settings,
86  const void *arg, int for_writing,
87  turbo_start start, d64copy_message_cb message_cb)
88 {
89  off_t filesize;
90  int stat_ok, is_image, error_info;
91  int tr = 0;
92  char *name = (char*)arg;
93 
94  the_file = NULL;
95  fs_settings = settings;
96  block_count = 0;
97 
98  stat_ok = arch_filesize(name, &filesize) == 0;
99  is_image = error_info = 0;
100 
101  if(stat_ok)
102  {
103  if(filesize == D71_BLOCKS * BLOCKSIZE)
104  {
105  is_image = 1;
106  block_count = D71_BLOCKS;
107  tr = D71_TRACKS;
108  }
109  else if(filesize == D71_BLOCKS * (BLOCKSIZE + 1))
110  {
111  is_image = 1;
112  error_info = 1;
113  block_count = D71_BLOCKS;
114  tr = D71_TRACKS;
115  }
116  else
117  {
118  block_count = STD_BLOCKS;
119  for( tr = STD_TRACKS; !is_image && tr <= TOT_TRACKS; )
120  {
121  is_image = filesize == block_count * BLOCKSIZE;
122  if(!is_image)
123  {
124  error_info = is_image =
125  filesize == block_count * (BLOCKSIZE + 1);
126  }
127  if(!is_image)
128  {
129  block_count += d64copy_sector_count( 0, tr++ );
130  }
131  }
132  if( is_image && tr != STD_TRACKS )
133  {
134  message_cb(1, "non-standard number or tracks: %d", tr);
135  }
136  }
137  }
138 
139  if(!for_writing)
140  {
141  if(stat_ok)
142  {
143  if(is_image)
144  {
145  the_file = fopen(name, "rb");
146  if(the_file == NULL)
147  {
148  message_cb(0, "could not open %s", name);
149  }
150  if(error_info)
151  {
152  message_cb(1, "image contains error information");
153  }
154  if(settings->end_track == -1)
155  {
156  settings->end_track = tr;
157  }
158  else if(settings->end_track > tr)
159  {
160  message_cb(1, "resetting end track to %d", tr);
161  settings->end_track = tr;
162  }
163  /* FIXME: error map */
164  }
165  else
166  {
167  message_cb(0, "neither a .d64 not .d71 file: %s", name);
168  }
169  }
170  else
171  {
172  message_cb(0, "could not stat %s", name);
173  }
174  }
175  else
176  {
177  the_file = fopen(name, is_image ? "r+b" : "wb");
178  if(the_file)
179  {
180  /* check whether we must resize or create an image file */
181  int new_tr;
182  if(settings->two_sided)
183  {
184  new_tr = D71_TRACKS;
185  }
186  else if(settings->end_track <= STD_TRACKS)
187  {
188  new_tr = STD_TRACKS;
189  }
190  else if(settings->end_track <= EXT_TRACKS)
191  {
192  new_tr = EXT_TRACKS;
193  }
194  else
195  {
196  new_tr = TOT_TRACKS;
197  }
198 
199  /* always use maximum size for error map */
200  error_map = calloc(ERROR_MAP_LENGTH, 1);
201  if(!error_map)
202  {
203  message_cb(0, "no memory for error map");
204  fclose(the_file);
205  if(!is_image)
206  {
207  arch_unlink(name);
208  }
209  return 1;
210  }
211 
212  if(is_image)
213  {
214  if(error_info)
215  {
216  if(fseek(the_file, block_count * BLOCKSIZE, SEEK_SET) != 0 ||
217  fread(error_map, block_count, 1, the_file) != 1)
218  {
219  message_cb(0, "%s: could not read error map", name);
220  fclose(the_file);
221  return 1;
222  }
223  }
224  if(fseek(the_file, block_count * BLOCKSIZE, SEEK_SET) != 0)
225  {
226  message_cb(0, "%s: could not seek to end of file", name);
227  fclose(the_file);
228  return 1;
229  }
230  }
231 
232  if(new_tr > tr)
233  {
234  /* grow image */
235  while(tr < new_tr)
236  {
237  block_count += d64copy_sector_count(settings->two_sided, ++tr);
238  }
239 
240  message_cb(1, "growing image file to %d blocks", block_count);
241 
242  if (arch_ftruncate(arch_fileno(the_file), block_count * BLOCKSIZE) != 0)
243  {
244  message_cb(0, "%s: could not extend image file", name);
245  fclose(the_file);
246  if(!is_image)
247  arch_unlink(name);
248  return 1;
249  }
250  }
251  }
252  else
253  {
254  message_cb(0, "could not open %s", name);
255  }
256  }
257  return the_file == NULL;
258 }
259 
260 static void close_disk(void)
261 {
262  int i, has_errors = 0;
263 
264  /* if writing the block was interrupted, make sure it is
265  * redone before closing the disk
266  */
267 
268  if (the_file && atom_execute)
269  {
270  atom_execute = 0;
271  write_block(atom_tr, atom_se, atom_blk, atom_size, atom_read_status);
272  }
273 
274  if (fs_settings)
275  {
276  switch(fs_settings->error_mode)
277  {
278  case em_always:
279  has_errors = 1;
280  break;
281  case em_never:
282  has_errors = 0;
283  break;
284  default:
285  if(error_map)
286  {
287  for(i = 0; !has_errors && i < block_count; i++)
288  {
289  has_errors = error_map[i] != 1;
290  }
291  }
292  break;
293  }
294  }
295 
296  if(the_file)
297  {
298  if(has_errors)
299  {
300  if(fseek(the_file, block_count * BLOCKSIZE, SEEK_SET) == 0)
301  {
302  fwrite(error_map, block_count, 1, the_file);
303  }
304  }
305  else
306  {
307  arch_ftruncate(arch_fileno(the_file), block_count * BLOCKSIZE);
308  }
309  }
310 
311  if(error_map)
312  {
313  free(error_map);
314  error_map = NULL;
315  }
316  if(the_file)
317  {
318  fclose(the_file);
319  the_file = NULL;
320  }
321 }
322 
323 DECLARE_TRANSFER_FUNCS(fs_transfer, 0, 0);
#define CBM_FILE
Definition: opencbm.h:87
int arch_filesize(const char *Filename, off_t *Filesize)
Obtain the size of a given file.
Definition: file.c:38
Define makros and functions which account for differences between the different architectures.