static ssize_t affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; char *start; ssize_t left, offset, size, sector; ssize_t blocksize; struct buffer_head *bh; void *data; pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino, (unsigned long)*ppos,count); if (!inode) { affs_error(inode->i_sb,"file_read_ofs","Inode = NULL"); return -EINVAL; } blocksize = AFFS_I2BSIZE(inode) - 24; if (!(S_ISREG(inode->i_mode))) { pr_debug("AFFS: file_read: mode = %07o",inode->i_mode); return -EINVAL; } if (*ppos >= inode->i_size || count <= 0) return 0; start = buf; for (;;) { left = MIN (inode->i_size - *ppos,count - (buf - start)); if (!left) break; sector = affs_bmap(inode,(u32)*ppos / blocksize); if (!sector) break; offset = (u32)*ppos % blocksize; bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode)); if (!bh) break; data = bh->b_data + 24; size = MIN(blocksize - offset,left); *ppos += size; copy_to_user(buf,data + offset,size); buf += size; affs_brelse(bh); } if (start == buf) return -EIO; return buf - start; }
static struct buffer_head * affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino) { struct buffer_head *bh; int intl = AFFS_I2FSTYPE(dir); s32 key; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name); bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir)); if (!bh) return NULL; if (namelen == 1 && name[0] == '.') { *ino = dir->i_ino; return bh; } if (namelen == 2 && name[0] == '.' && name[1] == '.') { *ino = affs_parent_ino(dir); return bh; } key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir))); for (;;) { unsigned char *cname; int cnamelen; affs_brelse(bh); bh = NULL; if (key == 0) break; bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir)); if (!bh) break; cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname); if (affs_match(name,namelen,cname,cnamelen,intl)) break; key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain); } *ino = key; return bh; }
void affs_write_inode(struct inode *inode) { struct buffer_head *bh; struct file_end *file_end; uid_t uid; gid_t gid; pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino); if (!inode->i_nlink) return; if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"write_inode","Cannot read block %lu",inode->i_ino); return; } file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode)); if (file_end->secondary_type == be32_to_cpu(ST_ROOT)) { secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered); } else { file_end->protect = cpu_to_be32(inode->u.affs_i.i_protect ^ FIBF_OWNER); file_end->byte_size = cpu_to_be32(inode->i_size); secs_to_datestamp(inode->i_mtime,&file_end->created); if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) { uid = inode->i_uid; gid = inode->i_gid; if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) { if (inode->i_uid == 0 || inode->i_uid == 0xFFFF) uid = inode->i_uid ^ ~0; if (inode->i_gid == 0 || inode->i_gid == 0xFFFF) gid = inode->i_gid ^ ~0; } if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) file_end->owner_uid = cpu_to_be16(uid); if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) file_end->owner_gid = cpu_to_be16(gid); } } affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); brelse(bh); }
static int affs_readdir(struct file *filp, void *dirent, filldir_t filldir) { int j, namelen; s32 i; int hash_pos; int chain_pos; unsigned long ino; int stored; unsigned char *name; struct buffer_head *dir_bh; struct buffer_head *fh_bh; struct inode *dir; struct inode *inode = filp->f_dentry->d_inode; pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos); stored = 0; dir_bh = NULL; fh_bh = NULL; dir = NULL; ino = inode->i_ino; if (filp->f_pos == 0) { filp->private_data = (void *)0; if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) { return 0; } ++filp->f_pos; stored++; } if (filp->f_pos == 1) { if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) { return stored; } filp->f_pos = 2; stored++; } chain_pos = (filp->f_pos - 2) & 0xffff; hash_pos = (filp->f_pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(inode->i_sb,"readdir","More than 65535 entries in chain"); chain_pos = 0; hash_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } if (!(dir_bh = affs_bread(inode->i_dev,inode->i_ino, AFFS_I2BSIZE(inode)))) goto readdir_done; while (1) { while (hash_pos < AFFS_I2HSIZE(inode) && !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]) hash_pos++; if (hash_pos >= AFFS_I2HSIZE(inode)) break; i = be32_to_cpu(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]); j = chain_pos; /* If the directory hasn't changed since the last call to readdir(), * we can jump directly to where we left off. */ if (filp->private_data && filp->f_version == inode->i_version) { i = (s32)filp->private_data; j = 0; pr_debug("AFFS: readdir() left off=%d\n",i); } filp->f_version = inode->i_version; pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos); while (i) { if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"readdir","Cannot read block %d",i); goto readdir_done; } ino = i; i = be32_to_cpu(FILE_END(fh_bh->b_data,inode)->hash_chain); if (j == 0) break; affs_brelse(fh_bh); fh_bh = NULL; j--; } if (fh_bh) { namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name); pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n", namelen,name,ino,i); filp->private_data = (void *)ino; if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0) goto readdir_done; filp->private_data = (void *)i; affs_brelse(fh_bh); fh_bh = NULL; stored++; } if (i == 0) { hash_pos++; chain_pos = 0; } else chain_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } readdir_done: affs_brelse(dir_bh); affs_brelse(fh_bh); pr_debug("AFFS: readdir()=%d\n",stored); return stored; }
int affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct buffer_head *old_bh; struct buffer_head *new_bh; unsigned long old_ino; unsigned long new_ino; int retval; pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n", old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode, new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode); if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len))) goto out; new_bh = NULL; retval = -ENOENT; old_bh = affs_find_entry(old_dir,old_dentry,&old_ino); if (!old_bh) goto end_rename; new_bh = affs_find_entry(new_dir,new_dentry,&new_ino); if (new_bh && !new_inode) { affs_error(old_inode->i_sb,"affs_rename", "No inode for entry found (key=%lu)\n",new_ino); goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { if (new_inode) { retval = -ENOTEMPTY; if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode))) goto end_rename; } retval = -ENOENT; if (affs_parent_ino(old_inode) != old_dir->i_ino) goto end_rename; } /* Unlink destination if it already exists */ if (new_inode) { if ((retval = affs_remove_header(new_bh,new_dir)) < 0) goto end_rename; new_inode->i_nlink = retval; mark_inode_dirty(new_inode); if (new_inode->i_ino == new_ino) new_inode->i_nlink = 0; } /* Remove header from its parent directory. */ if ((retval = affs_remove_hash(old_bh,old_dir))) goto end_rename; /* And insert it into the new directory with the new name. */ affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name); if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir))) goto end_rename; affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5); new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; new_dir->i_version = ++event; old_dir->i_version = ++event; retval = 0; mark_inode_dirty(new_dir); mark_inode_dirty(old_dir); mark_buffer_dirty(old_bh,1); end_rename: affs_brelse(old_bh); affs_brelse(new_bh); out: return retval; }
int affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct buffer_head *bh; struct inode *inode; char *p; unsigned long tmp; int i, maxlen, error; char c, lc; pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name,symname); maxlen = 4 * AFFS_I2HSIZE(dir) - 1; error = -ENOSPC; inode = affs_new_inode(dir); if (!inode) goto out; inode->i_op = &affs_symlink_inode_operations; inode->i_mode = S_IFLNK | 0777; inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode); error = -EIO; bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)); if (!bh) goto out_iput; i = 0; p = ((struct slink_front *)bh->b_data)->symname; lc = '/'; if (*symname == '/') { while (*symname == '/') symname++; while (inode->i_sb->u.affs_sb.s_volume[i]) /* Cannot overflow */ *p++ = inode->i_sb->u.affs_sb.s_volume[i++]; } while (i < maxlen && (c = *symname++)) { if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { *p++ = '/'; i++; symname += 2; lc = '/'; } else if (c == '.' && lc == '/' && *symname == '/') { symname++; lc = '/'; } else { *p++ = c; lc = c; i++; } if (lc == '/') while (*symname == '/') symname++; } *p = 0; mark_buffer_dirty(bh,1); affs_brelse(bh); mark_inode_dirty(inode); /* N.B. This test shouldn't be necessary ... dentry must be negative */ error = -EEXIST; bh = affs_find_entry(dir,dentry,&tmp); if (bh) goto out_release; /* N.B. Shouldn't we add the entry before dirtying the buffer? */ error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK); if (error) goto out_release; d_instantiate(dentry,inode); dir->i_version = ++event; mark_inode_dirty(dir); out: return error; out_release: affs_brelse(bh); out_iput: inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); goto out; }
void affs_truncate(struct inode *inode) { struct buffer_head *bh = NULL; int first; /* First block to be thrown away */ int block; s32 key; s32 *keyp; s32 ekey; s32 ptype, stype; int freethis; int net_blocksize; int blocksize = AFFS_I2BSIZE(inode); int rem; int ext; pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size); net_blocksize = blocksize - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0); first = (inode->i_size + net_blocksize - 1) / net_blocksize; if (inode->u.affs_i.i_lastblock < first - 1) { /* There has to be at least one new block to be allocated */ if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) { /* XXX Fine! No way to indicate an error. */ return /* -ENOSPC */; } bh = affs_getblock(inode,first - 1); if (!bh) { affs_warning(inode->i_sb,"truncate","Cannot extend file"); inode->i_size = net_blocksize * (inode->u.affs_i.i_lastblock + 1); } else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) { rem = inode->i_size % net_blocksize; DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize); affs_fix_checksum(blocksize,bh->b_data,5); mark_buffer_dirty(bh,0); } goto out_truncate; } ekey = inode->i_ino; ext = 0; /* Free all blocks starting at 'first' and all then-empty * extension blocks. Do not free the header block, though. */ while (ekey) { if (!(bh = affs_bread(inode->i_dev,ekey,blocksize))) { affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey); goto out_truncate; } if (affs_checksum_block(blocksize,bh->b_data,&ptype,&stype)) { affs_error(inode->i_sb,"truncate","Checksum error in header/ext block %d", ekey); goto out_truncate; } if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) { affs_error(inode->i_sb,"truncate", "Bad block (key=%d, ptype=%d, stype=%d)",ekey,ptype,stype); goto out_truncate; } /* Do we have to free this extension block after * freeing the data blocks pointed to? */ freethis = first == 0 && ekey != inode->i_ino; /* Free the data blocks. 'first' is relative to this * extension block and may well lie behind this block. */ for (block = first; block < AFFS_I2HSIZE(inode); block++) { keyp = &AFFS_BLOCK(bh->b_data,inode,block); key = be32_to_cpu(*keyp); if (key) { *keyp = 0; affs_free_block(inode->i_sb,key); } else break; } keyp = &GET_END_PTR(struct file_end,bh->b_data,blocksize)->extension; key = be32_to_cpu(*keyp); /* If 'first' is in this block or is the first * in the next one, this will be the last in * the list, thus we have to adjust the count * and zero the pointer to the next ext block. */ if (first <= AFFS_I2HSIZE(inode)) { ((struct file_front *)bh->b_data)->block_count = cpu_to_be32(first); first = 0; *keyp = 0; affs_fix_checksum(blocksize,bh->b_data,5); mark_buffer_dirty(bh,1); } else first -= AFFS_I2HSIZE(inode); affs_brelse(bh); bh = NULL; if (freethis) /* Don't bother fixing checksum */ affs_free_block(inode->i_sb,ekey); ekey = key; } block = ((inode->i_size + net_blocksize - 1) / net_blocksize) - 1; inode->u.affs_i.i_lastblock = block; /* If the file is not truncated to a block boundary, * the partial block after the EOF must be zeroed * so it cannot become accessible again. */ rem = inode->i_size % net_blocksize; if (rem) { if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) rem += 24; pr_debug("AFFS: Zeroing from offset %d in block %d\n",rem,block); bh = affs_getblock(inode,block); if (bh) { memset(bh->b_data + rem,0,blocksize - rem); if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) { ((struct data_front *)bh->b_data)->data_size = cpu_to_be32(rem); ((struct data_front *)bh->b_data)->next_data = 0; affs_fix_checksum(blocksize,bh->b_data,5); } mark_buffer_dirty(bh,1); } else affs_error(inode->i_sb,"truncate","Cannot read block %d",block); } out_truncate: affs_brelse(bh); /* Invalidate cache */ if (inode->u.affs_i.i_ec) { inode->u.affs_i.i_ec->max_ext = 0; for (key = 0; key < 3; key++) { inode->u.affs_i.i_ec->kc[key].kc_next_key = 0; inode->u.affs_i.i_ec->kc[key].kc_last = -1; } } mark_inode_dirty(inode); }
static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; off_t pos; ssize_t written; ssize_t c; ssize_t blocksize; struct buffer_head *bh; char *p; pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino, (unsigned long)*ppos,count); if (!count) return 0; if (!inode) { affs_error(inode->i_sb,"file_write_ofs","Inode = NULL"); return -EINVAL; } if (!S_ISREG(inode->i_mode)) { affs_error(inode->i_sb,"file_write_ofs", "Trying to write to non-regular file (mode=%07o)", inode->i_mode); return -EINVAL; } if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) return -ENOMEM; if (filp->f_flags & O_APPEND) pos = inode->i_size; else pos = *ppos; bh = NULL; blocksize = AFFS_I2BSIZE(inode) - 24; written = 0; while (written < count) { bh = affs_getblock(inode,pos / blocksize); if (!bh) { if (!written) written = -ENOSPC; break; } c = blocksize - (pos % blocksize); if (c > count - written) c = count - written; if (c != blocksize && !buffer_uptodate(bh)) { ll_rw_block(READ,1,&bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) { affs_brelse(bh); if (!written) written = -EIO; break; } } p = (pos % blocksize) + bh->b_data + 24; c -= copy_from_user(p,buf,c); if (!c) { affs_brelse(bh); if (!written) written = -EFAULT; break; } update_vm_cache(inode,pos,p,c); pos += c; buf += c; written += c; DATA_FRONT(bh)->data_size = cpu_to_be32(be32_to_cpu(DATA_FRONT(bh)->data_size) + c); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_uptodate(bh,1); mark_buffer_dirty(bh,0); affs_brelse(bh); } if (pos > inode->i_size) inode->i_size = pos; *ppos = pos; inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return written; }
static struct buffer_head * affs_getblock(struct inode *inode, s32 block) { struct super_block *sb = inode->i_sb; int ofs = sb->u.affs_sb.s_flags & SF_OFS; int ext = block / AFFS_I2HSIZE(inode); struct buffer_head *bh, *ebh, *pbh = NULL; struct key_cache *kc; s32 key, nkey; int cf, j, pt; int index; int err; pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block); if (block < 0) goto out_fail; key = calc_key(inode,&ext); block -= ext * AFFS_I2HSIZE(inode); pt = ext ? T_LIST : T_SHORT; /* Key refers now to the last known extension block, * ext is its sequence number (if 0, key refers to the * header block), and block is the block number relative * to the first block stored in that extension block. */ for (;;) { /* Loop over header block and extension blocks */ struct file_front *fdp; bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!bh) goto out_fail; fdp = (struct file_front *) bh->b_data; err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j); if (err || cf != pt || j != ST_FILE) { affs_error(sb, "getblock", "Block %d is not a valid %s", key, pt == T_SHORT ? "file header" : "ext block"); goto out_free_bh; } j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count); for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) { if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) { if (j > 0) { s32 k = AFFS_BLOCK(bh->b_data, inode, j - 1); pbh = affs_bread(inode->i_dev, be32_to_cpu(k), AFFS_I2BSIZE(inode)); } else pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock); if (!pbh) { affs_error(sb,"getblock", "Cannot get last block in file"); break; } } nkey = affs_new_data(inode); if (!nkey) break; inode->u.affs_i.i_lastblock++; if (AFFS_BLOCK(bh->b_data,inode,j)) { affs_warning(sb,"getblock","Block already allocated"); affs_free_block(sb,nkey); continue; } AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey); if (ofs) { ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode)); if (!ebh) { affs_error(sb,"getblock", "Cannot get block %d",nkey); affs_free_block(sb,nkey); AFFS_BLOCK(bh->b_data,inode,j) = 0; break; } DATA_FRONT(ebh)->primary_type = cpu_to_be32(T_DATA); DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino); DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1); affs_fix_checksum(AFFS_I2BSIZE(inode), ebh->b_data, 5); mark_buffer_dirty(ebh, 0); if (pbh) { DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24); DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey); affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5); mark_buffer_dirty(pbh,0); affs_brelse(pbh); } pbh = ebh; } cf = 1; } /* N.B. May need to release pbh after here */ if (cf) { if (pt == T_SHORT) fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0); fdp->block_count = cpu_to_be32(j); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); } if (block < j) { if (pbh) affs_brelse(pbh); break; } if (j < AFFS_I2HSIZE(inode)) { /* N.B. What about pbh here? */ goto out_free_bh; } block -= AFFS_I2HSIZE(inode); key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension); if (!key) { key = affs_new_header(inode); if (!key) goto out_free_bh; ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!ebh) { /* N.B. must free bh here */ goto out_free_block; } ((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST); ((struct file_front *)ebh->b_data)->own_key = cpu_to_be32(key); FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE); FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino); affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5); mark_buffer_dirty(ebh, 1); FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); affs_brelse(bh); bh = ebh; } pt = T_LIST; ext++; index = seqnum_to_index(ext); if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) { inode->u.affs_i.i_ec->ec[index] = key; inode->u.affs_i.i_ec->max_ext = index; } affs_brelse(bh); } /* Invalidate key cache */ for (j = 0; j < 4; j++) { kc = &inode->u.affs_i.i_ec->kc[j]; kc->kc_last = -1; } key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block)); affs_brelse(bh); if (!key) goto out_fail; bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode)); return bh; out_free_block: affs_free_block(sb, key); out_free_bh: affs_brelse(bh); out_fail: return NULL; }
static int affs_bmap(struct inode *inode, int block) { struct buffer_head *bh; s32 key, nkey; s32 ptype, stype; int ext; int index; int keycount; struct key_cache *kc; struct key_cache *tkc; struct timeval tv; s32 *keyp; int i; pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block); if (block < 0) { affs_error(inode->i_sb,"bmap","Block < 0"); return 0; } if (!inode->u.affs_i.i_ec) { if (alloc_ext_cache(inode)) { return 0; } } /* Try to find the requested key in the cache. * In order to speed this up as much as possible, * the cache line lookup is done in a separate * step. */ for (i = 0; i < 4; i++) { tkc = &inode->u.affs_i.i_ec->kc[i]; /* Look in any cache if the key is there */ if (block <= tkc->kc_last && block >= tkc->kc_first) { return tkc->kc_keys[block - tkc->kc_first]; } } kc = NULL; #ifdef OSKIT tv.tv_sec = CURRENT_TIME; tv.tv_usec = 0; #else tv = xtime; #endif for (i = 0; i < 4; i++) { tkc = &inode->u.affs_i.i_ec->kc[i]; if (tkc->kc_lru_time.tv_sec > tv.tv_sec) continue; if (tkc->kc_lru_time.tv_sec < tv.tv_sec || tkc->kc_lru_time.tv_usec < tv.tv_usec) { kc = tkc; tv = tkc->kc_lru_time; } } if (!kc) /* Really shouldn't happen */ kc = tkc; #ifdef OSKIT kc->kc_lru_time.tv_sec = CURRENT_TIME; kc->kc_lru_time.tv_usec = 0; #else kc->kc_lru_time = xtime; #endif keyp = kc->kc_keys; kc->kc_first = block; kc->kc_last = -1; keycount = AFFS_KCSIZE; /* Calculate sequence number of the extension block where the * number of the requested block is stored. 0 means it's in * the file header. */ ext = block / AFFS_I2HSIZE(inode); key = calc_key(inode,&ext); block -= ext * AFFS_I2HSIZE(inode); for (;;) { bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!bh) return 0; index = seqnum_to_index(ext); if (index > inode->u.affs_i.i_ec->max_ext && (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) { affs_brelse(bh); return 0; } nkey = be32_to_cpu(FILE_END(bh->b_data,inode)->extension); if (block < AFFS_I2HSIZE(inode)) { /* Fill cache as much as possible */ if (keycount) { kc->kc_first = ext * AFFS_I2HSIZE(inode) + block; keycount = keycount < AFFS_I2HSIZE(inode) - block ? keycount : AFFS_I2HSIZE(inode) - block; for (i = 0; i < keycount; i++) kc->kc_keys[i] = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block + i)); kc->kc_last = kc->kc_first + i - 1; } break; } block -= AFFS_I2HSIZE(inode); affs_brelse(bh); ext++; if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) { inode->u.affs_i.i_ec->ec[index] = nkey; inode->u.affs_i.i_ec->max_ext = index; } key = nkey; } kc->kc_this_key = key; kc->kc_this_seq = ext; kc->kc_next_key = nkey; key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block)); affs_brelse(bh); return key; }
void affs_read_inode(struct inode *inode) { struct buffer_head *bh; struct file_front *file_front; struct file_end *file_end; s32 block; unsigned long prot; s32 ptype, stype; unsigned short id; pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino); block = inode->i_ino; if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"read_inode","Cannot read block %d",block); return; } if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) { affs_error(inode->i_sb,"read_inode", "Checksum or type (ptype=%d) error on inode %d",ptype,block); affs_brelse(bh); return; } file_front = (struct file_front *)bh->b_data; file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode)); prot = (be32_to_cpu(file_end->protect) & ~0x10) ^ FIBF_OWNER; inode->u.affs_i.i_protect = prot; inode->u.affs_i.i_parent = be32_to_cpu(file_end->parent); inode->u.affs_i.i_original = 0; inode->u.affs_i.i_zone = 0; inode->u.affs_i.i_hlink = 0; inode->u.affs_i.i_pa_cnt = 0; inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; inode->u.affs_i.i_lastblock = -1; inode->i_nlink = 1; inode->i_mode = 0; if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) inode->i_mode = inode->i_sb->u.affs_sb.s_mode; else inode->i_mode = prot_to_mode(prot); if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID) inode->i_uid = inode->i_sb->u.affs_sb.s_uid; id = be16_to_cpu(file_end->owner_uid); if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID) inode->i_uid = inode->i_sb->u.affs_sb.s_uid; else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS) inode->i_uid = 0; else inode->i_uid = id; id = be16_to_cpu(file_end->owner_gid); if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID) inode->i_gid = inode->i_sb->u.affs_sb.s_gid; else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS) inode->i_gid = 0; else inode->i_gid = id; switch (be32_to_cpu(file_end->secondary_type)) { case ST_ROOT: inode->i_uid = inode->i_sb->u.affs_sb.s_uid; inode->i_gid = inode->i_sb->u.affs_sb.s_gid; case ST_USERDIR: if (be32_to_cpu(file_end->secondary_type) == ST_USERDIR || inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) { if (inode->i_mode & S_IRUSR) inode->i_mode |= S_IXUSR; if (inode->i_mode & S_IRGRP) inode->i_mode |= S_IXGRP; if (inode->i_mode & S_IROTH) inode->i_mode |= S_IXOTH; inode->i_mode |= S_IFDIR; } else inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; inode->i_size = 0; break; case ST_LINKDIR: affs_error(inode->i_sb,"read_inode","inode is LINKDIR"); affs_brelse(bh); return; case ST_LINKFILE: affs_error(inode->i_sb,"read_inode","inode is LINKFILE"); affs_brelse(bh); return; case ST_FILE: inode->i_mode |= S_IFREG; inode->i_size = be32_to_cpu(file_end->byte_size); if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) block = AFFS_I2BSIZE(inode) - 24; else block = AFFS_I2BSIZE(inode); inode->u.affs_i.i_lastblock = ((inode->i_size + block - 1) / block) - 1; break; case ST_SOFTLINK: inode->i_mode |= S_IFLNK; inode->i_size = 0; break; } inode->i_mtime = inode->i_atime = inode->i_ctime = (be32_to_cpu(file_end->created.ds_Days) * (24 * 60 * 60) + be32_to_cpu(file_end->created.ds_Minute) * 60 + be32_to_cpu(file_end->created.ds_Tick) / 50 + ((8 * 365 + 2) * 24 * 60 * 60)) + sys_tz.tz_minuteswest * 60; affs_brelse(bh); inode->i_op = NULL; if (S_ISREG(inode->i_mode)) { if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) { inode->i_op = &affs_file_inode_operations_ofs; } else { inode->i_op = &affs_file_inode_operations; } } else if (S_ISDIR(inode->i_mode)) { /* Maybe it should be controlled by mount parameter? */ inode->i_mode |= S_ISVTX; inode->i_op = &affs_dir_inode_operations; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &affs_symlink_inode_operations; }
int affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode, struct dentry *dentry, int type) { struct buffer_head *dir_bh; struct buffer_head *inode_bh; struct buffer_head *link_bh; int retval; const unsigned char *name = dentry->d_name.name; int len = dentry->d_name.len; pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino, len,name,type); if ((retval = affs_check_name(name,len))) return retval; if (len > 30) len = 30; dir_bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir)); inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)); link_bh = NULL; retval = -EIO; if (!dir_bh || !inode_bh) goto addentry_done; if (link) { link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link)); if (!link_bh) goto addentry_done; } ((struct dir_front *)inode_bh->b_data)->primary_type = cpu_to_be32(T_SHORT); ((struct dir_front *)inode_bh->b_data)->own_key = cpu_to_be32(inode->i_ino); DIR_END(inode_bh->b_data,inode)->dir_name[0] = len; strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len); DIR_END(inode_bh->b_data,inode)->secondary_type = cpu_to_be32(type); DIR_END(inode_bh->b_data,inode)->parent = cpu_to_be32(dir->i_ino); lock_super(inode->i_sb); retval = affs_insert_hash(dir->i_ino,inode_bh,dir); if (link_bh) { LINK_END(inode_bh->b_data,inode)->original = cpu_to_be32(link->i_ino); LINK_END(inode_bh->b_data,inode)->link_chain = FILE_END(link_bh->b_data,link)->link_chain; FILE_END(link_bh->b_data,link)->link_chain = cpu_to_be32(inode->i_ino); affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5); link->i_version = ++global_event; mark_inode_dirty(link); mark_buffer_dirty(link_bh,1); } affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5); affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5); dir->i_version = ++global_event; dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME; unlock_super(inode->i_sb); mark_inode_dirty(dir); mark_inode_dirty(inode); mark_buffer_dirty(dir_bh,1); mark_buffer_dirty(inode_bh,1); addentry_done: affs_brelse(dir_bh); affs_brelse(inode_bh); affs_brelse(link_bh); return retval; }