#include #include #include #include #include #include #include #include #include "file.h" #include "tree.h" #include "file_src.h" #include "messages.h" #include "ecma119.h" //TODO: refactor both hash and this hash table into a single one?? struct iso_file * iso_file_new(struct ecma119_write_target *t, struct iso_tree_node_file *f) { struct iso_file *file = calloc(1, sizeof(struct iso_file)); if (!file) return NULL; if (f->node.procedence == LIBISO_NEW) { file->path = f->loc.path; /*TODO strdup? it needs to be free on clear then */ file->real_dev = f->node.attrib.st_dev; file->real_ino = f->node.attrib.st_ino; file->src = iso_file_src_from_path(file->path); } else { file->block = f->loc.block; file->prev_img = 1; file->real_dev = 0; /* we use 0 as dev for prev. session files */ /* don't take care about inode number read from RR TX, block * number is good enouht for this. Moreover, when we are modifying * an image, we will modify file->block with the block where the * file needs to be written in the new image. So, we store the block * in original image here */ file->real_ino = f->loc.block; file->src = iso_file_src_from_prev_img(t->src, f->loc.block, f->node.attrib.st_size); } if (!file->src) goto creation_error; file->size = file->src->get_size(file->src); if (file->size != f->node.attrib.st_size) { char msg[PATH_MAX + 32]; /* can only happen on path files */ assert(f->node.procedence == LIBISO_NEW); sprintf(msg, "Size of file %s has changed\n", file->path); iso_msg_sorry(LIBISO_CANT_READ_FILE, msg); } file->nlink = 1; file->sort_weight = f->sort_weight; return file; creation_error:; free(file); return NULL; } static unsigned int iso_file_table_hash(const char *path) { unsigned int hash_num=0; const char *c; c=path; while(*c) hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++; return hash_num % FILE_HASH_NODES; } static inline unsigned int iso_file_table_hash_inode(dev_t dev, ino_t ino) { return (dev ^ ino) % FILE_HASH_NODES; } struct iso_file_table* iso_file_table_new(int cache_inodes) { struct iso_file_table *table = calloc(1, sizeof(struct iso_file_table)); table->cache_inodes = cache_inodes; return table; } static struct iso_file_hash_node * iso_file_table_node_new(struct iso_file *file) { struct iso_file_hash_node *node; node = calloc(1, sizeof(struct iso_file_hash_node) ); node->file = file; return node; } static void iso_file_table_node_free(struct iso_file_hash_node *node) { iso_file_src_free(node->file->src); free(node->file); free(node); } void iso_file_table_clear(struct iso_file_table *ft) { int i; for (i=0; i < FILE_HASH_NODES; i++) { struct iso_file_hash_node *node; node=ft->table[i]; if (!node) continue; ft->table[i] = NULL; do { struct iso_file_hash_node *next; next = node->next; iso_file_table_node_free(node); node = next; } while (node); } ft->count = 0; } /** * return 0 if equal, != 0 if not */ static int iso_table_compare_files(struct iso_file_table *ft, struct iso_file *f1, struct iso_file *f2) { assert(ft && f1 && f2); if (f1->prev_img || f2->prev_img) { if (f1->prev_img && f2->prev_img) return f1->real_ino != f2->real_ino; else return 1; } if (ft->cache_inodes) { return (f1->real_dev != f2->real_dev) || (f1->real_ino != f2->real_ino); } else { return strcmp(f1->path, f2->path); } } int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f) { struct iso_file_hash_node *node; unsigned int hash_num; assert(ft && f); /* find the hash number */ if (f->prev_img) hash_num = f->real_ino % FILE_HASH_NODES; else if (ft->cache_inodes) hash_num = iso_file_table_hash_inode(f->real_dev, f->real_ino); else hash_num = iso_file_table_hash(f->path); /* insert it */ node = ft->table[hash_num]; /* unfortunately, we can't safely consider that a file * won't be twice in the hash table so make sure it * doesn't already exists */ if (!node) { ft->table[hash_num]=iso_file_table_node_new(f); ft->count++; return 1; } /* if it's already in, we don't do anything */ if (!iso_table_compare_files(ft, f, node->file)) return 0; while (node->next) { node = node->next; /* if it's already in, we don't do anything */ if (!iso_table_compare_files(ft, f, node->file)) return 0; } node->next = iso_file_table_node_new(f); ft->count++; return 1; } /** 0 on equal, != 0 otherwise */ static int iso_table_compare_node_file(struct iso_file_table *ft, struct iso_tree_node_file *f1, struct iso_file *f2) { assert(ft && f1 && f2); if (f1->node.procedence || f2->prev_img) { if (f1->node.procedence && f2->prev_img) return f1->loc.block != f2->real_ino; else return 1; } if (ft->cache_inodes) { return (f1->node.attrib.st_dev != f2->real_dev) || (f1->node.attrib.st_ino != f2->real_ino); } else { return strcmp(f1->loc.path, f2->path); } } struct iso_file * iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f) { struct iso_file_hash_node *node; unsigned int hash_num; int equal; assert(ft && f); /* find the hash number */ if (f->node.procedence == LIBISO_PREVIMG) hash_num = f->loc.block % FILE_HASH_NODES; else if ( ft->cache_inodes ) hash_num = iso_file_table_hash_inode(f->node.attrib.st_dev, f->node.attrib.st_ino); else hash_num = iso_file_table_hash(f->loc.path); node = ft->table[hash_num]; if (!node) return NULL; equal = !iso_table_compare_node_file(ft, f, node->file); if (equal) return node->file; while (node->next) { node = node->next; equal = !iso_table_compare_node_file(ft, f, node->file); if (equal) return node->file; } return NULL; }