// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/xattr.h>
#include <linux/gfs2_ondisk.h>
#include <linux/posix_acl_xattr.h>
#include <linux/uaccess.h>
#include "gfs2.h"
#include "incore.h"
#include "acl.h"
#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "quota.h"
#include "rgrp.h"
#include "super.h"
#include "trans.h"
#include "util.h"
/*
* ea_calc_size - returns the actual number of bytes the request will take up
* (not counting any unstuffed data blocks)
*
* Returns: 1 if the EA should be stuffed
*/
static int ea_calc_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize,
unsigned int *size)
{
unsigned int jbsize = sdp->sd_jbsize;
/* Stuffed */
*size = ALIGN(sizeof(struct gfs2_ea_header) + nsize + dsize, 8);
if (*size <= jbsize)
return 1;
/* Unstuffed */
*size = ALIGN(sizeof(struct gfs2_ea_header) + nsize +
(sizeof(__be64) * DIV_ROUND_UP(dsize, jbsize)), 8);
return 0;
}
static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize)
{
unsigned int size;
if (dsize > GFS2_EA_MAX_DATA_LEN)
return -ERANGE;
ea_calc_size(sdp, nsize, dsize, &size);
/* This can only happen with 512 byte blocks */
if (size > sdp->sd_jbsize)
return -ERANGE;
return 0;
}
static bool gfs2_eatype_valid(struct gfs2_sbd *sdp, u8 type)
{
switch(sdp->sd_sb.sb_fs_format) {
case GFS2_FS_FORMAT_MAX:
return true;
case GFS2_FS_FORMAT_MIN:
return type <= GFS2_EATYPE_SECURITY;
default:
return false;
}
}
typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea,
struct gfs2_ea_header *prev, void *private);
static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
ea_call_t ea_call, void *data)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_ea_header *ea, *prev = NULL;
int error = 0;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_EA))
return -EIO;
for (ea = GFS2_EA_BH2FIRST(bh);; prev = ea, ea = GFS2_EA2NEXT(ea)) {
if (!GFS2_EA_REC_LEN(ea))
goto fail;
if (!(bh->b_data <= (char *)ea && (char *)GFS2_EA2NEXT(ea) <=
bh->b_data + bh->b_size))
goto fail;
if (!gfs2_eatype_valid(sdp, ea->ea_type))
goto fail;
error = ea_call(ip, bh, ea, prev, data);
if (error)
return error;
if (GFS2_EA_IS_LAST(ea)) {
if ((char *)GFS2_EA2NEXT(ea) !=
bh->b_data + bh->b_size)
goto fail;
break;
}
}
return error;
fail:
gfs2_consist_inode(ip);
return -EIO;
}
static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
{
struct buffer_head *bh, *eabh;
__be64 *eablk, *end;
int error;
error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0, &bh);
if (error)
return error;
if (!(ip->i_diskflags & GFS2_DIF_EA_INDIRECT)) {
error = ea_foreach_i(ip, bh, ea_call, data);
goto out;
}
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_IN)) {
error = -EIO;
goto out;
}
eablk = (__be64 *)(bh->b_data + sizeof(struct gfs2_meta_header));
end = eablk + GFS2_SB(&ip->i_inode)->sd_inptrs;
for (; eablk < end; eablk++) {
u64 bn;
if (!*eablk)
break;
bn = be64_to_cpu(*eablk);
error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, 0, &eabh);
if (error)
break;
error = ea_foreach_i(ip, eabh, ea_call, data);
brelse(eabh);
if (error)
break;
}
out:
brelse(bh);
return error;
}
struct ea_find {
int type;
const char *name;
size_t namel;
struct gfs2_ea_location *ef_el;
};
static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
void *private)
{
struct ea_find *ef = private;
if (ea->ea_type == GFS2_EATYPE_UNUSED)
return 0;
if (ea->ea_type == ef->type) {
if (ea->ea_name_len == ef->namel &&
!memcmp(GFS2_EA2NAME(ea), ef->name, ea->ea_name_len)) {
struct gfs2_ea_location *el = ef->ef_el;
get_bh(bh);
el->el_bh = bh;
el->el_ea = ea;
el->el_prev = prev;
return 1;
}
}
return 0;
}
static int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name,
struct gfs2_ea_location *el)
{
struct ea_find ef;
int error;
ef.type = type;
ef.name = name;
ef.namel = strlen(name);
ef.ef_el = el;
memset(el, 0, sizeof(struct gfs2_ea_location));
error = ea_foreach(ip, ea_find_i, &ef);
if (error > 0)
return 0;
return error;
}
/*
* ea_dealloc_unstuffed
*
* Take advantage of the fact that all unstuffed blocks are
* allocated from the same RG. But watch, this may not always
* be true.
*
* Returns: errno
*/
static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea,
struct gfs2_ea_header *prev, void *private)
{
int *leave = private;
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_rgrpd *rgd;
struct gfs2_holder rg_gh;
__be64 *dataptrs;
u64 bn = 0;
u64 bstart = 0;
unsigned int blen = 0;
unsigned int blks = 0;
unsigned int x;
int error;
error = gfs2_rindex_update(sdp);
if (error)
return error;
if (GFS2_EA_IS_STUFFED(ea))
return 0;
dataptrs = GFS2_EA2DATAPTRS(ea);
for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++) {
if (*dataptrs) {
blks++;
bn = be64_to_cpu(*dataptrs);
}
}
if (!blks)
return 0;
rgd = gfs2_blk2rgrpd(sdp, bn, 1);
if (!rgd) {
gfs2_consist_inode(ip);
return -EIO;
}
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
LM_FLAG_NODE_SCOPE, &rg_gh);
if (error)
return error;
error = gfs2_trans_begin(sdp, rgd->rd_length + RES_DINODE +
RES_EATTR + RES_STATFS + RES_QUOTA, blks);
if (error)
goto out_gunlock;
gfs2_trans_add_meta(ip->i_gl, bh);
dataptrs = GFS2_EA2DATAPTRS(ea);
for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++) {
if (!*dataptrs)
break;
bn = be64_to_cpu(*dataptrs);
if (bstart + blen == bn)
blen++;
else {
if (bstart)
gfs2_free_meta(ip, rgd, bstart, blen);
bstart = bn;
blen = 1;
}
*dataptrs = 0;
gfs2_add_inode_blocks(&ip->i_inode, -1);
}
if (bstart)
gfs2_free_meta(ip, rgd, bstart, blen);
if (prev && !leave) {
u32 len;
len = GFS2_EA_REC_LEN(prev) + GFS2_EA_REC_LEN(ea);
prev->ea_rec_len = cpu_to_be32(len);
if (GFS2_EA_IS_LAST(ea))
prev->ea_flags |= GFS2_EAFLAG_LAST;
} else {
ea->ea_type = GFS2_EATYPE_UNUSED;
ea->ea_num_ptrs = 0;
}
ip->i_inode.i_ctime = current_time(&ip->i_inode);
__mark_inode_dirty(&ip->i_inode, I_DIRTY_DATASYNC);
gfs2_trans_end(sdp);
out_gunlock:
gfs2_glock_dq_uninit(&rg_gh);
return error;
}
static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea,
struct gfs2_ea_header *prev, int leave)
{
int error;
error = gfs2_rindex_update(GFS2_SB(&ip->i_inode));
if (error)
return error;
error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE);
if (error)
goto out_alloc;
error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL);
gfs2_quota_unhold(ip);
out_alloc:
return error;
}
struct ea_list {
struct gfs2_ea_request *ei_er;
unsigned int ei_size;
};
static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
void *private)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct ea_list *ei = private;
struct gfs2_ea_request *er = ei->ei_er;
unsigned int ea_size;
char *prefix;
unsigned int l;
if (ea->ea_type == GFS2_EATYPE_UNUSED)
return 0;
BUG_ON(ea->ea_type > GFS2_EATYPE_SECURITY &&
sdp->sd_sb.sb_fs_format == GFS2_FS_FORMAT_MIN);
switch (ea->ea_type) {
case GFS2_EATYPE_USR:
prefix = "user.";
l = 5;
break;
case GFS2_EATYPE_SYS:
prefix = "system.";
l = 7;
break;
case GFS2_EATYPE_SECURITY:
prefix = "security.";
l = 9;
break;
case GFS2_EATYPE_TRUSTED:
prefix = "trusted.";
l = 8;
break;
default:
return 0;
}
ea_size = l + ea->ea_name_len + 1;
if (er->er_data_len) {
if (ei->ei_size + ea_size > er->er_data_len)
return -ERANGE;
me
|