1 #!/usr/bin/ocamlrun /usr/bin/ocaml
3 (* iso-attach attaches an updated 'virt-p2v.ml' 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.
7 * Copyright (C) 2007-2008 Red Hat Inc.
8 * Written by Richard W.M. Jones <rjones@redhat.com>
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.
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.
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
30 (* CD-ROM format has 2048 byte "sectors" and the final ISO image
31 * must be a multiple of 2048 bytes in size.
33 * The basic plan is that we will use store the file followed by a
34 * trailer, all rounded up to 2048 bytes:
36 * +------ - - - - - - - -----+------------+----------------+
37 * | file | padding | trailer |
38 * | | | magic | .... |
39 * +------ - - - - - - - -----+------------+----------------+
40 * |<---- total size is a multiple of 2048 bytes ---->|
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).
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.
53 let magic = "ISOATTACHMENT002"
54 let magiclen = String.length magic (* = 16 bytes *)
55 let trailerlen = magiclen + 8 + 8 (* magic + file start + true size *)
57 (* Ugh, would be really nice to have 64 bit pack/unpack functions
58 * in the stdlib, instead of this ugliness ...
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)
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;
70 let int64_of_string str =
75 (Int64.shift_left (Int64.of_int (Char.code str.[offs])) shift) !i
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;
82 let args = Array.to_list Sys.argv in
84 | [ _; "add"; isoname; attachment ] -> (* add an attachment *)
85 do_add isoname attachment
86 | [ _; "delete"; isoname ] -> (* delete any attachment *)
88 | [ _; "get"; isoname; output ] -> (* get attachment *)
90 | [ _; "has"; isoname ] -> (* is there an attachment? *)
96 iso-attach - Attach files to CD-ROM ISO images.
100 iso-attach add foo.iso file
101 Attach 'file' to 'foo.iso'.
103 iso-attach delete foo.iso
104 Remove attachment (if any) from 'foo.iso'.
106 iso-attach get foo.iso file
107 Get attachment from 'foo.iso' and save it as 'file'.
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.
116 iso-attach attaches an updated 'virt-p2v.ml' 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.
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.
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);
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;
139 Unix_error (err, syscall, param) ->
140 eprintf "%s:%s: %s\n" syscall param (error_message err);
143 and do_add isoname attachment =
144 let fd = openfile isoname [O_APPEND; O_WRONLY] 0 in
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";
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
155 let n = read fd2 buffer 0 bufsize in
157 ignore (write fd buffer 0 n);
162 let file_size = copy 0 in
165 (* How much padding to use so that file_size + trailer + padding
166 * = 2048 bytes multiple?
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 assert ((padding_size + file_size + trailerlen) land 2047 = 0);
174 (* Write the padding. *)
175 ignore (write fd (String.make padding_size 'x') 0 padding_size);
177 (* Write the magic. *)
178 ignore (write fd magic 0 magiclen);
180 (* Write the file start and true size. *)
181 let buffer = string_of_int64 iso_size in
182 ignore (write fd buffer 0 8);
183 let buffer = string_of_int64 (Int64.of_int file_size) in
184 ignore (write fd buffer 0 8);
188 and do_delete isoname =
189 let fd = openfile isoname [O_RDWR] 0 in
190 ignore (LargeFile.lseek fd (Int64.of_int ~-trailerlen) SEEK_END);
191 let buf = String.create magiclen in
192 if read fd buf 0 magiclen <> magiclen || buf <> magic then
193 failwith "no attachment found";
195 (* Read the start offset of the file. *)
196 let buf = String.create 8 in
197 if read fd buf 0 8 <> 8 then
198 failwith "cannot read attachment size";
199 let offset = int64_of_string buf in
201 (* Truncate to start of the file. *)
202 LargeFile.ftruncate fd offset;
206 and do_get isoname output =
207 let fd = openfile isoname [O_RDONLY] 0 in
208 ignore (LargeFile.lseek fd (Int64.of_int ~-trailerlen) SEEK_END);
209 let buf = String.create magiclen in
210 if read fd buf 0 magiclen <> magiclen || buf <> magic then
211 failwith "no attachment found";
213 (* Read the start and size. *)
214 let buf = String.create 8 in
215 if read fd buf 0 8 <> 8 then
216 failwith "cannot read attachment offset";
217 let offset = int64_of_string buf in
218 let buf = String.create 8 in
219 if read fd buf 0 8 <> 8 then
220 failwith "cannot read attachment size";
221 let size = Int64.to_int (int64_of_string buf) in
223 (* Seek to beginning of the attachment. *)
224 ignore (LargeFile.lseek fd offset SEEK_SET);
226 (* Copy out the attachment. *)
227 let fd2 = openfile output [O_WRONLY; O_CREAT; O_TRUNC] 0o644 in
228 let bufsize = 4 * 1024 in
229 let buffer = String.create bufsize in
230 let rec copy remaining =
231 if remaining > 0 then (
232 let n = min remaining bufsize in
233 let n = read fd buffer 0 n in
234 if n = 0 then failwith "corrupted or partial attachment";
235 ignore (write fd2 buffer 0 n);