diff options
Diffstat (limited to 'drivers/char/ftape/zftape/zftape-vtbl.c')
-rw-r--r-- | drivers/char/ftape/zftape/zftape-vtbl.c | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c new file mode 100644 index 000000000000..ad7f8be6340b --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-vtbl.c | |||
@@ -0,0 +1,757 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1995-1997 Claus-Justus Heine | ||
3 | |||
4 | This program is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 2, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program; see the file COPYING. If not, write to | ||
16 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
17 | USA. | ||
18 | |||
19 | * | ||
20 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ | ||
21 | * $Revision: 1.7.6.1 $ | ||
22 | * $Date: 1997/11/24 13:48:31 $ | ||
23 | * | ||
24 | * This file defines a volume table as defined in various QIC | ||
25 | * standards. | ||
26 | * | ||
27 | * This is a minimal implementation, just allowing ordinary DOS | ||
28 | * :( prgrams to identify the cartridge as used. | ||
29 | */ | ||
30 | |||
31 | #include <linux/errno.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/slab.h> | ||
34 | |||
35 | #include <linux/zftape.h> | ||
36 | #include "../zftape/zftape-init.h" | ||
37 | #include "../zftape/zftape-eof.h" | ||
38 | #include "../zftape/zftape-ctl.h" | ||
39 | #include "../zftape/zftape-write.h" | ||
40 | #include "../zftape/zftape-read.h" | ||
41 | #include "../zftape/zftape-rw.h" | ||
42 | #include "../zftape/zftape-vtbl.h" | ||
43 | |||
44 | #define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ | ||
45 | |||
46 | /* | ||
47 | * global variables | ||
48 | */ | ||
49 | int zft_qic_mode = 1; /* use the vtbl */ | ||
50 | int zft_old_ftape; /* prevents old ftaped tapes to be overwritten */ | ||
51 | int zft_volume_table_changed; /* for write_header_segments() */ | ||
52 | |||
53 | /* | ||
54 | * private variables (only exported for inline functions) | ||
55 | */ | ||
56 | LIST_HEAD(zft_vtbl); | ||
57 | |||
58 | /* We could also allocate these dynamically when extracting the volume table | ||
59 | * sizeof(zft_volinfo) is about 32 or something close to that | ||
60 | */ | ||
61 | static zft_volinfo tape_vtbl; | ||
62 | static zft_volinfo eot_vtbl; | ||
63 | static zft_volinfo *cur_vtbl; | ||
64 | |||
65 | static inline void zft_new_vtbl_entry(void) | ||
66 | { | ||
67 | struct list_head *tmp = &zft_last_vtbl->node; | ||
68 | zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo)); | ||
69 | |||
70 | list_add(&new->node, tmp); | ||
71 | new->count = zft_eom_vtbl->count ++; | ||
72 | } | ||
73 | |||
74 | void zft_free_vtbl(void) | ||
75 | { | ||
76 | for (;;) { | ||
77 | struct list_head *tmp = zft_vtbl.prev; | ||
78 | zft_volinfo *vtbl; | ||
79 | |||
80 | if (tmp == &zft_vtbl) | ||
81 | break; | ||
82 | list_del(tmp); | ||
83 | vtbl = list_entry(tmp, zft_volinfo, node); | ||
84 | zft_kfree(vtbl, sizeof(zft_volinfo)); | ||
85 | } | ||
86 | INIT_LIST_HEAD(&zft_vtbl); | ||
87 | cur_vtbl = NULL; | ||
88 | } | ||
89 | |||
90 | /* initialize vtbl, called by ftape_new_cartridge() | ||
91 | */ | ||
92 | void zft_init_vtbl(void) | ||
93 | { | ||
94 | zft_volinfo *new; | ||
95 | |||
96 | zft_free_vtbl(); | ||
97 | |||
98 | /* Create the two dummy vtbl entries | ||
99 | */ | ||
100 | new = zft_kmalloc(sizeof(zft_volinfo)); | ||
101 | list_add(&new->node, &zft_vtbl); | ||
102 | new = zft_kmalloc(sizeof(zft_volinfo)); | ||
103 | list_add(&new->node, &zft_vtbl); | ||
104 | zft_head_vtbl->end_seg = ft_first_data_segment; | ||
105 | zft_head_vtbl->blk_sz = zft_blk_sz; | ||
106 | zft_head_vtbl->count = -1; | ||
107 | zft_eom_vtbl->start_seg = ft_first_data_segment + 1; | ||
108 | zft_eom_vtbl->end_seg = ft_last_data_segment + 1; | ||
109 | zft_eom_vtbl->blk_sz = zft_blk_sz; | ||
110 | zft_eom_vtbl->count = 0; | ||
111 | |||
112 | /* Reset the pointer for zft_find_volume() | ||
113 | */ | ||
114 | cur_vtbl = zft_eom_vtbl; | ||
115 | |||
116 | /* initialize the dummy vtbl entries for zft_qic_mode == 0 | ||
117 | */ | ||
118 | eot_vtbl.start_seg = ft_last_data_segment + 1; | ||
119 | eot_vtbl.end_seg = ft_last_data_segment + 1; | ||
120 | eot_vtbl.blk_sz = zft_blk_sz; | ||
121 | eot_vtbl.count = -1; | ||
122 | tape_vtbl.start_seg = ft_first_data_segment; | ||
123 | tape_vtbl.end_seg = ft_last_data_segment; | ||
124 | tape_vtbl.blk_sz = zft_blk_sz; | ||
125 | tape_vtbl.size = zft_capacity; | ||
126 | tape_vtbl.count = 0; | ||
127 | } | ||
128 | |||
129 | /* check for a valid VTBL signature. | ||
130 | */ | ||
131 | static int vtbl_signature_valid(__u8 signature[4]) | ||
132 | { | ||
133 | const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */ | ||
134 | int j; | ||
135 | |||
136 | for (j = 0; | ||
137 | (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0); | ||
138 | j++); | ||
139 | return j < NR_ITEMS(vtbl_ids); | ||
140 | } | ||
141 | |||
142 | /* We used to store the block-size of the volume in the volume-label, | ||
143 | * using the keyword "blocksize". The blocksize written to the | ||
144 | * volume-label is in bytes. | ||
145 | * | ||
146 | * We use this now only for compatibility with old zftape version. We | ||
147 | * store the blocksize directly as binary number in the vendor | ||
148 | * extension part of the volume entry. | ||
149 | */ | ||
150 | static int check_volume_label(const char *label, int *blk_sz) | ||
151 | { | ||
152 | int valid_format; | ||
153 | char *blocksize; | ||
154 | TRACE_FUN(ft_t_flow); | ||
155 | |||
156 | TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME); | ||
157 | if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) { | ||
158 | *blk_sz = 1; /* smallest block size that we allow */ | ||
159 | valid_format = 0; | ||
160 | } else { | ||
161 | TRACE(ft_t_noise, "got old style zftape vtbl entry"); | ||
162 | /* get the default blocksize */ | ||
163 | /* use the kernel strstr() */ | ||
164 | blocksize= strstr(label, " blocksize "); | ||
165 | if (blocksize) { | ||
166 | blocksize += strlen(" blocksize "); | ||
167 | for(*blk_sz= 0; | ||
168 | *blocksize >= '0' && *blocksize <= '9'; | ||
169 | blocksize++) { | ||
170 | *blk_sz *= 10; | ||
171 | *blk_sz += *blocksize - '0'; | ||
172 | } | ||
173 | if (*blk_sz > ZFT_MAX_BLK_SZ) { | ||
174 | *blk_sz= 1; | ||
175 | valid_format= 0; | ||
176 | } else { | ||
177 | valid_format = 1; | ||
178 | } | ||
179 | } else { | ||
180 | *blk_sz= 1; | ||
181 | valid_format= 0; | ||
182 | } | ||
183 | } | ||
184 | TRACE_EXIT valid_format; | ||
185 | } | ||
186 | |||
187 | /* check for a zftape volume | ||
188 | */ | ||
189 | static int check_volume(__u8 *entry, zft_volinfo *volume) | ||
190 | { | ||
191 | TRACE_FUN(ft_t_flow); | ||
192 | |||
193 | if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
194 | strlen(ZFTAPE_SIG)) == 0) { | ||
195 | TRACE(ft_t_noise, "got new style zftape vtbl entry"); | ||
196 | volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ); | ||
197 | volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113]; | ||
198 | TRACE_EXIT 1; | ||
199 | } else { | ||
200 | TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | |||
205 | /* create zftape specific vtbl entry, the volume bounds are inserted | ||
206 | * in the calling function, zft_create_volume_headers() | ||
207 | */ | ||
208 | static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl) | ||
209 | { | ||
210 | TRACE_FUN(ft_t_flow); | ||
211 | |||
212 | memset(entry, 0, VTBL_SIZE); | ||
213 | memcpy(&entry[VTBL_SIG], VTBL_ID, 4); | ||
214 | sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count); | ||
215 | entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING); | ||
216 | entry[VTBL_M_NO] = 1; /* multi_cartridge_count */ | ||
217 | strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG); | ||
218 | PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz); | ||
219 | if (zft_qic113) { | ||
220 | PUT8(entry, VTBL_DATA_SIZE, vtbl->size); | ||
221 | entry[VTBL_CMPR] = VTBL_CMPR_UNREG; | ||
222 | if (vtbl->use_compression) { /* use compression: */ | ||
223 | entry[VTBL_CMPR] |= VTBL_CMPR_USED; | ||
224 | } | ||
225 | entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1; | ||
226 | } else { | ||
227 | PUT4(entry, VTBL_DATA_SIZE, vtbl->size); | ||
228 | entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; | ||
229 | if (vtbl->use_compression) { /* use compression: */ | ||
230 | entry[VTBL_K_CMPR] |= VTBL_CMPR_USED; | ||
231 | } | ||
232 | } | ||
233 | if (ft_format_code == fmt_big) { | ||
234 | /* SCSI like vtbl, store the number of used | ||
235 | * segments as 4 byte value | ||
236 | */ | ||
237 | PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1); | ||
238 | } else { | ||
239 | /* normal, QIC-80MC like vtbl | ||
240 | */ | ||
241 | PUT2(entry, VTBL_START, vtbl->start_seg); | ||
242 | PUT2(entry, VTBL_END, vtbl->end_seg); | ||
243 | } | ||
244 | TRACE_EXIT; | ||
245 | } | ||
246 | |||
247 | /* this one creates the volume headers for each volume. It is assumed | ||
248 | * that buffer already contains the old volume-table, so that vtbl | ||
249 | * entries without the zft_volume flag set can savely be ignored. | ||
250 | */ | ||
251 | static void zft_create_volume_headers(__u8 *buffer) | ||
252 | { | ||
253 | __u8 *entry; | ||
254 | struct list_head *tmp; | ||
255 | zft_volinfo *vtbl; | ||
256 | TRACE_FUN(ft_t_flow); | ||
257 | |||
258 | #ifdef ZFT_CMAP_HACK | ||
259 | if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
260 | strlen(ZFTAPE_SIG)) == 0) && | ||
261 | buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { | ||
262 | TRACE(ft_t_noise, "deleting cmap volume"); | ||
263 | memmove(buffer, buffer + VTBL_SIZE, | ||
264 | FT_SEGMENT_SIZE - VTBL_SIZE); | ||
265 | } | ||
266 | #endif | ||
267 | entry = buffer; | ||
268 | for (tmp = zft_head_vtbl->node.next; | ||
269 | tmp != &zft_eom_vtbl->node; | ||
270 | tmp = tmp->next) { | ||
271 | vtbl = list_entry(tmp, zft_volinfo, node); | ||
272 | /* we now fill in the values only for newly created volumes. | ||
273 | */ | ||
274 | if (vtbl->new_volume) { | ||
275 | create_zft_volume(entry, vtbl); | ||
276 | vtbl->new_volume = 0; /* clear the flag */ | ||
277 | } | ||
278 | |||
279 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl); | ||
280 | entry += VTBL_SIZE; | ||
281 | } | ||
282 | memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE); | ||
283 | TRACE_EXIT; | ||
284 | } | ||
285 | |||
286 | /* write volume table to tape. Calls zft_create_volume_headers() | ||
287 | */ | ||
288 | int zft_update_volume_table(unsigned int segment) | ||
289 | { | ||
290 | int result = 0; | ||
291 | __u8 *verify_buf = NULL; | ||
292 | TRACE_FUN(ft_t_flow); | ||
293 | |||
294 | TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, | ||
295 | zft_deblock_buf, | ||
296 | FT_RD_SINGLE),); | ||
297 | zft_create_volume_headers(zft_deblock_buf); | ||
298 | TRACE(ft_t_noise, "writing volume table segment %d", segment); | ||
299 | if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) { | ||
300 | TRACE_CATCH(zft_verify_write_segments(segment, | ||
301 | zft_deblock_buf, result, | ||
302 | verify_buf), | ||
303 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE)); | ||
304 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE); | ||
305 | } else { | ||
306 | TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, | ||
307 | FT_WR_SINGLE),); | ||
308 | } | ||
309 | TRACE_EXIT 0; | ||
310 | } | ||
311 | |||
312 | /* non zftape volumes are handled in raw mode. Thus we need to | ||
313 | * calculate the raw amount of data contained in those segments. | ||
314 | */ | ||
315 | static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl) | ||
316 | { | ||
317 | TRACE_FUN(ft_t_flow); | ||
318 | |||
319 | vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - | ||
320 | zft_calc_tape_pos(zft_last_vtbl->start_seg)); | ||
321 | vtbl->use_compression = 0; | ||
322 | vtbl->qic113 = zft_qic113; | ||
323 | if (vtbl->qic113) { | ||
324 | TRACE(ft_t_noise, | ||
325 | "Fake alien volume's size from " LL_X " to " LL_X, | ||
326 | LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size)); | ||
327 | } else { | ||
328 | TRACE(ft_t_noise, | ||
329 | "Fake alien volume's size from %d to " LL_X, | ||
330 | (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size)); | ||
331 | } | ||
332 | TRACE_EXIT; | ||
333 | } | ||
334 | |||
335 | |||
336 | /* extract an zftape specific volume | ||
337 | */ | ||
338 | static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl) | ||
339 | { | ||
340 | TRACE_FUN(ft_t_flow); | ||
341 | |||
342 | if (vtbl->qic113) { | ||
343 | vtbl->size = GET8(entry, VTBL_DATA_SIZE); | ||
344 | vtbl->use_compression = | ||
345 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; | ||
346 | } else { | ||
347 | vtbl->size = GET4(entry, VTBL_DATA_SIZE); | ||
348 | if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) { | ||
349 | vtbl->use_compression = | ||
350 | (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0; | ||
351 | } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) { | ||
352 | vtbl->use_compression = | ||
353 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; | ||
354 | } else { | ||
355 | TRACE(ft_t_warn, "Geeh! There is something wrong:\n" | ||
356 | KERN_INFO "QIC compression (Rev = K): %x\n" | ||
357 | KERN_INFO "QIC compression (Rev > K): %x", | ||
358 | entry[VTBL_K_CMPR], entry[VTBL_CMPR]); | ||
359 | } | ||
360 | } | ||
361 | TRACE_EXIT; | ||
362 | } | ||
363 | |||
364 | /* extract the volume table from buffer. "buffer" must already contain | ||
365 | * the vtbl-segment | ||
366 | */ | ||
367 | int zft_extract_volume_headers(__u8 *buffer) | ||
368 | { | ||
369 | __u8 *entry; | ||
370 | TRACE_FUN(ft_t_flow); | ||
371 | |||
372 | zft_init_vtbl(); | ||
373 | entry = buffer; | ||
374 | #ifdef ZFT_CMAP_HACK | ||
375 | if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
376 | strlen(ZFTAPE_SIG)) == 0) && | ||
377 | entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { | ||
378 | TRACE(ft_t_noise, "ignoring cmap volume"); | ||
379 | entry += VTBL_SIZE; | ||
380 | } | ||
381 | #endif | ||
382 | /* the end of the vtbl is indicated by an invalid signature | ||
383 | */ | ||
384 | while (vtbl_signature_valid(&entry[VTBL_SIG]) && | ||
385 | (entry - buffer) < FT_SEGMENT_SIZE) { | ||
386 | zft_new_vtbl_entry(); | ||
387 | if (ft_format_code == fmt_big) { | ||
388 | /* SCSI like vtbl, stores only the number of | ||
389 | * segments used | ||
390 | */ | ||
391 | unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS); | ||
392 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
393 | zft_last_vtbl->end_seg = | ||
394 | zft_last_vtbl->start_seg + num_segments - 1; | ||
395 | } else { | ||
396 | /* `normal', QIC-80 like vtbl | ||
397 | */ | ||
398 | zft_last_vtbl->start_seg = GET2(entry, VTBL_START); | ||
399 | zft_last_vtbl->end_seg = GET2(entry, VTBL_END); | ||
400 | } | ||
401 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; | ||
402 | /* check if we created this volume and get the | ||
403 | * blk_sz | ||
404 | */ | ||
405 | zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl); | ||
406 | if (zft_last_vtbl->zft_volume == 0) { | ||
407 | extract_alien_volume(entry, zft_last_vtbl); | ||
408 | } else { | ||
409 | extract_zft_volume(entry, zft_last_vtbl); | ||
410 | } | ||
411 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl); | ||
412 | entry +=VTBL_SIZE; | ||
413 | } | ||
414 | #if 0 | ||
415 | /* | ||
416 | * undefine to test end of tape handling | ||
417 | */ | ||
418 | zft_new_vtbl_entry(); | ||
419 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
420 | zft_last_vtbl->end_seg = ft_last_data_segment - 10; | ||
421 | zft_last_vtbl->blk_sz = zft_blk_sz; | ||
422 | zft_last_vtbl->zft_volume = 1; | ||
423 | zft_last_vtbl->qic113 = zft_qic113; | ||
424 | zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) | ||
425 | - zft_calc_tape_pos(zft_last_vtbl->start_seg)); | ||
426 | #endif | ||
427 | TRACE_EXIT 0; | ||
428 | } | ||
429 | |||
430 | /* this functions translates the failed_sector_log, misused as | ||
431 | * EOF-marker list, into a virtual volume table. The table mustn't be | ||
432 | * written to tape, because this would occupy the first data segment, | ||
433 | * which should be the volume table, but is actually the first segment | ||
434 | * that is filled with data (when using standard ftape). We assume, | ||
435 | * that we get a non-empty failed_sector_log. | ||
436 | */ | ||
437 | int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors) | ||
438 | { | ||
439 | unsigned int segment, sector; | ||
440 | int have_eom = 0; | ||
441 | int vol_no; | ||
442 | TRACE_FUN(ft_t_flow); | ||
443 | |||
444 | if ((num_failed_sectors >= 2) && | ||
445 | (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) | ||
446 | == | ||
447 | GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) && | ||
448 | (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) { | ||
449 | /* this should be eom. We keep the remainder of the | ||
450 | * tape as another volume. | ||
451 | */ | ||
452 | have_eom = 1; | ||
453 | } | ||
454 | zft_init_vtbl(); | ||
455 | zft_eom_vtbl->start_seg = ft_first_data_segment; | ||
456 | for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) { | ||
457 | zft_new_vtbl_entry(); | ||
458 | |||
459 | segment = GET2(&eof_map[vol_no].mark.segment, 0); | ||
460 | sector = GET2(&eof_map[vol_no].mark.date, 0); | ||
461 | |||
462 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
463 | zft_last_vtbl->end_seg = segment; | ||
464 | zft_eom_vtbl->start_seg = segment + 1; | ||
465 | zft_last_vtbl->blk_sz = 1; | ||
466 | zft_last_vtbl->size = | ||
467 | (zft_calc_tape_pos(zft_last_vtbl->end_seg) | ||
468 | - zft_calc_tape_pos(zft_last_vtbl->start_seg) | ||
469 | + (sector-1) * FT_SECTOR_SIZE); | ||
470 | TRACE(ft_t_noise, | ||
471 | "failed sector log: segment: %d, sector: %d", | ||
472 | segment, sector); | ||
473 | DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl); | ||
474 | } | ||
475 | if (!have_eom) { | ||
476 | zft_new_vtbl_entry(); | ||
477 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
478 | zft_last_vtbl->end_seg = ft_last_data_segment; | ||
479 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; | ||
480 | zft_last_vtbl->size = zft_capacity; | ||
481 | zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg); | ||
482 | zft_last_vtbl->blk_sz = 1; | ||
483 | DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl); | ||
484 | } | ||
485 | TRACE_EXIT 0; | ||
486 | } | ||
487 | |||
488 | /* update the internal volume table | ||
489 | * | ||
490 | * if before start of last volume: erase all following volumes if | ||
491 | * inside a volume: set end of volume to infinity | ||
492 | * | ||
493 | * this function is intended to be called every time _ftape_write() is | ||
494 | * called | ||
495 | * | ||
496 | * return: 0 if no new volume was created, 1 if a new volume was | ||
497 | * created | ||
498 | * | ||
499 | * NOTE: we don't need to check for zft_mode as ftape_write() does | ||
500 | * that already. This function gets never called without accessing | ||
501 | * zftape via the *qft* devices | ||
502 | */ | ||
503 | |||
504 | int zft_open_volume(zft_position *pos, int blk_sz, int use_compression) | ||
505 | { | ||
506 | TRACE_FUN(ft_t_flow); | ||
507 | |||
508 | if (!zft_qic_mode) { | ||
509 | TRACE_EXIT 0; | ||
510 | } | ||
511 | if (zft_tape_at_lbot(pos)) { | ||
512 | zft_init_vtbl(); | ||
513 | if(zft_old_ftape) { | ||
514 | /* clear old ftape's eof marks */ | ||
515 | zft_clear_ftape_file_marks(); | ||
516 | zft_old_ftape = 0; /* no longer old ftape */ | ||
517 | } | ||
518 | zft_reset_position(pos); | ||
519 | } | ||
520 | if (pos->seg_pos != zft_last_vtbl->end_seg + 1) { | ||
521 | TRACE_ABORT(-EIO, ft_t_bug, | ||
522 | "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", | ||
523 | pos->seg_pos, zft_last_vtbl->end_seg); | ||
524 | } | ||
525 | TRACE(ft_t_noise, "create new volume"); | ||
526 | if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) { | ||
527 | TRACE_ABORT(-ENOSPC, ft_t_err, | ||
528 | "Error: maxmimal number of volumes exhausted " | ||
529 | "(maxmimum is %d)", ZFT_MAX_VOLUMES); | ||
530 | } | ||
531 | zft_new_vtbl_entry(); | ||
532 | pos->volume_pos = pos->seg_byte_pos = 0; | ||
533 | zft_last_vtbl->start_seg = pos->seg_pos; | ||
534 | zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */ | ||
535 | zft_last_vtbl->blk_sz = blk_sz; | ||
536 | zft_last_vtbl->size = zft_capacity; | ||
537 | zft_last_vtbl->zft_volume = 1; | ||
538 | zft_last_vtbl->use_compression = use_compression; | ||
539 | zft_last_vtbl->qic113 = zft_qic113; | ||
540 | zft_last_vtbl->new_volume = 1; | ||
541 | zft_last_vtbl->open = 1; | ||
542 | zft_volume_table_changed = 1; | ||
543 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; | ||
544 | TRACE_EXIT 0; | ||
545 | } | ||
546 | |||
547 | /* perform mtfsf, mtbsf, not allowed without zft_qic_mode | ||
548 | */ | ||
549 | int zft_skip_volumes(int count, zft_position *pos) | ||
550 | { | ||
551 | const zft_volinfo *vtbl; | ||
552 | TRACE_FUN(ft_t_flow); | ||
553 | |||
554 | TRACE(ft_t_noise, "count: %d", count); | ||
555 | |||
556 | vtbl= zft_find_volume(pos->seg_pos); | ||
557 | while (count > 0 && vtbl != zft_eom_vtbl) { | ||
558 | vtbl = list_entry(vtbl->node.next, zft_volinfo, node); | ||
559 | count --; | ||
560 | } | ||
561 | while (count < 0 && vtbl != zft_first_vtbl) { | ||
562 | vtbl = list_entry(vtbl->node.prev, zft_volinfo, node); | ||
563 | count ++; | ||
564 | } | ||
565 | pos->seg_pos = vtbl->start_seg; | ||
566 | pos->seg_byte_pos = 0; | ||
567 | pos->volume_pos = 0; | ||
568 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
569 | zft_just_before_eof = vtbl->size == 0; | ||
570 | if (zft_cmpr_ops) { | ||
571 | (*zft_cmpr_ops->reset)(); | ||
572 | } | ||
573 | zft_deblock_segment = -1; /* no need to keep cache */ | ||
574 | TRACE(ft_t_noise, "repositioning to:\n" | ||
575 | KERN_INFO "zft_seg_pos : %d\n" | ||
576 | KERN_INFO "zft_seg_byte_pos : %d\n" | ||
577 | KERN_INFO "zft_tape_pos : " LL_X "\n" | ||
578 | KERN_INFO "zft_volume_pos : " LL_X "\n" | ||
579 | KERN_INFO "file number : %d", | ||
580 | pos->seg_pos, pos->seg_byte_pos, | ||
581 | LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count); | ||
582 | zft_resid = count < 0 ? -count : count; | ||
583 | TRACE_EXIT zft_resid ? -EINVAL : 0; | ||
584 | } | ||
585 | |||
586 | /* the following simply returns the raw data position of the EOM | ||
587 | * marker, MTIOCSIZE ioctl | ||
588 | */ | ||
589 | __s64 zft_get_eom_pos(void) | ||
590 | { | ||
591 | if (zft_qic_mode) { | ||
592 | return zft_calc_tape_pos(zft_eom_vtbl->start_seg); | ||
593 | } else { | ||
594 | /* there is only one volume in raw mode */ | ||
595 | return zft_capacity; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | /* skip to eom, used for MTEOM | ||
600 | */ | ||
601 | void zft_skip_to_eom(zft_position *pos) | ||
602 | { | ||
603 | TRACE_FUN(ft_t_flow); | ||
604 | pos->seg_pos = zft_eom_vtbl->start_seg; | ||
605 | pos->seg_byte_pos = | ||
606 | pos->volume_pos = | ||
607 | zft_just_before_eof = 0; | ||
608 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
609 | TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, | ||
610 | pos->seg_pos, LL(pos->tape_pos)); | ||
611 | TRACE_EXIT; | ||
612 | } | ||
613 | |||
614 | /* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos. | ||
615 | * NOTE: this function assumes that zft_last_vtbl points to a valid | ||
616 | * vtbl entry | ||
617 | * | ||
618 | * NOTE: this routine always positions before the EOF marker | ||
619 | */ | ||
620 | int zft_close_volume(zft_position *pos) | ||
621 | { | ||
622 | TRACE_FUN(ft_t_any); | ||
623 | |||
624 | if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */ | ||
625 | TRACE(ft_t_noise, "There are no volumes to finish"); | ||
626 | TRACE_EXIT -EIO; | ||
627 | } | ||
628 | if (pos->seg_byte_pos == 0 && | ||
629 | pos->seg_pos != zft_last_vtbl->start_seg) { | ||
630 | pos->seg_pos --; | ||
631 | pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); | ||
632 | } | ||
633 | zft_last_vtbl->end_seg = pos->seg_pos; | ||
634 | zft_last_vtbl->size = pos->volume_pos; | ||
635 | zft_volume_table_changed = 1; | ||
636 | zft_just_before_eof = 1; | ||
637 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; | ||
638 | zft_last_vtbl->open = 0; /* closed */ | ||
639 | TRACE_EXIT 0; | ||
640 | } | ||
641 | |||
642 | /* write count file-marks at current position. | ||
643 | * | ||
644 | * The tape is positioned after the eof-marker, that is at byte 0 of | ||
645 | * the segment following the eof-marker | ||
646 | * | ||
647 | * this function is only allowed in zft_qic_mode | ||
648 | * | ||
649 | * Only allowed when tape is at BOT or EOD. | ||
650 | */ | ||
651 | int zft_weof(unsigned int count, zft_position *pos) | ||
652 | { | ||
653 | |||
654 | TRACE_FUN(ft_t_flow); | ||
655 | |||
656 | if (!count) { /* write zero EOF marks should be a real no-op */ | ||
657 | TRACE_EXIT 0; | ||
658 | } | ||
659 | zft_volume_table_changed = 1; | ||
660 | if (zft_tape_at_lbot(pos)) { | ||
661 | zft_init_vtbl(); | ||
662 | if(zft_old_ftape) { | ||
663 | /* clear old ftape's eof marks */ | ||
664 | zft_clear_ftape_file_marks(); | ||
665 | zft_old_ftape = 0; /* no longer old ftape */ | ||
666 | } | ||
667 | } | ||
668 | if (zft_last_vtbl->open) { | ||
669 | zft_close_volume(pos); | ||
670 | zft_move_past_eof(pos); | ||
671 | count --; | ||
672 | } | ||
673 | /* now it's easy, just append eof-marks, that is empty | ||
674 | * volumes, to the end of the already recorded media. | ||
675 | */ | ||
676 | while (count > 0 && | ||
677 | pos->seg_pos <= ft_last_data_segment && | ||
678 | zft_eom_vtbl->count < ZFT_MAX_VOLUMES) { | ||
679 | TRACE(ft_t_noise, | ||
680 | "Writing zero sized file at segment %d", pos->seg_pos); | ||
681 | zft_new_vtbl_entry(); | ||
682 | zft_last_vtbl->start_seg = pos->seg_pos; | ||
683 | zft_last_vtbl->end_seg = pos->seg_pos; | ||
684 | zft_last_vtbl->size = 0; | ||
685 | zft_last_vtbl->blk_sz = zft_blk_sz; | ||
686 | zft_last_vtbl->zft_volume = 1; | ||
687 | zft_last_vtbl->use_compression = 0; | ||
688 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos); | ||
689 | zft_eom_vtbl->start_seg = ++ pos->seg_pos; | ||
690 | count --; | ||
691 | } | ||
692 | if (count > 0) { | ||
693 | /* there are two possibilities: end of tape, or the | ||
694 | * maximum number of files is exhausted. | ||
695 | */ | ||
696 | zft_resid = count; | ||
697 | TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid); | ||
698 | if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) { | ||
699 | TRACE_ABORT(-EINVAL, ft_t_warn, | ||
700 | "maximum allowed number of files " | ||
701 | "exhausted: %d", ZFT_MAX_VOLUMES); | ||
702 | } else { | ||
703 | TRACE_ABORT(-ENOSPC, | ||
704 | ft_t_noise, "reached end of tape"); | ||
705 | } | ||
706 | } | ||
707 | TRACE_EXIT 0; | ||
708 | } | ||
709 | |||
710 | const zft_volinfo *zft_find_volume(unsigned int seg_pos) | ||
711 | { | ||
712 | TRACE_FUN(ft_t_flow); | ||
713 | |||
714 | TRACE(ft_t_any, "called with seg_pos %d",seg_pos); | ||
715 | if (!zft_qic_mode) { | ||
716 | if (seg_pos > ft_last_data_segment) { | ||
717 | TRACE_EXIT &eot_vtbl; | ||
718 | } | ||
719 | tape_vtbl.blk_sz = zft_blk_sz; | ||
720 | TRACE_EXIT &tape_vtbl; | ||
721 | } | ||
722 | if (seg_pos < zft_first_vtbl->start_seg) { | ||
723 | TRACE_EXIT (cur_vtbl = zft_first_vtbl); | ||
724 | } | ||
725 | while (seg_pos > cur_vtbl->end_seg) { | ||
726 | cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node); | ||
727 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); | ||
728 | } | ||
729 | while (seg_pos < cur_vtbl->start_seg) { | ||
730 | cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node); | ||
731 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); | ||
732 | } | ||
733 | if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) { | ||
734 | TRACE(ft_t_bug, "This cannot happen"); | ||
735 | } | ||
736 | DUMP_VOLINFO(ft_t_noise, "", cur_vtbl); | ||
737 | TRACE_EXIT cur_vtbl; | ||
738 | } | ||
739 | |||
740 | /* this function really assumes that we are just before eof | ||
741 | */ | ||
742 | void zft_move_past_eof(zft_position *pos) | ||
743 | { | ||
744 | TRACE_FUN(ft_t_flow); | ||
745 | |||
746 | TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos); | ||
747 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos; | ||
748 | pos->seg_byte_pos = 0; | ||
749 | pos->volume_pos = 0; | ||
750 | if (zft_cmpr_ops) { | ||
751 | (*zft_cmpr_ops->reset)(); | ||
752 | } | ||
753 | zft_just_before_eof = 0; | ||
754 | zft_deblock_segment = -1; /* no need to cache it anymore */ | ||
755 | TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos); | ||
756 | TRACE_EXIT; | ||
757 | } | ||