763 lines
17 KiB
C
763 lines
17 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2008, 2009 Adrian Reber
|
|
* All rights reserved.
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the BSD License
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.opensource.org/licenses/bsd-license.php
|
|
*
|
|
* Contributors:
|
|
* Adrian Reber - initial implementation
|
|
*****************************************************************************/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <byteswap.h>
|
|
#include <getopt.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <calculatecrc.h>
|
|
#include <crclib.h>
|
|
|
|
#define VERSION 1
|
|
|
|
#ifdef _BIG_ENDIAN
|
|
#define cpu_to_be64(x) (x)
|
|
#define be64_to_cpu(x) (x)
|
|
#define be16_to_cpu(x) (x)
|
|
#define be32_to_cpu(x) (x)
|
|
#else
|
|
#define cpu_to_be64(x) bswap_64(x)
|
|
#define be64_to_cpu(x) bswap_64(x)
|
|
#define be16_to_cpu(x) bswap_16(x)
|
|
#define be32_to_cpu(x) bswap_32(x)
|
|
#endif
|
|
|
|
|
|
/* no board dependencies wanted here, let's hardcode SLOF's
|
|
* magic strings here */
|
|
|
|
#define FLASHFS_MAGIC "magic123"
|
|
#define FLASHFS_PLATFORM_MAGIC "JS2XBlade"
|
|
#define FLASHFS_PLATFORM_REVISION "1"
|
|
|
|
/* there seems to be no structure defined anywhere in the code
|
|
* which resembles the actual sloffs/romfs file header;
|
|
* so defining it here for now */
|
|
|
|
struct sloffs {
|
|
uint64_t next;
|
|
uint64_t len;
|
|
uint64_t flags;
|
|
uint64_t data;
|
|
char *name;
|
|
};
|
|
|
|
/* sloffs metadata size:
|
|
* 4 * 8: 4 * uint64_t + (filename length) */
|
|
#define SLOFFS_META (4 * 8)
|
|
#define ALIGN64(x) (((x) + 7) & ~7)
|
|
|
|
static struct sloffs *
|
|
next_file_mm(struct sloffs *sloffs)
|
|
{
|
|
return (struct sloffs *)((unsigned char *)sloffs +
|
|
be64_to_cpu(sloffs->next));
|
|
}
|
|
|
|
static int
|
|
next_file(const int fd, struct sloffs *sloffs)
|
|
{
|
|
int ret;
|
|
uint64_t size;
|
|
uint64_t offset;
|
|
char *name;
|
|
|
|
offset = 0;
|
|
|
|
/* if sloffs is not all NULL we want the next file
|
|
* else we just take the first file */
|
|
if (sloffs->name && sloffs->len && sloffs->data) {
|
|
offset = be64_to_cpu(sloffs->next);
|
|
/* we already read over the header; skip it in the seek */
|
|
offset -= be64_to_cpu(sloffs->data);
|
|
free(sloffs->name);
|
|
sloffs->name = NULL;
|
|
lseek(fd, offset, SEEK_CUR);
|
|
} else {
|
|
lseek(fd, offset, SEEK_SET);
|
|
}
|
|
|
|
ret = read(fd, sloffs, SLOFFS_META);
|
|
if (ret == -1)
|
|
return -1;
|
|
/* read the size of the header */
|
|
size = be64_to_cpu(sloffs->data);
|
|
/* get the size of the filename */
|
|
size -= SLOFFS_META;
|
|
name = malloc(size);
|
|
|
|
ret = read(fd, name, size);
|
|
if (ret == -1) {
|
|
free(name);
|
|
return -1;
|
|
}
|
|
sloffs->name = name;
|
|
return 0;
|
|
}
|
|
|
|
static struct sloffs *
|
|
find_file_mm(const void *data, const char *name)
|
|
{
|
|
struct sloffs *sloffs = (struct sloffs *)data;
|
|
|
|
for (;;) {
|
|
if (!strcmp((char *)&sloffs->name, name))
|
|
return sloffs;
|
|
|
|
if (be64_to_cpu(sloffs->next) == 0)
|
|
break;
|
|
sloffs = next_file_mm(sloffs);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct sloffs *
|
|
find_file(const int fd, const char *name, struct sloffs *sloffs)
|
|
{
|
|
memset(sloffs, 0, sizeof(struct sloffs));
|
|
|
|
if (next_file(fd, sloffs))
|
|
return NULL;
|
|
|
|
for (;;) {
|
|
if (!strcmp(sloffs->name, name))
|
|
return sloffs;
|
|
|
|
if (be64_to_cpu(sloffs->next) == 0)
|
|
break;
|
|
if (next_file(fd, sloffs))
|
|
return NULL;
|
|
}
|
|
|
|
free(sloffs->name);
|
|
return NULL;
|
|
}
|
|
|
|
static struct stH *
|
|
sloffs_header_mm(const void *data)
|
|
{
|
|
struct sloffs *sloffs;
|
|
struct stH *header;
|
|
|
|
/* find the "header" file with all the information about
|
|
* the flash image */
|
|
sloffs = find_file_mm(data, "header");
|
|
if (!sloffs) {
|
|
printf("sloffs file \"header\" not found. aborting...\n");
|
|
return NULL;
|
|
}
|
|
|
|
header = (struct stH *)((unsigned char *)sloffs +
|
|
be64_to_cpu(sloffs->data));
|
|
return header;
|
|
}
|
|
|
|
static struct stH *
|
|
sloffs_header(const int fd)
|
|
{
|
|
struct sloffs file;
|
|
struct sloffs *sloffs;
|
|
struct stH *header;
|
|
ssize_t rc;
|
|
|
|
header = (struct stH *)malloc(sizeof(struct stH));
|
|
|
|
/* find the "header" file with all the information about
|
|
* the flash image */
|
|
sloffs = find_file(fd, "header", &file);
|
|
if (!sloffs) {
|
|
printf("sloffs file \"header\" not found. aborting...\n");
|
|
return NULL;
|
|
}
|
|
|
|
rc = read(fd, header, sizeof(struct stH));
|
|
if (rc != sizeof(struct stH)) {
|
|
printf("Reading header, rc %ld, errno %d\n", rc, errno);
|
|
free(header);
|
|
header = NULL;
|
|
}
|
|
free(sloffs->name);
|
|
return header;
|
|
}
|
|
|
|
static uint64_t
|
|
header_length_mm(const void *data)
|
|
{
|
|
struct sloffs *sloffs;
|
|
|
|
/* find the "header" file with all the information about
|
|
* the flash image */
|
|
sloffs = find_file_mm(data, "header");
|
|
if (!sloffs) {
|
|
printf("sloffs file \"header\" not found. aborting...\n");
|
|
return 0;
|
|
}
|
|
return be64_to_cpu(sloffs->len);
|
|
}
|
|
|
|
static uint64_t
|
|
header_length(const int fd)
|
|
{
|
|
struct sloffs file;
|
|
struct sloffs *sloffs;
|
|
|
|
/* find the "header" file with all the information about
|
|
* the flash image */
|
|
sloffs = find_file(fd, "header", &file);
|
|
if (!sloffs) {
|
|
printf("sloffs file \"header\" not found. aborting...\n");
|
|
return 0;
|
|
}
|
|
|
|
free(sloffs->name);
|
|
return be64_to_cpu(sloffs->len);
|
|
}
|
|
|
|
static void
|
|
update_modification_time(struct stH *header)
|
|
{
|
|
struct tm *tm;
|
|
time_t caltime;
|
|
char dastr[16] = { 0, };
|
|
uint64_t date;
|
|
|
|
/* update modification date
|
|
* copied from create_crc.c */
|
|
caltime = time(NULL);
|
|
tm = localtime(&caltime);
|
|
strftime(dastr, 15, "0x%Y%m%d%H%M", tm);
|
|
date = cpu_to_be64(strtoll(dastr, NULL, 16));
|
|
|
|
/* this does not match the definition from
|
|
* struct stH, but we immitate the bug from
|
|
* flash image creation in create_crc.c.
|
|
* The date is in mdate and time in padding2. */
|
|
memcpy(&(header->mdate), &date, 8);
|
|
}
|
|
|
|
static void
|
|
update_crc(void *data)
|
|
{
|
|
uint64_t crc;
|
|
struct stH *header = sloffs_header_mm(data);
|
|
uint64_t len = be64_to_cpu(header->flashlen);
|
|
|
|
/* calculate header CRC */
|
|
header->ui64CRC = 0;
|
|
crc = checkCRC(data, header_length_mm(data), 0);
|
|
header->ui64CRC = cpu_to_be64(crc);
|
|
/* calculate flash image CRC */
|
|
crc = checkCRC(data, len, 0);
|
|
*(uint64_t *)(data + len - 8) = cpu_to_be64(crc);
|
|
}
|
|
|
|
static uint64_t
|
|
check_image_crc(const int fd, uint64_t len)
|
|
{
|
|
uint64_t crc;
|
|
uint64_t i;
|
|
uint64_t read_bytes;
|
|
unsigned char buffer[4096];
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
crc = 0;
|
|
read_bytes = 0;
|
|
while (read_bytes < len) {
|
|
i = read(fd, buffer, 4096);
|
|
read_bytes += i;
|
|
if (read_bytes > len)
|
|
i -= read_bytes - len;
|
|
crc = calCRCword(buffer, i, crc);
|
|
}
|
|
return crc;
|
|
}
|
|
static void
|
|
sloffs_append(const int file, const char *name, const char *dest)
|
|
{
|
|
void *append;
|
|
unsigned char *write_data;
|
|
void *write_start;
|
|
int fd;
|
|
int out;
|
|
struct stat stat;
|
|
struct stH *header;
|
|
uint64_t new_len;
|
|
struct sloffs *sloffs;
|
|
struct sloffs new_file;
|
|
uint64_t read_len;
|
|
int i;
|
|
ssize_t rc;
|
|
|
|
fd = open(name, O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
perror(name);
|
|
exit(1);
|
|
}
|
|
|
|
fstat(fd, &stat);
|
|
append = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
header = sloffs_header(file);
|
|
|
|
if (!header)
|
|
return;
|
|
|
|
new_len = ALIGN64(stat.st_size) + be64_to_cpu(header->flashlen);
|
|
/* add the length of the sloffs file meta information */
|
|
new_len += SLOFFS_META;
|
|
/* add the length of the filename */
|
|
new_len += ALIGN64(strlen(name) + 1);
|
|
|
|
out = open(dest, O_CREAT | O_RDWR | O_TRUNC, 00666);
|
|
|
|
if (out == -1) {
|
|
perror(dest);
|
|
exit(1);
|
|
}
|
|
|
|
/* write byte at the end to be able to mmap it */
|
|
lseek(out, new_len - 1, SEEK_SET);
|
|
rc = write(out, "", 1);
|
|
if (rc != 1) {
|
|
printf("Extending file failed, rc %ld, errno %d\n", rc, errno);
|
|
exit(1);
|
|
}
|
|
write_start = mmap(NULL, new_len, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, out, 0);
|
|
|
|
memset(write_start, 0, new_len);
|
|
memset(&new_file, 0, sizeof(struct sloffs));
|
|
|
|
new_file.len = cpu_to_be64(stat.st_size);
|
|
new_file.data = cpu_to_be64(SLOFFS_META + ALIGN64(strlen(name) + 1));
|
|
|
|
if (write_start == MAP_FAILED) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
|
|
lseek(file, 0, SEEK_SET);
|
|
write_data = write_start;
|
|
read_len = be64_to_cpu(header->flashlen);
|
|
for (;;) {
|
|
i = read(file, write_data, read_len);
|
|
if (i < 0) {
|
|
perror("read");
|
|
exit(1);
|
|
}
|
|
if (i == 0)
|
|
break;
|
|
write_data += i;
|
|
read_len -= i;
|
|
}
|
|
/* -8: overwrite old CRC */
|
|
write_data = write_start + be64_to_cpu(header->flashlen) - 8;
|
|
memcpy(write_data, &new_file, SLOFFS_META);
|
|
write_data += SLOFFS_META;
|
|
/* write the filename */
|
|
memcpy(write_data, name, strlen(name));
|
|
write_data += ALIGN64(strlen(name) + 1 );
|
|
memcpy(write_data, append, stat.st_size);
|
|
|
|
write_data = write_start;
|
|
|
|
/* find last file */
|
|
sloffs = (struct sloffs *)write_start;
|
|
for (;;) {
|
|
if (be64_to_cpu(sloffs->next) == 0)
|
|
break;
|
|
sloffs = next_file_mm(sloffs);
|
|
}
|
|
/* get the distance to the next file */
|
|
sloffs->next = ALIGN64(be64_to_cpu(sloffs->len));
|
|
/* and the offset were the data starts */
|
|
sloffs->next += be64_to_cpu(sloffs->data);
|
|
/* and we have to skip the end of file marker
|
|
* if one is there; if the last uint64_t is -1
|
|
* it is an end of file marker; this is a bit dangerous
|
|
* but there is no other way to detect the end of
|
|
* file marker */
|
|
if ((uint64_t)be64_to_cpu(*(uint64_t *)((unsigned char *)sloffs +
|
|
sloffs->next)) == (uint64_t)-1ULL)
|
|
sloffs->next += 8;
|
|
|
|
sloffs->next = cpu_to_be64(sloffs->next);
|
|
|
|
free(header);
|
|
/* update new length of flash image */
|
|
header = sloffs_header_mm(write_start);
|
|
header->flashlen = cpu_to_be64(new_len);
|
|
|
|
update_modification_time(header);
|
|
|
|
update_crc(write_start);
|
|
|
|
munmap(append, stat.st_size);
|
|
munmap(write_start, new_len);
|
|
close(fd);
|
|
close(out);
|
|
}
|
|
|
|
static void print_header_date(void *dptr)
|
|
{
|
|
uint8_t *date = dptr;
|
|
|
|
if (date[2] || date[3] || date[4] || date[5] || date[6] || date[7]) {
|
|
printf("%02x%02x-%02x-%02x %02x:%02x", date[2], date[3],
|
|
date[4], date[5], date[6], date[7]);
|
|
} else {
|
|
printf("N/A");
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
sloffs_dump(const int fd)
|
|
{
|
|
void *data;
|
|
struct stH *header;
|
|
struct sloffs file;
|
|
int i;
|
|
uint64_t crc;
|
|
uint64_t header_len;
|
|
ssize_t rc;
|
|
|
|
header = sloffs_header(fd);
|
|
|
|
if (!header)
|
|
return;
|
|
|
|
if (memcmp(FLASHFS_MAGIC, header->magic, strlen(FLASHFS_MAGIC))) {
|
|
printf("sloffs magic not found. "
|
|
"probably not a valid SLOF flash image. aborting...\n");
|
|
return;
|
|
}
|
|
printf(" Magic : %s\n", header->magic);
|
|
printf(" Platform : %s\n", header->platform_name);
|
|
printf(" Version : %s\n", header->version);
|
|
/* there is a bug in the date position;
|
|
* it should be at header->date, but it is at (header->date + 2) */
|
|
printf(" Build Date : ");
|
|
print_header_date(header->date);
|
|
printf("\n");
|
|
printf(" Modify Date : ");
|
|
print_header_date(header->mdate);
|
|
printf("\n");
|
|
printf(" Image Length: %ld", be64_to_cpu(header->flashlen));
|
|
printf(" (0x%lx) bytes\n", be64_to_cpu(header->flashlen));
|
|
printf(" Revision : %s\n", header->platform_revision);
|
|
crc = be64_to_cpu(header->ui64CRC);
|
|
printf(" Header CRC : 0x%016lx CRC check: ", crc);
|
|
/* to test the CRC of the header we need to know the actual
|
|
* size of the file and not just the size of the data
|
|
* which could be easily obtained with sizeof(struct stH);
|
|
* the actual size can only be obtained from the filesystem
|
|
* meta information */
|
|
header_len = header_length(fd);
|
|
/* no copy the header to memory to crc test it */
|
|
data = malloc(header_len);
|
|
lseek(fd, 0, SEEK_SET);
|
|
rc = read(fd, data, header_len);
|
|
if (rc != (ssize_t) header_len) {
|
|
printf("Reading header failed, rc %zd, errno %d\n", rc, errno);
|
|
return;
|
|
}
|
|
crc = calCRCword((unsigned char *)data, header_length(fd), 0);
|
|
free(data);
|
|
if (!crc)
|
|
printf("[OK]");
|
|
else
|
|
printf("[FAILED]");
|
|
printf("\n");
|
|
|
|
crc = be64_to_cpu(header->flashlen);
|
|
/* move to the CRC */
|
|
lseek(fd, crc - 8, SEEK_SET);
|
|
/* read it */
|
|
rc = read(fd, &crc, 8);
|
|
if (rc != 8) {
|
|
printf("Reading crc failed, rc %zd, errno %d\n", rc, errno);
|
|
return;
|
|
}
|
|
crc = be64_to_cpu(crc);
|
|
printf(" Image CRC : 0x%016lx CRC check: ", crc);
|
|
crc = check_image_crc(fd, be64_to_cpu(header->flashlen));
|
|
if (!crc)
|
|
printf("[OK]");
|
|
else
|
|
printf("[FAILED]");
|
|
printf("\n");
|
|
|
|
/* count number of files */
|
|
i = 0;
|
|
memset(&file, 0, sizeof(struct sloffs));
|
|
if (next_file(fd, &file))
|
|
return;
|
|
for (;;) {
|
|
i++;
|
|
|
|
if (be64_to_cpu(file.next) == 0)
|
|
break;
|
|
if (next_file(fd, &file))
|
|
return;
|
|
}
|
|
free(file.name);
|
|
printf(" Files : %d\n", i);
|
|
free(header);
|
|
}
|
|
|
|
static void
|
|
sloffs_list(const int fd)
|
|
{
|
|
const char *name_header = "File Name";
|
|
unsigned int i;
|
|
unsigned int max;
|
|
unsigned int line;
|
|
struct sloffs file;
|
|
uint64_t offset = 0;
|
|
|
|
memset(&file, 0, sizeof(struct sloffs));
|
|
|
|
if (next_file(fd, &file))
|
|
return;
|
|
|
|
/* find largest name */
|
|
max = strlen(name_header);
|
|
for (;;) {
|
|
if (max < strlen((char *)file.name))
|
|
max = strlen((char *)file.name);
|
|
|
|
if (be64_to_cpu(file.next) == 0)
|
|
break;
|
|
if (next_file(fd, &file))
|
|
return;
|
|
}
|
|
|
|
free(file.name);
|
|
|
|
|
|
/* have at least two spaces between name and size column */
|
|
max += 2;
|
|
|
|
/* header for listing */
|
|
line = printf(" Offset ");
|
|
line += printf("%s", name_header);
|
|
for (i = 0; i < max - strlen(name_header); i++)
|
|
line += printf(" ");
|
|
line += printf("Size ");
|
|
line += printf("Flags\n");
|
|
printf(" ");
|
|
for (i = 0; i <= line; i++)
|
|
printf("=");
|
|
printf("\n");
|
|
|
|
memset(&file, 0, sizeof(struct sloffs));
|
|
|
|
if (next_file(fd, &file))
|
|
return;
|
|
|
|
for (;;) {
|
|
printf(" 0x%08lx", offset);
|
|
offset += be64_to_cpu(file.next);
|
|
printf(" %s", file.name);
|
|
for (i = 0; i < max - strlen(file.name); i++)
|
|
printf(" ");
|
|
|
|
printf("%07ld ", be64_to_cpu(file.len));
|
|
printf("(0x%06lx)", be64_to_cpu(file.len));
|
|
printf(" 0x%08lx\n", be64_to_cpu(file.flags));
|
|
|
|
if (be64_to_cpu(file.next) == 0)
|
|
break;
|
|
if (next_file(fd, &file))
|
|
return;
|
|
}
|
|
free(file.name);
|
|
}
|
|
|
|
static void
|
|
sloffs_copy(const int file, const char *name)
|
|
{
|
|
uint64_t len;
|
|
int out;
|
|
unsigned char *write_buf;
|
|
int i;
|
|
struct stH *header;
|
|
ssize_t rc;
|
|
|
|
header = sloffs_header(file);
|
|
|
|
if (!header)
|
|
return;
|
|
|
|
len = be64_to_cpu(header->flashlen);
|
|
free(header);
|
|
|
|
out = open(name, O_CREAT | O_RDWR | O_TRUNC, 00666);
|
|
|
|
if (out == -1) {
|
|
perror(name);
|
|
exit(1);
|
|
}
|
|
/* write byte at the end to be able to mmap it */
|
|
lseek(out, len - 1, SEEK_SET);
|
|
rc = write(out, "", 1);
|
|
if (rc != 1) {
|
|
printf("Extending file failed, rc %zd, errno %d\n", rc, errno);
|
|
exit(1);
|
|
}
|
|
write_buf = mmap(NULL, len, PROT_WRITE, MAP_SHARED, out, 0);
|
|
|
|
if (write_buf == MAP_FAILED) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
|
|
lseek(file, 0, SEEK_SET);
|
|
|
|
for (;;) {
|
|
i = read(file, write_buf, len);
|
|
if (i < 0) {
|
|
perror("read");
|
|
exit(1);
|
|
}
|
|
if (i == 0)
|
|
break;
|
|
write_buf += i;
|
|
len -= i;
|
|
}
|
|
|
|
munmap(write_buf, len);
|
|
close(out);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
printf("sloffs lists or changes a SLOF flash image\n\n");
|
|
printf("Usage:\n");
|
|
printf(" sloffs [OPTION]... [FILE]\n\n");
|
|
printf("Options:\n");
|
|
printf(" -h, --help show this help, then exit\n");
|
|
printf(" -l, --list list all files in the flash image\n");
|
|
printf(" -v, --version print the version, then exit\n");
|
|
printf(" -d, --dump dump the information from the header\n");
|
|
printf(" -a, --append=FILENAME append file at the end of\n");
|
|
printf(" the existing image\n");
|
|
printf(" -o, --output=FILENAME if appending a file this parameter\n");
|
|
printf(" is necessary to specify the name of\n");
|
|
printf(" the output file\n");
|
|
printf(" -c, --copy=FILENAME copy SLOF image to specified file\n");
|
|
printf(" this is especially useful if the\n");
|
|
printf(" source file is /dev/slof_flash\n");
|
|
printf("\n");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int fd;
|
|
const struct option loption[] = {
|
|
{ "help", 0, NULL, 'h' },
|
|
{ "list", 0, NULL, 'l' },
|
|
{ "version", 0, NULL, 'v' },
|
|
{ "dump", 0, NULL, 'd' },
|
|
{ "append", 1, NULL, 'a' },
|
|
{ "output", 1, NULL, 'o' },
|
|
{ "copy", 1, NULL, 'o' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
const char *soption = "dhlva:o:c:";
|
|
int c;
|
|
char mode = 0;
|
|
char *append = NULL;
|
|
char *output = NULL;
|
|
|
|
for (;;) {
|
|
c = getopt_long(argc, argv, soption, loption, NULL);
|
|
if (c == -1)
|
|
break;
|
|
switch (c) {
|
|
case 'l':
|
|
mode = 'l';
|
|
break;
|
|
case 'v':
|
|
printf("sloffs (version %d)\n", VERSION);
|
|
exit(0);
|
|
case 'd':
|
|
mode = 'd';
|
|
break;
|
|
case 'a':
|
|
mode = 'a';
|
|
append = strdup(optarg);
|
|
break;
|
|
case 'o':
|
|
output = strdup(optarg);
|
|
break;
|
|
case 'c':
|
|
mode = 'c';
|
|
output = strdup(optarg);
|
|
break;
|
|
case 'h':
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
if (optind >= argc)
|
|
usage();
|
|
|
|
fd = open(argv[optind], O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
perror(argv[optind]);
|
|
exit(1);
|
|
}
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
switch (mode) {
|
|
case 'l':
|
|
sloffs_list(fd);
|
|
break;
|
|
case 'd':
|
|
sloffs_dump(fd);
|
|
break;
|
|
case 'a':
|
|
if (!output) {
|
|
printf("sloffs requires -o, --output=FILENAME"
|
|
" when in append mode\n\n");
|
|
usage();
|
|
}
|
|
sloffs_append(fd, append, output);
|
|
break;
|
|
case 'c':
|
|
sloffs_copy(fd, output);
|
|
break;
|
|
}
|
|
|
|
free(append);
|
|
free(output);
|
|
close(fd);
|
|
return 0;
|
|
}
|