xattr.c revision 9b24702912cd165acd5682b2c0c853496d320536
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Big Theory Statement for Extended Attribute (XATTR) directories
*
* The Solaris VFS layer presents extended file attributes using a special
* "XATTR" directory under files or directories that have extended file
* attributes. See fsattr(5) for background.
*
* This design avoids the need for a separate set of VFS or vnode functions
* for operating on XATTR objects. File system implementations that support
* XATTR instantiate a special XATTR directory using this module.
* Applications get to the XATTR directory by passing the LOOKUP_XATTR flag
* to fop_lookup. Once the XATTR directory is obtained, all other file
* system operations on extended attributes happen via the normal vnode
* functions, applied to the XATTR directory or its contents.
*
* The XATTR directories returned by fop_lookup (with LOOKUP_XATTR) are
* implemented differntly, depending on whether the file system supports
* "extended attributes" (XATTR), "system attributes" (SYSATTR), or both.
*
* When SYSATTR=true, XATTR=true:
* The XATTR directory is a "generic file system" (GFS) object
* that adds the special system attribute names (SUNWattr*) to
* the list of XATTR files presented by the underling FS.
* In this case, many operations are "passed through" to the
* lower-level FS.
*
* When SYSATTR=true, XATTR=false:
* The XATTR directory is a "generic file system" (GFS) object,
* presenting only the system attribute names (SUNWattr*)
* In this case there's no lower-level FS, only the GFS object.
*
* When SYSATTR=false, XATTR=true:
* The XATTR directory is implemented by the file system code,
* and this module is not involved after xattr_dir_lookup()
* returns the XATTR dir from the underlying file system.
*
* When SYSATTR=false, XATTR=false:
* xattr_dir_lookup just returns EINVAL
*
* In the first two cases (where we have system attributes) this module
* implements what can be thought of as a "translucent" directory containing
* both the system attribute names (SUNWattr*) and whatever XATTR names may
* exist in the XATTR directory of the underlying file system, if any.
*
* This affects operations on the (GFS) XATTR directory as follows:
*
* readdir: Merges the SUNWattr* names with any contents from the
* underlying XATTR directory.
*
* rename: If "to" or "from" is a SUNWattr name, special handling,
* else pass through to the lower FS.
*
* link: If "from" is a SUNWattr name, disallow.
*
* create: If a SUNWattr name, disallow, else pass to lower FS.
* remove: (same)
*
* open,close: Just pass through to the XATTR dir in the lower FS.
*
* lookup: Lookup an XATTR file in either the (GFS) XATTR directory
* or the "real" XATTR directory of the underlying FS.
* Note for file systems the support SYSATTR but not XATTR,
* only the GFS XATTR directory will exist. When both exist,
* gfs_vop_lookup uses the xattr_lookup_cb callback function
* which passes the lookup call through to the "real" FS.
*
* Operations on the XATTR _files_ are simpler:
*
* If the file vnode came from lookup at the GFS level, the file is one of
* the special SUNWattr* vnodes, and it's vnode operations (xattr_file_tops)
* allow only what's appropriate on these "files".
*
* If the file vnode came from the underlying FS, all operations on that
* object are handled through the vnode operations set by that FS.
*/
#include <sys/isa_defs.h>
#include <sys/sysmacros.h>
#include <sys/pathname.h>
typedef struct {
} xattr_file_t;
typedef struct {
} xattr_dir_t;
/* ARGSUSED */
static int
{
return (EACCES);
return (0);
}
/* ARGSUSED */
static int
{
return (EACCES);
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
static int
{
int error;
return (ENOSPC);
}
}
if (error) {
return (error);
}
return (0);
}
/* ARGSUSED */
static int
{
int error;
const char *domain;
return (EINVAL);
/*
*/
/*
* We need to access the real fs object.
* vp points to a GFS file; ppvp points to the real object.
*/
/*
* Iterate through the attrs associated with this view
*/
continue;
}
switch (attr) {
case F_SYSTEM:
break;
case F_READONLY:
break;
case F_HIDDEN:
break;
case F_ARCHIVE:
break;
case F_IMMUTABLE:
break;
case F_APPENDONLY:
break;
case F_NOUNLINK:
break;
case F_OPAQUE:
break;
case F_NODUMP:
break;
case F_AV_QUARANTINED:
break;
case F_AV_MODIFIED:
break;
case F_AV_SCANSTAMP:
break;
case F_CRTIME:
break;
case F_FSID:
0xffffffff));
fsid) == 0);
break;
case F_REPARSE:
break;
case F_GEN:
break;
case F_OFFLINE:
break;
case F_SPARSE:
break;
default:
break;
}
}
if (error)
return (error);
/*
* Process all the optional attributes together here. Notice that
* xoap was set when the optional attribute bits were set above.
*/
xoap->xoa_readonly) == 0);
}
xoap->xoa_hidden) == 0);
}
xoap->xoa_system) == 0);
}
xoap->xoa_archive) == 0);
}
xoap->xoa_immutable) == 0);
}
xoap->xoa_nounlink) == 0);
}
xoap->xoa_appendonly) == 0);
}
xoap->xoa_nodump) == 0);
}
xoap->xoa_opaque) == 0);
}
xoap->xoa_av_quarantined) == 0);
}
xoap->xoa_av_modified) == 0);
}
sizeof (xoap->xoa_av_scanstamp)) == 0);
}
sizeof (xoap->xoa_createtime) /
sizeof (uint64_t)) == 0);
}
xoap->xoa_reparse) == 0);
}
xoap->xoa_generation) == 0);
}
xoap->xoa_offline) == 0);
}
xoap->xoa_sparse) == 0);
}
}
/*
*/
return (ENOMEM);
SID_DOMAIN, domain) == 0);
nvl_sid) == 0);
}
}
return (ENOMEM);
SID_DOMAIN, domain) == 0);
nvl_sid) == 0);
}
}
return (0);
}
/*
* The size of a sysattr file is the size of the nvlist that will be
* returned by xattr_file_read(). A call to xattr_file_write() could
* change the size of that nvlist. That size is not stored persistently
* so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
*/
static int
{
return (ENOMEM);
}
return (EFAULT);
}
return (0);
}
/* ARGSUSED */
static int
{
int error;
if (error) {
return (error);
}
gethrestime(&now);
return (error);
}
/* ARGSUSED */
static int
{
char *buf;
int error;
/*
* Validate file offset and fasttrack empty reads
*/
return (EINVAL);
return (0);
return (ENOMEM);
return (EFAULT);
}
return (0);
}
KM_SLEEP) == 0);
return (error);
}
/* ARGSUSED */
static int
{
int error = 0;
char *buf;
char *domain;
return (EINVAL);
/*
* Validate file offset and size.
*/
return (EINVAL);
if (size == 0)
return (EINVAL);
return (EINVAL);
}
/*
* Copy and unpack the nvlist
*/
return (EFAULT);
}
return (EINVAL);
}
/*
* Fasttrack empty writes (nvlist with no nvpairs)
*/
return (0);
/*
* Validate the name and type of each attribute.
* Log any unknown names and continue. This will
* help if additional attributes are added later.
*/
nvpair_name(pair));
continue;
}
/*
* Verify nvlist type matches required type and view is OK
*/
return (EINVAL);
}
/*
* file system support ephemeral ID's
*/
return (EINVAL);
}
/*
* Retrieve data from nvpair
*/
switch (type) {
case DATA_TYPE_BOOLEAN_VALUE:
return (EINVAL);
}
break;
case DATA_TYPE_UINT64_ARRAY:
return (EINVAL);
}
break;
case DATA_TYPE_NVLIST:
return (EINVAL);
}
break;
case DATA_TYPE_UINT8_ARRAY:
return (EINVAL);
}
break;
default:
return (EINVAL);
}
switch (attr) {
/*
* If we have several similar optional attributes to
* process then we should do it all together here so that
* xoap and the requested bitmap can be set in one place.
*/
case F_READONLY:
break;
case F_HIDDEN:
break;
case F_SYSTEM:
break;
case F_ARCHIVE:
break;
case F_IMMUTABLE:
break;
case F_NOUNLINK:
break;
case F_APPENDONLY:
break;
case F_NODUMP:
break;
case F_AV_QUARANTINED:
break;
case F_AV_MODIFIED:
break;
case F_CRTIME:
break;
case F_OWNERSID:
case F_GROUPSID:
&rid)) {
return (EINVAL);
}
/*
* Now map domain+rid to ephemeral id's
*
* be set to UID_NOBODY by Winchester.
*/
if (attr == F_OWNERSID) {
} else {
}
break;
case F_AV_SCANSTAMP:
} else {
return (EINVAL);
}
break;
case F_REPARSE:
break;
case F_OFFLINE:
break;
case F_SPARSE:
break;
default:
break;
}
}
if (error)
return (error);
}
static int
{
switch (cmd) {
case _PC_XATTR_EXISTS:
case _PC_SATTR_ENABLED:
case _PC_SATTR_EXISTS:
*valp = 0;
return (0);
default:
}
}
static const fs_operation_def_t xattr_file_tops[] = {
{ NULL }
};
vnode_t *
{
return (vp);
}
vnode_t *
{
}
vnode_t *
{
}
static gfs_dirent_t xattr_dirents[] = {
{ NULL },
};
static int
is_sattr_name(char *s)
{
int i;
for (i = 0; i < XATTRDIR_NENTS; ++i) {
return (1);
}
}
return (0);
}
/*
* Given the name of an extended attribute file, determine if there is a
* normalization conflict with a sysattr view name.
*/
int
xattr_sysattr_casechk(char *s)
{
int i;
for (i = 0; i < XATTRDIR_NENTS; ++i) {
return (1);
}
return (0);
}
static int
{
int error;
/*
* Only copy system attrs if the views are the same
*/
return (EINVAL);
if (error)
return (error);
return (error);
}
/*
* Get the "real" XATTR directory associtated with the GFS XATTR directory.
* Note: This does NOT take any additional hold on the returned real_vp,
* because when this lookup succeeds we save the result in xattr_realvp
* and keep that hold until the GFS XATTR directory goes inactive.
*/
static int
{
char *nm = "";
int error;
/*
* Usually, we've already found the underlying XATTR directory
* during some previous lookup and stored it in xattr_realvp.
*/
return (0);
}
/*
* Lookup the XATTR dir in the underlying FS, relative to our
* "parent", which is the real object for which this GFS XATTR
* directory was created. Set the LOOKUP_HAVE_SYSATTR_DIR flag
* so that we don't get into an infinite loop with fop_lookup
* calling back to xattr_dir_lookup.
*/
if (error != 0)
return (error);
if (error != 0)
return (error);
/*
* Have the real XATTR directory. Save it -- but first
* check whether we lost a race doing the lookup.
*/
/*
* Note that the hold taken by the VOP_LOOKUP above is
* retained from here until xattr_dir_inactive.
*/
} else {
/* We lost the race. */
}
return (0);
}
/* ARGSUSED */
static int
{
int error;
return (EACCES);
}
/*
* If there is a real extended attribute directory,
* let the underlying FS see the VOP_OPEN call;
* otherwise just return zero.
*/
if (error == 0) {
} else {
error = 0;
}
return (error);
}
/* ARGSUSED */
static int
{
int error;
/*
* If there is a real extended attribute directory,
* let the underlying FS see the VOP_CLOSE call;
* otherwise just return zero.
*/
if (error == 0) {
} else {
error = 0;
}
return (error);
}
/*
* Retrieve the attributes on an xattr directory. If there is a "real"
* xattr directory, use that. Otherwise, get the attributes (represented
* by PARENT_ATTRMASK) from the "parent" node and fill in the rest. Note
* that VOP_GETATTR() could turn off bits in the va_mask.
*/
/* ARGSUSED */
static int
{
int error;
if (error == 0) {
if (error) {
return (error);
}
return (0);
}
/*
* There is no real xattr directory. Cobble together
* an entry using info from the parent object (if needed)
* plus information common to all xattrs.
*/
if (error) {
return (error);
}
/*
* VOP_GETATTR() might have turned off some bits in
* pvattr.va_mask. This means that the underlying
* file system couldn't process those attributes.
* We need to make sure those bits get turned off
* in the vattr_t structure that gets passed back
* to the caller. Figure out which bits were turned
* off (if any) then set pvattr.va_mask before it
* gets copied to the vattr_t that the caller sees.
*/
}
gethrestime(&now);
vap->va_blksize = 0;
vap->va_nblocks = 0;
return (0);
}
static int
{
int error;
/*
* If there is a real xattr directory, do the setattr there.
* Otherwise, just return success. The GFS directory is transient,
* and any setattr changes can disappear anyway.
*/
if (error == 0) {
}
error = 0;
}
return (error);
}
/* ARGSUSED */
static int
{
int error;
return (EACCES);
}
/*
* If there is a real xattr directory, check access there;
* otherwise just return success.
*/
if (error == 0) {
} else {
error = 0;
}
return (error);
}
static int
{
int error;
/*
* Don't allow creation of extended attributes with sysattr names.
*/
if (is_sattr_name(name)) {
}
if (error == 0) {
}
return (error);
}
static int
int flags)
{
int error;
if (is_sattr_name(name)) {
return (EACCES);
}
if (error == 0) {
}
return (error);
}
static int
{
int error;
return (EINVAL);
}
if (error == 0) {
}
return (error);
}
static int
{
int error;
/*
* We know that sdvp is a GFS dir, or we wouldn't be here.
* Get the real unnamed directory.
*/
if (error) {
return (error);
}
/*
* If the source and target are the same GFS directory, the
* underlying unnamed source and target dir will be the same.
*/
/*
* If the target dir is a different GFS directory,
* find its underlying unnamed dir.
*/
if (error) {
return (error);
}
} else {
/*
* Target dir is outside of GFS, pass it on through.
*/
}
return (error);
}
/*
* readdir_xattr_casecmp: given a system attribute name, see if there
* is a real xattr with the same normalized name.
*/
static int
int *eflags)
{
int error;
*eflags = 0;
if (error == 0) {
if (error == 0) {
error = 0;
}
}
return (error);
}
static int
{
int error;
int local_eof;
int reset_off = 0;
int has_xattrs = 0;
}
*eofp = 0;
/*
* See if there is a real extended attribute directory.
*/
if (error == 0) {
has_xattrs = 1;
}
/*
* Start by reading up the static entries.
*/
if (uiop->uio_loffset == 0) {
if (has_xattrs) {
/*
* If there is a real xattr dir, skip . and ..
* in the GFS dir. We'll pick them up below
* when we call into the underlying fs.
*/
}
if (error == 0) {
}
if (error) {
return (error);
}
!*eofp) {
int eflags;
/*
* Check to see if this sysattr set name has a
* case-insensitive conflict with a real xattr
* name.
*/
eflags = 0;
if (error)
break;
}
eflags);
if (error)
break;
} else {
*eofp = 1;
}
}
if (error) {
return (error);
}
/*
* We must read all of the static entries in the first
* call. Otherwise we won't know if uio_loffset in a
* subsequent call refers to the static entries or to those
* in an underlying fs.
*/
if (*eofp == 0)
return (EINVAL);
reset_off = 1;
}
if (!has_xattrs) {
*eofp = 1;
return (0);
}
*eofp = 0;
if (reset_off) {
uiop->uio_loffset = 0;
}
return (error);
}
/*
* Last reference on a (GFS) XATTR directory.
*
* If there's a real XATTR directory in the underlying FS, we will have
* taken a hold on that directory in xattr_dir_realdir. Now that the
* last hold on the GFS directory is gone, it's time to release that
* hold on the underlying XATTR directory.
*/
/* ARGSUSED */
static void
{
gfs_file_t *fp;
if (xattr_dir->xattr_realvp) {
}
}
}
static int
{
switch (cmd) {
case _PC_XATTR_EXISTS:
case _PC_SATTR_ENABLED:
case _PC_SATTR_EXISTS:
*valp = 0;
return (0);
default:
}
}
/* ARGSUSED */
static int
{
int error;
return (error);
}
static const fs_operation_def_t xattr_dir_tops[] = {
};
static gfs_opsvec_t xattr_opsvec[] = {
};
/*
* Callback supporting lookup in a GFS XATTR directory.
*/
static int
{
int error;
*inop = 0;
/*
* Return ENOENT for EACCES requests during lookup. Once an
* attribute create is attempted EACCES will be returned.
*/
if (error) {
return (ENOENT);
return (error);
}
if (error == 0) {
}
return (error);
}
/* ARGSUSED */
static ino64_t
{
/*
* We use index 0 for the directory fid. Start
* the file numbering at 1.
*/
}
void
xattr_init(void)
{
}
/*
* Get the XATTR dir for some file or directory.
* See vnode.c: fop_lookup()
*
* Note this only gets the GFS XATTR directory. We'll get the
* real XATTR directory later, in xattr_dir_realdir.
*/
int
{
int error = 0;
return (EINVAL);
/*
* If we're already in sysattr space, don't allow creation
* of another level of sysattrs.
*/
return (EINVAL);
}
} else {
int sysattrs_allowed = 1;
/*
* We have to drop the lock on dvp. gfs_dir_create will
* grab it for a VN_HOLD.
*/
/*
* If dvp allows xattr creation, but not sysattr
* creation, return the real xattr dir vp. We can't
* use the vfs feature mask here because _PC_SATTR_ENABLED
* has vnode-level granularity (e.g. .zfs).
*/
sysattrs_allowed = 0;
if (!xattrs_allowed && !sysattrs_allowed)
return (EINVAL);
if (!sysattrs_allowed) {
char *nm = "";
if (error)
return (error);
return (error);
}
/*
* Note that we act as if we were given CREATE_XATTR_DIR,
* but only for creation of the GFS directory.
*/
*vpp = gfs_dir_create(
/*
* We lost the race to create the xattr dir.
* Destroy this one, use the winner. We can't
* just call VN_RELE(*vpp), because the vnode
* is only partially initialized.
*/
/*
* There is an implied VN_HOLD(dvp) here. We should
* be doing a VN_RELE(dvp) to clean up the reference
* from *vpp, and then a VN_HOLD(dvp) for the new
* reference. Instead, we just leave the count alone.
*/
} else {
}
}
return (error);
}
int
{
int error;
char *nm;
return (EINVAL);
if (error)
return (error);
/*
* Start by getting the GFS sysattr directory. We might need
* to recreate it during the VOP_LOOKUP.
*/
nm = "";
if (error) {
return (EINVAL);
}
if (error)
return (error);
if (xfidp->dir_offset == 0) {
/*
* If we were looking for the directory, we're done.
*/
return (0);
}
return (EINVAL);
}
if (error) {
return (EINVAL);
}
return (error);
}