1 | 1 |
// genext2fs.c
|
2 | 2 |
//
|
3 | 3 |
// ext2 filesystem generator for embedded systems
|
4 | |
// Copyright (C) 2000 Xavier Bestel <xavier.bestel@free.fr>
|
|
4 |
// Copyright (C) 2000 Xavier Bestel <xav@bes.tel>
|
5 | 5 |
//
|
6 | 6 |
// Please direct support requests to https://github.com/bestouff/genext2fs/issues
|
7 | 7 |
//
|
|
147 | 147 |
# include <limits.h>
|
148 | 148 |
#endif
|
149 | 149 |
|
|
150 |
#ifdef HAVE_LIBARCHIVE
|
|
151 |
#include <archive.h>
|
|
152 |
#include <archive_entry.h>
|
|
153 |
#endif
|
|
154 |
|
150 | 155 |
#include "cache.h"
|
|
156 |
|
|
157 |
#define MIN(a, b) ((a) > (b) ? (b) : (a))
|
|
158 |
|
|
159 |
#define FSLAYER_DIR 0
|
|
160 |
#define FSLAYER_TABLE 1
|
|
161 |
#define FSLAYER_TAR 2
|
|
162 |
|
|
163 |
struct fslayer {
|
|
164 |
int type;
|
|
165 |
char * path;
|
|
166 |
};
|
|
167 |
|
|
168 |
#define TAR_BLOCKSIZE 512
|
|
169 |
#define TAR_FULLFILENAME (100 + 155 + 1)
|
|
170 |
|
|
171 |
struct tar_header {
|
|
172 |
char filename[100];
|
|
173 |
char filemode[8];
|
|
174 |
char uid[8];
|
|
175 |
char gid[8];
|
|
176 |
char filesize[12];
|
|
177 |
char mtime[12];
|
|
178 |
char checksum[8];
|
|
179 |
char filetype;
|
|
180 |
char linkedname[100];
|
|
181 |
char ustar[8];
|
|
182 |
char owner[32];
|
|
183 |
char group[32];
|
|
184 |
char major[8];
|
|
185 |
char minor[8];
|
|
186 |
char prefix[155];
|
|
187 |
};
|
151 | 188 |
|
152 | 189 |
struct stats {
|
153 | 190 |
unsigned long nblocks;
|
154 | 191 |
unsigned long ninodes;
|
155 | 192 |
};
|
156 | 193 |
|
|
194 |
|
|
195 |
// used types
|
|
196 |
|
|
197 |
typedef signed char int8;
|
|
198 |
typedef unsigned char uint8;
|
|
199 |
typedef signed short int16;
|
|
200 |
typedef unsigned short uint16;
|
|
201 |
typedef signed int int32;
|
|
202 |
typedef unsigned int uint32;
|
|
203 |
|
157 | 204 |
// block size
|
158 | 205 |
|
159 | |
static int blocksize = 1024;
|
|
206 |
static uint32 blocksize = 1024;
|
160 | 207 |
|
161 | 208 |
#define SUPERBLOCK_OFFSET 1024
|
162 | 209 |
#define SUPERBLOCK_SIZE 1024
|
|
309 | 356 |
//Given a block number find its offset within the block bitmap that covers it
|
310 | 357 |
#define GRP_BBM_OFFSET(fs,blk) \
|
311 | 358 |
( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb->s_blocks_per_group )
|
312 | |
|
313 | |
|
314 | |
// used types
|
315 | |
|
316 | |
typedef signed char int8;
|
317 | |
typedef unsigned char uint8;
|
318 | |
typedef signed short int16;
|
319 | |
typedef unsigned short uint16;
|
320 | |
typedef signed int int32;
|
321 | |
typedef unsigned int uint32;
|
322 | 359 |
|
323 | 360 |
|
324 | 361 |
// the GNU C library has a wonderful scanf("%as", string) which will
|
|
724 | 761 |
static void
|
725 | 762 |
swap_block(block b)
|
726 | 763 |
{
|
727 | |
int i;
|
|
764 |
uint32 i;
|
728 | 765 |
uint32 *blk = (uint32*)b;
|
729 | 766 |
for(i = 0; i < BLOCKSIZE/4; i++)
|
730 | 767 |
blk[i] = swab32(blk[i]);
|
|
1338 | 1375 |
{
|
1339 | 1376 |
if(!item)
|
1340 | 1377 |
{
|
1341 | |
int i;
|
|
1378 |
uint32 i;
|
1342 | 1379 |
uint8 bits;
|
1343 | 1380 |
for(i = 0; i < BLOCKSIZE; i++)
|
1344 | 1381 |
if((bits = b[i]) != (uint8)-1)
|
|
1799 | 1836 |
static void
|
1800 | 1837 |
inode_pos_finish(filesystem *fs, inode_pos *ipos)
|
1801 | 1838 |
{
|
|
1839 |
fs = fs; // unused
|
1802 | 1840 |
put_nod(ipos->ni);
|
1803 | 1841 |
}
|
1804 | 1842 |
|
|
1837 | 1875 |
uint32 bk;
|
1838 | 1876 |
directory *d;
|
1839 | 1877 |
dirwalker dw;
|
1840 | |
int reclen, nlen;
|
|
1878 |
uint32 reclen, nlen;
|
1841 | 1879 |
inode *node;
|
1842 | 1880 |
inode *pnode;
|
1843 | 1881 |
nod_info *dni, *ni;
|
|
2062 | 2100 |
#define COPY_BLOCKS 16
|
2063 | 2101 |
#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE)
|
2064 | 2102 |
|
|
2103 |
typedef off_t (*file_read_cb)(filesystem *fs, inode_pos *ipos, off_t size, void *data);
|
|
2104 |
|
|
2105 |
off_t fh_read(filesystem *fs, inode_pos *ipos, off_t size, void *data)
|
|
2106 |
{
|
|
2107 |
size_t readbytes;
|
|
2108 |
off_t remaining_size = size;
|
|
2109 |
int fullsize;
|
|
2110 |
|
|
2111 |
uint8 * b = malloc(CB_SIZE);
|
|
2112 |
if (!b)
|
|
2113 |
error_msg_and_die("mkfile_fs: out of memory");
|
|
2114 |
|
|
2115 |
readbytes = fread(b, 1, MIN(remaining_size, CB_SIZE), (FILE *)data);
|
|
2116 |
while (readbytes) {
|
|
2117 |
remaining_size -= readbytes;
|
|
2118 |
fullsize = rndup(readbytes, BLOCKSIZE);
|
|
2119 |
// Fill to end of block with zeros.
|
|
2120 |
memset(b + readbytes, 0, fullsize - readbytes);
|
|
2121 |
extend_inode_blk(fs, ipos, b, fullsize / BLOCKSIZE);
|
|
2122 |
readbytes = fread(b, 1, MIN(remaining_size, CB_SIZE), (FILE *)data);
|
|
2123 |
}
|
|
2124 |
|
|
2125 |
free(b);
|
|
2126 |
|
|
2127 |
return size;
|
|
2128 |
}
|
|
2129 |
|
|
2130 |
#ifdef HAVE_LIBARCHIVE
|
|
2131 |
off_t la_read(filesystem *fs, inode_pos *ipos, off_t s /* ignored */, void *data)
|
|
2132 |
{
|
|
2133 |
size_t readbytes;
|
|
2134 |
off_t actual_size = 0;
|
|
2135 |
int fullsize;
|
|
2136 |
|
|
2137 |
uint8 * b = malloc(CB_SIZE);
|
|
2138 |
if (!b)
|
|
2139 |
error_msg_and_die("mkfile_fs: out of memory");
|
|
2140 |
|
|
2141 |
readbytes = archive_read_data((struct archive *)data, b, CB_SIZE);
|
|
2142 |
while (readbytes) {
|
|
2143 |
actual_size += readbytes;
|
|
2144 |
fullsize = rndup(readbytes, BLOCKSIZE);
|
|
2145 |
// Fill to end of block with zeros.
|
|
2146 |
memset(b + readbytes, 0, fullsize - readbytes);
|
|
2147 |
extend_inode_blk(fs, ipos, b, fullsize / BLOCKSIZE);
|
|
2148 |
readbytes = archive_read_data((struct archive *)data, b, CB_SIZE);
|
|
2149 |
}
|
|
2150 |
|
|
2151 |
free(b);
|
|
2152 |
|
|
2153 |
return actual_size;
|
|
2154 |
|
|
2155 |
|
|
2156 |
}
|
|
2157 |
#endif
|
|
2158 |
|
2065 | 2159 |
// make a file from a FILE*
|
2066 | 2160 |
static uint32
|
2067 | |
mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
|
2068 | |
{
|
2069 | |
uint8 * b;
|
|
2161 |
mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, file_read_cb read_cb, void *data, off_t size, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
|
|
2162 |
{
|
2070 | 2163 |
uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
|
2071 | 2164 |
nod_info *ni;
|
2072 | 2165 |
inode *node = get_nod(fs, nod, &ni);
|
2073 | |
off_t size = 0;
|
2074 | |
size_t readbytes;
|
|
2166 |
off_t actual_size;
|
2075 | 2167 |
inode_pos ipos;
|
2076 | |
int fullsize;
|
2077 | |
|
2078 | |
b = malloc(CB_SIZE);
|
2079 | |
if (!b)
|
2080 | |
error_msg_and_die("mkfile_fs: out of memory");
|
|
2168 |
|
2081 | 2169 |
inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
|
2082 | |
readbytes = fread(b, 1, CB_SIZE, f);
|
2083 | |
while (readbytes) {
|
2084 | |
fullsize = rndup(readbytes, BLOCKSIZE);
|
2085 | |
// Fill to end of block with zeros.
|
2086 | |
memset(b + readbytes, 0, fullsize - readbytes);
|
2087 | |
extend_inode_blk(fs, &ipos, b, fullsize / BLOCKSIZE);
|
2088 | |
size += readbytes;
|
2089 | |
readbytes = fread(b, 1, CB_SIZE, f);
|
2090 | |
}
|
2091 | |
if (size > 0x7fffffff) {
|
|
2170 |
|
|
2171 |
actual_size = read_cb(fs, &ipos, size, data);
|
|
2172 |
|
|
2173 |
if (actual_size > 0x7fffffff) {
|
2092 | 2174 |
if (fs->sb->s_rev_level < 1)
|
2093 | 2175 |
fs_upgrade_rev1_largefile(fs);
|
2094 | 2176 |
fs->sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
|
2095 | 2177 |
}
|
2096 | |
node->i_dir_acl = size >> 32;
|
2097 | |
node->i_size = size;
|
|
2178 |
node->i_dir_acl = actual_size >> 32;
|
|
2179 |
node->i_size = actual_size;
|
2098 | 2180 |
inode_pos_finish(fs, &ipos);
|
2099 | 2181 |
put_nod(ni);
|
2100 | |
free(b);
|
2101 | 2182 |
return nod;
|
2102 | 2183 |
}
|
2103 | 2184 |
|
|
2134 | 2215 |
return mode;
|
2135 | 2216 |
}
|
2136 | 2217 |
|
|
2218 |
#define OCTAL_READ(field) tar_numeric_field_read((unsigned char*)field, sizeof field)
|
|
2219 |
|
|
2220 |
long long tar_numeric_field_read(unsigned char *field, size_t size)
|
|
2221 |
{
|
|
2222 |
size_t i;
|
|
2223 |
unsigned long long res = 0;
|
|
2224 |
// this is the 256-based GNU extension
|
|
2225 |
if(*field == 0x80 || *field == 0xff)
|
|
2226 |
{
|
|
2227 |
if(*field == 0xff)
|
|
2228 |
res = -1;
|
|
2229 |
for(i = 1; i < size; i++)
|
|
2230 |
res = (res << 8) + field[i];
|
|
2231 |
} else
|
|
2232 |
for(i = 0; i < size; i++)
|
|
2233 |
{
|
|
2234 |
char c = field[i];
|
|
2235 |
if(c == ' ')
|
|
2236 |
continue;
|
|
2237 |
if(c < '0' || c > '7')
|
|
2238 |
break;
|
|
2239 |
res = 8 * res + c - '0';
|
|
2240 |
}
|
|
2241 |
return res;
|
|
2242 |
}
|
|
2243 |
|
|
2244 |
int is_zero(char *block, size_t size)
|
|
2245 |
{
|
|
2246 |
size_t i;
|
|
2247 |
for(i = 0; i < size; i++)
|
|
2248 |
if(block[i])
|
|
2249 |
return 0;
|
|
2250 |
return 1;
|
|
2251 |
}
|
|
2252 |
|
|
2253 |
static void
|
|
2254 |
add2fs_from_tarball(filesystem *fs, uint32 this_nod, FILE * fh, int squash_uids, int squash_perms, uint32 fs_timestamp, struct stats *stats)
|
|
2255 |
{
|
|
2256 |
#ifndef HAVE_LIBARCHIVE
|
|
2257 |
uint32 nod, hlnod, oldnod;
|
|
2258 |
uint32 uid, gid, mode, major, minor, ctime, mtime;
|
|
2259 |
char buffer[TAR_BLOCKSIZE];
|
|
2260 |
char pathbuf[TAR_FULLFILENAME], *path, *path2 = NULL, *dir, *name;
|
|
2261 |
char type;
|
|
2262 |
struct tar_header *tarhead = (void*)buffer;
|
|
2263 |
size_t filesize, padding;
|
|
2264 |
int nbnull = 0;
|
|
2265 |
int i, checksum, signed_checksum, unsigned_checksum;
|
|
2266 |
int has_longname = 0;
|
|
2267 |
char *longname = NULL;
|
|
2268 |
size_t longname_size = 0;
|
|
2269 |
int has_longlink = 0;
|
|
2270 |
char *longlink = NULL;
|
|
2271 |
size_t longlink_size = 0;
|
|
2272 |
|
|
2273 |
size_t readbytes;
|
|
2274 |
while(1)
|
|
2275 |
{
|
|
2276 |
if (path2) {
|
|
2277 |
free(path2);
|
|
2278 |
path2 = NULL;
|
|
2279 |
}
|
|
2280 |
readbytes = fread(buffer, 1, TAR_BLOCKSIZE, fh);
|
|
2281 |
if (!readbytes)
|
|
2282 |
break;
|
|
2283 |
if (readbytes != TAR_BLOCKSIZE)
|
|
2284 |
error_msg_and_die("tarball has wrong size");
|
|
2285 |
if (is_zero(buffer, sizeof buffer))
|
|
2286 |
{
|
|
2287 |
if (nbnull++)
|
|
2288 |
break;
|
|
2289 |
continue;
|
|
2290 |
} else
|
|
2291 |
nbnull = 0;
|
|
2292 |
if (*(long *)tarhead->ustar != *(long *)"ustar\00000" && strcmp(tarhead->ustar, "ustar "))
|
|
2293 |
error_msg_and_die("not a tarball");
|
|
2294 |
signed_checksum = unsigned_checksum = 0;
|
|
2295 |
checksum = OCTAL_READ(tarhead->checksum);
|
|
2296 |
memset(tarhead->checksum, ' ', sizeof tarhead->checksum);
|
|
2297 |
for(i = 0; i < TAR_BLOCKSIZE; i++)
|
|
2298 |
{
|
|
2299 |
signed_checksum += (signed char)buffer[i];
|
|
2300 |
unsigned_checksum += (unsigned char)buffer[i];
|
|
2301 |
}
|
|
2302 |
if(checksum != signed_checksum && checksum != unsigned_checksum)
|
|
2303 |
error_msg_and_die("tarball corrupted");
|
|
2304 |
filesize = OCTAL_READ(tarhead->filesize);
|
|
2305 |
padding = rndup(filesize, TAR_BLOCKSIZE) - filesize;
|
|
2306 |
mtime = OCTAL_READ(tarhead->mtime);
|
|
2307 |
ctime = fs_timestamp;
|
|
2308 |
uid = OCTAL_READ(tarhead->uid);
|
|
2309 |
gid = OCTAL_READ(tarhead->gid);
|
|
2310 |
mode = OCTAL_READ(tarhead->filemode) & FM_IMASK;
|
|
2311 |
major = OCTAL_READ(tarhead->major);
|
|
2312 |
minor = OCTAL_READ(tarhead->minor);
|
|
2313 |
type = tarhead->filetype;
|
|
2314 |
if (squash_uids)
|
|
2315 |
uid = gid = 0;
|
|
2316 |
if (squash_perms)
|
|
2317 |
mode &= ~(FM_IRWXG | FM_IRWXO);
|
|
2318 |
if(has_longname)
|
|
2319 |
{
|
|
2320 |
path = longname;
|
|
2321 |
has_longname = 0;
|
|
2322 |
} else {
|
|
2323 |
strncpy(pathbuf, tarhead->prefix, sizeof tarhead->prefix);
|
|
2324 |
strncpy(pathbuf+strnlen(pathbuf, sizeof pathbuf), tarhead->filename, sizeof tarhead->filename);
|
|
2325 |
pathbuf[strnlen(pathbuf, sizeof pathbuf - 1)] = '\0';
|
|
2326 |
path = pathbuf;
|
|
2327 |
}
|
|
2328 |
if (stats)
|
|
2329 |
{
|
|
2330 |
switch (type)
|
|
2331 |
{
|
|
2332 |
case '2':
|
|
2333 |
if (strlen(tarhead->linkedname) >= 4 * (EXT2_TIND_BLOCK+1))
|
|
2334 |
stats->nblocks += (filesize + BLOCKSIZE - 1) / BLOCKSIZE;
|
|
2335 |
stats->ninodes++;
|
|
2336 |
break;
|
|
2337 |
case '0':
|
|
2338 |
case 0:
|
|
2339 |
case '7':
|
|
2340 |
stats->nblocks += (filesize + BLOCKSIZE - 1) / BLOCKSIZE;
|
|
2341 |
case '1':
|
|
2342 |
case '6':
|
|
2343 |
case '3':
|
|
2344 |
case '4':
|
|
2345 |
stats->ninodes++;
|
|
2346 |
break;
|
|
2347 |
default:
|
|
2348 |
break;
|
|
2349 |
}
|
|
2350 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2351 |
} else {
|
|
2352 |
if (fs && strcmp(path, "/") == 0) {
|
|
2353 |
// if the entry modifies the root node, don't call
|
|
2354 |
// basename and dirname but chmod the root node
|
|
2355 |
// directly
|
|
2356 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2357 |
if (type != '5') {
|
|
2358 |
error_msg("tarball entry \"%s\" skipped: root node must be a directory", path);
|
|
2359 |
continue;
|
|
2360 |
}
|
|
2361 |
mode |= FM_IFDIR;
|
|
2362 |
chmod_fs(fs, this_nod, mode, uid, gid);
|
|
2363 |
continue;
|
|
2364 |
}
|
|
2365 |
path2 = strdup(path);
|
|
2366 |
name = basename(path);
|
|
2367 |
dir = dirname(path2);
|
|
2368 |
if((!strcmp(name, ".")) || (!strcmp(name, "..")))
|
|
2369 |
{
|
|
2370 |
error_msg("tarball entry %s skipped", path);
|
|
2371 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2372 |
continue;
|
|
2373 |
}
|
|
2374 |
if(fs)
|
|
2375 |
{
|
|
2376 |
if(!(nod = find_path(fs, this_nod, dir)))
|
|
2377 |
{
|
|
2378 |
error_msg("tarball entry %s skipped: can't find directory '%s' to create '%s''", path, dir, name);
|
|
2379 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2380 |
continue;
|
|
2381 |
}
|
|
2382 |
}
|
|
2383 |
else
|
|
2384 |
nod = 0;
|
|
2385 |
switch (type)
|
|
2386 |
{
|
|
2387 |
case '5':
|
|
2388 |
if((oldnod = find_path(fs, nod, name)))
|
|
2389 |
chmod_fs(fs, nod = oldnod, mode, uid, gid);
|
|
2390 |
else
|
|
2391 |
nod = mkdir_fs(fs, nod, name, mode, uid, gid, ctime, mtime);
|
|
2392 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2393 |
break;
|
|
2394 |
case '0':
|
|
2395 |
case 0:
|
|
2396 |
case '7':
|
|
2397 |
nod = mkfile_fs(fs, nod, name, mode, fh_read, fh, filesize, uid, gid, ctime, mtime);
|
|
2398 |
fseek(fh, padding, SEEK_CUR);
|
|
2399 |
break;
|
|
2400 |
case '1':
|
|
2401 |
if(!(hlnod = find_path(fs, this_nod, tarhead->linkedname)))
|
|
2402 |
{
|
|
2403 |
error_msg("tarball entry %s skipped: can't find hardlink destination '%s' to create '%s''", path, dir, name);
|
|
2404 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2405 |
continue;
|
|
2406 |
}
|
|
2407 |
add2dir(fs, nod, hlnod, name);
|
|
2408 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2409 |
break;
|
|
2410 |
case '2':
|
|
2411 |
if(has_longlink)
|
|
2412 |
mklink_fs(fs, nod, name, strlen(longlink), (uint8*)longlink, uid, gid, ctime, mtime);
|
|
2413 |
else
|
|
2414 |
mklink_fs(fs, nod, name, strlen(tarhead->linkedname), (uint8*)tarhead->linkedname, uid, gid, ctime, mtime);
|
|
2415 |
has_longlink = 0;
|
|
2416 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2417 |
break;
|
|
2418 |
case '6':
|
|
2419 |
nod = mknod_fs(fs, nod, name, mode|FM_IFIFO, uid, gid, 0, 0, ctime, mtime);
|
|
2420 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2421 |
break;
|
|
2422 |
case '3':
|
|
2423 |
nod = mknod_fs(fs, nod, name, mode|FM_IFCHR, uid, gid, major, minor, ctime, mtime);
|
|
2424 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2425 |
break;
|
|
2426 |
case '4':
|
|
2427 |
nod = mknod_fs(fs, nod, name, mode|FM_IFBLK, uid, gid, major, minor, ctime, mtime);
|
|
2428 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2429 |
break;
|
|
2430 |
case 'L':
|
|
2431 |
if(has_longname)
|
|
2432 |
error_msg("tarball longname to '%s' hasn't been consumed", longname);
|
|
2433 |
if(filesize + padding > longname_size)
|
|
2434 |
{
|
|
2435 |
if(longname)
|
|
2436 |
free(longname);
|
|
2437 |
longname = malloc(longname_size = filesize + padding);
|
|
2438 |
}
|
|
2439 |
fread(longname, longname_size, 1, fh);
|
|
2440 |
has_longname = 1;
|
|
2441 |
break;
|
|
2442 |
case 'K':
|
|
2443 |
if(has_longlink)
|
|
2444 |
error_msg("tarball longlink to '%s' hasn't been consumed", longlink);
|
|
2445 |
if(filesize + padding > longlink_size)
|
|
2446 |
{
|
|
2447 |
if(longlink)
|
|
2448 |
free(longlink);
|
|
2449 |
longlink = malloc(longlink_size = filesize + padding);
|
|
2450 |
}
|
|
2451 |
fread(longlink, longlink_size, 1, fh);
|
|
2452 |
has_longlink = 1;
|
|
2453 |
break;
|
|
2454 |
default:
|
|
2455 |
error_msg("tarball entry %s skipped: bad type '%c' for entry '%s'", path, type, name);
|
|
2456 |
fseek(fh, filesize + padding, SEEK_CUR);
|
|
2457 |
continue;
|
|
2458 |
}
|
|
2459 |
}
|
|
2460 |
}
|
|
2461 |
if (path2)
|
|
2462 |
free(path2);
|
|
2463 |
if (nbnull != 2)
|
|
2464 |
error_msg_and_die("tarball has wrong size");
|
|
2465 |
if(longname)
|
|
2466 |
free(longname);
|
|
2467 |
if(longlink)
|
|
2468 |
free(longlink);
|
|
2469 |
#else
|
|
2470 |
int r;
|
|
2471 |
uint32 nod, lnknod;
|
|
2472 |
struct archive *a;
|
|
2473 |
struct archive_entry *entry;
|
|
2474 |
char *path2, *path3, *dir, *name, *lnk;
|
|
2475 |
size_t filesize;
|
|
2476 |
uint32 uid, gid, mode, ctime, mtime;
|
|
2477 |
a = archive_read_new();
|
|
2478 |
if (a == NULL)
|
|
2479 |
error_msg_and_die("Couldn't create archive reader.");
|
|
2480 |
if (archive_read_support_filter_all(a) != ARCHIVE_OK)
|
|
2481 |
error_msg_and_die("Couldn't enable decompression");
|
|
2482 |
if (archive_read_support_format_all(a) != ARCHIVE_OK)
|
|
2483 |
error_msg_and_die("Couldn't enable read formats");
|
|
2484 |
|
|
2485 |
if ((r = archive_read_open_FILE(a, fh)))
|
|
2486 |
error_msg_and_die("archive_read_open_FILE(): %s", archive_error_string(a));
|
|
2487 |
|
|
2488 |
for (;;) {
|
|
2489 |
r = archive_read_next_header(a, &entry);
|
|
2490 |
if (r == ARCHIVE_EOF)
|
|
2491 |
break;
|
|
2492 |
if (r != ARCHIVE_OK)
|
|
2493 |
error_msg_and_die("archive_read_next_header(): %s",
|
|
2494 |
archive_error_string(a));
|
|
2495 |
mode = archive_entry_mode(entry);
|
|
2496 |
if (stats)
|
|
2497 |
{
|
|
2498 |
// depending on the archive, the entry size might not
|
|
2499 |
// be set in the first place in which case the
|
|
2500 |
// estimate might be totally off
|
|
2501 |
filesize = archive_entry_size(entry);
|
|
2502 |
switch(mode & S_IFMT)
|
|
2503 |
{
|
|
2504 |
case S_IFLNK:
|
|
2505 |
if(filesize >= 4 * (EXT2_TIND_BLOCK+1))
|
|
2506 |
stats->nblocks += (filesize + BLOCKSIZE - 1) / BLOCKSIZE;
|
|
2507 |
stats->ninodes++;
|
|
2508 |
break;
|
|
2509 |
case S_IFREG:
|
|
2510 |
stats->nblocks += (filesize + BLOCKSIZE - 1) / BLOCKSIZE;
|
|
2511 |
case S_IFCHR:
|
|
2512 |
case S_IFBLK:
|
|
2513 |
case S_IFIFO:
|
|
2514 |
case S_IFSOCK:
|
|
2515 |
stats->ninodes++;
|
|
2516 |
break;
|
|
2517 |
case S_IFDIR:
|
|
2518 |
stats->ninodes++;
|
|
2519 |
break;
|
|
2520 |
default:
|
|
2521 |
break;
|
|
2522 |
}
|
|
2523 |
continue;
|
|
2524 |
}
|
|
2525 |
path2 = strdup(archive_entry_pathname(entry));
|
|
2526 |
path3 = strdup(archive_entry_pathname(entry));
|
|
2527 |
name = basename(path2);
|
|
2528 |
dir = dirname(path3);
|
|
2529 |
if(!(nod = find_path(fs, this_nod, dir)))
|
|
2530 |
{
|
|
2531 |
error_msg("can't find directory '%s' to create '%s''", dir, name);
|
|
2532 |
continue;
|
|
2533 |
}
|
|
2534 |
uid = archive_entry_uid(entry);
|
|
2535 |
gid = archive_entry_gid(entry);
|
|
2536 |
if (squash_uids)
|
|
2537 |
uid = gid = 0;
|
|
2538 |
if (squash_perms)
|
|
2539 |
mode &= ~(FM_IRWXG | FM_IRWXO);
|
|
2540 |
if(find_dir(fs, nod, name))
|
|
2541 |
chmod_fs(fs, nod, mode, uid, gid);
|
|
2542 |
// FIXME: if the entry is a regular file, update
|
|
2543 |
// content
|
|
2544 |
else {
|
|
2545 |
ctime = archive_entry_ctime(entry);
|
|
2546 |
mtime = archive_entry_mtime(entry);
|
|
2547 |
switch(mode & S_IFMT)
|
|
2548 |
{
|
|
2549 |
#if HAVE_STRUCT_STAT_ST_RDEV
|
|
2550 |
case S_IFCHR:
|
|
2551 |
mknod_fs(fs, nod, name, mode|FM_IFCHR, uid, gid, major(archive_entry_rdev(entry)), minor(archive_entry_rdev(entry)), ctime, mtime);
|
|
2552 |
break;
|
|
2553 |
case S_IFBLK:
|
|
2554 |
mknod_fs(fs, nod, name, mode|FM_IFBLK, uid, gid, major(archive_entry_rdev(entry)), minor(archive_entry_rdev(entry)), ctime, mtime);
|
|
2555 |
break;
|
|
2556 |
#endif
|
|
2557 |
case S_IFIFO:
|
|
2558 |
mknod_fs(fs, nod, name, mode|FM_IFIFO, uid, gid, 0, 0, ctime, mtime);
|
|
2559 |
break;
|
|
2560 |
case S_IFSOCK:
|
|
2561 |
mknod_fs(fs, nod, name, mode|FM_IFSOCK, uid, gid, 0, 0, ctime, mtime);
|
|
2562 |
break;
|
|
2563 |
case S_IFLNK:
|
|
2564 |
lnk = calloc(1, rndup(strlen(archive_entry_symlink(entry)), BLOCKSIZE));
|
|
2565 |
strcpy(lnk, archive_entry_symlink(entry));
|
|
2566 |
if (lnk == NULL)
|
|
2567 |
error_msg_and_die(memory_exhausted);
|
|
2568 |
mklink_fs(fs, nod, name, strlen(archive_entry_symlink(entry)), (uint8*)lnk, uid, gid, ctime, mtime);
|
|
2569 |
free(lnk);
|
|
2570 |
break;
|
|
2571 |
case S_IFREG:
|
|
2572 |
// we pass a filesize of zero because
|
|
2573 |
// libarchive will take care of it for
|
|
2574 |
// us and the archive does not
|
|
2575 |
// necessarily contain the file size
|
|
2576 |
// in the first place
|
|
2577 |
mkfile_fs(fs, nod, name, mode, la_read, a, 0, uid, gid, ctime, mtime);
|
|
2578 |
break;
|
|
2579 |
case S_IFDIR:
|
|
2580 |
mkdir_fs(fs, nod, name, mode, uid, gid, ctime, mtime);
|
|
2581 |
break;
|
|
2582 |
default:
|
|
2583 |
// probably a hardlink
|
|
2584 |
if (archive_entry_hardlink(entry) != NULL) {
|
|
2585 |
if(!(lnknod = find_path(fs, this_nod, archive_entry_hardlink(entry))))
|
|
2586 |
error_msg_and_die("path %s not found in filesystem", archive_entry_hardlink(entry));
|
|
2587 |
add2dir(fs, nod, lnknod, name);
|
|
2588 |
} else {
|
|
2589 |
error_msg("ignoring entry %s with mode %d", archive_entry_pathname(entry), mode & S_IFMT);
|
|
2590 |
}
|
|
2591 |
}
|
|
2592 |
}
|
|
2593 |
free(path2);
|
|
2594 |
free(path3);
|
|
2595 |
}
|
|
2596 |
archive_read_close(a);
|
|
2597 |
archive_read_free(a);
|
|
2598 |
#endif
|
|
2599 |
}
|
|
2600 |
|
2137 | 2601 |
// add or fixup entries to the filesystem from a text file
|
2138 | 2602 |
/* device table entries take the form of:
|
2139 | 2603 |
<path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
|
|
2145 | 2609 |
c Character special device file
|
2146 | 2610 |
b Block special device file
|
2147 | 2611 |
p Fifo (named pipe)
|
2148 | |
|
2149 | |
I don't bother with symlinks (permissions are irrelevant), hard
|
2150 | |
links (special cases of regular files), or sockets (why bother).
|
|
2612 |
l Symbolic link
|
|
2613 |
|
|
2614 |
I don't bother with hard links (special cases of regular files),
|
|
2615 |
or sockets.
|
2151 | 2616 |
|
2152 | 2617 |
Regular files must exist in the target root directory. If a char,
|
2153 | 2618 |
block, fifo, or directory does not exist, it will be created.
|
|
2192 | 2657 |
continue;
|
2193 | 2658 |
}
|
2194 | 2659 |
mode &= FM_IMASK;
|
|
2660 |
if (fs && strcmp(path, "/") == 0) {
|
|
2661 |
// if the entry modifies the root node, don't call
|
|
2662 |
// basename and dirname but chmod the root node
|
|
2663 |
// directly
|
|
2664 |
if (type != 'd') {
|
|
2665 |
error_msg("device table line %d skipped: root node must be a directory", lineno);
|
|
2666 |
continue;
|
|
2667 |
}
|
|
2668 |
mode |= FM_IFDIR;
|
|
2669 |
chmod_fs(fs, this_nod, mode, uid, gid);
|
|
2670 |
continue;
|
|
2671 |
}
|
2195 | 2672 |
path2 = strdup(path);
|
2196 | 2673 |
name = basename(path);
|
2197 | 2674 |
dir = dirname(path2);
|
|
2217 | 2694 |
break;
|
2218 | 2695 |
case 'f':
|
2219 | 2696 |
mode |= FM_IFREG;
|
|
2697 |
break;
|
|
2698 |
case 'l':
|
|
2699 |
mode |= FM_IFLNK;
|
2220 | 2700 |
break;
|
2221 | 2701 |
case 'p':
|
2222 | 2702 |
mode |= FM_IFIFO;
|
|
2290 | 2770 |
struct stat st;
|
2291 | 2771 |
char *lnk;
|
2292 | 2772 |
uint32 save_nod;
|
|
2773 |
off_t filesize;
|
2293 | 2774 |
|
2294 | 2775 |
if(!(dh = opendir(".")))
|
2295 | 2776 |
perror_msg_and_die(".");
|
|
2312 | 2793 |
switch(st.st_mode & S_IFMT)
|
2313 | 2794 |
{
|
2314 | 2795 |
case S_IFLNK:
|
2315 | |
if((st.st_mode & S_IFMT) == S_IFREG || st.st_size >= 4 * (EXT2_TIND_BLOCK+1))
|
|
2796 |
if(st.st_size >= 4 * (EXT2_TIND_BLOCK+1))
|
2316 | 2797 |
stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
|
2317 | 2798 |
stats->ninodes++;
|
2318 | 2799 |
break;
|
2319 | 2800 |
case S_IFREG:
|
2320 | |
if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
|
2321 | |
stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
|
|
2801 |
stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
|
2322 | 2802 |
case S_IFCHR:
|
2323 | 2803 |
case S_IFBLK:
|
2324 | 2804 |
case S_IFIFO:
|
|
2390 | 2870 |
break;
|
2391 | 2871 |
case S_IFREG:
|
2392 | 2872 |
fh = fopen(dent->d_name, "rb");
|
|
2873 |
fseek(fh, 0, SEEK_END);
|
|
2874 |
filesize = ftell(fh);
|
|
2875 |
rewind(fh);
|
2393 | 2876 |
if (!fh) {
|
2394 | 2877 |
error_msg("Unable to open file %s", dent->d_name);
|
2395 | 2878 |
break;
|
2396 | 2879 |
}
|
2397 | |
nod = mkfile_fs(fs, this_nod, name, mode, fh, uid, gid, ctime, mtime);
|
|
2880 |
nod = mkfile_fs(fs, this_nod, name, mode, fh_read, fh, filesize, uid, gid, ctime, mtime);
|
2398 | 2881 |
fclose(fh);
|
2399 | 2882 |
break;
|
2400 | 2883 |
case S_IFDIR:
|
|
2428 | 2911 |
|
2429 | 2912 |
// Copy size blocks from src to dst, putting holes in the output
|
2430 | 2913 |
// file (if possible) if the input block is all zeros.
|
2431 | |
// Copy size blocks from src to dst, putting holes in the output
|
2432 | |
// file (if possible) if the input block is all zeros.
|
2433 | 2914 |
static void
|
2434 | 2915 |
copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size)
|
2435 | 2916 |
{
|
|
2807 | 3288 |
if(fsize <= 0)
|
2808 | 3289 |
error_msg_and_die("wrong size while saving inode %d", nod);
|
2809 | 3290 |
if(fwrite(get_blk(fs, bk, &bi),
|
2810 | |
(fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
|
|
3291 |
((uint32)fsize > BLOCKSIZE) ? BLOCKSIZE : (uint32)fsize, 1, f) != 1)
|
2811 | 3292 |
error_msg_and_die("error while saving inode %d", nod);
|
2812 | 3293 |
put_blk(bi);
|
2813 | 3294 |
fsize -= BLOCKSIZE;
|
|
3087 | 3568 |
}
|
3088 | 3569 |
|
3089 | 3570 |
static void
|
3090 | |
populate_fs(filesystem *fs, char **dopt, int didx, int squash_uids, int squash_perms, uint32 fs_timestamp, struct stats *stats)
|
|
3571 |
populate_fs(filesystem *fs, struct fslayer *fslayers, int nlayers, int squash_uids, int squash_perms, uint32 fs_timestamp, struct stats *stats)
|
3091 | 3572 |
{
|
3092 | 3573 |
int i;
|
3093 | |
for(i = 0; i < didx; i++)
|
|
3574 |
for(i = 0; i < nlayers; i++)
|
3094 | 3575 |
{
|
3095 | 3576 |
struct stat st;
|
3096 | 3577 |
FILE *fh;
|
3097 | 3578 |
int pdir;
|
3098 | 3579 |
char *pdest;
|
3099 | 3580 |
uint32 nod = EXT2_ROOT_INO;
|
3100 | |
if(fs)
|
3101 | |
if((pdest = strchr(dopt[i], ':')))
|
3102 | |
{
|
3103 | |
*(pdest++) = 0;
|
3104 | |
if(!(nod = find_path(fs, EXT2_ROOT_INO, pdest)))
|
3105 | |
error_msg_and_die("path %s not found in filesystem", pdest);
|
3106 | |
}
|
3107 | |
stat(dopt[i], &st);
|
3108 | |
switch(st.st_mode & S_IFMT)
|
|
3581 |
if((pdest = strchr(fslayers[i].path, ':')))
|
3109 | 3582 |
{
|
3110 | |
case S_IFREG:
|
3111 | |
fh = xfopen(dopt[i], "rb");
|
|
3583 |
*(pdest++) = 0;
|
|
3584 |
if(fs && !(nod = find_path(fs, EXT2_ROOT_INO, pdest)))
|
|
3585 |
error_msg_and_die("path %s not found in filesystem", pdest);
|
|
3586 |
}
|
|
3587 |
/* do not compute stats when input is to be read from stdin */
|
|
3588 |
if (stats != NULL && strcmp(fslayers[i].path, "-") == 0) {
|
|
3589 |
continue;
|
|
3590 |
}
|
|
3591 |
stat(fslayers[i].path, &st);
|
|
3592 |
switch(fslayers[i].type)
|
|
3593 |
{
|
|
3594 |
case FSLAYER_TABLE:
|
|
3595 |
if(strcmp(fslayers[i].path, "-") == 0)
|
|
3596 |
fh = stdin;
|
|
3597 |
else if((st.st_mode & S_IFMT) != S_IFREG)
|
|
3598 |
error_msg_and_die("%s should be a file", fslayers[i].path);
|
|
3599 |
else
|
|
3600 |
fh = xfopen(fslayers[i].path, "rb");
|
|
3601 |
if(fs)
|
|
3602 |
fprintf(stderr, "nodes fixup and creation from device table %s\n", fslayers[i].path);
|
3112 | 3603 |
add2fs_from_file(fs, nod, fh, fs_timestamp, stats);
|
3113 | |
fclose(fh);
|
|
3604 |
if(strcmp(fslayers[i].path, "-") != 0)
|
|
3605 |
fclose(fh);
|
3114 | 3606 |
break;
|
3115 | |
case S_IFDIR:
|
|
3607 |
case FSLAYER_DIR:
|
|
3608 |
if((st.st_mode & S_IFMT) != S_IFDIR)
|
|
3609 |
error_msg_and_die("%s should be a directory", fslayers[i].path);
|
|
3610 |
if(fs)
|
|
3611 |
fprintf(stderr, "copying from directory %s\n", fslayers[i].path);
|
3116 | 3612 |
if((pdir = open(".", O_RDONLY)) < 0)
|
3117 | 3613 |
perror_msg_and_die(".");
|
3118 | |
if(chdir(dopt[i]) < 0)
|
3119 | |
perror_msg_and_die(dopt[i]);
|
|
3614 |
if(chdir(fslayers[i].path) < 0)
|
|
3615 |
perror_msg_and_die(fslayers[i].path);
|
3120 | 3616 |
add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
|
3121 | 3617 |
if(fchdir(pdir) < 0)
|
3122 | 3618 |
perror_msg_and_die("fchdir");
|
3123 | 3619 |
if(close(pdir) < 0)
|
3124 | 3620 |
perror_msg_and_die("close");
|
3125 | 3621 |
break;
|
3126 | |
default:
|
3127 | |
error_msg_and_die("%s is neither a file nor a directory", dopt[i]);
|
|
3622 |
case FSLAYER_TAR:
|
|
3623 |
if(strcmp(fslayers[i].path, "-") == 0)
|
|
3624 |
fh = stdin;
|
|
3625 |
else if((st.st_mode & S_IFMT) != S_IFREG)
|
|
3626 |
error_msg_and_die("%s should be a file", fslayers[i].path);
|
|
3627 |
else
|
|
3628 |
fh = xfopen(fslayers[i].path, "rb");
|
|
3629 |
if(fs)
|
|
3630 |
fprintf(stderr, "copying from tar archive %s\n", fslayers[i].path);
|
|
3631 |
add2fs_from_tarball(fs, nod, fh, squash_uids, squash_perms, fs_timestamp, stats);
|
|
3632 |
if(strcmp(fslayers[i].path, "-") != 0)
|
|
3633 |
fclose(fh);
|
|
3634 |
break;
|
3128 | 3635 |
}
|
3129 | 3636 |
}
|
3130 | 3637 |
}
|
|
3141 | 3648 |
fprintf(stderr, "Usage: %s [options] image\n"
|
3142 | 3649 |
"Create an ext2 filesystem image from directories/files\n\n"
|
3143 | 3650 |
" -x, --starting-image <image>\n"
|
3144 | |
" -d, --root <directory>\n"
|
3145 | |
" -D, --devtable <file>\n"
|
|
3651 |
" -d, --root <directory>[:path] Copy from a local directory into path (or root)\n"
|
|
3652 |
" -D, --devtable <file>[:path] Add or fixup nodes from a device table into path (or root)\n"
|
|
3653 |
" -a, --tarball <file>[:path] Copy from a tar archive into path (or root)\n"
|
3146 | 3654 |
" -B, --block-size <bytes>\n"
|
3147 | 3655 |
" -b, --size-in-blocks <blocks>\n"
|
3148 | 3656 |
" -i, --bytes-per-inode <bytes per inode>\n"
|
3149 | 3657 |
" -N, --number-of-inodes <number of inodes>\n"
|
3150 | 3658 |
" -L, --volume-label <string>\n"
|
3151 | 3659 |
" -m, --reserved-percentage <percentage of blocks to reserve>\n"
|
3152 | |
" -o, --creator-os <os> 'linux' (default), 'hurd', 'freebsd' or number.\n"
|
3153 | |
" -g, --block-map <path> Generate a block map file for this path.\n"
|
3154 | |
" -e, --fill-value <value> Fill unallocated blocks with value.\n"
|
3155 | |
" -z, --allow-holes Allow files with holes.\n"
|
3156 | |
" -f, --faketime Set filesystem timestamps to 0 (for testing).\n"
|
3157 | |
" -q, --squash Same as \"-U -P\".\n"
|
3158 | |
" -U, --squash-uids Squash owners making all files be owned by root.\n"
|
3159 | |
" -P, --squash-perms Squash permissions on all files.\n"
|
|
3660 |
" -o, --creator-os <os> 'linux' (default), 'hurd', 'freebsd' or number.\n"
|
|
3661 |
" -g, --block-map <path> Generate a block map file for this path.\n"
|
|
3662 |
" -e, --fill-value <value> Fill unallocated blocks with value.\n"
|
|
3663 |
" -z, --allow-holes Allow files with holes.\n"
|
|
3664 |
" -f, --faketime Set filesystem timestamps to 0 (for testing).\n"
|
|
3665 |
" -q, --squash Same as \"-U -P\".\n"
|
|
3666 |
" -U, --squash-uids Squash owners making all files be owned by root.\n"
|
|
3667 |
" -P, --squash-perms Squash permissions on all files.\n"
|
3160 | 3668 |
" -h, --help\n"
|
3161 | 3669 |
" -V, --version\n"
|
3162 | 3670 |
" -v, --verbose\n\n"
|
|
3201 | 3709 |
int creator_os = CREATOR_OS;
|
3202 | 3710 |
char * fsout = "-";
|
3203 | 3711 |
char * fsin = 0;
|
3204 | |
char * dopt[MAX_DOPT];
|
3205 | |
int didx = 0;
|
|
3712 |
struct fslayer layers[MAX_DOPT];
|
|
3713 |
int nlayers = 0;
|
3206 | 3714 |
char * gopt[MAX_GOPT];
|
3207 | 3715 |
int gidx = 0;
|
3208 | 3716 |
int verbose = 0;
|
|
3223 | 3731 |
{ "starting-image", required_argument, NULL, 'x' },
|
3224 | 3732 |
{ "root", required_argument, NULL, 'd' },
|
3225 | 3733 |
{ "devtable", required_argument, NULL, 'D' },
|
|
3734 |
{ "tarball", required_argument, NULL, 'a' },
|
3226 | 3735 |
{ "block-size", required_argument, NULL, 'B' },
|
3227 | 3736 |
{ "size-in-blocks", required_argument, NULL, 'b' },
|
3228 | 3737 |
{ "bytes-per-inode", required_argument, NULL, 'i' },
|
|
3245 | 3754 |
|
3246 | 3755 |
app_name = argv[0];
|
3247 | 3756 |
|
3248 | |
while((c = getopt_long(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
|
|
3757 |
while((c = getopt_long(argc, argv, "x:d:D:a:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
|
3249 | 3758 |
#else
|
3250 | 3759 |
app_name = argv[0];
|
3251 | 3760 |
|
3252 | |
while((c = getopt(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) {
|
|
3761 |
while((c = getopt(argc, argv, "x:d:D:a:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) {
|
3253 | 3762 |
#endif /* HAVE_GETOPT_LONG */
|
3254 | 3763 |
switch(c)
|
3255 | 3764 |
{
|
|
3257 | 3766 |
fsin = optarg;
|
3258 | 3767 |
break;
|
3259 | 3768 |
case 'd':
|
|
3769 |
layers[nlayers].type = FSLAYER_DIR;
|
|
3770 |
layers[nlayers++].path = optarg;
|
|
3771 |
break;
|
3260 | 3772 |
case 'D':
|
3261 | |
dopt[didx++] = optarg;
|
|
3773 |
layers[nlayers].type = FSLAYER_TABLE;
|
|
3774 |
layers[nlayers++].path = optarg;
|
|
3775 |
break;
|
|
3776 |
case 'a':
|
|
3777 |
layers[nlayers].type = FSLAYER_TAR;
|
|
3778 |
layers[nlayers++].path = optarg;
|
3262 | 3779 |
break;
|
3263 | 3780 |
case 'B':
|
3264 | 3781 |
blocksize = SI_atof(optarg);
|
|
3329 | 3846 |
if(creator_os < 0)
|
3330 | 3847 |
error_msg_and_die("Creator OS unknown.");
|
3331 | 3848 |
|
|
3849 |
int numstdin = 0;
|
|
3850 |
for(i = 0; i < nlayers; i++)
|
|
3851 |
if (strcmp(layers[i].path, "-") == 0)
|
|
3852 |
numstdin++;
|
|
3853 |
if (numstdin == 1 && nbinodes == -1 && bytes_per_inode == -1)
|
|
3854 |
fprintf(stderr, "Cannot count the required inodes for input from stdin -- use the -N or -i options to set the number of inodes or work with temporary files.");
|
|
3855 |
if (numstdin > 1)
|
|
3856 |
error_msg_and_die("only one input can come from stdin");
|
|
3857 |
|
3332 | 3858 |
if(fsin)
|
3333 | 3859 |
{
|
|
3860 |
fprintf(stderr, "starting from existing image %s", fsin);
|
3334 | 3861 |
if(strcmp(fsin, "-"))
|
3335 | 3862 |
{
|
3336 | 3863 |
FILE * fh = xfopen(fsin, "rb");
|
|
3350 | 3877 |
stats.ninodes = EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0);
|
3351 | 3878 |
stats.nblocks = 0;
|
3352 | 3879 |
|
3353 | |
populate_fs(NULL, dopt, didx, squash_uids, squash_perms, fs_timestamp, &stats);
|
|
3880 |
populate_fs(NULL, layers, nlayers, squash_uids, squash_perms, fs_timestamp, &stats);
|
3354 | 3881 |
|
3355 | 3882 |
if(nbinodes == -1)
|
3356 | 3883 |
nbinodes = stats.ninodes;
|
|
3366 | 3893 |
if(tmp_nbinodes > nbinodes)
|
3367 | 3894 |
nbinodes = tmp_nbinodes;
|
3368 | 3895 |
}
|
3369 | |
if(fs_timestamp == -1)
|
3370 | |
fs_timestamp = time(NULL);
|
|
3896 |
if(fs_timestamp == -1) {
|
|
3897 |
char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
|
|
3898 |
if (source_date_epoch == NULL) {
|
|
3899 |
fs_timestamp = time(NULL);
|
|
3900 |
} else {
|
|
3901 |
fs_timestamp = strtoll(source_date_epoch, NULL, 10);
|
|
3902 |
}
|
|
3903 |
}
|
3371 | 3904 |
fs = init_fs(nbblocks, nbinodes, nbresrvd, holes,
|
3372 | 3905 |
fs_timestamp, creator_os, bigendian, fsout);
|
|
3906 |
fs_upgrade_rev1_largefile(fs);
|
3373 | 3907 |
}
|
3374 | 3908 |
if (volumelabel != NULL)
|
3375 | 3909 |
strncpy((char *)fs->sb->s_volume_name, volumelabel,
|
3376 | 3910 |
sizeof(fs->sb->s_volume_name));
|
3377 | 3911 |
|
3378 | |
populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL);
|
|
3912 |
populate_fs(fs, layers, nlayers, squash_uids, squash_perms, fs_timestamp, NULL);
|
3379 | 3913 |
|
3380 | 3914 |
if(emptyval) {
|
3381 | 3915 |
uint32 b;
|