int read_chunk(void) { ssize_t s, len, offset; chunk_no++; len = chunk_size + spare_size; offset = 0; memset(chunk_data, 0xff, sizeof(chunk_data)); if (buf_len > buf_idx) { /* copy from buffer */ s = buf_len - buf_idx; if (s > len) s = len; memcpy(data, buffer+buf_idx, s); buf_idx += s; offset += s; } if (offset < len) { /* read from file */ s = xread(img_file, data+offset, len-offset); if (s < 0) prt_err(1, errno, "Read image file"); offset += s; } if (offset != 0 && offset != len) /* partial chunk */ prt_err(1, 0, "Broken image file"); return offset != 0; }
int read_chunk(void) { ssize_t s, len, offset; chunk_no++; len = chunk_size + spare_size; offset = 0; memset(data, 0xff, len); if (buf_len > buf_idx) { /* copy from buffer */ s = buf_len - buf_idx; if (s > len) s = len; memcpy(data, buffer+buf_idx, s); buf_idx += s; offset += s; } if (offset < len) { /* read from file */ s = safe_read(img_file, data+offset, len-offset); if (s < 0) prt_err(1, errno, "Read image file"); offset += s; } if (offset != 0 && offset != len) /* partial chunk */ prt_err(1, 0, "Broken image file"); if (offset == len && spare_off != 0) { /* bad block info */ memmove(data+chunk_size, data+chunk_size+spare_off, spare_size-spare_off); memset(data+len-spare_off, 0xff, spare_off); } return offset != 0; }
/* * save_lchown - call lchown and check result */ static void safe_lchown(const char *path, uid_t owner, gid_t group) { if (lchown(path, owner, group) < 0) { if (errno == EPERM || errno == EINVAL) warn_chown = 1; else prt_err(1, errno, "Can't chown %s", path); } }
void detect_flash_layout(int show, int first) { int cnt; int chunk, spare, off; memset(buffer, 0xff, sizeof(buffer)); buf_len = safe_read(img_file, buffer, sizeof(buffer)); if (buf_len < 0) prt_err(1, errno, "Read image file"); if (show) printf("Detected flash layout(s):\n"); cnt = 0; for (chunk = MIN_CHUNK_SIZE; chunk <= MAX_CHUNK_SIZE; chunk *= 2) { for (spare = MIN_SPARE_SIZE; spare <= MAX_SPARE_SIZE; spare += 16) { for (off = 0; off <= 2; off += 2) { if (check_layout(chunk, spare, off)) { cnt++; if (show) { printf("%2s -c %-2d -s %-3d : chunk size = %2dK, spare size = %3d, %sbad block info\n", off ? "-b" : "", chunk / 1024, spare, chunk / 1024, spare, off ? "" : "no "); } if (first) { chunk_size = chunk; spare_size = spare; spare_off = off; return; } } } } } if (cnt == 0) { if (show) { printf("-- none --\n"); exit(1); } else { prt_err(1, 0, "Can't determine flash layout, perhaps not a yaffs2 image"); } } }
void detect_chunk_size(void) { yaffs_ObjectHeader *oh; yaffs_PackedTags2 *pt, *pt2; int i; memset(buffer, 0xff, sizeof(buffer)); buf_len = xread(img_file, buffer, sizeof(buffer)); if (buf_len < 0) prt_err(1, errno, "Read image file"); oh = (yaffs_ObjectHeader *)buffer; if (oh->parentObjectId != YAFFS_OBJECTID_ROOT || (oh->type != YAFFS_OBJECT_TYPE_FILE && oh->type != YAFFS_OBJECT_TYPE_DIRECTORY && oh->type != YAFFS_OBJECT_TYPE_SYMLINK && oh->type != YAFFS_OBJECT_TYPE_HARDLINK && oh->type != YAFFS_OBJECT_TYPE_SPECIAL)) prt_err(1, 0, "Not a yaffs2 image"); for (i = 0; i < max_layout; i++) { pt = (yaffs_PackedTags2 *) (buffer + possible_layouts[i].chunk_size); pt2 = (yaffs_PackedTags2 *) (buffer + 2 * possible_layouts[i].chunk_size + possible_layouts[i].spare_size); if (pt->t.byteCount == 0xffff && pt->t.chunkId == 0 && ((pt2->t.byteCount == 0xffff && pt2->t.chunkId == 0) || (pt2->t.objectId == pt->t.objectId && pt2->t.chunkId == 1))) break; } if (i >= max_layout) prt_err(1, 0, "Can't determine chunk size"); chunk_size = possible_layouts[i].chunk_size; spare_size = possible_layouts[i].spare_size; if (opt_verbose) fprintf(stderr, "Header check OK, chunk size = %d, spare size = %d.\n", chunk_size, spare_size); }
static void init_obj_list(void) { object *obj; unsigned idx; for (idx = 0; idx < HASH_SIZE; idx++) obj_list[idx] = NULL; last_dir_id = 0; obj = malloc(offsetof(object, path_name) + 2); if (obj == NULL) prt_err(1, 0, "Malloc struct object failed."); obj->id = YAFFS_OBJECTID_ROOT; obj->type = YAFFS_OBJECT_TYPE_DIRECTORY; obj->prev_dir_id = 0; obj->atime = obj->mtime = 0; strcpy(obj->path_name, "."); idx = obj->id % HASH_SIZE; obj->next = obj_list[idx]; obj_list[idx] = obj; }
int main(int argc, char **argv) { int ch; int layout = 0; /* handle command line options */ opt_list = 0; opt_verbose = 0; opt_skip = 0; while ((ch = getopt(argc, argv, "l:tsvVh?")) > 0) { switch (ch) { case 'l': if (optarg[0] < '0' || optarg[0] > '0' + max_layout || optarg[1] != '\0') usage(); layout = optarg[0] - '0'; break; case 't': opt_list = 1; break; case 'v': opt_verbose = 1; break; case 's': opt_skip = 1; break; case 'V': printf("V%s\n", VERSION); exit(0); break; case 'h': case '?': default: usage(); break; } } /* extract rest of command line parameters */ if ((argc - optind) < 1 || (argc - optind) > 2) usage(); if (strcmp(argv[optind], "-") == 0) { /* image file from stdin ? */ img_file = 0; } else { img_file = open(argv[optind], O_RDONLY); if (img_file < 0) prt_err(1, errno, "Open image file failed"); } if (layout == 0) { detect_chunk_size(); } else { chunk_size = possible_layouts[layout-1].chunk_size; spare_size = possible_layouts[layout-1].spare_size; } spare_data = data + chunk_size; // Skip first chunk if (opt_skip) lseek(img_file, chunk_size+spare_size, SEEK_SET); if ((argc - optind) == 2 && !opt_list) { if (mkdirpath(argv[optind+1]) < 0) prt_err(1, errno, "Can't mkdir %s", argv[optind+1]); if (chdir(argv[optind+1]) < 0) prt_err(1, errno, "Can't chdir to %s", argv[optind+1]); } umask(0); init_obj_list(); while (read_chunk()) { process_chunk(); } set_dirs_utime(); close(img_file); return 0; }
void process_chunk(void) { yaffs_ObjectHeader oh; yaffs_PackedTags2 *pt; object *obj, *eq_obj; int out_file, remain, s; oh = *(yaffs_ObjectHeader *)chunk_data; pt = (yaffs_PackedTags2 *)spare_data; if (pt->t.byteCount == 0xffffffff) /* empty object */ return; else if (pt->t.byteCount != 0xffff) { /* not a new object */ prt_err(0, 0, "Warning: Invalid header at chunk #%d, skipping...", chunk_no); if (++warn_count >= MAX_WARN) prt_err(1, 0, "Giving up"); return; } obj = add_object(&oh, pt); /* listing */ if (opt_verbose) prt_node(obj->path_name, &oh); else if (opt_list) printf("%s\n", obj->path_name); if (opt_list) { if (oh.type == YAFFS_OBJECT_TYPE_FILE) { remain = oh.fileSize; /* skip over data chunks */ while(remain > 0) { if (!read_chunk()) prt_err(1, 0, "Broken image file"); remain -= pt->t.byteCount; } } return; } switch(oh.type) { case YAFFS_OBJECT_TYPE_FILE: remain = oh.fileSize; out_file = creat(obj->path_name, oh.yst_mode & STD_PERMS); if (out_file < 0) prt_err(1, errno, "Can't create file %s", obj->path_name); while(remain > 0) { if (!read_chunk()) prt_err(1, 0, "Broken image file"); s = (remain < pt->t.byteCount) ? remain : pt->t.byteCount; if (xwrite(out_file, chunk_data, s) < 0) prt_err(1, errno, "Can't write to %s", obj->path_name); remain -= s; } close(out_file); lchown(obj->path_name, oh.yst_uid, oh.yst_gid); if ((oh.yst_mode & EXTRA_PERMS) != 0 && chmod(obj->path_name, oh.yst_mode) < 0) prt_err(0, errno, "Warning: Can't chmod %s", obj->path_name); break; case YAFFS_OBJECT_TYPE_SYMLINK: if (symlink(oh.alias, obj->path_name) < 0) prt_err(1, errno, "Can't create symlink %s", obj->path_name); lchown(obj->path_name, oh.yst_uid, oh.yst_gid); break; case YAFFS_OBJECT_TYPE_DIRECTORY: if (pt->t.objectId != YAFFS_OBJECTID_ROOT && mkdir(obj->path_name, oh.yst_mode & STD_PERMS) < 0) prt_err(1, errno, "Can't create directory %s", obj->path_name); lchown(obj->path_name, oh.yst_uid, oh.yst_gid); if ((pt->t.objectId == YAFFS_OBJECTID_ROOT || (oh.yst_mode & EXTRA_PERMS) != 0) && chmod(obj->path_name, oh.yst_mode) < 0) prt_err(0, errno, "Warning: Can't chmod %s", obj->path_name); break; case YAFFS_OBJECT_TYPE_HARDLINK: eq_obj = get_object(oh.equivalentObjectId); if (eq_obj == NULL) prt_err(1, 0, "Invalid equivalentObjectId %u in object %u (%s)", oh.equivalentObjectId, pt->t.objectId, oh.name); if (link(eq_obj->path_name, obj->path_name) < 0) prt_err(1, errno, "Can't create hardlink %s", obj->path_name); break; case YAFFS_OBJECT_TYPE_SPECIAL: if (mknod(obj->path_name, oh.yst_mode, oh.yst_rdev) < 0) { if (errno == EPERM || errno == EINVAL) prt_err(0, errno, "Warning: Can't create device %s", obj->path_name); else prt_err(1, errno, "Can't create device %s", obj->path_name); } lchown(obj->path_name, oh.yst_uid, oh.yst_gid); break; case YAFFS_OBJECT_TYPE_UNKNOWN: break; } /* set file date and time */ switch(oh.type) { case YAFFS_OBJECT_TYPE_FILE: case YAFFS_OBJECT_TYPE_SPECIAL: #ifdef HAS_LUTIMES case YAFFS_OBJECT_TYPE_SYMLINK: #endif set_utime(obj->path_name, oh.yst_atime, oh.yst_mtime); break; case YAFFS_OBJECT_TYPE_DIRECTORY: default: break; } }
static object *add_object(yaffs_ObjectHeader *oh, yaffs_PackedTags2 *pt) { object *obj, *parent; unsigned idx; obj = get_object(pt->t.objectId); if (pt->t.objectId == YAFFS_OBJECTID_ROOT) { if (obj == NULL) prt_err(1, 0, "Missing root object"); if (oh->type != YAFFS_OBJECT_TYPE_DIRECTORY) prt_err(1, 0, "Root object must be directory"); if (last_dir_id == 0) last_dir_id = YAFFS_OBJECTID_ROOT; } else { if (oh->type != YAFFS_OBJECT_TYPE_FILE && oh->type != YAFFS_OBJECT_TYPE_DIRECTORY && oh->type != YAFFS_OBJECT_TYPE_SYMLINK && oh->type != YAFFS_OBJECT_TYPE_HARDLINK && oh->type != YAFFS_OBJECT_TYPE_SPECIAL && oh->type != YAFFS_OBJECT_TYPE_UNKNOWN) prt_err(1, 0, "Illegal type %d in object %u (%s)", oh->type, pt->t.objectId, oh->name); if (oh->name[0] == '\0' || strchr(oh->name, '/') != NULL || strcmp(oh->name, ".") == 0 || strcmp(oh->name, "..") == 0) prt_err(1, 0, "Illegal file name %s in object %u", oh->name, pt->t.objectId); if (obj != NULL) prt_err(1, 0, "Duplicate objectId %u", pt->t.objectId); parent = get_object(oh->parentObjectId); if (parent == NULL) prt_err(1, 0, "Invalid parentObjectId %u in object %u (%s)", oh->parentObjectId, pt->t.objectId, oh->name); if (parent->type != YAFFS_OBJECT_TYPE_DIRECTORY) prt_err(1, ENOTDIR, "File %s can't be created in %s", oh->name, parent->path_name); obj = malloc(offsetof(object, path_name) + strlen(parent->path_name) + strlen(oh->name) + 2); if (obj == NULL) prt_err(1, 0, "Malloc struct object failed."); obj->id = pt->t.objectId; obj->type = oh->type; if (obj->type == YAFFS_OBJECT_TYPE_DIRECTORY) { obj->prev_dir_id = last_dir_id; last_dir_id = obj->id; } else obj->prev_dir_id = 0; if (strcmp(parent->path_name, ".") == 0) { strcpy(obj->path_name, oh->name); } else { strcpy(obj->path_name, parent->path_name); strcat(obj->path_name, "/"); strcat(obj->path_name, oh->name); } idx = obj->id % HASH_SIZE; obj->next = obj_list[idx]; obj_list[idx] = obj; } obj->atime = oh->yst_atime; obj->mtime = oh->yst_mtime; return obj; }
int main(int argc, char **argv) { int ch; char *ep; int opt_detect; int opt_bad; int opt_chunk; int opt_spare; /* handle command line options */ opt_detect = 0; opt_bad = 0; opt_chunk = 0; opt_spare = 0; opt_list = 0; opt_verbose = 0; while ((ch = getopt(argc, argv, "dbc:s:tvVh?")) > 0) { switch (ch) { case 'd': opt_detect = 1; break; case 'b': opt_bad = 1; break; case 'c': opt_chunk = strtol(optarg, &ep, 0); if (*ep != '\0' || opt_chunk < 0 || opt_chunk > (MAX_CHUNK_SIZE / 1024) ) usage(); break; case 's': opt_spare = strtol(optarg, &ep, 0); if (*ep != '\0' || opt_spare < 0 || opt_spare > MAX_SPARE_SIZE) usage(); break; case 't': opt_list = 1; break; case 'v': opt_verbose = 1; break; case 'V': printf("V%s\n", VERSION); exit(0); break; case 'h': case '?': default: usage(); break; } } /* extract rest of command line parameters */ if ((argc - optind) < 1 || (argc - optind) > 2) usage(); if (strcmp(argv[optind], "-") == 0) { /* image file from stdin ? */ img_file = 0; } else { img_file = open(argv[optind], O_RDONLY); if (img_file < 0) prt_err(1, errno, "Open image file failed"); } if (opt_detect) { detect_flash_layout(1, 0); return 0; } if (opt_chunk == 0 || opt_spare == 0) { detect_flash_layout(0, 1); if (opt_verbose) prt_err(0, 0, "Header check OK, chunk size = %dK, spare size = %d, %sbad block info.", chunk_size/1024, spare_size, spare_off ? "" : "no "); } else { chunk_size = opt_chunk * 1024; spare_size = opt_spare; spare_off = opt_bad ? 2 : 0; } spare_data = data + chunk_size; if ((argc - optind) == 2 && !opt_list) { if (mkdirpath(argv[optind+1], 0755) < 0) prt_err(1, errno, "Can't mkdir %s", argv[optind+1]); if (chdir(argv[optind+1]) < 0) prt_err(1, errno, "Can't chdir to %s", argv[optind+1]); } umask(0); init_obj_list(); saved_chunk.objectId = 0; while (read_chunk()) { process_chunk(); } set_dirs_utime(); close(img_file); if (warn_chown) #ifdef __CYGWIN__ prt_err(0, 0, "Warning: Can't restore owner/group attribute (limitation of Cygwin/Windows)"); #else prt_err(0, 0, "Warning: Can't restore owner/group attribute, run unyaffs as root"); #endif return 0; }