static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf) { ssize_t buflen; buflen = vfs_getxattr(dentry, key, NULL, 0); if (buflen <= 0) return buflen; *buf = kmalloc(buflen, GFP_KERNEL); if (!*buf) return -ENOMEM; return vfs_getxattr(dentry, key, *buf, buflen); }
int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); err = ovl_want_write(dentry); if (err) goto out; err = -ENODATA; if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) goto out_drop_write; if (!OVL_TYPE_UPPER(type)) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) goto out_drop_write; err = ovl_copy_up(dentry); if (err) goto out_drop_write; ovl_path_upper(dentry, &realpath); } err = vfs_removexattr(realpath.dentry, name); out_drop_write: ovl_drop_write(dentry); out: return err; }
/* release all lower object references & free the file info structure */ static int wrapfs_file_release(struct inode *inode, struct file *file) { struct file *lower_file; #ifdef EXTRA_CREDIT int CHKSUM_SIZE =0; char *algo = kmalloc(sizeof(char)*10,GFP_KERNEL); int *algo_len = kmalloc(sizeof(int)*1,GFP_KERNEL); char *getchkbuf = kmalloc(sizeof(char)*32,GFP_KERNEL); #else char *getchkbuf = kmalloc(sizeof(char)*CHKSUM_SIZE,GFP_KERNEL); #endif char *has_integrity = kmalloc(sizeof(char)*1,GFP_KERNEL); int rc; lower_file = wrapfs_lower_file(file); if (lower_file) { if(lower_file->f_mode == O_RDONLY) goto out_release; else if(!S_ISDIR(lower_file->f_path.dentry->d_inode->i_mode)&& wrapfs_get_write_dirty(inode) == WRITE_DIRTY_BIT) { #ifdef EXTRA_CREDIT CHKSUM_SIZE = get_default_chksum_size(lower_file->f_path.dentry,algo,algo_len); #endif if(!has_integrity || !getchkbuf) { rc = -ENOMEM; goto out_release; } spin_lock(&inode->i_lock); wrapfs_set_write_dirty(inode,READ_DIRTY_BIT); spin_unlock(&inode->i_lock); if(vfs_getxattr(lower_file->f_path.dentry,XATTR_HAS_INTEGRITY,has_integrity,1)>0) { if(!memcmp(has_integrity,"1",1)) { calculate_checksum(lower_file,getchkbuf,CHKSUM_SIZE); rc = vfs_setxattr(lower_file->f_path.dentry,XATTR_INTEGRITY_VAL,getchkbuf,CHKSUM_SIZE,XATTR_CREATE); if(rc == -EEXIST) rc = vfs_setxattr(lower_file->f_path.dentry,XATTR_INTEGRITY_VAL,getchkbuf,CHKSUM_SIZE,XATTR_REPLACE); } } } out_release: wrapfs_set_lower_file(file, NULL); fput(lower_file); } kfree(WRAPFS_F(file)); kfree(getchkbuf); kfree(has_integrity); #ifdef EXTRA_CREDIT kfree(algo); kfree(algo_len); #endif return 0; }
int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; enum ovl_path_type type; if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ovl_is_private_xattr(name)) return -ENODATA; type = ovl_path_real(dentry, &realpath); if (type == OVL_PATH_LOWER) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) return err; err = ovl_copy_up(dentry); if (err) return err; ovl_path_upper(dentry, &realpath); } return vfs_removexattr(realpath.dentry, name); }
/* * Extended attribute GET operations */ static ssize_t getxattr(struct dentry *d, const char __user *name, void __user *value, size_t size) { ssize_t error; void *kvalue = NULL; char kname[XATTR_NAME_MAX + 1]; error = strncpy_from_user(kname, name, sizeof(kname)); if (error == 0 || error == sizeof(kname)) error = -ERANGE; if (error < 0) return error; if (size) { if (size > XATTR_SIZE_MAX) size = XATTR_SIZE_MAX; kvalue = kzalloc(size, GFP_KERNEL); if (!kvalue) return -ENOMEM; } error = vfs_getxattr(d, kname, kvalue, size); if (error > 0) { if (size && copy_to_user(value, kvalue, error)) error = -EFAULT; } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { /* The file system tried to returned a value bigger than XATTR_SIZE_MAX bytes. Not possible. */ error = -E2BIG; } kfree(kvalue); return error; }
int ovl_removexattr(struct dentry *dentry, const char *name) { int err; struct path realpath; enum ovl_path_type type; err = ovl_want_write(dentry); if (err) goto out; if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ovl_is_private_xattr(name)) goto out_drop_write; type = ovl_path_real(dentry, &realpath); if (type == OVL_PATH_LOWER) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) goto out_drop_write; err = ovl_copy_up(dentry); if (err) goto out_drop_write; ovl_path_upper(dentry, &realpath); } err = vfs_removexattr(realpath.dentry, name); out_drop_write: ovl_drop_write(dentry); out: return err; }
/* * BKL held by caller. * dentry->d_inode->i_mutex locked */ ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *lower_dentry = NULL; struct dentry *parent; int err = -EOPNOTSUPP; bool valid; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false); if (unlikely(!valid)) { err = -ESTALE; goto out; } lower_dentry = unionfs_lower_dentry(dentry); err = vfs_getxattr(lower_dentry, (char *) name, value, size); out: unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ovl_is_private_xattr(name)) return -ENODATA; return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); }
ssize_t ovl_getxattr(struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size) { struct dentry *realdentry = ovl_dentry_real(dentry); if (ovl_is_private_xattr(name)) return -ENODATA; return vfs_getxattr(realdentry, name, value, size); }
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) return -ENODATA; return vfs_getxattr(realpath.dentry, name, value, size); }
int ovl_xattr_get(struct dentry *dentry, const char *name, void *value, size_t size) { struct dentry *realdentry = ovl_dentry_real(dentry); ssize_t res; const struct cred *old_cred; old_cred = ovl_override_creds(dentry->d_sb); res = vfs_getxattr(realdentry, name, value, size); revert_creds(old_cred); return res; }
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size) { ssize_t res; const struct cred *old_cred; struct dentry *realdentry = ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry); old_cred = ovl_override_creds(dentry->d_sb); res = vfs_getxattr(realdentry, name, value, size); revert_creds(old_cred); return res; }
static bool ovl_is_opaquedir(struct dentry *dentry) { int res; char val; if (!S_ISDIR(dentry->d_inode->i_mode)) return false; res = vfs_getxattr(dentry, ovl_opaque_xattr, &val, 1); if (res == 1 && val == 'y') return true; return false; }
bool ovl_is_whiteout(struct dentry *dentry) { int res; char val; if (!dentry) return false; if (!dentry->d_inode) return false; if (!S_ISLNK(dentry->d_inode->i_mode)) return false; res = vfs_getxattr(dentry, ovl_whiteout_xattr, &val, 1); if (res == 1 && val == 'y') return true; return false; }
static ssize_t diaryfs_getxattr(struct dentry *dentry, const char * name, void *buffer, size_t size) { int err; struct dentry * lower_dentry; struct path lower_path; diaryfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; if (!lower_dentry->d_inode->i_op->getxattr) { err = -EOPNOTSUPP; goto out; } err = vfs_getxattr(lower_dentry, name, buffer, size); if (err) goto out; fsstack_copy_attr_atime(dentry->d_inode, lower_path.dentry->d_inode); out: diaryfs_put_lower_path(dentry, &lower_path); return err; }
ssize_t amfs_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) { int err; struct dentry *lower_dentry; struct path lower_path; amfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; if (!lower_dentry->d_inode->i_op || !lower_dentry->d_inode->i_op->getxattr) { err = -EINVAL; goto out; } err = vfs_getxattr(lower_dentry, name, buffer, size); out: amfs_put_lower_path(dentry, &lower_path); return err; }
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, const void *value, size_t size, int flags) { int err; struct dentry *upperdentry = ovl_i_dentry_upper(inode); struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); const struct cred *old_cred; err = ovl_want_write(dentry); if (err) goto out; if (!value && !upperdentry) { err = vfs_getxattr(realdentry, name, NULL, 0); if (err < 0) goto out_drop_write; } if (!upperdentry) { err = ovl_copy_up(dentry); if (err) goto out_drop_write; realdentry = ovl_dentry_upper(dentry); } old_cred = ovl_override_creds(dentry->d_sb); if (value) err = vfs_setxattr(realdentry, name, value, size, flags); else { WARN_ON(flags != XATTR_REPLACE); err = vfs_removexattr(realdentry, name); } revert_creds(old_cred); out_drop_write: ovl_drop_write(dentry); out: return err; }
int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { int err; struct path realpath; enum ovl_path_type type = ovl_path_real(dentry, &realpath); const struct cred *old_cred; err = ovl_want_write(dentry); if (err) goto out; if (!value && !OVL_TYPE_UPPER(type)) { err = vfs_getxattr(realpath.dentry, name, NULL, 0); if (err < 0) goto out_drop_write; } err = ovl_copy_up(dentry); if (err) goto out_drop_write; if (!OVL_TYPE_UPPER(type)) ovl_path_upper(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); if (value) err = vfs_setxattr(realpath.dentry, name, value, size, flags); else { WARN_ON(flags != XATTR_REPLACE); err = vfs_removexattr(realpath.dentry, name); } revert_creds(old_cred); out_drop_write: ovl_drop_write(dentry); out: return err; }
unsigned int ovl_get_nlink(struct dentry *lowerdentry, struct dentry *upperdentry, unsigned int fallback) { int nlink_diff; int nlink; char buf[13]; int err; if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1) return fallback; err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1); if (err < 0) goto fail; buf[err] = '\0'; if ((buf[0] != 'L' && buf[0] != 'U') || (buf[1] != '+' && buf[1] != '-')) goto fail; err = kstrtoint(buf + 1, 10, &nlink_diff); if (err < 0) goto fail; nlink = d_inode(buf[0] == 'L' ? lowerdentry : upperdentry)->i_nlink; nlink += nlink_diff; if (nlink <= 0) goto fail; return nlink; fail: pr_warn_ratelimited("overlayfs: failed to get index nlink (%pd2, err=%i)\n", upperdentry, err); return fallback; }
static int wrapfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int err = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; int alloc_size = 1024; char *buf = kmalloc(alloc_size, GFP_KERNEL); wrapfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); if (err) goto out; err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, wrapfs_lower_inode(dir)->i_nlink); /*****************************************************/ if (!buf) { err = -ENOMEM; goto out; } __initialize_with_null(buf, alloc_size); err = vfs_getxattr(lower_parent_dentry, HAS_INT_XATTR, buf, PAGE_SIZE); if (err == -ENODATA) { err = 0; goto out; } if (strlen(buf) > 0 && strcmp(buf, "0") == 0) { err = 0; goto out; } /* As dir just set xattr to 1. NO calculation of integrity is required. */ err = vfs_setxattr(dentry, HAS_INT_XATTR, "1", 1, 0); if (err) goto out; /******************************************************/ out: mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); wrapfs_put_lower_path(dentry, &lower_path); return err; }
int calculate_integrity(struct file *filp, char *ibuf, int ilen) { int r = -1 , ret = -1; ssize_t vfs_read_retval = 0; loff_t file_offset = 0; mm_segment_t oldfs = get_fs(); char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); struct scatterlist sg; struct crypto_hash *tfm = NULL; struct hash_desc desc; char *algo = kmalloc(1024, GFP_KERNEL); if (!algo) { ret = -ENOMEM; goto out; } __initialize_with_null(algo, 1024); #ifdef EXTRA_CREDIT ret = vfs_getxattr(filp->f_path.dentry, INT_TYPE_XATTR, algo, 1024); if (ret <= 0) __initialize_with_null(algo, 1024); #endif if (*algo == '\0') strcpy(algo, DEFAULT_ALGO); if (!buf) goto out; __initialize_with_null(ibuf, ilen); if (!filp->f_op->read) { r = -2; goto out; } filp->f_pos = 0; set_fs(KERNEL_DS); tfm = crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { r = -EINVAL; goto out; } desc.tfm = tfm; desc.flags = 0; if (crypto_hash_digestsize(tfm) > ilen) { r = -EINVAL; goto out; } crypto_hash_setkey(tfm, key, strlen(key)); ret = crypto_hash_init(&desc); if (ret) { r = ret; goto out; } sg_init_table(&sg, 1); file_offset = 0; do { vfs_read_retval = vfs_read(filp, buf, PAGE_SIZE, &file_offset); if (vfs_read_retval < 0) { ret = vfs_read_retval; goto out; } sg_set_buf(&sg, (u8 *)buf, vfs_read_retval); ret = crypto_hash_update(&desc, &sg, sg.length); if (ret) { r = ret; goto out; } if (vfs_read_retval < ksize(buf)) break; } while (1); ret = crypto_hash_final(&desc, ibuf); if (ret) { r = ret; goto out; } out: kfree(buf); kfree(algo); if (!IS_ERR(tfm)) crypto_free_hash(tfm); set_fs(oldfs); return ret; }
int cachefiles_check_object_xattr(struct cachefiles_object *object, struct cachefiles_xattr *auxdata) { struct cachefiles_xattr *auxbuf; struct dentry *dentry = object->dentry; int ret; _enter("%p,#%d", object, auxdata->len); ASSERT(dentry); ASSERT(dentry->d_inode); auxbuf = kmalloc(sizeof(struct cachefiles_xattr) + 512, GFP_KERNEL); if (!auxbuf) { _leave(" = -ENOMEM"); return -ENOMEM; } /* read the current type label */ ret = vfs_getxattr(dentry, cachefiles_xattr_cache, &auxbuf->type, 512 + 1); if (ret < 0) { if (ret == -ENODATA) goto stale; /* no attribute - power went off * mid-cull? */ if (ret == -ERANGE) goto bad_type_length; cachefiles_io_error_obj(object, "Can't read xattr on %lu (err %d)", dentry->d_inode->i_ino, -ret); goto error; } /* check the on-disk object */ if (ret < 1) goto bad_type_length; if (auxbuf->type != auxdata->type) goto stale; auxbuf->len = ret; /* consult the netfs */ if (object->fscache.cookie->def->check_aux) { enum fscache_checkaux result; unsigned int dlen; dlen = auxbuf->len - 1; _debug("checkaux %s #%u", object->fscache.cookie->def->name, dlen); result = fscache_check_aux(&object->fscache, &auxbuf->data, dlen); switch (result) { /* entry okay as is */ case FSCACHE_CHECKAUX_OKAY: goto okay; /* entry requires update */ case FSCACHE_CHECKAUX_NEEDS_UPDATE: break; /* entry requires deletion */ case FSCACHE_CHECKAUX_OBSOLETE: goto stale; default: BUG(); } /* update the current label */ ret = vfs_setxattr(dentry, cachefiles_xattr_cache, &auxdata->type, auxdata->len, XATTR_REPLACE); if (ret < 0) { cachefiles_io_error_obj(object, "Can't update xattr on %lu" " (error %d)", dentry->d_inode->i_ino, -ret); goto error; } } okay: ret = 0; error: kfree(auxbuf); _leave(" = %d", ret); return ret; bad_type_length: kerror("Cache object %lu xattr length incorrect", dentry->d_inode->i_ino); ret = -EIO; goto error; stale: ret = -ESTALE; goto error; }
int cachefiles_check_object_type(struct cachefiles_object *object) { struct dentry *dentry = object->dentry; char type[3], xtype[3]; int ret; ASSERT(dentry); ASSERT(dentry->d_inode); if (!object->fscache.cookie) strcpy(type, "C3"); else snprintf(type, 3, "%02x", object->fscache.cookie->def->type); _enter("%p{%s}", object, type); /* attempt to install a type label directly */ ret = vfs_setxattr(dentry, cachefiles_xattr_cache, type, 2, XATTR_CREATE); if (ret == 0) { _debug("SET"); /* we succeeded */ goto error; } if (ret != -EEXIST) { kerror("Can't set xattr on %*.*s [%lu] (err %d)", dentry->d_name.len, dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino, -ret); goto error; } /* read the current type label */ ret = vfs_getxattr(dentry, cachefiles_xattr_cache, xtype, 3); if (ret < 0) { if (ret == -ERANGE) goto bad_type_length; kerror("Can't read xattr on %*.*s [%lu] (err %d)", dentry->d_name.len, dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino, -ret); goto error; } /* check the type is what we're expecting */ if (ret != 2) goto bad_type_length; if (xtype[0] != type[0] || xtype[1] != type[1]) goto bad_type; ret = 0; error: _leave(" = %d", ret); return ret; bad_type_length: kerror("Cache object %lu type xattr length incorrect", dentry->d_inode->i_ino); ret = -EIO; goto error; bad_type: xtype[2] = 0; kerror("Cache object %*.*s [%lu] type %s not %s", dentry->d_name.len, dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino, xtype, type); ret = -EIO; goto error; }
/* copyup all extended attrs for a given dentry */ static int copyup_xattrs(struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) { int err = 0; ssize_t list_size = -1; char *name_list = NULL; char *attr_value = NULL; char *name_list_orig = NULL; list_size = vfs_listxattr(old_hidden_dentry, NULL, 0); if (list_size <= 0) { err = list_size; goto out; } name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); if (!name_list || IS_ERR(name_list)) { err = PTR_ERR(name_list); goto out; } list_size = vfs_listxattr(old_hidden_dentry, name_list, list_size); attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); if (!attr_value || IS_ERR(attr_value)) { err = PTR_ERR(name_list); goto out; } name_list_orig = name_list; while (*name_list) { ssize_t size; /* Lock here since vfs_getxattr doesn't lock for us */ mutex_lock(&old_hidden_dentry->d_inode->i_mutex); size = vfs_getxattr(old_hidden_dentry, name_list, attr_value, XATTR_SIZE_MAX); mutex_unlock(&old_hidden_dentry->d_inode->i_mutex); if (size < 0) { err = size; goto out; } if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out; } /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_hidden_dentry, name_list, attr_value, size, 0); if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: name_list = name_list_orig; if (name_list) unionfs_xattr_free(name_list, list_size + 1); if (attr_value) unionfs_xattr_free(attr_value, XATTR_SIZE_MAX); /* It is no big deal if this fails, we just roll with the punches. */ if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; }
static int wrapfs_open(struct inode *inode, struct file *file) { int err = 0; struct file *lower_file = NULL; struct path lower_path; #ifdef EXTRA_CREDIT int CHKSUM_SIZE =0; char *algo = kmalloc(sizeof(char)*10,GFP_KERNEL); int *algo_len = kmalloc(sizeof(char)*1,GFP_KERNEL); char *chkbuf = kmalloc(sizeof(char)*32,GFP_KERNEL); char *getchkbuf = kmalloc(sizeof(char)*32,GFP_KERNEL); #else char *chkbuf = kmalloc(sizeof(char)*CHKSUM_SIZE,GFP_KERNEL); char *getchkbuf = kmalloc(sizeof(char)*CHKSUM_SIZE,GFP_KERNEL); #endif char *has_integrity = kmalloc(sizeof(char)*1,GFP_KERNEL); int rc = 0; if(!chkbuf || !has_integrity || !getchkbuf) { err = -ENOMEM; goto out_err; } /* don't open unhashed/deleted files */ if (d_unhashed(file->f_path.dentry)) { err = -ENOENT; goto out_err; } file->private_data = kzalloc(sizeof(struct wrapfs_file_info), GFP_KERNEL); if (!WRAPFS_F(file)) { err = -ENOMEM; goto out_err; } /* open lower object and link wrapfs's file struct to lower's */ wrapfs_get_lower_path(file->f_path.dentry, &lower_path); lower_file = dentry_open(lower_path.dentry, lower_path.mnt, file->f_flags, current_cred()); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); lower_file = wrapfs_lower_file(file); if (lower_file) { wrapfs_set_lower_file(file, NULL); fput(lower_file); /* fput calls dput for lower_dentry */ } } else { #ifdef EXTRA_CREDIT CHKSUM_SIZE = get_default_chksum_size(lower_path.dentry,algo,algo_len); #endif rc = vfs_getxattr(lower_path.dentry,XATTR_HAS_INTEGRITY,has_integrity,1); if(rc > 0 && !S_ISDIR(lower_path.dentry->d_inode->i_mode)) { wrapfs_set_lower_file(file,lower_file); if(lower_file->f_mode == O_TRUNC) wrapfs_set_write_dirty(inode,WRITE_DIRTY_BIT); if(!memcmp(has_integrity,"1",1) && wrapfs_get_write_dirty(inode)!=WRITE_DIRTY_BIT && rc ==1) { if(vfs_getxattr(lower_path.dentry,XATTR_INTEGRITY_VAL,chkbuf,CHKSUM_SIZE)>0) { //mutex_lock(&lower_path.dentry->d_inode->i_mutex); calculate_checksum(lower_file,getchkbuf,CHKSUM_SIZE); if(memcmp(chkbuf,getchkbuf,CHKSUM_SIZE)) { printk("Integrity mismatch\n"); err = -EPERM; wrapfs_set_lower_file(file,NULL); fput(lower_file); } //mutex_unlock(&lower_path.dentry->d_inode->i_mutex); } } else if(!memcmp(has_integrity,"0",1) && rc ==1) { if(vfs_getxattr(lower_path.dentry,XATTR_INTEGRITY_VAL,chkbuf,CHKSUM_SIZE)>0) { err = -EIO; wrapfs_set_lower_file(file,NULL); fput(lower_file); } else wrapfs_set_lower_file(file,lower_file); } else { printk("File corrupted.Unexpected value for has_integrity attribute\n"); err = -EPERM; wrapfs_set_lower_file(file,NULL); fput(lower_file); } } else if(vfs_getxattr(lower_path.dentry,XATTR_HAS_INTEGRITY,has_integrity,1)<=0 && vfs_getxattr(lower_path.dentry,XATTR_INTEGRITY_VAL,chkbuf,CHKSUM_SIZE)>0) { err = -EIO; wrapfs_set_lower_file(file,NULL); fput(lower_file); } else { wrapfs_set_lower_file(file, lower_file); } } if (err) kfree(WRAPFS_F(file)); else fsstack_copy_attr_all(inode, wrapfs_lower_inode(inode)); out_err: kfree(chkbuf); kfree(getchkbuf); kfree(has_integrity); #ifdef EXTRA_CREDIT kfree(algo); kfree(algo_len); #endif return err; }
/* copyup all extended attrs for a given dentry */ static int copyup_xattrs(struct dentry *old_lower_dentry, struct dentry *new_lower_dentry) { int err = 0; ssize_t list_size = -1; char *name_list = NULL; char *attr_value = NULL; char *name_list_buf = NULL; /* query the actual size of the xattr list */ list_size = vfs_listxattr(old_lower_dentry, NULL, 0); if (list_size <= 0) { err = list_size; goto out; } /* allocate space for the actual list */ name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); if (unlikely(!name_list || IS_ERR(name_list))) { err = PTR_ERR(name_list); goto out; } name_list_buf = name_list; /* save for kfree at end */ /* now get the actual xattr list of the source file */ list_size = vfs_listxattr(old_lower_dentry, name_list, list_size); if (list_size <= 0) { err = list_size; goto out; } /* allocate space to hold each xattr's value */ attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); if (unlikely(!attr_value || IS_ERR(attr_value))) { err = PTR_ERR(name_list); goto out; } /* in a loop, get and set each xattr from src to dst file */ while (*name_list) { ssize_t size; /* Lock here since vfs_getxattr doesn't lock for us */ mutex_lock(&old_lower_dentry->d_inode->i_mutex); size = vfs_getxattr(old_lower_dentry, name_list, attr_value, XATTR_SIZE_MAX); mutex_unlock(&old_lower_dentry->d_inode->i_mutex); if (size < 0) { err = size; goto out; } if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out; } /* Don't lock here since vfs_setxattr does it for us. */ err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); /* * Selinux depends on "security.*" xattrs, so to maintain * the security of copied-up files, if Selinux is active, * then we must copy these xattrs as well. So we need to * temporarily get FOWNER privileges. * XXX: move entire copyup code to SIOQ. */ if (err == -EPERM && !capable(CAP_FOWNER)) { cap_raise(current->cap_effective, CAP_FOWNER); err = vfs_setxattr(new_lower_dentry, name_list, attr_value, size, 0); cap_lower(current->cap_effective, CAP_FOWNER); } if (err < 0) goto out; name_list += strlen(name_list) + 1; } out: unionfs_xattr_kfree(name_list_buf); unionfs_xattr_kfree(attr_value); /* Ignore if xattr isn't supported */ if (err == -ENOTSUPP || err == -EOPNOTSUPP) err = 0; return err; }
static int wrapfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { int err = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path, saved_path; int alloc_size = 1024; char *buf = kmalloc(alloc_size, GFP_KERNEL); char *fbuf = kmalloc(alloc_size, GFP_KERNEL); struct vfsmount *mnt = NULL; struct file *filp = NULL; wrapfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); err = mnt_want_write(lower_path.mnt); if (err) goto out_unlock; pathcpy(&saved_path, &nd->path); pathcpy(&nd->path, &lower_path); err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); pathcpy(&nd->path, &saved_path); if (err) goto out; err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /*********************************************************/ if (!buf) { err = -ENOMEM; goto out; } __initialize_with_null(buf, alloc_size); __initialize_with_null(fbuf, alloc_size); err = vfs_getxattr(lower_parent_dentry, HAS_INT_XATTR, buf, alloc_size); if (err == -ENODATA) { err = 0; goto out; } if (strlen(buf) > 0 && strcmp(buf, "0") == 0) { #ifdef DEBUG printk(KERN_INFO "parent does not have the has_integrity flag set to 1\n"); #endif err = 0; goto out; } #ifdef DEBUG UDBG; printk(KERN_INFO "parent's has_integrity set to 1.Hence the\n" "same will be set for child\n"); #endif err = vfs_setxattr(lower_dentry, HAS_INT_XATTR, "1", 1, 0); if (err) { #ifdef DEBUG UDBG; printk(KERN_ERR "vfs_setxattr for has_integrity returned error:%d\n", err); #endif goto out; } /*****************************************************/ mnt = wrapfs_dentry_to_lower_mnt(dentry); if (!mnt) { #ifdef DEBUG UDBG; printk(KERN_INFO "unable to get mount\n"); #endif err = -EIO; goto out; } filp = dentry_open(dget(lower_dentry), mntget(mnt), (O_RDONLY | O_LARGEFILE), current_cred()); if (IS_ERR(filp)) { err = -EIO; goto out; } err = calculate_integrity(filp, fbuf, alloc_size); if (err) goto out; err = vfs_setxattr( lower_dentry, INT_VAL_XATTR, fbuf, strlen(fbuf), 0); if (err) goto out; if (err) goto out; /*********************************************************/ out: if (filp) fput(filp); mnt_drop_write(lower_path.mnt); out_unlock: unlock_dir(lower_parent_dentry); wrapfs_put_lower_path(dentry, &lower_path); return err; }