/** * @file tarfs_tar.c * * Contains routines working with tar format * With a little work it can be used in other programs reading tar files * * @author Jaroslav Drazan * @author Petr Cermak */ /* ************************************************************************* * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * ************************************************************************* */ #include "tarfs_tar.h" #include "tarfs_util.h" #include "tarfs_common.h" #include /** * One block of tar */ typedef struct posix_header { /* byte offset */ char name[100]; /* 0 */ char mode[8]; /* 100 */ char uid[8]; /* 108 */ char gid[8]; /* 116 */ char size[12]; /* 124 */ char mtime[12]; /* 136 */ char chksum[8]; /* 148 */ char typeflag; /* 156 */ char linkname[100];/* 157 */ char magic[6]; /* 257 */ char version[2]; /* 263 */ char uname[32]; /* 265 */ char gname[32]; /* 297 */ char devmajor[8]; /* 329 */ char devminor[8]; /* 337 */ char prefix[155]; /* 345 */ char pad [12]; /* 500 */ } posix_header; #define TMAGIC "ustar"/**< ustar and a null */ #define TMAGLEN 6 #define TVERSION "00" /**< 00 and no null */ #define TVERSLEN 2 /* Values used in typeflag field. */ #define REGTYPE '0' /**< regular file */ #define AREGTYPE '\0' /**< regular file */ #define LNKTYPE '1' /**< link */ #define SYMTYPE '2' /**< reserved */ #define CHRTYPE '3' /**< character special */ #define BLKTYPE '4' /**< block special */ #define DIRTYPE '5' /**< directory */ #define FIFOTYPE '6' /**< FIFO special */ #define CONTTYPE '7' /**< reserved */ /* Bits used in the mode field, values in octal. */ #define TSUID 04000 /**< set UID on execution */ #define TSGID 02000 /**< set GID on execution */ #define TSVTX 01000 /**< reserved */ /* file permissions */ #define TUREAD 00400 /**< read by owner */ #define TUWRITE 00200 /**< write by owner */ #define TUEXEC 00100 /**< execute/search by owner */ #define TGREAD 00040 /**< read by group */ #define TGWRITE 00020 /**< write by group */ #define TGEXEC 00010 /**< execute/search by group */ #define TOREAD 00004 /**< read by other */ #define TOWRITE 00002 /**< write by other */ #define TOEXEC 00001 /**< execute by other */ /** * @brief For given inode number, returns offset of its header block * * @param X Inode number * @return Offset of header block */ #define OFFSET_BY_INO(X) ((X) - 1) /** * @brief Controls if given block is a valid tar block * * Controls the magic and checksum fields * * @param blok Block to be checked * @return 1 - OK, 0 - error */ int is_tar(posix_header* blok) { int j = 0; unsigned int checksum = 0; //kontrola spravnosti bloku //zajimave, TMAGIC a TVERSION nak nesouhlasej (asi se ponek zmenil format), //takze tomu trochu pomuzu if (memcmp(blok->magic, TMAGIC, TMAGLEN - 1)) { return 0; } //kontrola kontrolniho souctu - soucet bytu hlavicky az na checksum(ten se bere jako '') for (j = 0; j < sizeof(posix_header); j++){ checksum += ((unsigned char*)(blok))[j]; } checksum += 8 * ' '; for (j = 0; j < 8; j++) { checksum -= ((unsigned char*) blok->chksum)[j]; } checksum -= oct_to_dec(blok->chksum, 6);//6 staci - vic stejne nejde //nevychazi kontrolni soucet if(checksum) { return 0; } return 1; } /** * @brief Performs quick valiadtion of tar * * This function controls only the first block * * @param s Super block structure needed to perform i/o operations * @return 1 - OK, 0 - is not tar, or other error */ int tar_quick_validation(struct super_block* s) { posix_header* blok; int ret_value; blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL); if (blok == 0 || s == 0) { return 0; } if (!read_block(s, 0, blok)) { kfree(blok); return 0; } ret_value = is_tar(blok); kfree(blok); return ret_value; } /** * @brief Helper routine for @a tar_get_root_inode * * @return Root inode number, TARFS_EVAL if any error occures */ unsigned long get_root_ino_pom(posix_header* blok) { if (!blok) { return TARFS_EVAL; } // je tam zabaleny i ./ if(blok->name[0] == '.' && blok->name[1] == '/' && blok->typeflag == DIRTYPE) { return 1; } return TARFS_ROOT_INO; } /** * @brief Returns number of root inode * * @param s Super block structure needed to perform i/o operations * @return Number of root inode, TARFS_EVAL - error */ unsigned long tar_get_root_ino(struct super_block* s) { unsigned long ret_val; unsigned long size; unsigned long i = 0; posix_header* blok; if(!(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL))) { return -1; } while (read_block(s, i, blok)) { if (!is_tar(blok)) { //asi jsem na konci taru (nebo je archive poskozenej, nebo to tu neni) goto EOut1; } switch(blok->typeflag) { default : goto EOut1; break; case 'L' : //dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); i += hcc(size, TARFS_BLKSIZE); i++; break; case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE :;//znakove zarizeni case BLKTYPE :;//blokove zarizeni case DIRTYPE :;//adresar case FIFOTYPE: ret_val = get_root_ino_pom(blok); kfree(blok); return ret_val; break; } } // errors EOut1: kfree(blok); return TARFS_EVAL; } /** * @brief Returns number of blocks in tar * * @param s Super block structure needed to perform i/o operations * @return Number of blocks, 0 - error (or empty) */ unsigned long tar_length(struct super_block* s) { unsigned long i = 0; unsigned long size = 0; posix_header *blok; //rychlou validaci delam proto, abych objevil, ze dany soubor je //"prazdny tar", tj. dva nulove bloky if (!tar_quick_validation(s)) { goto EOut1; } blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL); if(!blok) { goto EOut1; } while (read_block(s, i, blok)) { if (!is_tar(blok)) { //asi jsem na konci taru (to jest, jsou tu 2 nulove bloky) if (is_nullblock(blok, TARFS_BLKSIZE) && read_block(s, i + 1, blok)) { if(is_nullblock(blok,TARFS_BLKSIZE)) { kfree(blok); //je to tar; return i + 1;//vracim delku } else { //neni to tar break; } } //neni to tar break; } size = oct_to_dec(blok->size, TARFS_NUMLENGTH); //preskocim datove bloky i += hcc(size,TARFS_BLKSIZE); i++; } kfree(blok); EOut1: //neni to tar return 0; } /** * @brief Helper routine, reads filename of file starting at given position * * @param s Super block structure needed to perform i/o operations * @param file_name [out] Filename of file starting at offset. (Allocated) * @param size Size of filename buffer * @param offset Where to start reading from * @return 1 - OK, 0 - Error */ int pom_read_name(struct super_block* s, char** file_name, unsigned long size, unsigned long offset) { unsigned long i; unsigned long block_count; void* blok; if (!(blok = kmalloc(TARFS_BLKSIZE * sizeof(char), GFP_KERNEL))) { goto EOut3; } if (!(*file_name = (char*) kmalloc((size + 1) * sizeof(char), GFP_KERNEL))) { goto EOut2; } block_count = hcc(size, TARFS_BLKSIZE); if (block_count < 1) { goto EOut1; } for ( i = 0; i < block_count; i++) { if (!read_block(s, offset + i, blok)) { goto EOut1; } memcpy((*file_name) + TARFS_BLKSIZE * i, blok, (unsigned int) ((size - TARFS_BLKSIZE * i < TARFS_BLKSIZE) ? size - TARFS_BLKSIZE * i : TARFS_BLKSIZE)); } (*file_name)[size] = 0; //ukoncim retezec kfree(blok); return 1; // errors EOut1: kfree(*file_name); EOut2: kfree(blok); EOut3: return 0; } /** * @brief Searches for given file in tar * * @param s Super block structure needed to perform i/o operations * @param name Name of file to be found (zero-terminated) * @see offset_by_name2 * @return Position, where desired file is located, TARFS_EVAL if any error occures */ unsigned long offset_by_name(struct super_block* s, char* name) { char* file_name; unsigned long size; unsigned long correction = 0; posix_header* blok; unsigned long i = 0; int skip = 0; //skip rika, zda automaticky preskocit dalsi header blok taru if (!name || *name == 0) { return TARFS_EVAL; } /* if(!strcmp(name,"/")) { //korenovy adresar dostane 0; return 0; } */ if (! (blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL))) { goto EOut2; } while (read_block(s, i, blok)) { if(!is_tar(blok)) { //asi jsem na konci taru (nebo je archive poskozenej, nebo to tu neni) goto EOut1; } if (skip) { size = oct_to_dec(blok->size, TARFS_NUMLENGTH); //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); i++; skip = 0; continue; } switch (blok->typeflag) { default : //nepodporovany/spatny format goto EOut1; break; case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); if (size == 0) { //neni to tar goto EOut1; } size--; //preskocim datove bloky correction = hcc(size,TARFS_BLKSIZE) + 1; i += hcc(size, TARFS_BLKSIZE); break; case 'L' : // dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor size = oct_to_dec(blok->size, TARFS_NUMLENGTH); if (size == 0) { //neni to tar goto EOut1; } size--; if (pom_read_name(s, &file_name, size, i + 1)) { if (!file_name) { goto EOut1; } if(name_comparator(file_name, name, size)) { //nasel jsem to:-) kfree(file_name); kfree(blok); return i - correction; } if (file_name) { kfree(file_name); } skip = 1;//dalsi bude pomocny header tehoz souboru //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); correction = 0; break; } case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE :;//znakove zarizeni case BLKTYPE :;//blokove zarizeni case DIRTYPE :;//adresar case FIFOTYPE:;//pojmenovana pipe if (name_comparator(blok, name, 100)) { //uspech kfree(blok); return i - correction; } else { size = oct_to_dec(blok->size, TARFS_NUMLENGTH); //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); correction = 0; break; } break; }//konec switch i++; } // errors EOut1: kfree(blok); //tak sem bych se dostat nikdy nemel, jestli jo, tak je vadnej archive EOut2: return TARFS_EVAL; } /** * @brief Does the same thing as @a offset_by_name, with some changes * * This function searches from the position pos and * takes a bit different parameters * * @param s Super block structure needed to perform i/o operations * @param pos Position where to start search from * @param name Name of file to be found (doesn't have to be zero-terminated) * @param fsize Length of name * @see offset_by_name * @return Position, where desired file is located, TARFS_EVAL if any error occures */ unsigned long offset_by_name2(struct super_block* s, unsigned long pos, char* name, unsigned int fsize) { char* file_name; unsigned long size; unsigned long correction = 0; posix_header *blok; unsigned long i = pos; int skip = 0; //skip rika, zda automaticky preskocit dalsi header blok taru if (!name || *name == 0) { goto EOut2; } blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL); while (read_block(s, i, blok)) { if(!is_tar(blok)){ //asi jsem na konci taru (nebo je archive poskozenej, nebo to tu neni) goto EOut1; } if (skip) { size = oct_to_dec(blok->size, TARFS_NUMLENGTH); //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); i++; skip = 0; continue; } switch(blok->typeflag) { default : //nepodporovany/spatny format goto EOut1; break;//break pro soumernost :-) case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); if (size == 0) { goto EOut1; } size--; //preskocim datove bloky correction = hcc(size, TARFS_BLKSIZE) + 1; i += hcc(size, TARFS_BLKSIZE); break; case 'L' : // dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor size = oct_to_dec(blok->size, TARFS_NUMLENGTH); if (size == 0) { goto EOut1; } size--; if (pom_read_name(s, &file_name, size, i + 1)) { if (!file_name) { goto EOut1; } if (name_comparator2(file_name, name, size, fsize)) { //nasel jsem to:-) kfree(file_name); kfree(blok); return i - correction; } if (file_name) { kfree(file_name); } //dalsi bude pomocny header tehoz souboru skip = 1; //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); correction = 0; break; } case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE :;//znakove zarizeni case BLKTYPE :;//blokove zarizeni case DIRTYPE :;//adresar case FIFOTYPE:;//pojmenovana pipe size = short_name_size(blok->name); if (name_comparator2(blok->name, name, size, fsize)) { //uspech kfree(blok); return i - correction; } else { size = oct_to_dec(blok->size, TARFS_NUMLENGTH); //preskocim datove bloky i += hcc(size, TARFS_BLKSIZE); correction = 0; } break; } //konec switch i++; } //tak sem bych se dostat nikdy nemel, jestli jo, tak je vadnej archive // errors EOut1: kfree(blok); EOut2: return TARFS_EVAL; } /** * @brief Returns position, where real datablock is stored * * This function has meaning only for skipping blocks with * type 'K' or 'L'. Otherwise use @a find_nolink * * @param s Super block structure needed to perform i/o operations * @param whence Position where to start searching from * @return Position of datablock, TARFS_EVAL if any error occures * @see find_nolink */ unsigned long skip_blocks(struct super_block* s, unsigned long whence) { unsigned long size; unsigned long i = whence; posix_header* blok; if (!(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL))) { goto EOut2; } while (read_block(s, i, blok)) { if (!is_tar(blok)){ //jsem na konci taru => archive je poskozeny goto EOut1; } switch (blok->typeflag) { default : goto EOut1; break; case 'L' : //dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); i += hcc(size, TARFS_BLKSIZE) + 1; break; case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE :;//znakove zarizeni case BLKTYPE :;//blokove zarizeni case DIRTYPE :;//adresar case FIFOTYPE: kfree(blok); return i; break; } } // errors EOut1: kfree(blok); EOut2: return TARFS_EVAL; } /** * @brief Converts from types of tar inodes to types defined in fs.h * * @param blok A tar block to be examined * @param type [out] Type of block * @return 0 - OK, -1 otherwise */ int get_type(posix_header* blok, unsigned* type) { switch(blok->typeflag) { default : //nechci ani 'K' a 'L' *type = DT_UNKNOWN; return -1; break; case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink *type = DT_REG; break; case SYMTYPE ://symlink, vratim info o symlinku *type = DT_LNK; break; case CHRTYPE ://znakove zarizeni *type = DT_CHR; break; case BLKTYPE ://blokove zarizeni *type = DT_BLK; break; case DIRTYPE ://adresar *type = DT_DIR; break; case FIFOTYPE: //pojmenovana pipe *type = DT_FIFO; break; } return 0; } /** * @brief Reads inode from tar * * Fills inode data like times, acces rights, size of file * * @param s Super block structure needed to perform i/o operations * @param inode Inode structure to be filled * @param rdev [out] If inode type is device, this parameter is filled with value * kdev_t_to_nr(major, minor) * @return Type of inode, as defined in stat.h (S_IFREG, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFDIR), 0 in case of error */ int tar_fill_inode(struct super_block* s, struct inode* inode, int* rdev) { unsigned long offset; unsigned long size = 0; int should_continue = 1; char* link_name = 0; int ret_type = 0; posix_header* blok; // root. Na to tu mame jinou funkci, ktera se mela zavolat if (inode->i_ino == TARFS_ROOT_INO) { goto EOut3; } offset = OFFSET_BY_INO(inode->i_ino); blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL); if (!blok) { goto EOut3; } while (should_continue) { if ( !read_block(s, offset, blok) || !is_tar(blok) ) { goto EOut1; } switch (blok->typeflag) { case 'L' : //dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor offset = skip_blocks(s, offset); if (offset == TARFS_EVAL) { goto EOut1; } break; case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); size--; if (!pom_read_name(s, &link_name, size, offset + 1)) { goto EOut2; } offset = skip_blocks(s, offset); if (offset == TARFS_EVAL) { goto EOut2; } break; case REGTYPE : //normalni soubor - alternativni kod case AREGTYPE: //normalni soubor - preferovany kod //dodej si typ souboru ret_type = S_IFREG; should_continue = 0; break; case LNKTYPE : //jako typ souboru si dej normal file if (!link_name) { //jeste jsem to nenacetl size = short_name_size(blok->linkname); if(!(link_name = (char*) kmalloc((size + 1) * sizeof(char), GFP_KERNEL))) { goto EOut2; } memcpy(link_name, blok->linkname, (unsigned int) size); link_name[size] = 0; } offset = offset_by_name(s, link_name); kfree(link_name); if (offset == TARFS_EVAL) { goto EOut2; } link_name = 0; size = 0; break; case SYMTYPE : if(link_name) { inode->i_size = strlen(link_name);//linkname by mel byt null-terminated kfree(link_name); } else { inode->i_size = short_name_size(blok->linkname); } ret_type = S_IFLNK; should_continue = 0; break; case CHRTYPE : //znakove zarizeni ret_type = S_IFCHR; *rdev = kdev_t_to_nr(MKDEV((int) oct_to_dec(blok->devmajor, 7), (int) oct_to_dec(blok->devminor, 7))); should_continue = 0; break; case BLKTYPE : //blokove zarizeni ret_type = S_IFBLK; *rdev = kdev_t_to_nr(MKDEV((int) oct_to_dec(blok->devmajor, 7), (int) oct_to_dec(blok->devminor, 7))); should_continue = 0; break; case FIFOTYPE: //pojmenovana pipe //zatim nevim, jak vyplnit ret_type = S_IFIFO; should_continue = 0; break; case DIRTYPE : //adresar ret_type = S_IFDIR; should_continue = 0; break; default : if(link_name) { kfree(link_name); } kfree(blok); return 0; break; } } //nacetl jsem typove zavisle udaje, takze nactu jeste to, co je spolecne //nactu mtime(nic vic posix_verze taru neobsahuje - mimo to, jsme read only, //takze na tom tolik nezalezi inode->i_ctime = (time_t) inode->i_atime = inode->i_mtime = oct_to_dec(blok->mtime, TARFS_NUMLENGTH); //nactu size, pouze pokud to neni symlink.. ten ma jinou velikost if (blok->typeflag != SYMTYPE) { inode->i_size = (loff_t) oct_to_dec(blok->size, TARFS_NUMLENGTH); } //nactu gid inode->i_gid = (gid_t) oct_to_dec(blok->gid, 7); //nactu uid inode->i_uid = (uid_t) oct_to_dec(blok->uid, 7); //nactu mode inode->i_mode = (umode_t) oct_to_dec(blok->mode, 7); //jsme read_only, takze to staci (mimo to, zjisteni by trvalo v taru dlouho) inode->i_nlink = 1; kfree(blok); return ret_type; // errors EOut1: kfree(blok); EOut2: if (link_name != 0) { kfree(link_name); } EOut3: return 0; } /** * @brief Returns physical position of file of given inode * * If hardlink, it jumps to the linked position * * @param s Super block structure needed to perform i/o operations * @param ino Inode number of file to be found * @return Position of searched file, TARFS_EVAL in case of error */ unsigned long find_nolink(struct super_block* s, unsigned long ino) { unsigned long offset; char* link_name = 0; unsigned long size = 0; unsigned int linklen; posix_header* blok; // inode je offset + 1 offset = ino - 1; if( !(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL)) ) { goto EOut2; } while (1) { if (!read_block(s, offset, blok) || !is_tar(blok)) { if (link_name != 0) { kfree(link_name); } goto EOut1; } switch(blok->typeflag) { case 'L' : //dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor offset = skip_blocks(s, offset); if (offset == TARFS_EVAL) { goto EOut1; } break; case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link size = oct_to_dec(blok->size, TARFS_NUMLENGTH); size--; if (link_name != 0) { goto EOut1; } if (!pom_read_name(s, &link_name, size, offset + 1)) { goto EOut1; } offset = skip_blocks(s, offset); if (offset == TARFS_EVAL) { kfree(link_name); goto EOut1; } break; case LNKTYPE : if (!link_name) { //jeste jsem to nenacetl linklen = short_name_size(blok->linkname); if (!(link_name = (char*) kmalloc((linklen + 1) * sizeof(char), GFP_KERNEL))) { goto EOut1; } memcpy(link_name, blok->linkname, linklen); link_name[linklen] = 0; } offset = offset_by_name(s, link_name); kfree(link_name); if (offset == TARFS_EVAL) { goto EOut1; } link_name = 0; break; case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE ://znakove zarizeni case BLKTYPE ://blokove zarizeni case FIFOTYPE://pojmenovana pipe case DIRTYPE ://adresar if(link_name) { kfree(link_name); link_name = 0; } kfree(blok); return offset; break; default: if (link_name) { kfree(link_name); } goto EOut1; break; } } // errors EOut1: kfree(blok); EOut2: return TARFS_EVAL; } /** *@brief reads symlinks target * * @param s Super block structure needed to perform i/o operations * @param ino - inode number of symlink * @return NULL-terminated string which represents symlink's target 0 - error */ char* symlinkname(struct super_block* s, unsigned long ino) { unsigned long pos; unsigned long size; unsigned int linklen; char* ret_val; posix_header* blok; if (!(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL))) { goto EOut2; } pos = OFFSET_BY_INO(ino); if (!read_block(s, pos, blok) || !is_tar(blok)) { goto EOut1; } if (blok->typeflag == 'K') { size = oct_to_dec(blok->size, TARFS_NUMLENGTH); size--; if (!pom_read_name(s, &ret_val, size, pos + 1)) { goto EOut1; } pos = skip_blocks(s, pos + hcc(size, TARFS_BLKSIZE) + 1); if (!read_block(s, pos, blok) || !is_tar(blok) || blok->typeflag != SYMTYPE) { kfree(ret_val); goto EOut1; } kfree(blok); return ret_val; } if (blok->typeflag == SYMTYPE) { linklen = short_name_size(blok->linkname); if( !(ret_val = (char*) kmalloc(linklen * sizeof(char), GFP_KERNEL)) ) { goto EOut1; } memcpy(ret_val, blok->linkname, linklen); kfree(blok); return ret_val; } // errors EOut1: kfree(blok); EOut2: return 0; } /** * @brief Fills buffer with data from given inode * * Reads data from inode with given offset to the end of file or end of buffer (max PAGE_SIZE bytes) * * @param s Super block structure needed to perform i/o operations * @param inode Inode with data to be read * @param offset Offset of desired data * @param buffer Alredy allocated buffer of size PAGE_SIZE (PAGE_SIZE should be multiple of TARFS_BLKSIZE) * @return 0 - EOF, 1 - OK, 2 - Symlink, TARFS_EVAL otherwise */ unsigned long tar_read_page(struct super_block* s, struct inode* inode, loff_t offset, void* buffer) { char* blok; unsigned long diff; unsigned long start_shift; unsigned long end_shift; unsigned long pos; unsigned long j; char* linkname; if (offset > inode->i_size) { goto EOut2; } //korenovy adresar ma nulocou velikost, takze prectu 0 bytu if (inode->i_ino == TARFS_ROOT_INO) { return 0; } //do diff dam minimum z PAGE_SIZE a inode->i_size - offset, //tj. v diff je pocet bytu, ktere prectu diff = (unsigned long) (inode->i_size - offset); diff = (unsigned long) ((diff > PAGE_SIZE) ? PAGE_SIZE : diff); //kdyz nemam co cist, tak nebudu zbytecne cist ze souboru if (!diff) { return 0; } //pokud to neni symlink, nastavi linkname na 0 linkname = symlinkname(s, inode->i_ino); if (linkname) { memcpy(buffer, linkname + offset, (unsigned int) diff); memset((void*) (buffer + diff), 0, (unsigned int) (PAGE_SIZE - diff)); kfree(linkname); return 2; } blok = (char*) kmalloc(TARFS_BLKSIZE, GFP_KERNEL); if (!blok) { goto EOut2; } pos = find_nolink(s, inode->i_ino); if (pos == TARFS_EVAL) { goto EOut1; } pos++; //skocim na data start_shift = (unsigned long) (offset / TARFS_BLKSIZE); end_shift = hcc((unsigned long) offset + diff, TARFS_BLKSIZE); //pokud je offset zarovnany - asi nejcastejsi pripad, proto osetreno zvlast if (offset % TARFS_BLKSIZE == 0) { //nacte buffer for (j = start_shift; j < end_shift; j++) { if (!read_block(s, pos + j, blok)) { goto EOut1; } memcpy(buffer + TARFS_BLKSIZE * (j - start_shift), blok, TARFS_BLKSIZE); } //doplnim buffer nulama(abych nenacital nahodna data) memset((void*) (buffer + diff), 0, (unsigned int) (PAGE_SIZE - diff)); kfree(blok); return 1; } //jinak budu muset nacist o blok vic a uparavit okraje //else //nacte prostredek bufferu for (j = start_shift + 1; j < end_shift - 1; j++) { if (!read_block(s, pos + j, blok)) { goto EOut1; } memcpy((void*) (buffer + TARFS_BLKSIZE * (j - start_shift) - offset % TARFS_BLKSIZE), blok, TARFS_BLKSIZE); } //nactu zacatek bufferu if (!read_block(s, pos + start_shift, blok)) { goto EOut1; } memcpy(buffer, blok + offset % TARFS_BLKSIZE, (unsigned int) (TARFS_BLKSIZE - offset % TARFS_BLKSIZE)); //nactu konec bufferu if (!read_block(s, pos + end_shift - 1, blok)) { goto EOut1; } memcpy((void*) (buffer + TARFS_BLKSIZE * (j - start_shift) - offset % TARFS_BLKSIZE), blok, (unsigned int) (offset % TARFS_BLKSIZE)); //doplnim buffer nulama(abych nenacital nahodna data) memset((void*) (buffer + diff), 0, (unsigned int) (PAGE_SIZE - diff)); // vse nacteno OK kfree(blok); return 1; // errors EOut1: kfree(blok); EOut2: return TARFS_EVAL; } /** * @brief Reads directory entries * * Reads directory entries, until stopped by returning <0 from filldir function, * after that, it stores last entry id read into filp->f_pos. When called again, * it continues from the last f_pos stored. * * @param filp A file structure representing directory being read * @param dirent Falls through this function to the filldir call * @param filldir Pointer to a function called to add new dir entry * @return Count of directory entries read, TARFS_EVAL otherwise */ unsigned long tar_readdir(struct file* filp, void* dirent, filldir_t filldir) { unsigned long i; unsigned long pos; unsigned long dsize; unsigned long fsize; char* dir_name; char* fname; posix_header* blok = 0; unsigned long count = 0; struct super_block* s = filp->f_dentry->d_inode->i_sb; if ( !(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL)) ) { goto EOut4; } //nejprve musim nacist jmeno adresare (v taru se vse pamatuje uplnou cestou) //a pozici, kde mam zacit nacitat soubory if (filp->f_dentry->d_inode->i_ino == TARFS_ROOT_INO) { //pokud je to rootovsky adresar dir_name = (char*) kmalloc(sizeof(char), GFP_KERNEL); dir_name[0] = 0; dsize = 0; pos = 0; } else { pos = OFFSET_BY_INO(filp->f_dentry->d_inode->i_ino); if (!read_block(s, pos, blok)) { goto EOut3; } if (blok->typeflag != 'L' && blok->typeflag != DIRTYPE) { //pokud to neni adresar (nebo alespon potencialne adresar) goto EOut3; } //jestlize ma adresar dlouhe jmeno if (blok->typeflag == 'L') { dsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); dsize--; if (!pom_read_name(s, &dir_name, dsize, pos++)) { goto EOut3; } pos += hcc(dsize, TARFS_BLKSIZE); //jeste musim overit, ze jmeno, ktere jsem nacetl patri opravdu adresari if ( !read_block(s, pos, blok) ) { goto EOut2; } if (blok->typeflag != DIRTYPE) { goto EOut2; } pos++;//za ten prave nacteny blok } else { dsize = short_name_size(blok->name); //nulu na konec nedam if ( !(dir_name = (char*) kmalloc(dsize * sizeof(char), GFP_KERNEL)) ) { goto EOut3; } memcpy(dir_name, blok->name, (unsigned int) dsize); pos++; } } // docetl jsem jmeno adresare a pozici, // kde by meli zacit soubory toho adresare(pokud nejake ma) //zjistuji, jestli uz jsem nejake soubory nacetl a pokud ano, tak zmenim pos //ve f_pos je ulozen offset naposledy nactene polozky if (filp->f_pos > 0 && filp->f_pos < TARFS_PARENTDIR_FPOS && filp->f_pos <= (pos + 1)) { //chyba - to nelze goto EOut2; } // Pridam adresar "." if (filp->f_pos == 0) { //prvni cteni, musim si zapamatovat link na "." if (filldir(dirent, ".", 1, 0, filp->f_dentry->d_inode->i_ino, DT_DIR) < 0) { //dalsi soubory uz nemam cist goto Return_Count2; // v count je 0 } count++; filp->f_pos = TARFS_THISDIR_FPOS; } // Pridam adresar ".." if (filp->f_pos == TARFS_THISDIR_FPOS) { //zatim jsem nacetl '.', musim si zapamatovat link na ".." i = filp->f_dentry->d_parent->d_inode->i_ino; // ne-root if (filldir(dirent, "..", 2, 0, i, DT_DIR) < 0) { //dalsi soubory uz nemam cist goto Return_Count2; } count++; filp->f_pos = TARFS_PARENTDIR_FPOS; } if (filp->f_pos != TARFS_PARENTDIR_FPOS) { //uz jsem neco precetl, takze upravim pos //nejprve se dostanu na hlavni header block naposledy cteneho souboru pos = skip_blocks(s, (unsigned long) filp->f_pos - 1); if (pos == TARFS_EVAL || !read_block(s, pos, blok) || !is_tar(blok)) { goto EOut2; } pos += hcc(oct_to_dec(blok->size, TARFS_NUMLENGTH), TARFS_BLKSIZE) + 1; } //ted vim, kde zacit cist jmena souboru (od pos) while (read_block(s, pos, blok) && is_tar(blok)) { unsigned long pom_fsize; int rv; unsigned type; switch(blok->typeflag) { default: goto EOut2; break; case 'L' : //dalsi blok bude jmeno souboru, pak bude nasledovat ten soubor fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH) - 1; //tar - nevim proc to zvetsuje o 1 if (!pom_read_name(s, &fname, fsize, pos + 1)) { goto EOut2; } if ( !(rv = is_right_prefix(dir_name, dsize, fname, fsize)) ) { //dalsi soubory uz nejsou soucasti tohohle adresare //nechavam filp->f_pos nedotceno goto Return_Count1; } if (rv < 0) { //zanoril jsem se moc, musim preskakat ty soubory pos += hcc(fsize, TARFS_BLKSIZE) + 1; if( !read_block(s, pos, blok) || !is_tar(blok) ) { goto EOut1; } filp->f_pos = (loff_t) pos + 1; //abych ho uz nezkoumal fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); pos += hcc(fsize, TARFS_BLKSIZE) + 1; break; } if (fname[fsize - 1] == '/') { pom_fsize = fsize - 1; } else { pom_fsize = fsize; } //posunu se na hlavni blok souboru; pos += hcc(fsize, TARFS_BLKSIZE) + 1; if ( !read_block(s, pos, blok) || !is_tar(blok) ) { goto EOut1; } if (get_type(blok, &type) < 0) { goto EOut1; } //pridavam jmeno if (filldir(dirent, fname + dsize, pom_fsize - dsize, 0, pos + 1, type) < 0) { //dalsi soubory uz nemam cist goto Return_Count1; } count++; filp->f_pos = (loff_t) pos + 1; kfree(fname); fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); pos += hcc(fsize,TARFS_BLKSIZE) + 1; break; case 'K' : // dalsi blok bude jmeno linku, pak bude nasledovat ten link //takze jen preskocim na dalsi blok fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); pos += hcc(fsize,TARFS_BLKSIZE) + 1; break; case REGTYPE ://normalni soubor - alternativni kod case AREGTYPE://normalni soubor - preferovany kod case LNKTYPE ://hardlink case SYMTYPE ://symlink, vratim info o symlinku case CHRTYPE :;//znakove zarizeni case BLKTYPE :;//blokove zarizeni case DIRTYPE :;//adresar case FIFOTYPE: //je to pojmenovana pipe fsize = short_name_size(blok->name); fname = blok->name; if( !(rv = is_right_prefix(dir_name, dsize, fname, fsize)) ) { //dalsi soubory uz nejsou soucasti tohohle adresare //nechavam filp->f_pos nedotceno goto Return_Count2; } if (rv < 0) { //zanoril jsem se moc, musim preskakat ty soubory filp->f_pos = (loff_t) pos + 1;//abych ho uz nezkoumal fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); pos += hcc(fsize, TARFS_BLKSIZE) + 1; break; } if (fname[fsize - 1] == '/') { pom_fsize = fsize - 1; } else { pom_fsize = fsize; } if (get_type(blok, &type) < 0) { goto EOut2; } //pridavam jmeno if (filldir(dirent, fname + dsize, pom_fsize - dsize, 0, pos + 1, type) < 0) { //dalsi soubory uz nemam cist goto Return_Count2; } count++; filp->f_pos = (loff_t) pos + 1; fsize = oct_to_dec(blok->size, TARFS_NUMLENGTH); pos += hcc(fsize,TARFS_BLKSIZE) + 1; break; } //end switch } //end while if (is_nullblock(blok, TARFS_BLKSIZE)) { goto Return_Count2; } goto EOut2; // return Return_Count1: kfree(fname); Return_Count2: kfree(blok); kfree(dir_name); return count; // errors EOut1: kfree(fname); EOut2: kfree(dir_name); EOut3: kfree(blok); EOut4: return TARFS_EVAL; } /** * @brief Searches for file in given directory * * @param s Super block structure needed to perform i/o operations * @param ino Inode of directory to be searched in * @param fname Name of desired file * @param fsize Size of fname * @return Inode number of file if found, @a TARFS_EVAL otherwise */ unsigned long tar_lookup(struct super_block* s, unsigned long ino, const char* fname, unsigned int fsize) { unsigned long pos, fullsize, dsize; unsigned long ret_val; posix_header* blok; char* fullname;//jmeno souboru s celou cestou (v nasem FS, bez / na zacatku) char* dname; if (ino < 0 || !fname || fsize < 1) { goto EOut2; } if ( !(blok = (posix_header*) kmalloc(sizeof(posix_header), GFP_KERNEL)) ) { goto EOut2; } if (ino == TARFS_ROOT_INO) { //ino je korenovy adresar if ( !(fullname = (char*) kmalloc(fsize * sizeof(char), GFP_KERNEL)) ) { goto EOut1; } memcpy(fullname, fname, fsize); fullsize = fsize; pos = -1;//posune se o dal } else { pos = OFFSET_BY_INO(ino); if ( !read_block(s, pos, blok) || !is_tar(blok) ) { goto EOut1; } //pokud to neni adresar ani potencialne if (blok->typeflag != 'L' && blok->typeflag != DIRTYPE) { goto EOut1; } if (blok->typeflag == 'L') { //pokud to ma dlouhe jmeno dsize = oct_to_dec(blok->size, TARFS_NUMLENGTH) - 1; if (!pom_read_name(s, &dname, dsize, pos + 1 )) { goto EOut1; } fullsize = dsize + fsize; if( !(fullname = (char*) kmalloc(fullsize * sizeof(char), GFP_KERNEL)) ) { kfree(dname); goto EOut1; } memcpy(fullname,dname,dsize); kfree(dname); memcpy(fullname+dsize,fname,fsize); pos += hcc(dsize+1,TARFS_BLKSIZE)+1; if( !read_block(s, pos, blok) && !is_tar(blok) && blok->typeflag != DIRTYPE ) { //kdyz to neni adresar (s dlouhym jmenem) kfree(fullname); goto EOut1; } } else { //je to primo adresar dsize = short_name_size(blok->name); fullsize = fsize + dsize; if ( !(fullname = (char*) kmalloc(fullsize * sizeof(char), GFP_KERNEL)) ) { goto EOut1; } memcpy(fullname, blok->name, dsize); memcpy(fullname + dsize, fname, fsize); } } //ted mam v fullsize delku full path zadaneho souboru //a v fullname full path zadaneho souboru; pos++;//skocim na 1.soubor za tim adresarem ret_val = offset_by_name2(s, pos, fullname, fullsize); kfree(blok); kfree(fullname); return (ret_val == TARFS_EVAL) ? TARFS_EVAL : (ret_val + 1); // errors EOut1: kfree(blok); EOut2: return TARFS_EVAL; }