|  | /**************************************************************************** | 
|  | * tools/gencromfs.c | 
|  | * | 
|  | * Licensed to the Apache Software Foundation (ASF) under one or more | 
|  | * contributor license agreements.  See the NOTICE file distributed with | 
|  | * this work for additional information regarding copyright ownership.  The | 
|  | * ASF licenses this file to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance with the | 
|  | * License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the | 
|  | * License for the specific language governing permissions and limitations | 
|  | * under the License. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Included Files | 
|  | ****************************************************************************/ | 
|  |  | 
|  | #define _GNU_SOURCE 1 | 
|  |  | 
|  | #include <sys/stat.h> | 
|  | #include <stdint.h> | 
|  | #include <stdbool.h> | 
|  | #include <unistd.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <dirent.h> | 
|  | #include <limits.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Pre-processor Definitions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* HOST_BIGENDIAN or TGT_BIGENDIAN may be defined on the command line to | 
|  | * adapt to different host/target combinations.  Otherwise, both are assumed | 
|  | * to be the same endian-ness. | 
|  | */ | 
|  |  | 
|  | #undef HOST_TGTSWAP | 
|  |  | 
|  | #if (defined(HOST_BIGENDIAN) && !defined(TGT_BIGENDIAN)) || \ | 
|  | (!defined(HOST_BIGENDIAN) && defined(TGT_BIGENDIAN)) | 
|  | #  define HOST_TGTSWAP 1 | 
|  | #endif | 
|  |  | 
|  | #define UNUSED(a) ((void)(a)) | 
|  |  | 
|  | /* mkstemp() has been giving me errors on Cygwin */ | 
|  |  | 
|  | #undef USE_MKSTEMP | 
|  |  | 
|  | #define TMP_NAMLEN         32         /* Actually only 22 */ | 
|  | #ifdef USE_MKSTEMP | 
|  | #  define TMP_NAME         "/tmp/gencromfs-XXXXXX" | 
|  | #else | 
|  | #  define TMP_NAME         "/tmp/gencromfs-%06u" | 
|  | #endif | 
|  |  | 
|  | #define NUTTX_IXOTH        (1 << 0)   /* Must match NuttX's S_IXOTH */ | 
|  | #define NUTTX_IWOTH        (1 << 1)   /* Must match NuttX's S_IWOTH */ | 
|  | #define NUTTX_IROTH        (1 << 2)   /* Must match NuttX's S_IROTH */ | 
|  |  | 
|  | #define NUTTX_IRXOTH       (NUTTX_IROTH | NUTTX_IXOTH) | 
|  |  | 
|  | #define NUTTX_IXGRP        (1 << 3)   /* Must match NuttX's S_IXGRP */ | 
|  | #define NUTTX_IWGRP        (1 << 4)   /* Must match NuttX's S_IWGRP */ | 
|  | #define NUTTX_IRGRP        (1 << 5)   /* Must match NuttX's S_IRGRP */ | 
|  |  | 
|  | #define NUTTX_IRXGRP       (NUTTX_IRGRP | NUTTX_IXGRP) | 
|  |  | 
|  | #define NUTTX_IXUSR        (1 << 6)   /* Must match NuttX's S_IXUSR */ | 
|  | #define NUTTX_IWUSR        (1 << 7)   /* Must match NuttX's S_IWUSR */ | 
|  | #define NUTTX_IRUSR        (1 << 8)   /* Must match NuttX's S_IRUSR */ | 
|  |  | 
|  | #define NUTTX_IRXUSR       (NUTTX_IRUSR | NUTTX_IXUSR) | 
|  |  | 
|  | #define NUTTX_IFDIR        (4 << 12)   /* Must match NuttX's S_IFDIR */ | 
|  | #define NUTTX_IFREG        (8 << 12)   /* Must match NuttX's S_IFREG */ | 
|  | #define NUTTX_IFLNK        (10 << 12)  /* Must match NuttX's S_IFLNK */ | 
|  |  | 
|  | #define DIR_MODEFLAGS      (NUTTX_IFDIR | NUTTX_IRXUSR | NUTTX_IRXGRP | NUTTX_IRXOTH) | 
|  | #define DIRLINK_MODEFLAGS  (NUTTX_IFLNK | NUTTX_IRXUSR | NUTTX_IRXGRP | NUTTX_IRXOTH) | 
|  | #define FILE_MODEFLAGS     (NUTTX_IFREG | NUTTX_IRUSR | NUTTX_IRGRP | NUTTX_IROTH) | 
|  |  | 
|  | #define CROMFS_MAGIC       0x4d4f5243 | 
|  | #define CROMFS_BLOCKSIZE   512 | 
|  |  | 
|  | #define LZF_BUFSIZE        512 | 
|  | #define LZF_HLOG           13 | 
|  | #define LZF_HSIZE          (1 << LZF_HLOG) | 
|  |  | 
|  | #define LZF_TYPE0_HDR      0 | 
|  | #define LZF_TYPE1_HDR      1 | 
|  | #define LZF_TYPE0_HDR_SIZE 5 | 
|  | #define LZF_TYPE1_HDR_SIZE 7 | 
|  |  | 
|  | #define LZF_FRST(p)        (((p[0]) << 8) | p[1]) | 
|  | #define LZF_NEXT(v,p)      (((v) << 8) | p[2]) | 
|  | #define LZF_NDX(h)         ((((h ^ (h << 5)) >> (3*8 - LZF_HLOG)) - h*5) & (LZF_HSIZE - 1)) | 
|  |  | 
|  | #define LZF_MAX_LIT        (1 <<  5) | 
|  | #define LZF_MAX_OFF        (1 << LZF_HLOG) | 
|  | #define LZF_MAX_REF        ((1 << 8) + (1 << 3)) | 
|  |  | 
|  | #define HEX_PER_LINE       8 | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Types | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* Maximum size of an offset.  This should normally be size_t since this is | 
|  | * an in-memory file system.  However, size_t is 32-bits on most 32-bit | 
|  | * target machines but 64-bits on 64-host machines.  We restrict offsets to | 
|  | * 32-bits for commonality (limiting the size of the CROMFS image to 4Gb) | 
|  | * | 
|  | * Similarly, the NuttX mode_t is only 16-bits so uint16_t is explicitly used | 
|  | * for NuttX file modes. | 
|  | */ | 
|  |  | 
|  | /* CROMFS structures */ | 
|  |  | 
|  | struct cromfs_volume_s | 
|  | { | 
|  | uint32_t cv_magic;      /* Must be first.  Must be CROMFS_MAGIC */ | 
|  | uint16_t cv_nnodes;     /* Total number of nodes in-use */ | 
|  | uint16_t cv_nblocks;    /* Total number of data blocks in-use */ | 
|  | uint32_t cv_root;       /* Offset to the first node in the root file system */ | 
|  | uint32_t cv_fsize;      /* Size of the compressed file system image */ | 
|  | uint32_t cv_bsize;      /* Optimal block size for transfers */ | 
|  | }; | 
|  |  | 
|  | struct cromfs_node_s | 
|  | { | 
|  | uint16_t cn_mode;       /* File type, attributes, and access mode bits */ | 
|  | uint16_t cn_pad;        /* Not used */ | 
|  | uint32_t cn_name;       /* Offset from the beginning of the volume header to the | 
|  | * node name string.  NUL-terminated. */ | 
|  | uint32_t cn_size;       /* Size of the uncompressed data (in bytes) */ | 
|  | uint32_t cn_peer;       /* Offset to next node in this directory (for readdir()) */ | 
|  | union | 
|  | { | 
|  | uint32_t cn_child;    /* Offset to first node in sub-directory (directories only) */ | 
|  | uint32_t cn_link;     /* Offset to an arbitrary node (for hard link) */ | 
|  | uint32_t cn_blocks;   /* Offset to first block of compressed data (for read) */ | 
|  | } u; | 
|  | }; | 
|  |  | 
|  | /* LZF headers */ | 
|  |  | 
|  | struct lzf_header_s       /* Common data header */ | 
|  | { | 
|  | uint8_t lzf_magic[2];   /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;       /* LZF_TYPE0_HDR or LZF_TYPE1_HDR */ | 
|  | }; | 
|  |  | 
|  | struct lzf_type0_header_s /* Uncompressed data header */ | 
|  | { | 
|  | uint8_t lzf_magic[2];   /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;       /* LZF_TYPE0_HDR */ | 
|  | uint8_t lzf_len[2];     /* Data length (big-endian) */ | 
|  | }; | 
|  |  | 
|  | struct lzf_type1_header_s /* Compressed data header */ | 
|  | { | 
|  | uint8_t lzf_magic[2];   /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;       /* LZF_TYPE1_HDR */ | 
|  | uint8_t lzf_clen[2];    /* Compressed data length (big-endian) */ | 
|  | uint8_t lzf_ulen[2];    /* Uncompressed data length (big-endian) */ | 
|  | }; | 
|  |  | 
|  | /* LZF data buffer */ | 
|  |  | 
|  | union lzf_result_u | 
|  | { | 
|  | struct | 
|  | { | 
|  | uint8_t lzf_magic[2];     /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;         /* LZF_TYPE0_HDR or LZF_TYPE1_HDR */ | 
|  | } cmn;                      /* Common data header */ | 
|  | struct | 
|  | { | 
|  | uint8_t lzf_magic[2];     /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;         /* LZF_TYPE0_HDR */ | 
|  | uint8_t lzf_len[2];       /* Data length (big-endian) */ | 
|  | uint8_t lzf_buffer[LZF_BUFSIZE]; | 
|  | } uncompressed;             /* Uncompressed data header */ | 
|  | struct | 
|  | { | 
|  | uint8_t lzf_magic[2];     /* [0]='Z', [1]='V' */ | 
|  | uint8_t lzf_type;         /* LZF_TYPE1_HDR */ | 
|  | uint8_t lzf_clen[2];      /* Compressed data length (big-endian) */ | 
|  | uint8_t lzf_ulen[2];      /* Uncompressed data length (big-endian) */ | 
|  | uint8_t lzf_buffer[LZF_BUFSIZE + 16]; | 
|  | } compressed; | 
|  | }; | 
|  |  | 
|  | /* LZF hash table */ | 
|  |  | 
|  | static uint8_t *g_lzf_hashtab[LZF_HSIZE]; | 
|  |  | 
|  | /* Type of the callback from traverse_directory() */ | 
|  |  | 
|  | typedef int (*traversal_callback_t)(const char *dirpath, const char *name, | 
|  | void *arg, bool lastentry); | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Data | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static char *g_progname;       /* Name of this program */ | 
|  | static char *g_dirname;        /* Source directory path */ | 
|  | static char *g_outname;        /* Output file path */ | 
|  |  | 
|  | static FILE *g_outstream;      /* Main output stream */ | 
|  | static FILE *g_tmpstream;      /* Temporary file output stream */ | 
|  |  | 
|  | static const char g_delim[] = | 
|  | "**************************************" | 
|  | "**************************************"; | 
|  |  | 
|  | static const char g_license[] = | 
|  | " *\n" | 
|  | " * Licensed to the Apache Software Foundation (ASF) under one or more\n" | 
|  | " * contributor license agreements. See the NOTICE file distributed with\n" | 
|  | " * this work for additional information regarding copyright ownership.\n" | 
|  | " * The ASF licenses this file to you under the Apache License, Version\n" | 
|  | " * 2.0 (the \"License\"); you mayn`t use this file except in compliance\n" | 
|  | " * with the License. You may obtain a copy of the License at\n" | 
|  | " *\n" | 
|  | " *   http://www.apache.org/licenses/LICENSE-2.0\n" | 
|  | " *\n" | 
|  | " * Unless required by applicable law or agreed to in writing, software\n" | 
|  | " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" | 
|  | " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n" | 
|  | " * implied. See the License for the specific language governing\n" | 
|  | " * permissions and limitations under the License.\n" | 
|  | " *\n"; | 
|  |  | 
|  | static uint32_t g_offset;        /* Current image offset */ | 
|  | static uint32_t g_diroffset;     /* Offset for '.' */ | 
|  | static uint32_t g_parent_offset; /* Offset for '..' */ | 
|  |  | 
|  | static unsigned int g_nnodes;  /* Number of nodes generated */ | 
|  | static unsigned int g_nblocks; /* Number of blocks of data generated */ | 
|  | static unsigned int g_nhex;    /* Number of hex characters on output line */ | 
|  | #ifndef USE_MKSTEMP | 
|  | static unsigned int g_ntmps;   /* Number temporary files */ | 
|  | #endif | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Function Prototypes | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void show_usage(void); | 
|  | static void verify_directory(void); | 
|  | static void verify_outfile(void); | 
|  | static void init_outfile(void); | 
|  | static FILE *open_tmpfile(void); | 
|  | #ifndef USE_MKSTEMP | 
|  | static void unlink_tmpfiles(void); | 
|  | #endif | 
|  | static void append_tmpfile(FILE *dest, FILE *src); | 
|  | static void dump_hexbuffer(FILE *stream, const void *buffer, | 
|  | unsigned int nbytes); | 
|  | static void dump_nextline(FILE *stream); | 
|  | static size_t lzf_compress(const uint8_t *inbuffer, unsigned int inlen, | 
|  | union lzf_result_u *result); | 
|  | static uint16_t get_mode(mode_t mode); | 
|  | #ifdef HOST_TGTSWAP | 
|  | static inline uint16_t tgt_uint16(uint16_t a); | 
|  | static inline uint32_t tgt_uint32(uint32_t a); | 
|  | #  define TGT_UINT16(a) tgt_uint16(a) | 
|  | #  define TGT_UINT32(a) tgt_uint32(a) | 
|  | #else | 
|  | #  define TGT_UINT16(a) (a) | 
|  | #  define TGT_UINT32(a) (a) | 
|  | #endif | 
|  | static void gen_dirlink(const char *name, uint32_t tgtoffs, bool dirempty); | 
|  | static void gen_directory(const char *path, const char *name, mode_t mode, | 
|  | bool lastentry); | 
|  | static void gen_file(const char *path, const char *name, mode_t mode, | 
|  | bool lastentry); | 
|  | static int  dir_notempty(const char *dirpath, const char *name, | 
|  | void *arg, bool lastentry); | 
|  | static int  process_direntry(const char *dirpath, const char *name, | 
|  | void *arg, bool lastentry); | 
|  | static int  traverse_directory(const char *dirpath, | 
|  | traversal_callback_t callback, void *arg); | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Functions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void show_usage(void) | 
|  | { | 
|  | fprintf(stderr, "USAGE: %s <dir-path> <out-file>\n", g_progname); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | static void verify_directory(void) | 
|  | { | 
|  | struct stat buf; | 
|  | int len; | 
|  | int ret; | 
|  |  | 
|  | /* Trim any trailing '/' characters from the directory path. */ | 
|  |  | 
|  | len = strlen(g_dirname); | 
|  | while (len > 1 && g_dirname[len - 1] == '/') | 
|  | { | 
|  | g_dirname[len - 1] = '\0'; | 
|  | len--; | 
|  | } | 
|  |  | 
|  | if (len < 1) | 
|  | { | 
|  | fprintf(stderr, "ERROR: Source <dir-path> %s is invalid\n", | 
|  | g_dirname); | 
|  | show_usage(); | 
|  | } | 
|  |  | 
|  | /* stat the source directory containing the file system image */ | 
|  |  | 
|  | ret = stat(g_dirname, &buf); | 
|  | if (ret < 0) | 
|  | { | 
|  | int errcode = errno; | 
|  | if (errcode == ENOENT) | 
|  | { | 
|  | fprintf(stderr, "ERROR: Source <dir-path> %s does not exist\n", | 
|  | g_dirname); | 
|  | } | 
|  | else | 
|  | { | 
|  | fprintf(stderr, "ERROR: stat(%s) failed: %s\n", | 
|  | g_dirname, strerror(errcode)); | 
|  | } | 
|  |  | 
|  | show_usage(); | 
|  | } | 
|  |  | 
|  | /* Verify that the source is, indeed, a directory */ | 
|  |  | 
|  | else if (!S_ISDIR(buf.st_mode)) | 
|  | { | 
|  | fprintf(stderr, "ERROR: Source <dir-path> %s is not a directory\n", | 
|  | g_dirname); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void verify_outfile(void) | 
|  | { | 
|  | struct stat buf; | 
|  | int ret; | 
|  |  | 
|  | /* stat the destination file */ | 
|  |  | 
|  | ret = stat(g_outname, &buf); | 
|  | if (ret < 0) | 
|  | { | 
|  | int errcode = errno; | 
|  | if (errcode != ENOENT) | 
|  | { | 
|  | fprintf(stderr, "ERROR: stat(%s) failed: %s\n", | 
|  | g_outname, strerror(errcode)); | 
|  | show_usage(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Something exists at this path.  Verify that the destination is a regular | 
|  | * file | 
|  | */ | 
|  |  | 
|  | else if (!S_ISREG(buf.st_mode)) | 
|  | { | 
|  | fprintf(stderr, "ERROR: Destination <out-file> %s exists\n", | 
|  | g_outname); | 
|  | show_usage(); | 
|  | } | 
|  | else | 
|  | { | 
|  | printf("Existing file %s will be replaced\n", g_outname); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void init_outfile(void) | 
|  | { | 
|  | fprintf(g_outstream, "/%s\n", g_delim); | 
|  | fprintf(g_outstream, " * %s\n", g_outname); | 
|  | fprintf(g_outstream, "%s", g_license); | 
|  | fprintf(g_outstream, " %s/\n\n", g_delim); | 
|  |  | 
|  | fprintf(g_outstream, "/%s\n", g_delim); | 
|  | fprintf(g_outstream, " * Included Files\n"); | 
|  | fprintf(g_outstream, " %s/\n\n", g_delim); | 
|  | fprintf(g_outstream, "#include <stdint.h>\n\n"); | 
|  |  | 
|  | fprintf(g_outstream, "/%s\n", g_delim); | 
|  | fprintf(g_outstream, " * Private Data\n"); | 
|  | fprintf(g_outstream, " %s/\n\n", g_delim); | 
|  | } | 
|  |  | 
|  | static FILE *open_tmpfile(void) | 
|  | { | 
|  | FILE *tmpstream; | 
|  | #ifdef USE_MKSTEMP | 
|  | int fd; | 
|  |  | 
|  | fd = mkstemp(TMP_NAME); | 
|  | if (fd < 0) | 
|  | { | 
|  | fprintf(stderr, "Failed to create temporary file: %s\n", | 
|  | strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | tmpstream = fdopen(fd, "w+"); | 
|  | if (!tmpstream) | 
|  | { | 
|  | fprintf(stderr, "fdopen for tmp file failed: %s\n", strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | #else | 
|  | char tmpname[TMP_NAMLEN]; | 
|  |  | 
|  | snprintf(tmpname, TMP_NAMLEN, TMP_NAME, g_ntmps); | 
|  | g_ntmps++; | 
|  |  | 
|  | tmpstream = fopen(tmpname, "w+"); | 
|  | if (!tmpstream) | 
|  | { | 
|  | fprintf(stderr, "fopen for tmp file %s failed: %s\n", | 
|  | tmpname, strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return tmpstream; | 
|  | } | 
|  |  | 
|  | #ifndef USE_MKSTEMP | 
|  | static void unlink_tmpfiles(void) | 
|  | { | 
|  | char tmpname[TMP_NAMLEN]; | 
|  | unsigned int i; | 
|  |  | 
|  | for (i = 0; i < g_ntmps; i++) | 
|  | { | 
|  | snprintf(tmpname, TMP_NAMLEN, TMP_NAME, i); | 
|  | unlink(tmpname); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void append_tmpfile(FILE *dest, FILE *src) | 
|  | { | 
|  | uint8_t iobuffer[1024]; | 
|  | size_t nread; | 
|  |  | 
|  | /* Rewind the source directory to be beginning.  We assume that the dest | 
|  | * is already at the end. | 
|  | */ | 
|  |  | 
|  | rewind(src); | 
|  |  | 
|  | /* Then append the source to the destination */ | 
|  |  | 
|  | do | 
|  | { | 
|  | nread = fread(iobuffer, 1, 1024, src); | 
|  | if (nread > 0) | 
|  | { | 
|  | fwrite(iobuffer, 1, nread, dest); | 
|  | } | 
|  | } | 
|  | while (nread > 0); | 
|  |  | 
|  | /* We can now close the src temporary file */ | 
|  |  | 
|  | fclose(src); | 
|  | } | 
|  |  | 
|  | static void dump_hexbuffer(FILE *stream, const void *buffer, | 
|  | unsigned int nbytes) | 
|  | { | 
|  | uint8_t *ptr = (uint8_t *)buffer; | 
|  |  | 
|  | while (nbytes > 0) | 
|  | { | 
|  | if (g_nhex == 0) | 
|  | { | 
|  | fprintf(stream, " "); | 
|  | } | 
|  |  | 
|  | fprintf(stream, " 0x%02x,", *ptr++); | 
|  |  | 
|  | if (++g_nhex >= HEX_PER_LINE) | 
|  | { | 
|  | fprintf(stream, "\n"); | 
|  | g_nhex = 0; | 
|  | } | 
|  |  | 
|  | nbytes--; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void dump_nextline(FILE *stream) | 
|  | { | 
|  | if (g_nhex > 0) | 
|  | { | 
|  | fprintf(stream, "\n"); | 
|  | g_nhex = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static size_t lzf_compress(const uint8_t *inbuffer, unsigned int inlen, | 
|  | union lzf_result_u *result) | 
|  | { | 
|  | const uint8_t *inptr  = inbuffer; | 
|  | uint8_t *outptr = result->compressed.lzf_buffer; | 
|  | const uint8_t *inend  = inptr + inlen; | 
|  | uint8_t *outend = outptr + LZF_BUFSIZE; | 
|  | const uint8_t *ref; | 
|  | uintptr_t off; | 
|  | ssize_t cs; | 
|  | ssize_t retlen; | 
|  | unsigned int hval; | 
|  | int lit; | 
|  |  | 
|  | if (inlen == 0) | 
|  | { | 
|  | cs = 0; | 
|  | goto genhdr; | 
|  | } | 
|  |  | 
|  | memset(g_lzf_hashtab, 0, sizeof(g_lzf_hashtab)); | 
|  | lit = 0; /* Start run */ | 
|  | outptr++; | 
|  |  | 
|  | hval = LZF_FRST(inptr); | 
|  | while (inptr < inend - 2) | 
|  | { | 
|  | uint8_t **hslot; | 
|  |  | 
|  | hval   = LZF_NEXT(hval, inptr); | 
|  | hslot  = &g_lzf_hashtab[LZF_NDX(hval)]; | 
|  | ref    = *hslot; | 
|  | *hslot = (uint8_t *)inptr; | 
|  |  | 
|  | if (ref < inptr && /* the next test will actually take care of this, but this is faster */ | 
|  | (off = inptr - ref - 1) < LZF_MAX_OFF && | 
|  | ref > (uint8_t *)inbuffer && | 
|  | ref[2] == inptr[2] && | 
|  | ((ref[1] << 8) | ref[0]) == ((inptr[1] << 8) | inptr[0])) | 
|  | { | 
|  | /* Match found at *ref++ */ | 
|  |  | 
|  | unsigned int len = 2; | 
|  | unsigned int maxlen = inend - inptr - len; | 
|  | maxlen = maxlen > LZF_MAX_REF ? LZF_MAX_REF : maxlen; | 
|  |  | 
|  | /* First a faster conservative test */ | 
|  |  | 
|  | if ((outptr + 3 + 1) >= outend) | 
|  | { | 
|  | /* Second the exact but rare test */ | 
|  |  | 
|  | if (outptr - !lit + 3 + 1 >= outend) | 
|  | { | 
|  | cs = 0; | 
|  | goto genhdr; | 
|  | } | 
|  | } | 
|  |  | 
|  | outptr[(-lit) - 1] = lit - 1; /* Stop run */ | 
|  | outptr -= !lit;               /* Undo run if length is zero */ | 
|  |  | 
|  | for (; ; ) | 
|  | { | 
|  | if (maxlen > 16) | 
|  | { | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | len++; | 
|  | if (ref[len] != inptr[len]) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | do | 
|  | { | 
|  | len++; | 
|  | } | 
|  | while (len < maxlen && ref[len] == inptr[len]); | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | len -= 2; /* len is now #octets - 1 */ | 
|  | inptr++; | 
|  |  | 
|  | if (len < 7) | 
|  | { | 
|  | *outptr++ = (off >> 8) + (len << 5); | 
|  | } | 
|  | else | 
|  | { | 
|  | *outptr++ = (off >> 8) + (7 << 5); | 
|  | *outptr++ = len - 7; | 
|  | } | 
|  |  | 
|  | *outptr++ = off; | 
|  |  | 
|  | lit = 0; outptr++; /* start run */ | 
|  |  | 
|  | inptr += len + 1; | 
|  |  | 
|  | if (inptr >= inend - 2) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | inptr -= len + 1; | 
|  |  | 
|  | do | 
|  | { | 
|  | hval = LZF_NEXT(hval, inptr); | 
|  | g_lzf_hashtab[LZF_NDX(hval)] = (uint8_t *)inptr; | 
|  | inptr++; | 
|  | } | 
|  | while (len--); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* One more literal byte we must copy */ | 
|  |  | 
|  | if (outptr >= outend) | 
|  | { | 
|  | cs = 0; | 
|  | goto genhdr; | 
|  | } | 
|  |  | 
|  | lit++; | 
|  | *outptr++ = *inptr++; | 
|  |  | 
|  | if (lit == LZF_MAX_LIT) | 
|  | { | 
|  | outptr[(-lit) - 1] = lit - 1; /* Stop run */ | 
|  | lit = 0;                      /* Start run */ | 
|  | outptr++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* At most 3 bytes can be missing here */ | 
|  |  | 
|  | if (outptr + 3 > outend) | 
|  | { | 
|  | cs = 0; | 
|  | goto genhdr; | 
|  | } | 
|  |  | 
|  | while (inptr < inend) | 
|  | { | 
|  | lit++; *outptr++ = *inptr++; | 
|  |  | 
|  | if (lit == LZF_MAX_LIT) | 
|  | { | 
|  | outptr[(-lit) - 1] = lit - 1; /* Stop run */ | 
|  | lit = 0;                      /* Start run */ | 
|  | outptr++; | 
|  | } | 
|  | } | 
|  |  | 
|  | outptr[(-lit) - 1] = lit - 1; /* End run */ | 
|  | outptr -= !lit;               /* Undo run if length is zero */ | 
|  |  | 
|  | cs = outptr - (uint8_t *)result->compressed.lzf_buffer; | 
|  |  | 
|  | genhdr: | 
|  | if (cs > 0) | 
|  | { | 
|  | /* Write compressed header */ | 
|  |  | 
|  | result->compressed.lzf_magic[0]   = 'Z'; | 
|  | result->compressed.lzf_magic[1]   = 'V'; | 
|  | result->compressed.lzf_type       = LZF_TYPE1_HDR; | 
|  | result->compressed.lzf_clen[0]    = cs >> 8; | 
|  | result->compressed.lzf_clen[1]    = cs & 0xff; | 
|  | result->compressed.lzf_ulen[0]    = inlen >> 8; | 
|  | result->compressed.lzf_ulen[1]    = inlen & 0xff; | 
|  | retlen                            = cs + LZF_TYPE1_HDR_SIZE; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Write uncompressed header */ | 
|  |  | 
|  | result->uncompressed.lzf_magic[0] = 'Z'; | 
|  | result->uncompressed.lzf_magic[1] = 'V'; | 
|  | result->uncompressed.lzf_type     = LZF_TYPE0_HDR; | 
|  | result->uncompressed.lzf_len[0]   = inlen >> 8; | 
|  | result->uncompressed.lzf_len[1]   = inlen & 0xff; | 
|  |  | 
|  | /* Copy uncompressed data into the result buffer */ | 
|  |  | 
|  | memcpy(result->uncompressed.lzf_buffer, inbuffer, inlen); | 
|  | retlen                            = inlen + LZF_TYPE0_HDR_SIZE; | 
|  | } | 
|  |  | 
|  | return retlen; | 
|  | } | 
|  |  | 
|  | static uint16_t get_mode(mode_t mode) | 
|  | { | 
|  | uint16_t ret = 0; | 
|  |  | 
|  | /* Convert mode to CROMFS NuttX read-only mode */ | 
|  |  | 
|  | if ((mode & S_IXOTH) != 0) | 
|  | { | 
|  | ret |= NUTTX_IXOTH; | 
|  | } | 
|  |  | 
|  | if ((mode & S_IROTH) != 0) | 
|  | { | 
|  | ret |= NUTTX_IROTH; | 
|  | } | 
|  |  | 
|  | if ((mode & S_IXGRP) != 0) | 
|  | { | 
|  | ret |= NUTTX_IXGRP; | 
|  | } | 
|  |  | 
|  | if ((mode & S_IRGRP) != 0) | 
|  | { | 
|  | ret |= NUTTX_IRGRP; | 
|  | } | 
|  |  | 
|  | if ((mode & S_IXUSR) != 0) | 
|  | { | 
|  | ret |= NUTTX_IXUSR; | 
|  | } | 
|  |  | 
|  | if ((mode & S_IRUSR) != 0) | 
|  | { | 
|  | ret |= NUTTX_IRUSR; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef HOST_TGTSWAP | 
|  | static inline uint16_t tgt_uint16(uint16_t a) | 
|  | { | 
|  | /* [15:8][7:0] -> [7:0][15:8] */ | 
|  |  | 
|  | return (a >> 8) | (a << 8); | 
|  | } | 
|  |  | 
|  | static inline uint32_t tgt_uint32(uint32_t a) | 
|  | { | 
|  | /* [31:24][23:16][15:8][7:0] -> [7:0][15:8][23:16][31:24] */ | 
|  |  | 
|  | return (a >> 24) | ((a >> 8) & 0x0000ff00) | | 
|  | ((a << 8) & 0x00ff0000) | (a << 24); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void gen_dirlink(const char *name, uint32_t tgtoffs, bool dirempty) | 
|  | { | 
|  | struct cromfs_node_s node; | 
|  | int namlen; | 
|  |  | 
|  | namlen          = strlen(name) + 1; | 
|  |  | 
|  | /* Generate the hardlink node */ | 
|  |  | 
|  | fprintf(g_tmpstream, "\n  /* Offset %6lu:  Hard link %s */\n\n", | 
|  | (unsigned long)g_offset, name); | 
|  |  | 
|  | node.cn_mode    = TGT_UINT16(DIRLINK_MODEFLAGS); | 
|  | node.cn_pad     = 0; | 
|  |  | 
|  | g_offset       += sizeof(struct cromfs_node_s); | 
|  | node.cn_name    = TGT_UINT32(g_offset); | 
|  | node.cn_size    = 0; | 
|  |  | 
|  | g_offset       += namlen; | 
|  | node.cn_peer    = TGT_UINT32(dirempty ? 0 : g_offset); | 
|  | node.u.cn_link  = TGT_UINT32(tgtoffs); | 
|  |  | 
|  | dump_hexbuffer(g_tmpstream, &node, sizeof(struct cromfs_node_s)); | 
|  | dump_hexbuffer(g_tmpstream, name, namlen); | 
|  | dump_nextline(g_tmpstream); | 
|  |  | 
|  | g_nnodes++; | 
|  | } | 
|  |  | 
|  | static void gen_directory(const char *path, const char *name, mode_t mode, | 
|  | bool lastentry) | 
|  | { | 
|  | struct cromfs_node_s node; | 
|  | uint32_t save_offset        = g_offset; | 
|  | uint32_t save_diroffset     = g_diroffset; | 
|  | uint32_t save_parent_offset = g_parent_offset; | 
|  | FILE *save_tmpstream        = g_tmpstream; | 
|  | FILE *subtree_stream; | 
|  | int namlen; | 
|  | int result; | 
|  |  | 
|  | namlen          = strlen(name) + 1; | 
|  |  | 
|  | /* Open a new temporary file */ | 
|  |  | 
|  | subtree_stream  = open_tmpfile(); | 
|  | g_tmpstream     = subtree_stream; | 
|  |  | 
|  | /* Update the offset to account for the file node which we have not yet | 
|  | * written (we can't, we don't have enough information yet) | 
|  | */ | 
|  |  | 
|  | g_offset       += sizeof(struct cromfs_node_s) + namlen; | 
|  |  | 
|  | /* Update offsets for the subdirectory */ | 
|  |  | 
|  | g_parent_offset = g_diroffset;  /* New offset for '..' */ | 
|  | g_diroffset     = g_offset;     /* New offset for '.' */ | 
|  |  | 
|  | /* We are going to traverse the new directory twice; the first time just | 
|  | * see if the directory is empty.  The second time is the real thing. | 
|  | */ | 
|  |  | 
|  | result = traverse_directory(path, dir_notempty, NULL); | 
|  |  | 
|  | /* Generate the '.' and '..' links for the directory (in the new temporary | 
|  | * file). | 
|  | */ | 
|  |  | 
|  | gen_dirlink(".", g_diroffset, false); | 
|  | gen_dirlink("..", g_parent_offset, result == 0); | 
|  | if (result != 0) | 
|  | { | 
|  | /* Then recurse to generate all of the nodes for the subtree */ | 
|  |  | 
|  | traverse_directory(path, process_direntry, NULL); | 
|  | } | 
|  |  | 
|  | /* When traverse_directory() returns, all of the nodes in the sub-tree | 
|  | * under 'name' will have been written to the new tmpfile.  g_offset is | 
|  | * correct, but other settings are not. | 
|  | * | 
|  | * Restore the state. | 
|  | */ | 
|  |  | 
|  | g_tmpstream     = save_tmpstream; | 
|  | g_diroffset     = save_diroffset; | 
|  | g_parent_offset = save_parent_offset; | 
|  |  | 
|  | /* Generate the directory node */ | 
|  |  | 
|  | fprintf(g_tmpstream, "\n  /* Offset %6lu:  Directory %s */\n\n", | 
|  | (unsigned long)save_offset, path); | 
|  |  | 
|  | node.cn_mode    = TGT_UINT16(NUTTX_IFDIR | get_mode(mode)); | 
|  | node.cn_pad     = 0; | 
|  |  | 
|  | save_offset    += sizeof(struct cromfs_node_s); | 
|  | node.cn_name    = TGT_UINT32(save_offset); | 
|  | node.cn_size    = 0; | 
|  |  | 
|  | save_offset    += namlen; | 
|  | node.cn_peer    = TGT_UINT32(lastentry ? 0 : g_offset); | 
|  | node.u.cn_child = TGT_UINT32(save_offset); | 
|  |  | 
|  | dump_hexbuffer(g_tmpstream, &node, sizeof(struct cromfs_node_s)); | 
|  | dump_hexbuffer(g_tmpstream, name, namlen); | 
|  | dump_nextline(g_tmpstream); | 
|  |  | 
|  | g_nnodes++; | 
|  |  | 
|  | /* Now append the sub-tree nodes in the new tmpfile to the previous | 
|  | * tmpfile | 
|  | */ | 
|  |  | 
|  | append_tmpfile(g_tmpstream, subtree_stream); | 
|  | } | 
|  |  | 
|  | static void gen_file(const char *path, const char *name, mode_t mode, | 
|  | bool lastentry) | 
|  | { | 
|  | struct cromfs_node_s node; | 
|  | union lzf_result_u result; | 
|  | uint32_t nodeoffs = g_offset; | 
|  | FILE *save_tmpstream = g_tmpstream; | 
|  | FILE *outstream; | 
|  | FILE *instream; | 
|  | uint8_t iobuffer[LZF_BUFSIZE]; | 
|  | size_t nread; | 
|  | size_t ntotal; | 
|  | size_t blklen; | 
|  | size_t blktotal; | 
|  | unsigned int blkno; | 
|  | int namlen; | 
|  |  | 
|  | namlen      = strlen(name) + 1; | 
|  |  | 
|  | /* Open a new temporary file */ | 
|  |  | 
|  | outstream   = open_tmpfile(); | 
|  | g_tmpstream = outstream; | 
|  | g_offset    = nodeoffs + sizeof(struct cromfs_node_s) + namlen; | 
|  |  | 
|  | /* Open the source data file */ | 
|  |  | 
|  | instream    = fopen(path, "r"); | 
|  | if (!instream) | 
|  | { | 
|  | fprintf(stderr, "fopen for source file %s failed: %s\n", | 
|  | path, strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* Then read data from the file, compress it, and write it to the new | 
|  | * temporary file | 
|  | */ | 
|  |  | 
|  | blkno       = 0; | 
|  | ntotal      = 0; | 
|  | blktotal    = 0; | 
|  |  | 
|  | do | 
|  | { | 
|  | /* Read the next chunk from the file */ | 
|  |  | 
|  | nread = fread(iobuffer, 1, LZF_BUFSIZE, instream); | 
|  | if (nread > 0) | 
|  | { | 
|  | uint16_t clen; | 
|  |  | 
|  | /* Compress the chunk */ | 
|  |  | 
|  | blklen = lzf_compress(iobuffer, nread, &result); | 
|  | if (result.cmn.lzf_type == LZF_TYPE0_HDR) | 
|  | { | 
|  | clen = nread; | 
|  | } | 
|  | else | 
|  | { | 
|  | clen = (uint16_t)result.compressed.lzf_clen[0] << 8 | | 
|  | (uint16_t)result.compressed.lzf_clen[1]; | 
|  | } | 
|  |  | 
|  | fprintf(g_tmpstream, | 
|  | "\n  /* Offset %6lu:  " | 
|  | "Block %u blklen=%lu Uncompressed=%lu Compressed=%u " | 
|  | "*/\n\n",  (unsigned long)g_offset, blkno, (long)blklen, | 
|  | (long)nread, clen); | 
|  | dump_hexbuffer(g_tmpstream, &result, blklen); | 
|  | dump_nextline(g_tmpstream); | 
|  |  | 
|  | ntotal   += nread; | 
|  | blktotal += blklen; | 
|  | g_offset += blklen; | 
|  |  | 
|  | g_nblocks++; | 
|  | blkno++; | 
|  | } | 
|  | } | 
|  | while (nread > 0); | 
|  |  | 
|  | /* Restore the old tmpfile context */ | 
|  |  | 
|  | g_tmpstream        = save_tmpstream; | 
|  |  | 
|  | /* Now we have enough information to generate the file node */ | 
|  |  | 
|  | fprintf(g_tmpstream, "\n  /* Offset %6lu:  File %s:  " | 
|  | "Uncompressed=%lu Compressed=%lu */\n\n", | 
|  | (unsigned long)nodeoffs, path, (unsigned long)ntotal, | 
|  | (unsigned long)blktotal); | 
|  |  | 
|  | node.cn_mode       = TGT_UINT16(NUTTX_IFREG | get_mode(mode)); | 
|  | node.cn_pad        = 0; | 
|  |  | 
|  | nodeoffs          += sizeof(struct cromfs_node_s); | 
|  | node.cn_name       = TGT_UINT32(nodeoffs); | 
|  |  | 
|  | node.cn_size       = TGT_UINT32(ntotal); | 
|  |  | 
|  | nodeoffs          += namlen; | 
|  | node.u.cn_blocks   = TGT_UINT32(nodeoffs); | 
|  |  | 
|  | nodeoffs          += blktotal; | 
|  | node.cn_peer       = TGT_UINT32(lastentry ? 0 : nodeoffs); | 
|  |  | 
|  | dump_hexbuffer(g_tmpstream, &node, sizeof(struct cromfs_node_s)); | 
|  | dump_hexbuffer(g_tmpstream, name, namlen); | 
|  | dump_nextline(g_tmpstream); | 
|  |  | 
|  | g_nnodes++; | 
|  |  | 
|  | /* Now append the sub-tree nodes in the new tmpfile to the previous | 
|  | * tmpfiles | 
|  | */ | 
|  |  | 
|  | append_tmpfile(g_tmpstream, outstream); | 
|  | } | 
|  |  | 
|  | static int dir_notempty(const char *dirpath, const char *name, | 
|  | void *arg, bool lastentry) | 
|  | { | 
|  | struct stat buf; | 
|  | char *path; | 
|  | int ret; | 
|  |  | 
|  | ret = asprintf(&path, "%s/%s", dirpath, name); | 
|  | if (ret < 0) | 
|  | { | 
|  | fprintf(stderr, "ERROR: asprintf() failed\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* stat() should not fail for any reason */ | 
|  |  | 
|  | ret = stat(path, &buf); | 
|  | if (ret < 0) | 
|  | { | 
|  | int errcode = errno; | 
|  | fprintf(stderr, "ERROR: stat(%s) failed: %s\n", | 
|  | path, strerror(errcode)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* The directory is not empty if it contains with a file or a directory | 
|  | * entry.  Anything else will be ignored and the directly may be | 
|  | * effectively empty. | 
|  | */ | 
|  |  | 
|  | free(path); | 
|  | return (S_ISREG(buf.st_mode) || S_ISDIR(buf.st_mode)); | 
|  | } | 
|  |  | 
|  | static int process_direntry(const char *dirpath, const char *name, | 
|  | void *arg, bool lastentry) | 
|  | { | 
|  | struct stat buf; | 
|  | char *path; | 
|  | int ret; | 
|  |  | 
|  | ret = asprintf(&path, "%s/%s", dirpath, name); | 
|  | if (ret < 0) | 
|  | { | 
|  | fprintf(stderr, "ERROR: asprintf() failed\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | ret = stat(path, &buf); | 
|  | if (ret < 0) | 
|  | { | 
|  | int errcode = errno; | 
|  | if (errcode == ENOENT) | 
|  | { | 
|  | fprintf(stderr, "ERROR: Directory entry %s does not exist\n", | 
|  | path); | 
|  | } | 
|  | else | 
|  | { | 
|  | fprintf(stderr, "ERROR: stat(%s) failed: %s\n", | 
|  | path, strerror(errcode)); | 
|  | } | 
|  |  | 
|  | show_usage(); | 
|  | } | 
|  |  | 
|  | /* Verify that the source is, indeed, a directory */ | 
|  |  | 
|  | else if (S_ISDIR(buf.st_mode)) | 
|  | { | 
|  | gen_directory(path, name, buf.st_mode, lastentry); | 
|  | } | 
|  | else if (S_ISREG(buf.st_mode)) | 
|  | { | 
|  | gen_file(path, name, buf.st_mode, lastentry); | 
|  | } | 
|  | else | 
|  | { | 
|  | fprintf(stderr, "Omitting entry %s\n", path); | 
|  | } | 
|  |  | 
|  | free(path); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int traverse_directory(const char *dirpath, | 
|  | traversal_callback_t callback, void *arg) | 
|  | { | 
|  | DIR *dirp; | 
|  | struct dirent *direntry; | 
|  | char name[NAME_MAX + 1]; | 
|  | int ret = 0; | 
|  |  | 
|  | /* Open the directory */ | 
|  |  | 
|  | dirp = opendir(dirpath); | 
|  | if (dirp == NULL) | 
|  | { | 
|  | fprintf(stderr, "ERROR: opendir(%s) failed: %s\n", | 
|  | dirpath, strerror(errno)); | 
|  | show_usage(); | 
|  | } | 
|  |  | 
|  | /* Visit each entry in the directory */ | 
|  |  | 
|  | direntry = readdir(dirp); | 
|  | while (direntry != NULL) | 
|  | { | 
|  | /* Preserve the name from the directory entry.  The return value | 
|  | * from readdir() only persists until the next time that readdir() | 
|  | * is called (alternatively, use readdir_r). | 
|  | */ | 
|  |  | 
|  | strncpy(name, direntry->d_name, NAME_MAX + 1); | 
|  |  | 
|  | /* Get the next entry in advance so that we can anticipate the end of | 
|  | * the directory. | 
|  | */ | 
|  |  | 
|  | direntry = readdir(dirp); | 
|  |  | 
|  | /* Skip the '.' and '..' hard links */ | 
|  |  | 
|  | if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) | 
|  | { | 
|  | /* Process the directory entry */ | 
|  |  | 
|  | ret = callback(dirpath, name, arg, direntry == NULL); | 
|  | if (ret != 0) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | closedir(dirp); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Public Functions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | int main(int argc, char **argv, char **envp) | 
|  | { | 
|  | struct cromfs_volume_s vol; | 
|  | char *ptr; | 
|  | int result; | 
|  |  | 
|  | /* Verify arguments */ | 
|  |  | 
|  | ptr = strrchr(argv[0], '/'); | 
|  | g_progname = ptr == NULL ? argv[0] : ptr + 1; | 
|  |  | 
|  | if (argc != 3) | 
|  | { | 
|  | fprintf(stderr, "Unexpected number of arguments\n"); | 
|  | show_usage(); | 
|  | } | 
|  |  | 
|  | g_dirname  = argv[1]; | 
|  | g_outname  = argv[2]; | 
|  |  | 
|  | verify_directory(); | 
|  | verify_outfile(); | 
|  |  | 
|  | g_outstream = fopen(g_outname, "w"); | 
|  | if (!g_outstream) | 
|  | { | 
|  | fprintf(stderr, "open %s failed: %s\n", g_outname, strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | g_tmpstream = open_tmpfile(); | 
|  |  | 
|  | /* Set up the initial boilerplate at the beginning of each file */ | 
|  |  | 
|  | init_outfile(); | 
|  |  | 
|  | /* Set up some initial offsets */ | 
|  |  | 
|  | g_offset        = sizeof(struct cromfs_volume_s);  /* Current image offset */ | 
|  | g_diroffset     = sizeof(struct cromfs_volume_s);  /* Offset for '.' */ | 
|  | g_parent_offset = sizeof(struct cromfs_volume_s);  /* Offset for '..' */ | 
|  |  | 
|  | /* We are going to traverse the new directory twice; the first time just | 
|  | * see if the directory is empty.  The second time is the real thing. | 
|  | */ | 
|  |  | 
|  | result = traverse_directory(g_dirname, dir_notempty, NULL); | 
|  |  | 
|  | /* Generate the '.' link for the root directory (it can't have a '..') */ | 
|  |  | 
|  | gen_dirlink(".", g_diroffset, result == 0); | 
|  | if (result != 0) | 
|  | { | 
|  | /* Then traverse each entry in the directory, generating node data for | 
|  | * each directory entry encountered. | 
|  | */ | 
|  |  | 
|  | traverse_directory(g_dirname, process_direntry, NULL); | 
|  | } | 
|  |  | 
|  | /* Now append the volume header to output file */ | 
|  |  | 
|  | fprintf(g_outstream, "/* CROMFS image */\n\n"); | 
|  | fprintf(g_outstream, "const uint8_t aligned_data(4) g_cromfs_image[] =\n"); | 
|  | fprintf(g_outstream, "{\n"); | 
|  | fprintf(g_outstream, "  /* Offset %6lu:  Volume header */\n\n", 0ul); | 
|  |  | 
|  | vol.cv_magic    = TGT_UINT32(CROMFS_MAGIC); | 
|  | vol.cv_nnodes   = TGT_UINT16(g_nnodes); | 
|  | vol.cv_nblocks  = TGT_UINT16(g_nblocks); | 
|  | vol.cv_root     = TGT_UINT32(sizeof(struct cromfs_volume_s)); | 
|  | vol.cv_fsize    = TGT_UINT32(g_offset); | 
|  | vol.cv_bsize    = TGT_UINT32(CROMFS_BLOCKSIZE); | 
|  |  | 
|  | dump_hexbuffer(g_outstream, &vol, sizeof(struct cromfs_volume_s)); | 
|  | dump_nextline(g_outstream); | 
|  | fprintf(g_outstream, "\n  /* Offset %6lu:  Root directory */\n", | 
|  | (unsigned long)sizeof(struct cromfs_volume_s)); | 
|  |  | 
|  | /* Finally append the nodes to the output file */ | 
|  |  | 
|  | append_tmpfile(g_outstream, g_tmpstream); | 
|  | fprintf(g_outstream, "};\n"); | 
|  |  | 
|  | fclose(g_outstream); | 
|  | #ifndef USE_MKSTEMP | 
|  | unlink_tmpfiles(); | 
|  | #endif | 
|  | return 0; | 
|  | } |