www.pudn.com > cryptfs030905.rar > inode.c
/* * Copyright (c) 1997-2003 Erez Zadok * Copyright (c) 2001-2003 Stony Brook University * * For specific licensing information, see the COPYING file distributed with * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. * * This Copyright notice must be kept intact and distributed with all * fistgen sources INCLUDING sources generated by fistgen. */ /* * $Id: inode.c,v 1.29 2003/07/29 18:17:20 ezk Exp $ */ #ifdef HAVE_CONFIG_H # include#endif /* HAVE_CONFIG_H */ #ifdef FISTGEN # include "fist_cryptfs.h" #endif /* FISTGEN */ #include "fist.h" #include "cryptfs.h" STATIC int cryptfs_create(inode_t *dir, dentry_t *dentry, int mode) { int err; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); /* CPW: Moved below print_entry_location */ ASSERT(hidden_dentry != NULL); fist_checkinode(dir, "cryptfs_create"); hidden_dir_dentry = lock_parent(hidden_dentry); err = PTR_ERR(hidden_dir_dentry); if (IS_ERR(hidden_dir_dentry)) goto out; err = vfs_create(hidden_dir_dentry->d_inode, hidden_dentry, mode); /* XXX this could potentially return a negative hidden_dentry! */ if (err) goto out_lock; err = cryptfs_interpose(hidden_dentry, dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode); out_lock: unlock_dir(hidden_dir_dentry); out: fist_checkinode(dir, "post cryptfs_create"); print_exit_status(err); return err; } STATIC dentry_t * cryptfs_lookup(inode_t *dir, dentry_t *dentry) { int err = 0; dentry_t *hidden_dir_dentry; dentry_t *hidden_dentry; const char *name; vnode_t *this_vnode; dentry_t *this_dir; unsigned int namelen; char *encoded_name; unsigned int encoded_namelen; print_entry_location(); hidden_dir_dentry = cryptfs_hidden_dentry(dentry->d_parent); /* CPW: Moved below print_entry_location */ name = dentry->d_name.name; namelen = dentry->d_name.len; fist_checkinode(dir, "cryptfs_lookup"); this_vnode = dir; this_dir = hidden_dir_dentry; fist_print_dentry("cryptfs_lookup IN", dentry); fist_print_dentry("cryptfs_lookup: dentry->d_parent IN", dentry->d_parent); fist_print_dentry("cryptfs_lookup: hidden_dir_dentry IN", hidden_dir_dentry); fist_print_inode("cryptfs_lookup: dir IN", dir); if (hidden_dir_dentry->d_inode) fist_print_inode("cryptfs_lookup: hidden_dir_dentry->d_inode", hidden_dir_dentry->d_inode); /* must initialize dentry operations */ dentry->d_op = &cryptfs_dops; ; /* increase refcount of base dentry (lookup_one[_len] will decrement) */ // THIS IS RIGHT! (don't "fix" it) // NO THIS IS WRONG IN 2.3.99-pre6. lookup_one[_len] will NOT decrement // dget(hidden_dir_dentry); encoded_namelen = cryptfs_encode_filename(name, namelen, &encoded_name, SKIP_DOTS, dir, dir->i_sb); /*if there was an error in encoding filenames*/ if (encoded_namelen < 0) { err = encoded_namelen; goto out_free; } /* will allocate a new hidden dentry if needed */ hidden_dentry = lookup_one_len(encoded_name, hidden_dir_dentry, encoded_namelen - 1); kfree(encoded_name); if (IS_ERR(hidden_dentry)) { /* * this produces an unusual dentry: one that has neither an * inode, nor a private structure attached to it. All cleanup * methods (d_delete, d_release, etc) must be prepared to deal * with such dentries. Ion 09/29/2001 */ printk("ERR from hidden_dentry!!!\n"); err = PTR_ERR(hidden_dentry); goto out; } ; /* update parent directory's atime */ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); /* link the upper and lower dentries */ dtopd(dentry) = (struct cryptfs_dentry_info *) kmalloc(sizeof(struct cryptfs_dentry_info), GFP_KERNEL); if (!dtopd(dentry)) { err = -ENOMEM; goto out_dput; } dtohd(dentry) = hidden_dentry; //assign the the parent directory's key to the child directory dtokey(dentry)= dtokey(dentry->d_parent); /* lookup is special: it needs to handle negative dentries */ if (!hidden_dentry->d_inode) { d_add(dentry, NULL); fist_print_dentry("cryptfs_lookup OUT hidden_dentry", hidden_dentry); goto out; } fist_dprint(6, "lookup \"%s\" -> inode %d\n", name, hidden_dentry->d_inode->i_ino); err = cryptfs_interpose(hidden_dentry, dentry, dir->i_sb, 1); if (err) goto out_free; fist_checkinode(dentry->d_inode, "cryptfs_lookup OUT: dentry->d_inode:"); fist_checkinode(dir, "cryptfs_lookup OUT: dir"); fist_print_dentry("cryptfs_lookup OUT hidden_dentry", hidden_dentry); fist_print_inode("cryptfs_lookup OUT hidden_inode", hidden_dentry->d_inode); /* All is well */ goto out; out_free: d_drop(dentry); /* so that our bad dentry will get destroyed */ kfree(dtopd(dentry)); dtopd(dentry) = NULL; /* be safe */ out_dput: dput(hidden_dentry); out: fist_print_dentry("cryptfs_lookup OUT", dentry); print_exit_status(err); return ERR_PTR(err); } STATIC int cryptfs_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry) { int err; dentry_t *hidden_old_dentry; dentry_t *hidden_new_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_old_dentry = cryptfs_hidden_dentry(old_dentry); /* CPW: Moved below print_entry_location */ hidden_new_dentry = cryptfs_hidden_dentry(new_dentry); fist_checkinode(dir, "cryptfs_link-dir"); fist_checkinode(old_dentry->d_inode, "cryptfs_link-oldinode"); dget(hidden_old_dentry); dget(hidden_new_dentry); hidden_dir_dentry = lock_parent(hidden_new_dentry); err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry); if (err || !hidden_new_dentry->d_inode) goto out_lock; err = cryptfs_interpose(hidden_new_dentry, new_dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode); /* propagate number of hard-links */ old_dentry->d_inode->i_nlink = itohi(old_dentry->d_inode)->i_nlink; out_lock: unlock_dir(hidden_dir_dentry); dput(hidden_new_dentry); dput(hidden_old_dentry); if (!new_dentry->d_inode) d_drop(new_dentry); print_exit_status(err); return err; } STATIC int cryptfs_unlink(inode_t *dir, dentry_t *dentry) { int err = 0; inode_t *hidden_dir; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_dir = itohi(dir); /* CPW: Moved below print_entry_location */ hidden_dentry = cryptfs_hidden_dentry(dentry); ASSERT(hidden_dentry != NULL); fist_checkinode(dir, "cryptfs_unlink-dir"); dget(dentry); hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ dget(hidden_dentry); err = vfs_unlink(hidden_dir, hidden_dentry); dput(hidden_dentry); if (!err) /* vfs_unlink does that */ d_delete(hidden_dentry); out_lock: fist_copy_attr_times(dir, hidden_dir); /* propagate number of hard-links */ dentry->d_inode->i_nlink = itohi(dentry->d_inode)->i_nlink; unlock_dir(hidden_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) { // fist_print_dentry("cryptfs_unlink: 0", dentry); d_drop(dentry); // fist_print_dentry("cryptfs_unlink: 1", dentry); } dput(dentry); fist_checkinode(dir, "post cryptfs_unlink-dir"); print_exit_status(err); return err; } STATIC int cryptfs_symlink(inode_t *dir, dentry_t *dentry, const char *symname) { int err; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; char *encoded_symname; unsigned int encoded_symlen; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "cryptfs_symlink-dir"); dget(hidden_dentry); hidden_dir_dentry = lock_parent(hidden_dentry); encoded_symlen = cryptfs_encode_filename(symname, strlen(symname), &encoded_symname, DO_DOTS, dir, dir->i_sb); /* if length is negative, return back */ if (encoded_symlen < 0) { err = encoded_symlen; goto out_lock; } err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, encoded_symname); kfree(encoded_symname); if (err || !hidden_dentry->d_inode) goto out_lock; err = cryptfs_interpose(hidden_dentry, dentry, dir->i_sb, 0); if (err) goto out_lock; fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode); fist_checkinode(dir, "post cryptfs_symlink-dir"); out_lock: unlock_dir(hidden_dir_dentry); dput(hidden_dentry); if (!dentry->d_inode) d_drop(dentry); print_exit_status(err); return err; } STATIC int cryptfs_mkdir(inode_t *dir, dentry_t *dentry, int mode) { int err; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "cryptfs_mkdir-dir"); hidden_dir_dentry = lock_parent(hidden_dentry); err = vfs_mkdir(hidden_dir_dentry->d_inode, hidden_dentry, mode); if (err || !hidden_dentry->d_inode) goto out; err = cryptfs_interpose(hidden_dentry, dentry, dir->i_sb, 0); if (err) goto out; fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode); /* update number of links on parent directory */ dir->i_nlink = hidden_dir_dentry->d_inode->i_nlink; fist_checkinode(dir, "post cryptfs_mkdir-dir"); out: unlock_dir(hidden_dir_dentry); if (!dentry->d_inode) d_drop(dentry); print_exit_status(err); return err; } STATIC int cryptfs_rmdir(inode_t *dir, dentry_t *dentry) { int err = 0; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "cryptfs_rmdir-dir"); dget(dentry); hidden_dir_dentry = lock_parent(hidden_dentry); /* avoid destroying the hidden inode if the file is in use */ dget(hidden_dentry); err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); dput(hidden_dentry); if (!err) /* vfs_rmdir does that */ d_delete(hidden_dentry); out_lock: fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); /* copy the nlink count for our dentry and our parent's dentry */ dir->i_nlink = hidden_dir_dentry->d_inode->i_nlink; unlock_dir(hidden_dir_dentry); /* * call d_drop so the system "forgets" about us */ if (!err) d_drop(dentry); dput(dentry); print_exit_status(err); return err; } STATIC int cryptfs_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev) { int err; dentry_t *hidden_dentry; dentry_t *hidden_dir_dentry; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "cryptfs_mknod-dir"); hidden_dir_dentry = lock_parent(hidden_dentry); err = vfs_mknod(hidden_dir_dentry->d_inode, hidden_dentry, mode, dev); if (err || !hidden_dentry->d_inode) goto out; err = cryptfs_interpose(hidden_dentry, dentry, dir->i_sb, 0); if (err) goto out; fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode); out: unlock_dir(hidden_dir_dentry); if (!dentry->d_inode) d_drop(dentry); fist_checkinode(dir, "post cryptfs_mknod-dir"); print_exit_status(err); return err; } STATIC int cryptfs_rename(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry) { int err; dentry_t *hidden_old_dentry; dentry_t *hidden_new_dentry; dentry_t *hidden_old_dir_dentry; dentry_t *hidden_new_dir_dentry; print_entry_location(); hidden_old_dentry = cryptfs_hidden_dentry(old_dentry);/* CPW: Moved below print_entry_location */ hidden_new_dentry = cryptfs_hidden_dentry(new_dentry); fist_checkinode(old_dir, "cryptfs_rename-old_dir"); fist_checkinode(new_dir, "cryptfs_rename-new_dir"); dget(hidden_old_dentry); dget(hidden_new_dentry); hidden_old_dir_dentry = get_parent(hidden_old_dentry); hidden_new_dir_dentry = get_parent(hidden_new_dentry); double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, hidden_new_dir_dentry->d_inode, hidden_new_dentry); if (err) goto out_lock; fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); if (new_dir != old_dir) fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode); out_lock: // double_unlock will dput the new/old parent dentries whose refcnts // were incremented via get_parent above. double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); dput(hidden_new_dentry); dput(hidden_old_dentry); fist_checkinode(new_dir, "post cryptfs_rename-new_dir"); print_exit_status(err); return err; } STATIC int cryptfs_readlink(dentry_t *dentry, char *buf, int bufsiz) { int err; dentry_t *hidden_dentry; char *decoded_name, *hidden_buf; mm_segment_t old_fs; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry);/* CPW: Moved below print_entry_location */ fist_print_dentry("cryptfs_readlink IN", dentry); if (!hidden_dentry->d_inode->i_op || !hidden_dentry->d_inode->i_op->readlink) { err = -EINVAL; goto out; } hidden_buf = kmalloc(bufsiz, GFP_KERNEL); if (hidden_buf == NULL) { printk("Out of memory.\n"); err = -ENOMEM; goto out; } old_fs = get_fs(); set_fs(KERNEL_DS); err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, hidden_buf, bufsiz); set_fs(old_fs); if (err >= 0) { // don't do this, it's not zero-terminated!!! // fist_dprint(7, "READLINK: link \"%s\", length %d\n", hidden_buf, err); err = cryptfs_decode_filename(hidden_buf, err, &decoded_name, DO_DOTS, dentry->d_inode, dentry->d_sb); if (err > 0) { if (copy_to_user(buf, decoded_name, err)) err = -EFAULT; kfree(decoded_name); } fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); } kfree(hidden_buf); out: print_exit_status(err); return err; } STATIC int cryptfs_follow_link(dentry_t *dentry, struct nameidata *nd) { char *buf; int len = PAGE_SIZE, err; mm_segment_t old_fs; print_entry_location(); // fist_print_dentry("cryptfs_follow_link dentry IN", dentry); buf = kmalloc(len, GFP_KERNEL); if (!buf) { err = -ENOMEM; goto out; } /* read the symlink, and then we will follow it */ old_fs = get_fs(); set_fs(KERNEL_DS); err = dentry->d_inode->i_op->readlink(dentry, buf, len); set_fs(old_fs); if (err < 0) goto out_free; buf[err] = 0; // terminate the buffer -- XXX still needed? // XXX: FIXME w/ cryptfs_encode_filename() /* * vfs_follow_link will increase the nd's mnt refcnt * we assume that some other VFS code decrements it. */ err = vfs_follow_link(nd, buf); out_free: kfree(buf); out: #if 0 if (err < 0) { dput(nd->dentry); printk("EZK follow_link() mnt_cnt %d\n", atomic_read(&nd->mnt->mnt_count)); mntput(nd->mnt); } #endif print_exit_status(err); return err; } /* * Function to handle truncations extending the size of the file for non-SCA * file systems. */ STATIC int cryptfs_truncate(dentry_t *dentry, loff_t new_length) { inode_t *inode = dentry->d_inode; file_t fake_file; page_t *tmp_page; int index; unsigned to; int err = 0; print_entry_location(); /* * if this is a truncate past the end of the file, call cryptfs_fill_zeros */ if (new_length > inode->i_size) { memset(&fake_file, 0, sizeof(fake_file)); fake_file.f_dentry = dentry; index = new_length >> PAGE_CACHE_SHIFT; tmp_page = cryptfs_get1page(&fake_file, index); if (IS_ERR(tmp_page)) { err = PTR_ERR(tmp_page); goto out; } to = new_length & ~PAGE_CACHE_MASK; /* * 'to' is passed to cryptfs_fill_zeros instead of a 'from' * because unlike the normal case where we fill in zeros * till 'from', we need to fill it in with zeros till the * end of the file */ err = cryptfs_fill_zeros(&fake_file, tmp_page, to); } out: print_exit_status(err); return err; } STATIC int cryptfs_permission(inode_t *inode, int mask) { inode_t *hidden_inode; int mode; int err; print_entry_location(); hidden_inode = itohi(inode);/* CPW: Moved below print_entry_location */ mode = inode->i_mode; err = vfs_permission(inode, mask); if (err) goto out; err = permission(hidden_inode, mask); out: print_exit_status(err); return err; } STATIC int cryptfs_inode_revalidate(dentry_t *dentry) { int err = 0; dentry_t *hidden_dentry; inode_t *hidden_inode; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry);/* CPW: Moved below print_entry_location */ hidden_inode = hidden_dentry->d_inode; // fist_print_dentry("cryptfs_inode_revalidate IN", dentry); if (hidden_inode->i_op && hidden_inode->i_op->revalidate) { err = hidden_inode->i_op->revalidate(hidden_dentry); if (!err) fist_copy_attr_all(dentry->d_inode, hidden_inode); } // fist_print_dentry("cryptfs_inode_revalidate OUT", dentry); print_exit_status(err); return err; } STATIC int cryptfs_setattr(dentry_t *dentry, struct iattr *ia) { int err = 0; dentry_t *hidden_dentry; inode_t *inode; inode_t *hidden_inode; print_entry_location(); hidden_dentry = cryptfs_hidden_dentry(dentry); inode = dentry->d_inode; hidden_inode = itohi(inode); fist_checkinode(inode, "cryptfs_setattr"); if (ia->ia_valid & ATTR_SIZE) { err = cryptfs_truncate(dentry, ia->ia_size); if (err < 0) goto out; } err = notify_change(hidden_dentry, ia); #if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) out: #endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ /* * The lower file system might has changed the attributes, even if * notify_change above resulted in an error(!) so we copy the * hidden_inode's attributes (and a few more) to our inode. */ fist_copy_attr_all(inode, hidden_inode); fist_checkinode(inode, "post cryptfs_setattr"); print_exit_status(err); return err; } #ifdef NOT_USED_YET STATIC int cryptfs_getattr(dentry_t *dentry, struct iattr *ia) { return -ENOSYS; } #endif /* NOT_USED_YET */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) /* This is lifted from fs/xattr.c */ static void * xattr_alloc(size_t size, size_t limit) { void *ptr; if (size > limit) return ERR_PTR(-E2BIG); if (!size) /* size request, no buffer is needed */ return NULL; else if (size <= PAGE_SIZE) ptr = kmalloc((unsigned long) size, GFP_KERNEL); else ptr = vmalloc((unsigned long) size); if (!ptr) return ERR_PTR(-ENOMEM); return ptr; } static void xattr_free(void *ptr, size_t size) { if (!size) /* size request, no buffer was needed */ return; else if (size <= PAGE_SIZE) kfree(ptr); else vfree(ptr); } /* BKL held by caller. * dentry->d_inode->i_sem down */ STATIC int cryptfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *hidden_dentry = NULL; int err = -ENOTSUPP; char *encoded_suffix = NULL; int namelen = 0; int sufflen = 0; int prelen = 0; /* Define these anyway so we don't need as much ifdef'ed code. */ char *encoded_name = NULL; char *encoded_value = NULL; print_entry_location(); hidden_dentry = dtohd(dentry); ASSERT(hidden_dentry); ASSERT(hidden_dentry->d_inode); ASSERT(hidden_dentry->d_inode->i_op); fist_dprint(18, "getxattr: name=\"%s\", value %d bytes\n", name, size); if (hidden_dentry->d_inode->i_op->getxattr) { const char *suffix; namelen = strlen(name); suffix = strchr(name, '.'); if (suffix) { suffix ++; prelen = (suffix - name) - 1; sufflen = namelen - (prelen + 1); } else { prelen = 0; sufflen = namelen; suffix = name; } sufflen = cryptfs_encode_filename(suffix, sufflen, &encoded_suffix, DO_DOTS, dentry->d_inode, dentry->d_inode->i_sb) - 1; encoded_name = xattr_alloc(prelen + sufflen + 2, XATTR_SIZE_MAX); if (IS_ERR(encoded_name)) { err = PTR_ERR(encoded_name); encoded_name = NULL; goto out; } if (prelen) { memcpy(encoded_name, name, prelen); encoded_name[prelen] = '.'; memcpy(encoded_name + prelen + 1, encoded_suffix, sufflen); encoded_name[prelen + 1 + sufflen] = '\0'; } else { memcpy(encoded_name + prelen, encoded_suffix, sufflen); encoded_name[sufflen] = '\0'; } fist_dprint(18, "ENCODED XATTR NAME: %s\n", encoded_name); encoded_value = xattr_alloc(size, XATTR_SIZE_MAX); if (IS_ERR(encoded_value)) { err = PTR_ERR(encoded_value); encoded_name = NULL; goto out; } down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); /* Decode the value. */ if ((err > 0) && size) { int ret; ret = cryptfs_encode_block(encoded_value, value, err, dentry->d_inode, dentry->d_inode->i_sb, 0); if (ret < 0) { err = ret; goto out; } } } out: if (encoded_name) { xattr_free(encoded_name, namelen + 1); } if (encoded_value) { xattr_free(encoded_value, size); } print_exit_status(err); return err; } /* BKL held by caller. * dentry->d_inode->i_sem down */ STATIC int #if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,20) /* kernel is 2.4.20 */ cryptfs_setxattr(struct dentry *dentry, const char *name, void *value, size_t size, int flags) # else /* kernel is not 2.4.20 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) cryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) # endif /* kernel 2.4.21 or newer */ # endif /* kernel is not 2.4.20 */ { struct dentry *hidden_dentry = NULL; int err = -ENOTSUPP; char *encoded_suffix = NULL; int namelen = 0; int sufflen = 0; int prelen = 0; /* Define these anyway, so we don't have as much ifdef'ed code. */ char *encoded_value = NULL; char *encoded_name = NULL; print_entry_location(); hidden_dentry = dtohd(dentry); ASSERT(hidden_dentry); ASSERT(hidden_dentry->d_inode); ASSERT(hidden_dentry->d_inode->i_op); fist_dprint(18, "setxattr: name=\"%s\", value %d bytes, flags=%x\n", name, size, flags); if (hidden_dentry->d_inode->i_op->setxattr) { const char *suffix; namelen = strlen(name); suffix = strchr(name, '.'); if (suffix) { suffix ++; prelen = (suffix - name) - 1; sufflen = namelen - (prelen + 1); } else { prelen = 0; sufflen = namelen; suffix = name; } sufflen = cryptfs_encode_filename(suffix, sufflen, &encoded_suffix, DO_DOTS, dentry->d_inode, dentry->d_inode->i_sb) - 1; encoded_name = xattr_alloc(prelen + sufflen + 2, XATTR_SIZE_MAX); if (IS_ERR(encoded_name)) { err = PTR_ERR(encoded_name); encoded_name = NULL; goto out; } if (prelen) { memcpy(encoded_name, name, prelen); encoded_name[prelen] = '.'; memcpy(encoded_name + prelen + 1, encoded_suffix, sufflen); encoded_name[prelen + 1 + sufflen] = '\0'; } else { memcpy(encoded_name + prelen, encoded_suffix, sufflen); encoded_name[sufflen] = '\0'; } fist_dprint(18, "ENCODED XATTR NAME: %s\n", encoded_name); /* Encode the value. */ encoded_value = xattr_alloc(size, XATTR_SIZE_MAX); if (IS_ERR(encoded_value)) { err = PTR_ERR(encoded_value); encoded_value = NULL; goto out; } err = cryptfs_encode_block(value, encoded_value, size, dentry->d_inode, dentry->d_inode->i_sb, 0); if (err < 0) { goto out; } down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->setxattr(hidden_dentry, encoded_name, encoded_value, size, flags); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } out: if (encoded_name) { xattr_free(encoded_name, namelen + 1); } if (encoded_suffix) { kfree(encoded_suffix); } if (encoded_value) { xattr_free(encoded_value, size); } print_exit_status(err); return err; } /* BKL held by caller. * dentry->d_inode->i_sem down */ STATIC int cryptfs_removexattr(struct dentry *dentry, const char *name) { struct dentry *hidden_dentry = NULL; int err = -ENOTSUPP; char *encoded_name; char *encoded_suffix = NULL; int namelen = 0; int sufflen = 0; int prelen = 0; print_entry_location(); hidden_dentry = dtohd(dentry); ASSERT(hidden_dentry); ASSERT(hidden_dentry->d_inode); ASSERT(hidden_dentry->d_inode->i_op); fist_dprint(18, "removexattr: name=\"%s\"\n", name); if (hidden_dentry->d_inode->i_op->removexattr) { const char *suffix; namelen = strlen(name); suffix = strchr(name, '.'); if (suffix) { suffix ++; prelen = (suffix - name) - 1; sufflen = namelen - (prelen + 1); } else { prelen = 0; sufflen = namelen; suffix = name; } sufflen = cryptfs_encode_filename(suffix, sufflen, &encoded_suffix, DO_DOTS, dentry->d_inode, dentry->d_inode->i_sb) - 1; encoded_name = xattr_alloc(prelen + sufflen + 2, XATTR_SIZE_MAX); if (IS_ERR(encoded_name)) { err = PTR_ERR(encoded_name); encoded_name = NULL; goto out; } if (prelen) { memcpy(encoded_name, name, prelen); encoded_name[prelen] = '.'; memcpy(encoded_name + prelen + 1, encoded_suffix, sufflen); encoded_name[prelen + 1 + sufflen] = '\0'; } else { memcpy(encoded_name + prelen, encoded_suffix, sufflen); encoded_name[sufflen] = '\0'; } fist_dprint(18, "ENCODED XATTR NAME: %s\n", encoded_name); down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); } out: print_exit_status(err); return err; } /* BKL held by caller. * dentry->d_inode->i_sem down */ STATIC int cryptfs_listxattr(struct dentry *dentry, char *list, size_t size) { struct dentry *hidden_dentry = NULL; int err = -ENOTSUPP; char *encoded_list = NULL; print_entry_location(); hidden_dentry = dtohd(dentry); ASSERT(hidden_dentry); ASSERT(hidden_dentry->d_inode); ASSERT(hidden_dentry->d_inode->i_op); if (hidden_dentry->d_inode->i_op->listxattr) { encoded_list = xattr_alloc(size, XATTR_LIST_MAX); if (IS_ERR(encoded_list)) { err = PTR_ERR(encoded_list); encoded_list = NULL; goto out; } down(&hidden_dentry->d_inode->i_sem); /* lock_kernel() already done by caller. */ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); /* unlock_kernel() will be done by caller. */ up(&hidden_dentry->d_inode->i_sem); /* The problem is we don't know how big the xattrs * will be without decoding them ourselves. So, * we just have to copy it attribute by attribute * to figure out. * * XXX: If the buffer was set to zero then we * actually end up lying and passing through the * error. For some transformations this works, but * you may need to change it (e.g., using an SCA * in the name ala cryptfs). */ if (encoded_list) { int i; int newsize = 0; char *cur = encoded_list; char *encoded_suffix; while (cur && (cur < (encoded_list + err))) { const char *suffix; int namelen, sufflen, prelen; ASSERT(cur); printk("cur: %s\n", cur); namelen = strlen(cur); suffix = strchr(cur, '.'); if (suffix) { suffix ++; prelen = (suffix - cur) - 1; sufflen = namelen - (prelen + 1); } else { prelen = 0; sufflen = namelen; suffix = cur; } sufflen = cryptfs_decode_filename(suffix, sufflen, &encoded_suffix, DO_DOTS, dentry->d_inode, dentry->d_inode->i_sb); if (prelen) { if (newsize + sufflen + prelen + 2 < size) { /* COPY IT IN */ memcpy(list + newsize, cur, prelen); list[newsize + prelen] = '.'; memcpy(list + newsize + prelen + 1, encoded_suffix, sufflen); list[newsize + prelen + 1 + sufflen] = '\0'; } newsize += sufflen + prelen + 2; } else { if (newsize + sufflen + 1 < size) { memcpy(list + newsize, encoded_suffix, sufflen); list[newsize + sufflen] = '\0'; } newsize += sufflen + 1; } cur += (namelen + 1); } err = newsize; } } out: if (encoded_list) { xattr_free(encoded_list, size); } print_exit_status(err); return err; } # endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ struct inode_operations cryptfs_symlink_iops = { readlink: cryptfs_readlink, follow_link: cryptfs_follow_link, permission: cryptfs_permission, revalidate: cryptfs_inode_revalidate, setattr: cryptfs_setattr, #if 0 // XXX: off, b/c the VFS doesn't call getattr yet getattr: cryptfs_getattr, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) setxattr: cryptfs_setxattr, getxattr: cryptfs_getxattr, listxattr: cryptfs_listxattr, removexattr: cryptfs_removexattr # endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ }; struct inode_operations cryptfs_dir_iops = { create: cryptfs_create, lookup: cryptfs_lookup, link: cryptfs_link, unlink: cryptfs_unlink, symlink: cryptfs_symlink, mkdir: cryptfs_mkdir, rmdir: cryptfs_rmdir, mknod: cryptfs_mknod, rename: cryptfs_rename, /* no readlink/follow_link for non-symlinks */ // off because we have setattr // truncate: cryptfs_truncate, permission: cryptfs_permission, revalidate: cryptfs_inode_revalidate, setattr: cryptfs_setattr, #if 0 // XXX: off, b/c the VFS doesn't call getattr yet getattr: cryptfs_getattr, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) setxattr: cryptfs_setxattr, getxattr: cryptfs_getxattr, listxattr: cryptfs_listxattr, removexattr: cryptfs_removexattr # endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ }; struct inode_operations cryptfs_main_iops = { permission: cryptfs_permission, revalidate: cryptfs_inode_revalidate, setattr: cryptfs_setattr, #if 0 // XXX: off, b/c the VFS doesn't call getattr yet getattr: cryptfs_getattr, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) setxattr: cryptfs_setxattr, getxattr: cryptfs_getxattr, listxattr: cryptfs_listxattr, removexattr: cryptfs_removexattr # endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ }; /* * Local variables: * c-basic-offset: 4 * End: */