1 /* febootstrap-supermin-helper reimplementation in C.
2 * Copyright (C) 2009-2010 Red Hat Inc.
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 of the License, or
7 * (at your option) any later version.
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33 #include "full-write.h"
35 #include "xvasprintf.h"
39 /* Buffer size used in copy operations throughout. Large for
40 * greatest efficiency.
42 #define BUFFER_SIZE 65536
44 static int out_fd = -1;
45 static off_t out_offset = 0;
47 static void write_file_to_fd (const char *filename);
48 static void write_file_len_to_fd (const char *filename, size_t len);
49 static void write_padding (size_t len);
50 static void cpio_append_fts_entry (FTSENT *entry);
51 static void cpio_append_stat (const char *filename, const struct stat *);
52 static void cpio_append (const char *filename);
53 static void cpio_append_trailer (void);
55 /* Copy contents of buffer to out_fd and keep out_offset correct. */
57 write_to_fd (const void *buffer, size_t len)
59 if (full_write (out_fd, buffer, len) != len)
60 error (EXIT_FAILURE, errno, "write");
64 /* Copy contents of file to out_fd. */
66 write_file_to_fd (const char *filename)
68 char buffer[BUFFER_SIZE];
73 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
75 fd2 = open (filename, O_RDONLY);
77 error (EXIT_FAILURE, errno, "open: %s", filename);
79 r = read (fd2, buffer, sizeof buffer);
83 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
85 error (EXIT_FAILURE, errno, "read: %s", filename);
87 write_to_fd (buffer, r);
90 if (close (fd2) == -1)
91 error (EXIT_FAILURE, errno, "close: %s", filename);
94 /* Copy file of given length to output, and fail if the file has
98 write_file_len_to_fd (const char *filename, size_t len)
100 char buffer[BUFFER_SIZE];
104 fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd);
106 int fd2 = open (filename, O_RDONLY);
108 error (EXIT_FAILURE, errno, "open: %s", filename);
110 ssize_t r = read (fd2, buffer, sizeof buffer);
114 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
116 error (EXIT_FAILURE, errno, "read: %s", filename);
118 write_to_fd (buffer, r);
121 error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename);
124 if (close (fd2) == -1)
125 error (EXIT_FAILURE, errno, "close: %s", filename);
128 error (EXIT_FAILURE, 0, "febootstrap-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename);
131 /* Append the file pointed to by FTSENT to the cpio output. */
133 cpio_append_fts_entry (FTSENT *entry)
135 if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK)
136 cpio_append (entry->fts_path);
138 cpio_append_stat (entry->fts_path, entry->fts_statp);
141 /* Append the file named 'filename' to the cpio output. */
143 cpio_append (const char *filename)
147 if (lstat (filename, &statbuf) == -1)
148 error (EXIT_FAILURE, errno, "lstat: %s", filename);
149 cpio_append_stat (filename, &statbuf);
152 /* Append the file to the cpio output. */
153 #define PADDING(len) ((((len) + 3) & ~3) - (len))
155 #define CPIO_HEADER_LEN (6 + 13*8)
158 cpio_append_stat (const char *filename, const struct stat *statbuf)
160 const char *orig_filename = filename;
162 if (*filename == '/')
164 if (*filename == '\0')
168 fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n",
169 orig_filename, statbuf->st_mode, out_fd);
171 /* Regular files and symlinks are the only ones that have a "body"
172 * in this cpio entry.
174 int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode);
176 size_t len = strlen (filename) + 1;
178 char header[CPIO_HEADER_LEN + 1];
179 snprintf (header, sizeof header,
183 "%08X" "%08X" /* uid, gid */
186 "%08X" /* file length */
187 "%08X" "%08X" /* device holding file major, minor */
188 "%08X" "%08X" /* for specials, device major, minor */
189 "%08X" /* name length (including \0 byte) */
190 "%08X", /* checksum (not used by the kernel) */
191 (unsigned) statbuf->st_ino, statbuf->st_mode,
192 statbuf->st_uid, statbuf->st_gid,
193 (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime,
194 has_body ? (unsigned) statbuf->st_size : 0,
195 major (statbuf->st_dev), minor (statbuf->st_dev),
196 major (statbuf->st_rdev), minor (statbuf->st_rdev),
199 /* Write the header. */
200 write_to_fd (header, CPIO_HEADER_LEN);
202 /* Follow with the filename, and pad it. */
203 write_to_fd (filename, len);
204 size_t padding_len = PADDING (CPIO_HEADER_LEN + len);
205 write_padding (padding_len);
207 /* Follow with the file or symlink content, and pad it. */
209 if (S_ISREG (statbuf->st_mode))
210 write_file_len_to_fd (orig_filename, statbuf->st_size);
211 else if (S_ISLNK (statbuf->st_mode)) {
213 if (readlink (orig_filename, tmp, sizeof tmp) == -1)
214 error (EXIT_FAILURE, errno, "readlink: %s", orig_filename);
215 write_to_fd (tmp, statbuf->st_size);
218 padding_len = PADDING (statbuf->st_size);
219 write_padding (padding_len);
225 cpio_append_trailer (void)
228 memset (&statbuf, 0, sizeof statbuf);
229 statbuf.st_nlink = 1;
230 cpio_append_stat ("TRAILER!!!", &statbuf);
232 /* CPIO seems to pad up to the next block boundary, ie. up to
233 * the next 512 bytes.
235 write_padding (((out_offset + 511) & ~511) - out_offset);
236 assert ((out_offset & 511) == 0);
239 /* Write 'len' bytes of zeroes out. */
241 write_padding (size_t len)
243 static const char buffer[512] = { 0 };
246 size_t n = len < sizeof buffer ? len : sizeof buffer;
247 write_to_fd (buffer, n);
253 cpio_start (const char *appliance)
255 out_fd = open (appliance, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644);
257 error (EXIT_FAILURE, errno, "open: %s", appliance);
264 cpio_append_trailer ();
266 /* Finish off and close output file. */
267 if (close (out_fd) == -1)
268 error (EXIT_FAILURE, errno, "close");
271 struct writer cpio_writer = {
272 .wr_start = cpio_start,
274 .wr_file = cpio_append,
275 .wr_file_stat = cpio_append_stat,
276 .wr_fts_entry = cpio_append_fts_entry,
277 .wr_cpio_file = write_file_to_fd,