1 /*
2 * Copyright (C) 1996, 1997 Claus Heine
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU 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, USA.
17
18 *
19 * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $
20 * $Revision: 1.3 $
21 * $Date: 1997/11/06 00:50:29 $
22 *
23 * This file contains the writing code
24 * for the QIC-117 floppy-tape driver for Linux.
25 */
26
27 #include <linux/errno.h>
28 #include <linux/mm.h>
29
30 #include <linux/zftape.h>
31
32 #if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
33 #include <asm/uaccess.h>
34 #else
35 #include <asm/segment.h>
36 #endif
37
38 #include "../zftape/zftape-init.h"
39 #include "../zftape/zftape-eof.h"
40 #include "../zftape/zftape-ctl.h"
41 #include "../zftape/zftape-write.h"
42 #include "../zftape/zftape-read.h"
43 #include "../zftape/zftape-rw.h"
44 #include "../zftape/zftape-vtbl.h"
45
46 /* Global vars.
47 */
48
49 /* Local vars.
50 */
51 static int last_write_failed;
52 static int need_flush;
53
zft_prevent_flush(void)54 void zft_prevent_flush(void)
55 {
56 need_flush = 0;
57 }
58
zft_write_header_segments(__u8 * buffer)59 static int zft_write_header_segments(__u8* buffer)
60 {
61 int header_1_ok = 0;
62 int header_2_ok = 0;
63 unsigned int time_stamp;
64 TRACE_FUN(ft_t_noise);
65
66 TRACE_CATCH(ftape_abort_operation(),);
67 ftape_seek_to_bot(); /* prevents extra rewind */
68 if (GET4(buffer, 0) != FT_HSEG_MAGIC) {
69 TRACE_ABORT(-EIO, ft_t_err,
70 "wrong header signature found, aborting");
71 }
72 /* Be optimistic: */
73 PUT4(buffer, FT_SEG_CNT,
74 zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2);
75 if ((time_stamp = zft_get_time()) != 0) {
76 PUT4(buffer, FT_WR_DATE, time_stamp);
77 if (zft_label_changed) {
78 PUT4(buffer, FT_LABEL_DATE, time_stamp);
79 }
80 }
81 TRACE(ft_t_noise,
82 "writing first header segment %d", ft_header_segment_1);
83 header_1_ok = zft_verify_write_segments(ft_header_segment_1,
84 buffer, FT_SEGMENT_SIZE,
85 zft_deblock_buf) >= 0;
86 TRACE(ft_t_noise,
87 "writing second header segment %d", ft_header_segment_2);
88 header_2_ok = zft_verify_write_segments(ft_header_segment_2,
89 buffer, FT_SEGMENT_SIZE,
90 zft_deblock_buf) >= 0;
91 if (!header_1_ok) {
92 TRACE(ft_t_warn, "Warning: "
93 "update of first header segment failed");
94 }
95 if (!header_2_ok) {
96 TRACE(ft_t_warn, "Warning: "
97 "update of second header segment failed");
98 }
99 if (!header_1_ok && !header_2_ok) {
100 TRACE_ABORT(-EIO, ft_t_err, "Error: "
101 "update of both header segments failed.");
102 }
103 TRACE_EXIT 0;
104 }
105
zft_update_header_segments(void)106 int zft_update_header_segments(void)
107 {
108 TRACE_FUN(ft_t_noise);
109
110 /* must NOT use zft_write_protected, as it also includes the
111 * file access mode. But we also want to update when soft
112 * write protection is enabled (O_RDONLY)
113 */
114 if (ft_write_protected || zft_old_ftape) {
115 TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update");
116 }
117 if (!zft_header_read) {
118 TRACE_ABORT(0, ft_t_noise, "Nothing to update");
119 }
120 if (!zft_header_changed) {
121 zft_header_changed = zft_written_segments > 0;
122 }
123 if (!zft_header_changed && !zft_volume_table_changed) {
124 TRACE_ABORT(0, ft_t_noise, "Nothing to update");
125 }
126 TRACE(ft_t_noise, "Updating header segments");
127 if (ftape_get_status()->fti_state == writing) {
128 TRACE_CATCH(ftape_loop_until_writes_done(),);
129 }
130 TRACE_CATCH(ftape_abort_operation(),);
131
132 zft_deblock_segment = -1; /* invalidate the cache */
133 if (zft_header_changed) {
134 TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),);
135 }
136 if (zft_volume_table_changed) {
137 TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),);
138 }
139 zft_header_changed =
140 zft_volume_table_changed =
141 zft_label_changed =
142 zft_written_segments = 0;
143 TRACE_CATCH(ftape_abort_operation(),);
144 ftape_seek_to_bot();
145 TRACE_EXIT 0;
146 }
147
read_merge_buffer(int seg_pos,__u8 * buffer,int offset,int seg_sz)148 static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz)
149 {
150 int result = 0;
151 const ft_trace_t old_tracing = TRACE_LEVEL;
152 TRACE_FUN(ft_t_flow);
153
154 if (zft_qic_mode) {
155 /* writing in the middle of a volume is NOT allowed
156 *
157 */
158 TRACE(ft_t_noise, "No need to read a segment");
159 memset(buffer + offset, 0, seg_sz - offset);
160 TRACE_EXIT 0;
161 }
162 TRACE(ft_t_any, "waiting");
163 ftape_start_writing(FT_WR_MULTI);
164 TRACE_CATCH(ftape_loop_until_writes_done(),);
165
166 TRACE(ft_t_noise, "trying to read segment %d from offset %d",
167 seg_pos, offset);
168 SET_TRACE_LEVEL(ft_t_bug);
169 result = zft_fetch_segment_fraction(seg_pos, buffer,
170 FT_RD_SINGLE,
171 offset, seg_sz - offset);
172 SET_TRACE_LEVEL(old_tracing);
173 if (result != (seg_sz - offset)) {
174 TRACE(ft_t_noise, "Ignore error: read_segment() result: %d",
175 result);
176 memset(buffer + offset, 0, seg_sz - offset);
177 }
178 TRACE_EXIT 0;
179 }
180
181 /* flush the write buffer to tape and write an eof-marker at the
182 * current position if not in raw mode. This function always
183 * positions the tape before the eof-marker. _ftape_close() should
184 * then advance to the next segment.
185 *
186 * the parameter "finish_volume" describes whether to position before
187 * or after the possibly created file-mark. We always position after
188 * the file-mark when called from ftape_close() and a flush was needed
189 * (that is ftape_write() was the last tape operation before calling
190 * ftape_flush) But we always position before the file-mark when this
191 * function get's called from outside ftape_close()
192 */
zft_flush_buffers(void)193 int zft_flush_buffers(void)
194 {
195 int result;
196 int data_remaining;
197 int this_segs_size;
198 TRACE_FUN(ft_t_flow);
199
200 TRACE(ft_t_data_flow,
201 "entered, ftape_state = %d", ftape_get_status()->fti_state);
202 if (ftape_get_status()->fti_state != writing && !need_flush) {
203 TRACE_ABORT(0, ft_t_noise, "no need for flush");
204 }
205 zft_io_state = zft_idle; /* triggers some initializations for the
206 * read and write routines
207 */
208 if (last_write_failed) {
209 ftape_abort_operation();
210 TRACE_EXIT -EIO;
211 }
212 TRACE(ft_t_noise, "flushing write buffers");
213 this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
214 if (this_segs_size == zft_pos.seg_byte_pos) {
215 zft_pos.seg_pos ++;
216 data_remaining = zft_pos.seg_byte_pos = 0;
217 } else {
218 data_remaining = zft_pos.seg_byte_pos;
219 }
220 /* If there is any data not written to tape yet, append zero's
221 * up to the end of the sector (if using compression) or merge
222 * it with the data existing on the tape Then write the
223 * segment(s) to tape.
224 */
225 TRACE(ft_t_noise, "Position:\n"
226 KERN_INFO "seg_pos : %d\n"
227 KERN_INFO "byte pos : %d\n"
228 KERN_INFO "remaining: %d",
229 zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining);
230 if (data_remaining > 0) {
231 do {
232 this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
233 if (this_segs_size > data_remaining) {
234 TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos,
235 zft_deblock_buf,
236 data_remaining,
237 this_segs_size),
238 last_write_failed = 1);
239 }
240 result = ftape_write_segment(zft_pos.seg_pos,
241 zft_deblock_buf,
242 FT_WR_MULTI);
243 if (result != this_segs_size) {
244 TRACE(ft_t_err, "flush buffers failed");
245 zft_pos.tape_pos -= zft_pos.seg_byte_pos;
246 zft_pos.seg_byte_pos = 0;
247
248 last_write_failed = 1;
249 TRACE_EXIT result;
250 }
251 zft_written_segments ++;
252 TRACE(ft_t_data_flow,
253 "flush, moved out buffer: %d", result);
254 /* need next segment for more data (empty segments?)
255 */
256 if (result < data_remaining) {
257 if (result > 0) {
258 /* move remainder to buffer beginning
259 */
260 memmove(zft_deblock_buf,
261 zft_deblock_buf + result,
262 FT_SEGMENT_SIZE - result);
263 }
264 }
265 data_remaining -= result;
266 zft_pos.seg_pos ++;
267 } while (data_remaining > 0);
268 TRACE(ft_t_any, "result: %d", result);
269 zft_deblock_segment = --zft_pos.seg_pos;
270 if (data_remaining == 0) { /* first byte next segment */
271 zft_pos.seg_byte_pos = this_segs_size;
272 } else { /* after data previous segment, data_remaining < 0 */
273 zft_pos.seg_byte_pos = data_remaining + result;
274 }
275 } else {
276 TRACE(ft_t_noise, "zft_deblock_buf empty");
277 zft_pos.seg_pos --;
278 zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos);
279 ftape_start_writing(FT_WR_MULTI);
280 }
281 TRACE(ft_t_any, "waiting");
282 if ((result = ftape_loop_until_writes_done()) < 0) {
283 /* that's really bad. What to to with zft_tape_pos?
284 */
285 TRACE(ft_t_err, "flush buffers failed");
286 }
287 TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d",
288 zft_pos.seg_pos, zft_pos.seg_byte_pos);
289 last_write_failed =
290 need_flush = 0;
291 TRACE_EXIT result;
292 }
293
294 /* return-value: the number of bytes removed from the user-buffer
295 *
296 * out:
297 * int *write_cnt: how much actually has been moved to the
298 * zft_deblock_buf
299 * int req_len : MUST NOT BE CHANGED, except at EOT, in
300 * which case it may be adjusted
301 * in :
302 * char *buff : the user buffer
303 * int buf_pos_write : copy of buf_len_wr int
304 * this_segs_size : the size in bytes of the actual segment
305 * char
306 * *zft_deblock_buf : zft_deblock_buf
307 */
zft_simple_write(int * cnt,__u8 * dst_buf,const int seg_sz,const __u8 * src_buf,const int req_len,const zft_position * pos,const zft_volinfo * volume)308 static int zft_simple_write(int *cnt,
309 __u8 *dst_buf, const int seg_sz,
310 const __u8 *src_buf, const int req_len,
311 const zft_position *pos,const zft_volinfo *volume)
312 {
313 int space_left;
314 TRACE_FUN(ft_t_flow);
315
316 /* volume->size holds the tape capacity while volume is open */
317 if (pos->tape_pos + volume->blk_sz > volume->size) {
318 TRACE_EXIT -ENOSPC;
319 }
320 /* remaining space in this segment, NOT zft_deblock_buf
321 */
322 space_left = seg_sz - pos->seg_byte_pos;
323 *cnt = req_len < space_left ? req_len : space_left;
324 #if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
325 if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) {
326 TRACE_EXIT -EFAULT;
327 }
328 #else
329 TRACE_CATCH(verify_area(VERIFY_READ, src_buf, *cnt),);
330 memcpy_fromfs(dst_buf + pos->seg_byte_pos, src_buf, *cnt);
331 #endif
332 TRACE_EXIT *cnt;
333 }
334
check_write_access(int req_len,const zft_volinfo ** volume,zft_position * pos,const unsigned int blk_sz)335 static int check_write_access(int req_len,
336 const zft_volinfo **volume,
337 zft_position *pos,
338 const unsigned int blk_sz)
339 {
340 int result;
341 TRACE_FUN(ft_t_flow);
342
343 if ((req_len % zft_blk_sz) != 0) {
344 TRACE_ABORT(-EINVAL, ft_t_info,
345 "write-count %d must be multiple of block-size %d",
346 req_len, blk_sz);
347 }
348 if (zft_io_state == zft_writing) {
349 /* all other error conditions have been checked earlier
350 */
351 TRACE_EXIT 0;
352 }
353 zft_io_state = zft_idle;
354 TRACE_CATCH(zft_check_write_access(pos),);
355 /* If we haven't read the header segment yet, do it now.
356 * This will verify the configuration, get the bad sector
357 * table and read the volume table segment
358 */
359 if (!zft_header_read) {
360 TRACE_CATCH(zft_read_header_segments(),);
361 }
362 /* fine. Now the tape is either at BOT or at EOD,
363 * Write start of volume now
364 */
365 TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),);
366 *volume = zft_find_volume(pos->seg_pos);
367 DUMP_VOLINFO(ft_t_noise, "", *volume);
368 zft_just_before_eof = 0;
369 /* now merge with old data if neccessary */
370 if (!zft_qic_mode && pos->seg_byte_pos != 0){
371 result = zft_fetch_segment(pos->seg_pos,
372 zft_deblock_buf,
373 FT_RD_SINGLE);
374 if (result < 0) {
375 if (result == -EINTR || result == -ENOSPC) {
376 TRACE_EXIT result;
377 }
378 TRACE(ft_t_noise,
379 "ftape_read_segment() result: %d. "
380 "This might be normal when using "
381 "a newly\nformatted tape", result);
382 memset(zft_deblock_buf, '\0', pos->seg_byte_pos);
383 }
384 }
385 zft_io_state = zft_writing;
386 TRACE_EXIT 0;
387 }
388
fill_deblock_buf(__u8 * dst_buf,const int seg_sz,zft_position * pos,const zft_volinfo * volume,const char * usr_buf,const int req_len)389 static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
390 zft_position *pos, const zft_volinfo *volume,
391 const char *usr_buf, const int req_len)
392 {
393 int cnt = 0;
394 int result = 0;
395 TRACE_FUN(ft_t_flow);
396
397 if (seg_sz == 0) {
398 TRACE_ABORT(0, ft_t_data_flow, "empty segment");
399 }
400 TRACE(ft_t_data_flow, "\n"
401 KERN_INFO "remaining req_len: %d\n"
402 KERN_INFO " buf_pos: %d",
403 req_len, pos->seg_byte_pos);
404 /* zft_deblock_buf will not contain a valid segment any longer */
405 zft_deblock_segment = -1;
406 if (zft_use_compression) {
407 TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
408 TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt,
409 dst_buf, seg_sz,
410 usr_buf, req_len,
411 pos, volume),);
412 } else {
413 TRACE_CATCH(result= zft_simple_write(&cnt,
414 dst_buf, seg_sz,
415 usr_buf, req_len,
416 pos, volume),);
417 }
418 pos->volume_pos += result;
419 pos->seg_byte_pos += cnt;
420 pos->tape_pos += cnt;
421 TRACE(ft_t_data_flow, "\n"
422 KERN_INFO "removed from user-buffer : %d bytes.\n"
423 KERN_INFO "copied to zft_deblock_buf: %d bytes.\n"
424 KERN_INFO "zft_tape_pos : " LL_X " bytes.",
425 result, cnt, LL(pos->tape_pos));
426 TRACE_EXIT result;
427 }
428
429
430 /* called by the kernel-interface routine "zft_write()"
431 */
_zft_write(const char * buff,int req_len)432 int _zft_write(const char* buff, int req_len)
433 {
434 int result = 0;
435 int written = 0;
436 int write_cnt;
437 int seg_sz;
438 static const zft_volinfo *volume = NULL;
439 TRACE_FUN(ft_t_flow);
440
441 zft_resid = req_len;
442 last_write_failed = 1; /* reset to 0 when successful */
443 /* check if write is allowed
444 */
445 TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),);
446 while (req_len > 0) {
447 /* Allow us to escape from this loop with a signal !
448 */
449 FT_SIGNAL_EXIT(_DONT_BLOCK);
450 seg_sz = zft_get_seg_sz(zft_pos.seg_pos);
451 if ((write_cnt = fill_deblock_buf(zft_deblock_buf,
452 seg_sz,
453 &zft_pos,
454 volume,
455 buff,
456 req_len)) < 0) {
457 zft_resid -= written;
458 if (write_cnt == -ENOSPC) {
459 /* leave the remainder to flush_buffers()
460 */
461 TRACE(ft_t_info, "No space left on device");
462 last_write_failed = 0;
463 if (!need_flush) {
464 need_flush = written > 0;
465 }
466 TRACE_EXIT written > 0 ? written : -ENOSPC;
467 } else {
468 TRACE_EXIT result;
469 }
470 }
471 if (zft_pos.seg_byte_pos == seg_sz) {
472 TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos,
473 zft_deblock_buf,
474 FT_WR_ASYNC),
475 zft_resid -= written);
476 zft_written_segments ++;
477 zft_pos.seg_byte_pos = 0;
478 zft_deblock_segment = zft_pos.seg_pos;
479 ++zft_pos.seg_pos;
480 }
481 written += write_cnt;
482 buff += write_cnt;
483 req_len -= write_cnt;
484 } /* while (req_len > 0) */
485 TRACE(ft_t_data_flow, "remaining in blocking buffer: %d",
486 zft_pos.seg_byte_pos);
487 TRACE(ft_t_data_flow, "just written bytes: %d", written);
488 last_write_failed = 0;
489 zft_resid -= written;
490 need_flush = need_flush || written > 0;
491 TRACE_EXIT written; /* bytes written */
492 }
493