1 #!/usr/bin/ocamlrun /usr/bin/ocaml
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.
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 = "ISOATTACHMENT003"
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' 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 let attachment_size = padding_size + file_size + trailerlen in
173 assert (attachment_size land 2047 = 0);
175 (* Write the padding. *)
176 ignore (write fd (String.make padding_size 'x') 0 padding_size);
178 (* Write the magic. *)
179 ignore (write fd magic 0 magiclen);
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);
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";
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
202 let orig_size = LargeFile.lseek fd (Int64.neg attachment_size) SEEK_END in
204 (* Truncate to start of the file. *)
205 LargeFile.ftruncate fd orig_size;
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";
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
226 (* Seek to beginning of the attachment. *)
227 ignore (LargeFile.lseek fd (Int64.neg attachment_size) SEEK_END);
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);
249 (* This file must end with a newline. *)