Japanese translation.
[virt-p2v.git] / iso-attach
1 #!/usr/bin/ocamlrun /usr/bin/ocaml
2 (* -*- tuareg -*- *)
3 (* iso-attach attaches an updated 'virt-p2v' file to the end of
4  * an ISO image.  This is just for quick developer builds because it
5  * takes ages to rebuild a full ISO.
6  *
7  * Copyright (C) 2007-2008 Red Hat Inc.
8  * Written by Richard W.M. Jones <rjones@redhat.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *)
24
25 #load "unix.cma";;
26
27 open Printf
28 open Unix
29
30 (* CD-ROM format has 2048 byte "sectors" and the final ISO image
31  * must be a multiple of 2048 bytes in size.
32  *
33  * The basic plan is that we will use store the file followed by a
34  * trailer, all rounded up to 2048 bytes:
35  *
36  * +------ - - - - - - - -----+------------+----------------+
37  * | file                     | padding    | trailer        |
38  * |                          |            | magic | ....   |
39  * +------ - - - - - - - -----+------------+----------------+
40  * |<----    total size is a multiple of 2048 bytes    ---->|
41  *
42  * The magic string is used to identify that there is an
43  * attachment, and is followed by a few extra fields which
44  * identify the file start and true file size.  (Note the
45  * original filename is not stored because it is not needed).
46  *
47  * Attachments can be stacked.  This script only deals with the
48  * top-most attachment (ie. the one at the very end of the file).
49  * If you want to attach lots of files, a better way is to
50  * stuff them into a tarball or ZIP file and attach that.
51  *)
52
53 let magic = "ISOATTACHMENT003"
54 let magiclen = String.length magic  (* = 16 bytes *)
55 let trailerlen = magiclen + 8 + 8   (* magic + file start + true size *)
56
57 (* Ugh, would be really nice to have 64 bit pack/unpack functions
58  * in the stdlib, instead of this ugliness ...
59  *)
60 let string_of_int64 i =
61   let str = String.create 8 in
62   let shift_mask i shift =
63     Char.chr (Int64.to_int (Int64.shift_right_logical i shift) land 0xff)
64   in
65   str.[0] <- shift_mask i 56;  str.[1] <- shift_mask i 48;
66   str.[2] <- shift_mask i 40;  str.[3] <- shift_mask i 32;
67   str.[4] <- shift_mask i 24;  str.[5] <- shift_mask i 16;
68   str.[6] <- shift_mask i 8;   str.[7] <- shift_mask i 0;
69   str
70 let int64_of_string str =
71   let i = ref 0L in
72   let add offs shift =
73     i :=
74       Int64.logor
75         (Int64.shift_left (Int64.of_int (Char.code str.[offs])) shift) !i
76   in
77   add 0 56;  add 1 48;  add 2 40;  add 3 32;
78   add 4 24;  add 5 16;  add 6 8;   add 7 0;
79   !i
80
81 let rec main () =
82   let args = Array.to_list Sys.argv in
83   match args with
84   | [ _; "add"; isoname; attachment ] -> (* add an attachment *)
85       do_add isoname attachment
86   | [ _; "delete"; isoname ] ->         (* delete any attachment *)
87       do_delete isoname
88   | [ _; "get"; isoname; output ] ->    (* get attachment *)
89       do_get isoname output
90   | [ _; "has"; isoname ] ->            (* is there an attachment? *)
91       do_has isoname
92   | _ ->
93       eprintf "\
94 NAME
95
96   iso-attach - Attach files to CD-ROM ISO images.
97
98 SYNOPSIS
99
100   iso-attach add foo.iso file
101     Attach 'file' to 'foo.iso'.
102
103   iso-attach delete foo.iso
104     Remove attachment (if any) from 'foo.iso'.
105
106   iso-attach get foo.iso file
107     Get attachment from 'foo.iso' and save it as 'file'.
108
109   iso-attach has foo.iso
110     Exit with 0 (true) if there is an attachment.
111     Exit with 1 (false) if there is no attachment.
112     Exit with 2 if there was some other error, eg. file not found.
113
114 DESCRIPTION
115
116 iso-attach attaches an updated 'virt-p2v' file to the end of
117 an ISO image.  This is just for quick developer builds because it
118 takes ages to rebuild a full ISO.
119
120 Note that attachments are stacked, so you can add more than one
121 attachment.  In this case 'get' operation returns the most recently
122 added and 'delete' operation deletes only the most recently added.
123 ";
124       exit 1
125
126 and do_has isoname =
127   let fd =
128     try openfile isoname [O_RDONLY] 0
129     with Unix_error (err, syscall, param) ->
130       eprintf "%s:%s: %s\n" syscall param (error_message err);
131       exit 2 in
132   try
133     ignore (LargeFile.lseek fd (Int64.of_int ~-trailerlen) SEEK_END);
134     let buf = String.create magiclen in
135     if read fd buf 0 magiclen <> magiclen then exit 1;
136     if buf <> magic then exit 1;
137     exit 0
138   with
139     Unix_error (err, syscall, param) ->
140       eprintf "%s:%s: %s\n" syscall param (error_message err);
141       exit 1
142
143 and do_add isoname attachment =
144   let fd = openfile isoname [O_APPEND; O_WRONLY] 0 in
145
146   let iso_size = (LargeFile.fstat fd).LargeFile.st_size in
147   if Int64.logand iso_size 2047L <> 0L then
148     failwith "ISO image is not a multiple of 2048 bytes in size";
149
150   (* Copy the attachment itself to the end of the file. *)
151   let fd2 = openfile attachment [O_RDONLY] 0 in
152   let bufsize = 4 * 1024 in
153   let buffer = String.create bufsize in
154   let rec copy size =
155     let n = read fd2 buffer 0 bufsize in
156     if n > 0 then (
157       ignore (write fd buffer 0 n);
158       copy (size + n)
159     )
160     else size
161   in
162   let file_size = copy 0 in
163   close fd2;
164
165   (* How much padding to use so that file_size + trailer + padding
166    * = 2048 bytes multiple?
167    *)
168   let padding_size = 
169     let size = file_size + trailerlen in
170     let over = size land 2047 in
171     if over > 0 then 2048-over else 0 in
172   let attachment_size = padding_size + file_size + trailerlen in
173   assert (attachment_size land 2047 = 0);
174
175   (* Write the padding. *)
176   ignore (write fd (String.make padding_size 'x') 0 padding_size);
177
178   (* Write the magic. *)
179   ignore (write fd magic 0 magiclen);
180
181   (* Write the attachment size and true file size. *)
182   let buffer = string_of_int64 (Int64.of_int attachment_size) in
183   ignore (write fd buffer 0 8);
184   let buffer = string_of_int64 (Int64.of_int file_size) in
185   ignore (write fd buffer 0 8);
186
187   close fd
188
189 and do_delete isoname =
190   let fd = openfile isoname [O_RDWR] 0 in
191   ignore (LargeFile.lseek fd (Int64.of_int ~-trailerlen) SEEK_END);
192   let buf = String.create magiclen in
193   if read fd buf 0 magiclen <> magiclen || buf <> magic then
194     failwith "no attachment found";
195
196   (* Read the size of the attachment *)
197   let buf = String.create 8 in
198   if read fd buf 0 8 <> 8 then
199     failwith "cannot read attachment size";
200   let attachment_size = int64_of_string buf in
201
202   let orig_size = LargeFile.lseek fd (Int64.neg attachment_size) SEEK_END in
203
204   (* Truncate to start of the file. *)
205   LargeFile.ftruncate fd orig_size;
206
207   close fd
208
209 and do_get isoname output =
210   let fd = openfile isoname [O_RDONLY] 0 in
211   ignore (LargeFile.lseek fd (Int64.of_int ~-trailerlen) SEEK_END);
212   let buf = String.create magiclen in
213   if read fd buf 0 magiclen <> magiclen || buf <> magic then
214     failwith "no attachment found";
215
216   (* Read the attachment size and true size. *)
217   let buf = String.create 8 in
218   if read fd buf 0 8 <> 8 then
219     failwith "cannot read attachment size";
220   let attachment_size = int64_of_string buf in
221   let buf = String.create 8 in
222   if read fd buf 0 8 <> 8 then
223     failwith "cannot read file true size";
224   let size = Int64.to_int (int64_of_string buf) in
225
226   (* Seek to beginning of the attachment. *)
227   ignore (LargeFile.lseek fd (Int64.neg attachment_size) SEEK_END);
228
229   (* Copy out the attachment. *)
230   let fd2 = openfile output [O_WRONLY; O_CREAT; O_TRUNC] 0o644 in
231   let bufsize = 4 * 1024 in
232   let buffer = String.create bufsize in
233   let rec copy remaining =
234     if remaining > 0 then (
235       let n = min remaining bufsize in
236       let n = read fd buffer 0 n in
237       if n = 0 then failwith "corrupted or partial attachment";
238       ignore (write fd2 buffer 0 n);
239       copy (remaining - n)
240     )
241   in
242   copy size;
243   close fd2;
244
245   close fd
246
247 let () = main ()
248
249 (* This file must end with a newline. *)