/* * ext3301 read: wrapper for the standard file read function. * modifications: handling encryption and immediate files. * original: do_sync_read */ ssize_t ext3301_read(struct file * filp, char __user * buf, size_t len, loff_t * ppos) { struct inode * i = FILP_INODE(filp); ssize_t ret = 0; dbg(KERN_DEBUG "Read: '%s'\n", FILP_NAME(filp)); //Check if the file is immediate (requires special read behaviour) if (I_ISIM(i)) { dbg_im(KERN_DEBUG "- Read-immediate\n"); ret = ext3301_read_immediate(filp, buf, len, ppos); } else { dbg_im(KERN_DEBUG "- Read-regular\n"); ret = do_sync_read(filp, buf, len, ppos); } //Check if the file is in the encryption tree if (ext3301_isencrypted(filp->f_path.dentry)) { //Decrypt the data which was read dbg_cr(KERN_DEBUG "- Encrypting data (%d bytes)\n", (int)len); if (ext3301_cryptbuf(buf, len) < 0) return -EIO; } return ret; }
void dbg_prompt(int i) { msg_print(MSG_PROMPT); msg_print(i); dbg_cr(); }
void dbg_print_cr(char * s) { dbg_print(s); dbg_cr(); }
void dbg_printnum_cr(char * s, unsigned long no) { dbg_printnum(s, no); dbg_cr(); }
/* * ext3301: modified to encrypt/decrypt files moving to/from * an encrypted folder. * Rename still succeeds if an encrypt/decrypt operation fails. */ static int ext2_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 page * dir_page = NULL; struct ext2_dir_entry_2 * dir_de = NULL; struct page * old_page; struct ext2_dir_entry_2 * old_de; int err = -ENOENT; bool is_encryptable, src_encrypt, dest_encrypt; int i; struct file * fcrypt; ssize_t nchunk, nread, nwritten; loff_t fpos, fseekpos; unsigned int fsize, fremaining; char * buf, * strbuf1, * strbuf2, * path_src, * path_dest; size_t blocksize = INODE_BLKSIZE(old_inode); // dquot_initialize(old_dir); dquot_initialize(new_dir); old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page); if (!old_de) goto out; if (S_ISDIR(old_inode->i_mode)) { err = -EIO; dir_de = ext2_dotdot(old_inode, &dir_page); if (!dir_de) goto out_old; } if (new_inode) { struct page *new_page; struct ext2_dir_entry_2 *new_de; err = -ENOTEMPTY; if (dir_de && !ext2_empty_dir (new_inode)) goto out_dir; err = -ENOENT; new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; ext2_set_link(new_dir, new_de, new_page, old_inode, 1); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) drop_nlink(new_inode); inode_dec_link_count(new_inode); } else { err = ext2_add_link(new_dentry, old_inode); if (err) goto out_dir; if (dir_de) inode_inc_link_count(new_dir); } // allocate buffers strbuf1 = kmalloc((size_t)512, GFP_KERNEL); strbuf2 = kmalloc((size_t)512, GFP_KERNEL); buf = kmalloc(blocksize, GFP_KERNEL); if (!buf || !strbuf1 || !strbuf2) return -ENOMEM; // check if the source XOR destination lie under /encrypt, // and both entries are regular or immediate files is_encryptable = (I_ISIM(old_inode) || I_ISREG(old_inode)); src_encrypt = ext3301_isencrypted(old_dentry); dest_encrypt = ext3301_isencrypted(new_dentry); path_src = ext3301_getpath(old_dentry, strbuf1, blocksize); path_dest = ext3301_getpath(new_dentry, strbuf2, blocksize); // decide whether to encrypt dbg(KERN_DEBUG "rename (%s --> %s)\n", path_src, path_dest); if (is_encryptable) { dbg_cr(KERN_DEBUG "- File encryptable type (regular/immediate)\n"); if (src_encrypt && dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving inside /encrypt (no change))\n"); } else if (src_encrypt) { dbg_cr(KERN_DEBUG "- File moving out of /encrypt. Decrypting..\n"); goto cryptstart; } else if (dest_encrypt) { dbg_cr(KERN_DEBUG "- File moving into /encrypt. Encrypting..\n"); goto cryptstart; } else { dbg_cr(KERN_DEBUG "- Src/dest directories not encryptable\n"); } } else { dbg_cr(KERN_DEBUG "- File not an encryptable type\n"); } goto cryptdone; /* encrypt/decrypt file */ cryptstart: // open file if (!path_src) goto cryptfail; fcrypt = kfile_open(path_src, O_RDWR); if (!fcrypt) goto cryptfail; fsize = FILP_FSIZE(fcrypt); fremaining = fsize; fpos = 0; fseekpos = 0; dbg_cr(KERN_DEBUG " - Opened %s (Fsize: %d)\n", FILP_NAME(fcrypt), fsize); // special case: nothing to encrypt if (fsize==0) goto cryptclose; // loop: read, encrypt, write while (fremaining > 0) { // choose a chunk size nchunk = (fremaining > blocksize ? blocksize : (ssize_t)fremaining); dbg_cr(KERN_DEBUG " - Starting a %d-byte chunk at pos %u.\n", (int)nchunk, (unsigned int)fpos); // read a chunk; make sure we read all bytes requested. fpos = fseekpos; nread = kfile_read(fcrypt, buf, (size_t)nchunk, &fpos); // this inequality covers error conditions (nread<0) and // partial reads (0<=nread<=nchunk && nread != nchunk) if (nread != nchunk) { kfile_close(fcrypt); goto cryptfail; } // encrypt the buffer for (i=0; i<nchunk; i++) buf[i] ^= crypter_key; // write the chunk back fpos = fseekpos; nwritten = kfile_write(fcrypt, buf, (size_t)nchunk, &fpos); if (nwritten != nchunk) { kfile_close(fcrypt); goto cryptfail; } // move the file marker forward, decrease the #bytes remaining fseekpos += nchunk; fremaining -= nchunk; } // sync the read/write operations to disk. Very important! kfile_sync(fcrypt); cryptclose: kfile_close(fcrypt); goto cryptdone; cryptfail: // encrypt/decrypt failed if (dest_encrypt) printk(KERN_WARNING "Crypting file entering /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); else if (src_encrypt) printk(KERN_WARNING "Decrypting file leaving /%s failed: ino %lu\n", crypter_dir, INODE_INO(old_inode)); goto cryptdone; cryptdone: /* * Like most other Unix systems, set the ctime for inodes on a * rename. */ old_inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(old_inode); ext2_delete_entry (old_de, old_page); if (dir_de) { if (old_dir != new_dir) ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0); else { kunmap(dir_page); page_cache_release(dir_page); } inode_dec_link_count(old_dir); } goto out_free; out_dir: if (dir_de) { kunmap(dir_page); page_cache_release(dir_page); } out_old: kunmap(old_page); page_cache_release(old_page); out: return err; out_free: // free buffers kfree(strbuf1); kfree(strbuf2); kfree(buf); return 0; }
/* * ext3301 write: wrapper for the standard file write function. * modifications: handling encryption and immediate files. * original: do_sync_write */ ssize_t ext3301_write(struct file * filp, char __user * buf, size_t len, loff_t * ppos) { ssize_t ret, written; struct inode * i = FILP_INODE(filp); dbg_im(KERN_DEBUG "Write: '%s'\n", FILP_NAME(filp)); //Encryption: Check if the file is in the encryption tree if (ext3301_isencrypted(filp->f_path.dentry)) { //Encrypt the data being written dbg_cr(KERN_DEBUG "- Encrypting data (%d bytes)\n", (int)len); if (ext3301_cryptbuf(buf, len) < 0) return -EIO; } //Immediate file only: walk ppos forward manually for Append mode if (I_ISIM(i) && (FILP_FLAGS(filp) & O_APPEND)) { dbg_im(KERN_DEBUG "O_APPEND: walking ppos to EoF\n"); *ppos += INODE_ISIZE(i); } //Immediate file only: Check if it needs to grow into a regular file if (I_ISIM(i) && (*ppos+len > EXT3301_IM_SIZE(i))) { dbg_im(KERN_DEBUG "- IM-->REG conversion\n"); ret = ext3301_im2reg(filp); if (ret < 0) { printk(KERN_DEBUG "IM-->REG conversion fail: ino %lu, err %d\n", INODE_INO(i), (int)ret); return ret; } //Append mode: undo the ppos offset. We are now writing to a //regular file, and the default methods already handle this. if (FILP_FLAGS(filp) & O_APPEND) { dbg_im(KERN_DEBUG "O_APPEND: walking ppos back (REG)\n"); *ppos -= INODE_ISIZE(i); } } //Write to file (immediate and regular files have different methods) if (I_ISIM(i)) { dbg_im(KERN_DEBUG "- Write-immediate\n"); written = ext3301_write_immediate(filp, buf, len, ppos); } else { dbg_im(KERN_DEBUG "- Write-regular\n"); written = do_sync_write(filp, buf, len, ppos); } //Regular file only: Check if it's small enough to convert to immediate if (INODE_TYPE(i)==DT_REG && (INODE_ISIZE(i)<=EXT3301_IM_SIZE(i))) { dbg_im(KERN_DEBUG "- REG-->IM conversion\n"); ret = ext3301_reg2im(filp); if (ret < 0) { printk(KERN_DEBUG "REG-->IM file conversion failed: ino %lu\n", INODE_INO(i)); return ret; } } return written; }