From 16d0e200e33dd5a9809eee2c484dd845bf3f85c0 Mon Sep 17 00:00:00 2001 From: Andrew Hanushevsky Date: Wed, 6 Jun 2018 09:19:38 -0700 Subject: [PATCH] [Server] Implement user settable extended file attributes. --- src/XProtocol/XProtocol.cc | 7 +- src/XProtocol/XProtocol.hh | 106 +++-- src/XrdOfs/XrdOfs.cc | 174 +------- src/XrdOfs/XrdOfs.hh | 22 +- src/XrdOfs/XrdOfsConfig.cc | 75 ++++ src/XrdOfs/XrdOfsFSctl.cc | 640 +++++++++++++++++++++++++++++ src/XrdOss/XrdOss.hh | 4 + src/XrdOss/XrdOssConfig.cc | 1 + src/XrdServer.cmake | 5 +- src/XrdSfs/XrdSfsFAttr.hh | 101 +++++ src/XrdSfs/XrdSfsInterface.hh | 58 +-- src/XrdXrootd/XrdXrootdConfig.cc | 19 + src/XrdXrootd/XrdXrootdProtocol.cc | 5 + src/XrdXrootd/XrdXrootdProtocol.hh | 15 + src/XrdXrootd/XrdXrootdXeq.cc | 28 +- src/XrdXrootd/XrdXrootdXeq.hh | 55 +++ src/XrdXrootd/XrdXrootdXeqFAttr.cc | 499 ++++++++++++++++++++++ 17 files changed, 1563 insertions(+), 251 deletions(-) create mode 100644 src/XrdOfs/XrdOfsFSctl.cc create mode 100644 src/XrdSfs/XrdSfsFAttr.hh create mode 100644 src/XrdXrootd/XrdXrootdXeq.hh create mode 100644 src/XrdXrootd/XrdXrootdXeqFAttr.cc diff --git a/src/XProtocol/XProtocol.cc b/src/XProtocol/XProtocol.cc index 5547f62a2f6..88230883096 100644 --- a/src/XProtocol/XProtocol.cc +++ b/src/XProtocol/XProtocol.cc @@ -86,7 +86,9 @@ const char *errNames[kXR_ERRFENCE-kXR_ArgInvalid] = "Request in progress", // kXR_inProgress "Quota exceeded", // kXR_overQuota "Invalid signature", // kXR_SigVerErr - "Decryption failed" // kXR_DecryptErr + "Decryption failed", // kXR_DecryptErr + "Overloaded", // kXR_Overloaded + "Invalid payload format" // kXR_BadPayload }; const char *reqNames[kXR_REQFENCE-kXR_auth] = @@ -97,7 +99,8 @@ const char *reqNames[kXR_REQFENCE-kXR_auth] = "sync", "stat", "set", "write", "admin", "prepare", "statx", "endsess", "bind", "readv", "verifyw", "locate", - "truncate", "sigver", "decrypt", "writev" + "truncate", "sigver", "decrypt", "writev", + "fattr" }; // Following value is used to determine if the error or request code is diff --git a/src/XProtocol/XProtocol.hh b/src/XProtocol/XProtocol.hh index df4ae011a35..1b34bbf6afc 100644 --- a/src/XProtocol/XProtocol.hh +++ b/src/XProtocol/XProtocol.hh @@ -140,6 +140,7 @@ enum XRequestTypes { kXR_sigver, // 3029 kXR_decrypt, // 3030 kXR_writev, // 3031 + kXR_fattr, // 3032 kXR_REQFENCE // Always last valid request code +1 }; @@ -289,6 +290,24 @@ enum XSecCrypto { kXR_rsaKey = 0x80 // The rsa key was used }; +// kXR_fattr subcodes +// +enum xfaSubCode { + kXR_fattrDel = 0, + kXR_fattrGet = 1, + kXR_fattrList = 2, + kXR_fattrSet = 3, + kXR_fatrrMaxSC = 3 // Highest valid subcode +}; + +// kXR_fattr limits +// +enum xfaLimits { + kXR_faMaxVars = 16, + kXR_faMaxNlen = 248, + kXR_faMaxVlen = 65536 +}; + //_______________________________________________ // PROTOCOL DEFINITION: SERVER'S RESPONSES TYPES //_______________________________________________ @@ -310,15 +329,15 @@ enum XResponseType { //_______________________________________________ enum XActionCode { - kXR_asyncab = 5000, - kXR_asyncdi, - kXR_asyncms, - kXR_asyncrd, - kXR_asyncwt, - kXR_asyncav, - kXR_asynunav, - kXR_asyncgo, - kXR_asynresp + kXR_asyncab = 5000, + kXR_asyncdi, // 5001 + kXR_asyncms, // 5002 + kXR_asyncrd, // 5003 + kXR_asyncwt, // 5004 + kXR_asyncav, // 5005 + kXR_asynunav, // 5006 + kXR_asyncgo, // 5007 + kXR_asynresp // 5008 }; //_______________________________________________ @@ -326,31 +345,33 @@ enum XActionCode { //_______________________________________________ // enum XErrorCode { - kXR_ArgInvalid = 3000, - kXR_ArgMissing, - kXR_ArgTooLong, - kXR_FileLocked, - kXR_FileNotOpen, - kXR_FSError, - kXR_InvalidRequest, - kXR_IOError, - kXR_NoMemory, - kXR_NoSpace, - kXR_NotAuthorized, - kXR_NotFound, - kXR_ServerError, - kXR_Unsupported, - kXR_noserver, - kXR_NotFile, - kXR_isDirectory, - kXR_Cancelled, - kXR_ChkLenErr, - kXR_ChkSumErr, - kXR_inProgress, - kXR_overQuota, - kXR_SigVerErr, - kXR_DecryptErr, - kXR_Overloaded, + kXR_ArgInvalid = 3000, + kXR_ArgMissing, // 3001 + kXR_ArgTooLong, // 3002 + kXR_FileLocked, // 3003 + kXR_FileNotOpen, // 3004 + kXR_FSError, // 3005 + kXR_InvalidRequest, // 3006 + kXR_IOError, // 3007 + kXR_NoMemory, // 3008 + kXR_NoSpace, // 3009 + kXR_NotAuthorized, // 3010 + kXR_NotFound, // 3011 + kXR_ServerError, // 3012 + kXR_Unsupported, // 3013 + kXR_noserver, // 3014 + kXR_NotFile, // 3015 + kXR_isDirectory, // 3016 + kXR_Cancelled, // 3017 + kXR_ChkLenErr, // 3018 + kXR_ChkSumErr, // 3019 + kXR_inProgress, // 3020 + kXR_overQuota, // 3021 + kXR_SigVerErr, // 3022 + kXR_DecryptErr, // 3023 + kXR_Overloaded, // 3024 + kXR_fsReadOnly, // 3025 + kXR_BadPayload, // 3026 kXR_ERRFENCE, // Always last valid errcode + 1 kXR_noErrorYet = 10000 }; @@ -425,6 +446,20 @@ struct ClientEndsessRequest { kXR_char sessid[16]; kXR_int32 dlen; }; +struct ClientFattrRequest { + kXR_char streamid[2]; + kXR_unt16 requestid; + kXR_char fhandle[4]; + kXR_char subcode; + kXR_char numattr; + kXR_char options; + kXR_char reserved[9]; + kXR_int32 dlen; + +// Valid options: +// + static const int isNew = 0x01; // For set, the variable must not exist +}; struct ClientGetfileRequest { kXR_char streamid[2]; kXR_unt16 requestid; @@ -630,6 +665,7 @@ typedef union { struct ClientDecryptRequest decrypt; struct ClientDirlistRequest dirlist; struct ClientEndsessRequest endsess; + struct ClientFattrRequest fattr; struct ClientGetfileRequest getfile; struct ClientLocateRequest locate; struct ClientLoginRequest login; @@ -923,6 +959,7 @@ static int mapError(int rc) case EILSEQ: return kXR_SigVerErr; case ERANGE: return kXR_DecryptErr; case EUSERS: return kXR_Overloaded; + case EROFS: return kXR_fsReadOnly; default: return kXR_FSError; } } @@ -955,6 +992,7 @@ static int toErrno( int xerr ) case kXR_SigVerErr: return EILSEQ; case kXR_DecryptErr: return ERANGE; case kXR_Overloaded: return EUSERS; + case kXR_fsReadOnly: return EROFS; default: return ENOMSG; } } diff --git a/src/XrdOfs/XrdOfs.cc b/src/XrdOfs/XrdOfs.cc index ed0521172ec..fea47e525ec 100644 --- a/src/XrdOfs/XrdOfs.cc +++ b/src/XrdOfs/XrdOfs.cc @@ -42,6 +42,8 @@ #include #include +#include "XProtocol/XProtocol.hh" + #include "XrdCks/XrdCks.hh" #include "XrdCks/XrdCksConfig.hh" #include "XrdCks/XrdCksData.hh" @@ -141,6 +143,7 @@ XrdOfs::XrdOfs() Finder = 0; Balancer = 0; evsObject = 0; + ossRPList = 0; myRole = strdup("server"); OssIsProxy = 0; ossRW =' '; @@ -178,6 +181,11 @@ XrdOfs::XrdOfs() // tpcRdrHost= 0; tpcRdrPort= 0; + +// Eextended attribute limits +// + usxMaxNsz = kXR_faMaxNlen; + usxMaxVsz = kXR_faMaxVlen; } /******************************************************************************/ @@ -1557,172 +1565,6 @@ int XrdOfs::exists(const char *path, // In return XrdOfsFS->Emsg(epname, einfo, retc, "locate", path); } -/******************************************************************************/ -/* f s c t l */ -/******************************************************************************/ - -int XrdOfs::fsctl(const int cmd, - const char *args, - XrdOucErrInfo &einfo, - const XrdSecEntity *client) -/* - Function: Perform filesystem operations: - - Input: cmd - Operation command (currently supported): - SFS_FSCTL_LOCATE - locate file - SFS_FSCTL_STATCC - return cluster config status - SFS_FSCTL_STATFS - return file system info (physical) - SFS_FSCTL_STATLS - return file system info (logical) - SFS_FSCTL_STATXA - return file extended attributes - arg - Command dependent argument: - - Locate: The path whose location is wanted - buf - The stat structure to hold the results - einfo - Error/Response information structure. - client - Authentication credentials, if any. - - Output: Returns SFS_OK upon success and SFS_ERROR upon failure. -*/ -{ - EPNAME("fsctl"); - static int PrivTab[] = {XrdAccPriv_Delete, XrdAccPriv_Insert, - XrdAccPriv_Lock, XrdAccPriv_Lookup, - XrdAccPriv_Rename, XrdAccPriv_Read, - XrdAccPriv_Write}; - static char PrivLet[] = {'d', 'i', - 'k', 'l', - 'n', 'r', - 'w'}; - static const int PrivNum = sizeof(PrivLet); - - int retc, i, blen, privs, opcode = cmd & SFS_FSCTL_CMD; - const char *tident = einfo.getErrUser(); - char *bP, *cP; - XTRACE(fsctl, args, ""); - -// Process the LOCATE request -// - if (opcode == SFS_FSCTL_LOCATE) - {static const int locMask = (SFS_O_FORCE|SFS_O_NOWAIT|SFS_O_RESET| - SFS_O_HNAME|SFS_O_RAWIO); - struct stat fstat; - char pbuff[1024], rType[3]; - const char *Resp[2] = {rType, pbuff}; - const char *locArg, *opq, *Path = Split(args,&opq,pbuff,sizeof(pbuff)); - XrdNetIF::ifType ifType; - int Resp1Len; - int find_flag = SFS_O_LOCATE | (cmd & locMask); - XrdOucEnv loc_Env(opq ? opq+1 : 0,0,client); - - if (cmd & SFS_O_TRUNC) locArg = (char *)"*"; - else { if (*Path == '*') {locArg = Path; Path++;} - else locArg = Path; - AUTHORIZE(client,0,AOP_Stat,"locate",Path,einfo); - } - if (Finder && Finder->isRemote() - && (retc = Finder->Locate(einfo, locArg, find_flag, &loc_Env))) - return fsError(einfo, retc); - - if (cmd & SFS_O_TRUNC) {rType[0] = 'S'; rType[1] = ossRW;} - else {if ((retc = XrdOfsOss->Stat(Path, &fstat, 0, &loc_Env))) - return XrdOfsFS->Emsg(epname, einfo, retc, "locate", Path); - rType[0] = ((fstat.st_mode & S_IFBLK) == S_IFBLK ? 's' : 'S'); - rType[1] = (fstat.st_mode & S_IWUSR ? 'w' : 'r'); - } - rType[2] = '\0'; - - ifType = XrdNetIF::GetIFType((einfo.getUCap() & XrdOucEI::uIPv4) != 0, - (einfo.getUCap() & XrdOucEI::uIPv64) != 0, - (einfo.getUCap() & XrdOucEI::uPrip) != 0); - bool retHN = (cmd & SFS_O_HNAME) != 0; - if ((Resp1Len = myIF->GetDest(pbuff, sizeof(pbuff), ifType, retHN))) - {einfo.setErrInfo(Resp1Len+3, (const char **)Resp, 2); - return SFS_DATA; - } - return Emsg(epname, einfo, ENETUNREACH, "locate", Path); - } - -// Process the STATFS request -// - if (opcode == SFS_FSCTL_STATFS) - {char pbuff[1024]; - const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); - XrdOucEnv fs_Env(opq ? opq+1 : 0,0,client); - AUTHORIZE(client,0,AOP_Stat,"statfs",Path,einfo); - if (Finder && Finder->isRemote() - && (retc = Finder->Space(einfo, Path, &fs_Env))) - return fsError(einfo, retc); - bP = einfo.getMsgBuff(blen); - if ((retc = XrdOfsOss->StatFS(Path, bP, blen, &fs_Env))) - return XrdOfsFS->Emsg(epname, einfo, retc, "statfs", args); - einfo.setErrCode(blen+1); - return SFS_DATA; - } - -// Process the STATLS request -// - if (opcode == SFS_FSCTL_STATLS) - {char pbuff[1024]; - const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); - XrdOucEnv statls_Env(opq ? opq+1 : 0,0,client); - AUTHORIZE(client,0,AOP_Stat,"statfs",Path,einfo); - if (Finder && Finder->isRemote()) - {statls_Env.Put("cms.qvfs", "1"); - if ((retc = Finder->Space(einfo, Path, &statls_Env))) - {if (retc == SFS_DATA) retc = Reformat(einfo); - return fsError(einfo, retc); - } - } - bP = einfo.getMsgBuff(blen); - if ((retc = XrdOfsOss->StatLS(statls_Env, Path, bP, blen))) - return XrdOfsFS->Emsg(epname, einfo, retc, "statls", Path); - einfo.setErrCode(blen+1); - return SFS_DATA; - } - -// Process the STATXA request -// - if (opcode == SFS_FSCTL_STATXA) - {char pbuff[1024]; - const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); - XrdOucEnv xa_Env(opq ? opq+1 : 0,0,client); - AUTHORIZE(client,0,AOP_Stat,"statxa",Path,einfo); - if (Finder && Finder->isRemote() - && (retc = Finder->Locate(einfo,Path,SFS_O_RDONLY|SFS_O_STAT,&xa_Env))) - return fsError(einfo, retc); - bP = einfo.getMsgBuff(blen); - if ((retc = XrdOfsOss->StatXA(Path, bP, blen, &xa_Env))) - return XrdOfsFS->Emsg(epname, einfo, retc, "statxa", Path); - if (!client || !XrdOfsFS->Authorization) privs = XrdAccPriv_All; - else privs = XrdOfsFS->Authorization->Access(client, Path, AOP_Any); - cP = bP + blen; strcpy(cP, "&ofs.ap="); cP += 8; - if (privs == XrdAccPriv_All) *cP++ = 'a'; - else {for (i = 0; i < PrivNum; i++) - if (PrivTab[i] & privs) *cP++ = PrivLet[i]; - if (cP == (bP + blen + 1)) *cP++ = '?'; - } - *cP++ = '\0'; - einfo.setErrCode(cP-bP+1); - return SFS_DATA; - } - -// Process the STATCC request (this should always succeed) -// - if (opcode == SFS_FSCTL_STATCC) - {static const int lcc_flag = SFS_O_LOCATE | SFS_O_LOCAL; - XrdOucEnv lcc_Env(0,0,client); - if (Finder) retc = Finder ->Locate(einfo,".",lcc_flag,&lcc_Env); - else if (Balancer) retc = Balancer->Locate(einfo,".",lcc_flag,&lcc_Env); - else retc = SFS_ERROR; - if (retc != SFS_DATA) einfo.setErrInfo(5, "none|"); - return fsError(einfo, SFS_DATA); - } - -// Operation is not supported -// - return XrdOfsFS->Emsg(epname, einfo, ENOTSUP, "fsctl", args); - -} - /******************************************************************************/ /* g e t S t a t s */ /******************************************************************************/ diff --git a/src/XrdOfs/XrdOfs.hh b/src/XrdOfs/XrdOfs.hh index 6eb00c9688c..09a8bd9daa9 100644 --- a/src/XrdOfs/XrdOfs.hh +++ b/src/XrdOfs/XrdOfs.hh @@ -47,6 +47,7 @@ class XrdOss; class XrdOssDF; class XrdOssDir; class XrdOucEnv; +class XrdOucPListAnchor; class XrdSysError; class XrdSysLogger; class XrdOucStream; @@ -181,6 +182,7 @@ class XrdCks; class XrdCmsClient; class XrdOfsConfigPI; class XrdOfsPoscq; +class XrdSfsFACtl; class XrdOfs : public XrdSfsFileSystem { @@ -358,6 +360,9 @@ static int Emsg(const char *, XrdOucErrInfo &, int, const char *x, XrdOfsHandle *hP); static int Emsg(const char *, XrdOucErrInfo &, int, const char *x, const char *y=""); + int ctlFAttr(XrdSfsFACtl &faCtl, + XrdOucErrInfo &einfo, + const XrdSecEntity *client); static int fsError(XrdOucErrInfo &myError, int rc); const char *Split(const char *Args, const char **Opq, char *Path, int Plen); int Stall(XrdOucErrInfo &, int, const char *); @@ -374,6 +379,7 @@ char *myRole; XrdAccAuthorize *Authorization; // ->Authorization Service XrdCmsClient *Balancer; // ->Cluster Local Interface XrdOfsEvs *evsObject; // ->Event Notifier +XrdOucPListAnchor*ossRPList; // ->Oss exoprt list XrdOfsPoscq *poscQ; // -> poscQ if persist on close enabled char *poscLog; // -> Directory for posc recovery log @@ -394,6 +400,9 @@ char myRType[4]; // Role type for consistency with the cms XrdVersionInfo *myVersion; // Version number compiled against +int usxMaxNsz; // Maximum length of attribute name +int usxMaxVsz; // Maximum length of attribute value + static XrdOfsHandle *dummyHandle; XrdSysMutex ocMutex; // Global mutex for open/close @@ -401,11 +410,17 @@ XrdSysMutex ocMutex; // Global mutex for open/close /* O t h e r D a t a */ /******************************************************************************/ +// Internal file attribute methods +// +int ctlFADel(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo); +int ctlFAGet(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo); +int ctlFALst(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo); +int ctlFASet(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo); + // Common functions // - int remove(const char type, const char *path, - XrdOucErrInfo &out_error, const XrdSecEntity *client, - const char *opaque); +int remove(const char type, const char *path, XrdOucErrInfo &out_error, + const XrdSecEntity *client, const char *opaque); // Function used during Configuration // @@ -432,5 +447,6 @@ int xtpc(XrdOucStream &, XrdSysError &); int xtpcal(XrdOucStream &, XrdSysError &); int xtpcr(XrdOucStream &, XrdSysError &); int xtrace(XrdOucStream &, XrdSysError &); +int xatr(XrdOucStream &, XrdSysError &); }; #endif diff --git a/src/XrdOfs/XrdOfsConfig.cc b/src/XrdOfs/XrdOfsConfig.cc index ca7cfdbe643..c34eb3f1a3f 100644 --- a/src/XrdOfs/XrdOfsConfig.cc +++ b/src/XrdOfs/XrdOfsConfig.cc @@ -41,6 +41,7 @@ #include #include "XrdVersion.hh" +#include "XProtocol/XProtocol.hh" #include "XrdCks/XrdCks.hh" @@ -282,6 +283,10 @@ int XrdOfs::Configure(XrdSysError &Eroute, XrdOucEnv *EnvInfo) { } } +// Extract out the export list should it have been supplied by the oss plugin +// + ossRPList = (XrdOucPListAnchor *)EnvInfo->GetPtr("XrdOssRPList*"); + // Initialize redirection. We type te herald here to minimize confusion // if (Options & haveRole) @@ -718,6 +723,7 @@ int XrdOfs::ConfigXeq(char *var, XrdOucStream &Config, TS_Xeq("role", xrole); TS_Xeq("tpc", xtpc); TS_Xeq("trace", xtrace); + TS_Xeq("xattr", xatr); TS_XPI("xattrlib", theAtrLib); // Screen out the subcluster directive (we need to track that) @@ -1679,6 +1685,75 @@ int XrdOfs::xtrace(XrdOucStream &Config, XrdSysError &Eroute) return 0; } +/******************************************************************************/ +/* x a t r */ +/******************************************************************************/ + +/* Function: xatr + + Purpose: To parse the directive: xattr [maxnsz ] [maxvsz ] + + [uset {on|off}] + + on enables user settable extended attributes. + + off disaables user settable extended attributes. + + maximum length of an attribute name. The user + specifiable limit will be 8 less. + + maximum length of an attribute value. + + Notes: 1. This directive is not cummalative. + + Output: 0 upon success or !0 upon failure. +*/ + +int XrdOfs::xatr(XrdOucStream &Config, XrdSysError &Eroute) +{ + char *val; + static const int xanRsv = 7; + int maxN = kXR_faMaxNlen, maxV = kXR_faMaxVlen; + bool isOn = true; + + while((val = Config.GetWord())) + { if (!strcmp("maxnsz", val)) + {if (!(val = Config.GetWord())) + {Eroute.Emsg("Config","xattr maxnsz value not specified"); + return 1; + } + if (XrdOuca2x::a2i(Eroute,"maxnsz",val,&maxN, + xanRsv+1,kXR_faMaxNlen+xanRsv)) return 1; + } + else if (!strcmp("maxvsz", val)) + {if (!(val = Config.GetWord())) + {Eroute.Emsg("Config","xattr maxvsz value not specified"); + return 1; + } + if (XrdOuca2x::a2i(Eroute,"maxvsz",val,&maxV,0,kXR_faMaxVlen)) + return 1; + } + else if (!strcmp("uset", val)) + {if (!(val = Config.GetWord())) + {Eroute.Emsg("Config","xattr uset value not specified"); + return 1; + } + if (!strcmp("on", val)) isOn = true; + else if (!strcmp("off", val)) isOn = false; + else {Eroute.Emsg("Config", "invalid xattr uset value -", val); + return 1; + } + } + else {Eroute.Emsg("Config", "invalid xattr option -", val); + return 1; + } + } + + usxMaxNsz = (isOn ? maxN-xanRsv : 0); + usxMaxVsz = maxV; + return 0; +} + /******************************************************************************/ /* t h e R o l e */ /******************************************************************************/ diff --git a/src/XrdOfs/XrdOfsFSctl.cc b/src/XrdOfs/XrdOfsFSctl.cc new file mode 100644 index 00000000000..09798eb4c96 --- /dev/null +++ b/src/XrdOfs/XrdOfsFSctl.cc @@ -0,0 +1,640 @@ +/******************************************************************************/ +/* */ +/* X r d O f s F S c t l . c c */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Deprtment of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XrdNet/XrdNetIF.hh" + +#include "XrdOfs/XrdOfs.hh" +#include "XrdOfs/XrdOfsTrace.hh" +#include "XrdOfs/XrdOfsSecurity.hh" + +#include "XrdCms/XrdCmsClient.hh" + +#include "XrdOss/XrdOss.hh" + +#include "XrdSys/XrdSysError.hh" +#include "XrdSys/XrdSysFAttr.hh" +#include "XrdSys/XrdSysHeaders.hh" +#include "XrdSys/XrdSysPlatform.hh" + +#include "XrdOuc/XrdOucEnv.hh" +#include "XrdOuc/XrdOucERoute.hh" +#include "XrdOuc/XrdOucExport.hh" +#include "XrdOuc/XrdOucTrace.hh" + +#include "XrdSec/XrdSecEntity.hh" +#include "XrdSfs/XrdSfsFAttr.hh" +#include "XrdSfs/XrdSfsFlags.hh" +#include "XrdSfs/XrdSfsInterface.hh" + +#ifdef AIX +#include +#endif + +/******************************************************************************/ +/* E r r o r R o u t i n g O b j e c t */ +/******************************************************************************/ + +extern XrdSysError OfsEroute; + +extern XrdOucTrace OfsTrace; + +/******************************************************************************/ +/* F i l e S y s t e m O b j e c t */ +/******************************************************************************/ + +extern XrdOfs* XrdOfsFS; + +/******************************************************************************/ +/* S t o r a g e S y s t e m O b j e c t */ +/******************************************************************************/ + +extern XrdOss *XrdOfsOss; + +/******************************************************************************/ +/* L o c a l F u n c t i o n s a n d O b j e c t s */ +/******************************************************************************/ + +namespace +{ +XrdSysMutex faMutex; + +static const int faSize = 8192-sizeof(XrdSfsFABuff); +} + +/******************************************************************************/ +/* G e t F A B u f f */ +/******************************************************************************/ + +namespace +{ +bool GetFABuff(XrdSfsFACtl &faCtl, int sz=0) +{ + XrdSfsFABuff *fabP = (XrdSfsFABuff *)malloc(sz + sizeof(XrdSfsFABuff)); + +// Check if we allocate a buffer +// + if (!fabP) return false; + +// Setup the buffer +// + fabP->next = faCtl.fabP; + faCtl.fabP = fabP; + fabP->dlen = sz; + return true; +} +} + +/******************************************************************************/ +/* G e t F A V a l */ +/******************************************************************************/ + +namespace +{ +bool GetFAVal(XrdSfsFACtl &faCtl, char *&bP, int &bL, unsigned int k) +{ + int rc; + +// Get the attribute value +// + rc = XrdSysFAttr::Xat->Get(faCtl.info[k].Name, bP, bL, faCtl.path); + +// If all went well, record the value and update incomming information +// + if (rc >= 0) + {faCtl.info[k].faRC = 0; + faCtl.info[k].Value = bP; + faCtl.info[k].VLen = rc; + bP += rc; bL -= rc; + return true; + } + +// Check for any error other than buffer too small +// + if (rc != -ERANGE) + {faCtl.info[k].faRC = -rc; + return true; + } + +// Buffer is too small, tell the caller to recover +// + return false; +} +} + +/******************************************************************************/ +/* G u l p F A V a l */ +/******************************************************************************/ + +namespace +{ +bool GulpFAVal(XrdSfsFACtl &faCtl, char *&bP, int &bL, unsigned int k) +{ + XrdSysMutexHelper mHelper(faMutex); + char *bzP = 0; + int n = 0; + +// Get the size of the attribute value +// + if (!GetFAVal(faCtl, bzP, n, k)) + {faCtl.info[k].faRC = ERANGE; + faCtl.info[k].VLen = 0; + return true; + } + +// Allocate a new buffer to hold this and possible some more values +// + n = faCtl.info[k].VLen; + faCtl.info[k].VLen = 0; + if (n < faSize/2) n = faSize; + if (!GetFABuff(faCtl, n)) return false; + +// Now fetch the variable in a right sized buffer +// + bP = faCtl.fabP->data; + bL = faCtl.fabP->dlen; + if (!GetFAVal(faCtl, bP, bL, k)) faCtl.info[k].faRC = ERANGE; + else {bP += faCtl.info[k].VLen; + bL -= faCtl.info[k].VLen; + } + return true; +} +} + +/******************************************************************************/ +/* S e t N o M e m */ +/******************************************************************************/ + +namespace +{ +int SetNoMem(XrdSfsFACtl &faCtl, unsigned int iX) +{ + +// Set no memory error for remaining attributes +// + for (unsigned int i = iX; i < faCtl.iNum; i++) faCtl.info[i].faRC = ENOMEM; + return SFS_OK; +} +} + +/******************************************************************************/ +/* f s c t l ( V e r s i o n 1 ) */ +/******************************************************************************/ + +int XrdOfs::fsctl(const int cmd, + const char *args, + XrdOucErrInfo &einfo, + const XrdSecEntity *client) +/* + Function: Perform filesystem operations: + + Input: cmd - Operation command (currently supported): + SFS_FSCTL_FATTR - Manipulate extended attributes + SFS_FSCTL_LOCATE - locate file + SFS_FSCTL_STATCC - return cluster config status + SFS_FSCTL_STATFS - return file system info (physical) + SFS_FSCTL_STATLS - return file system info (logical) + SFS_FSCTL_STATXA - return file extended attributes + arg - Command dependent argument: + - Locate: The path whose location is wanted + buf - The stat structure to hold the results + einfo - Error/Response information structure. + client - Authentication credentials, if any. + + Output: Returns SFS_OK upon success and SFS_ERROR upon failure. +*/ +{ + EPNAME("fsctl"); + static int PrivTab[] = {XrdAccPriv_Delete, XrdAccPriv_Insert, + XrdAccPriv_Lock, XrdAccPriv_Lookup, + XrdAccPriv_Rename, XrdAccPriv_Read, + XrdAccPriv_Write}; + static char PrivLet[] = {'d', 'i', + 'k', 'l', + 'n', 'r', + 'w'}; + static const int PrivNum = sizeof(PrivLet); + + int retc, i, blen, privs, opcode = cmd & SFS_FSCTL_CMD; + const char *tident = einfo.getErrUser(); + char *bP, *cP; + XTRACE(fsctl, args, ""); + +// Process the LOCATE request +// + if (opcode == SFS_FSCTL_LOCATE) + {static const int locMask = (SFS_O_FORCE|SFS_O_NOWAIT|SFS_O_RESET| + SFS_O_HNAME|SFS_O_RAWIO); + struct stat fstat; + char pbuff[1024], rType[3]; + const char *Resp[2] = {rType, pbuff}; + const char *locArg, *opq, *Path = Split(args,&opq,pbuff,sizeof(pbuff)); + XrdNetIF::ifType ifType; + int Resp1Len; + int find_flag = SFS_O_LOCATE | (cmd & locMask); + XrdOucEnv loc_Env(opq ? opq+1 : 0,0,client); + + if (cmd & SFS_O_TRUNC) locArg = (char *)"*"; + else { if (*Path == '*') {locArg = Path; Path++;} + else locArg = Path; + AUTHORIZE(client,0,AOP_Stat,"locate",Path,einfo); + } + if (Finder && Finder->isRemote() + && (retc = Finder->Locate(einfo, locArg, find_flag, &loc_Env))) + return fsError(einfo, retc); + + if (cmd & SFS_O_TRUNC) {rType[0] = 'S'; rType[1] = ossRW;} + else {if ((retc = XrdOfsOss->Stat(Path, &fstat, 0, &loc_Env))) + return XrdOfsFS->Emsg(epname, einfo, retc, "locate", Path); + rType[0] = ((fstat.st_mode & S_IFBLK) == S_IFBLK ? 's' : 'S'); + rType[1] = (fstat.st_mode & S_IWUSR ? 'w' : 'r'); + } + rType[2] = '\0'; + + ifType = XrdNetIF::GetIFType((einfo.getUCap() & XrdOucEI::uIPv4) != 0, + (einfo.getUCap() & XrdOucEI::uIPv64) != 0, + (einfo.getUCap() & XrdOucEI::uPrip) != 0); + bool retHN = (cmd & SFS_O_HNAME) != 0; + if ((Resp1Len = myIF->GetDest(pbuff, sizeof(pbuff), ifType, retHN))) + {einfo.setErrInfo(Resp1Len+3, (const char **)Resp, 2); + return SFS_DATA; + } + return Emsg(epname, einfo, ENETUNREACH, "locate", Path); + } + +// Process the STATFS request +// + if (opcode == SFS_FSCTL_STATFS) + {char pbuff[1024]; + const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); + XrdOucEnv fs_Env(opq ? opq+1 : 0,0,client); + AUTHORIZE(client,0,AOP_Stat,"statfs",Path,einfo); + if (Finder && Finder->isRemote() + && (retc = Finder->Space(einfo, Path, &fs_Env))) + return fsError(einfo, retc); + bP = einfo.getMsgBuff(blen); + if ((retc = XrdOfsOss->StatFS(Path, bP, blen, &fs_Env))) + return XrdOfsFS->Emsg(epname, einfo, retc, "statfs", args); + einfo.setErrCode(blen+1); + return SFS_DATA; + } + +// Process the STATLS request +// + if (opcode == SFS_FSCTL_STATLS) + {char pbuff[1024]; + const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); + XrdOucEnv statls_Env(opq ? opq+1 : 0,0,client); + AUTHORIZE(client,0,AOP_Stat,"statfs",Path,einfo); + if (Finder && Finder->isRemote()) + {statls_Env.Put("cms.qvfs", "1"); + if ((retc = Finder->Space(einfo, Path, &statls_Env))) + {if (retc == SFS_DATA) retc = Reformat(einfo); + return fsError(einfo, retc); + } + } + bP = einfo.getMsgBuff(blen); + if ((retc = XrdOfsOss->StatLS(statls_Env, Path, bP, blen))) + return XrdOfsFS->Emsg(epname, einfo, retc, "statls", Path); + einfo.setErrCode(blen+1); + return SFS_DATA; + } + +// Process the STATXA request +// + if (opcode == SFS_FSCTL_STATXA) + {char pbuff[1024]; + const char *opq, *Path = Split(args, &opq, pbuff, sizeof(pbuff)); + XrdOucEnv xa_Env(opq ? opq+1 : 0,0,client); + AUTHORIZE(client,0,AOP_Stat,"statxa",Path,einfo); + if (Finder && Finder->isRemote() + && (retc = Finder->Locate(einfo,Path,SFS_O_RDONLY|SFS_O_STAT,&xa_Env))) + return fsError(einfo, retc); + bP = einfo.getMsgBuff(blen); + if ((retc = XrdOfsOss->StatXA(Path, bP, blen, &xa_Env))) + return XrdOfsFS->Emsg(epname, einfo, retc, "statxa", Path); + if (!client || !XrdOfsFS->Authorization) privs = XrdAccPriv_All; + else privs = XrdOfsFS->Authorization->Access(client, Path, AOP_Any); + cP = bP + blen; strcpy(cP, "&ofs.ap="); cP += 8; + if (privs == XrdAccPriv_All) *cP++ = 'a'; + else {for (i = 0; i < PrivNum; i++) + if (PrivTab[i] & privs) *cP++ = PrivLet[i]; + if (cP == (bP + blen + 1)) *cP++ = '?'; + } + *cP++ = '\0'; + einfo.setErrCode(cP-bP+1); + return SFS_DATA; + } + +// Process the STATCC request (this should always succeed) +// + if (opcode == SFS_FSCTL_STATCC) + {static const int lcc_flag = SFS_O_LOCATE | SFS_O_LOCAL; + XrdOucEnv lcc_Env(0,0,client); + if (Finder) retc = Finder ->Locate(einfo,".",lcc_flag,&lcc_Env); + else if (Balancer) retc = Balancer->Locate(einfo,".",lcc_flag,&lcc_Env); + else retc = SFS_ERROR; + if (retc != SFS_DATA) einfo.setErrInfo(5, "none|"); + return fsError(einfo, SFS_DATA); + } + +// Process the FATTR request. +// + if (opcode == SFS_FSCTL_FATTR) + {if (args) return ctlFAttr((XrdSfsFACtl &)*args, einfo, client); + else {XrdOucEnv *envP = einfo.getEnv(); + if (!envP || !usxMaxNsz) return SFS_ERROR; + envP->PutInt("usxMaxNsz", usxMaxNsz); + envP->PutInt("usxMaxVsz", usxMaxVsz); + return SFS_OK; + } + } + +// Operation is not supported +// + return XrdOfsFS->Emsg(epname, einfo, ENOTSUP, "fsctl", args); +} + +/******************************************************************************/ +/* c t l F A t t r */ +/******************************************************************************/ + +int XrdOfs::ctlFAttr(XrdSfsFACtl &faCtl, + XrdOucErrInfo &einfo, + const XrdSecEntity *client) +{ + EPNAME("ctlFAttr"); + XrdOucEnv FAttr_Env(faCtl.pcgi,0,client); + const char *accType; + long long xOpts; + + struct faArgs {const char *name; int fArg; Access_Operation aop;}; + + static faArgs faTab[] = {{ "del fattr", SFS_O_RDWR, AOP_Update}, // del + { "get fattr", 0, AOP_Read}, // get + {"list fattr", 0, AOP_Read}, // list + { "set fattr", SFS_O_RDWR, AOP_Update} // set + }; + static const int faNum = sizeof(faTab)/sizeof(struct faArgs); + + int rc; + +// Make sure request code is valid (we also set some options) +// + if (faCtl.rqst > faNum) + return Emsg(epname, einfo, EINVAL, "process fattrs", faCtl.path); + accType = faTab[faCtl.rqst].name; + +// Extract the export options if we can +// + xOpts = (ossRPList ? ossRPList->Find(faCtl.path) : 0); + +// Perform authrorization and redirection if required +// + if (faCtl.opts & XrdSfsFACtl::accChk) + {int luFlag = faTab[faCtl.rqst].fArg; + Access_Operation aOP = faTab[faCtl.rqst].aop; + + AUTHORIZE(client, 0, aOP, accType ,faCtl.path, einfo); + + if (Finder && Finder->isRemote() + && (rc = Finder->Locate(einfo, faCtl.path, luFlag, &FAttr_Env))) + return fsError(einfo, rc); + + if (aOP == AOP_Update && xOpts & XRDEXP_NOTRW) + return Emsg(epname, einfo, EROFS, accType, faCtl.path); + } + +// If this is a proxy server then hand this request to the storage system +// as it will need to be executed elsewhere. +// + if (OssIsProxy) + {faCtl.envP = &FAttr_Env; + rc = XrdOfsOss->FSctl(XRDOSS_FSCTLFA, 0, (const char *)&faCtl); + if (rc) return XrdOfsFS->Emsg(epname, einfo, rc, accType, faCtl.path); + return SFS_OK; + } + +// Make sure we can use xattrs on the path +// + if (xOpts & XRDEXP_NOXATTR) + return XrdOfsFS->Emsg(epname, einfo, EPERM, accType, faCtl.path); + +// Fan out for processing this on the local file system +// + switch(faCtl.rqst) + {case XrdSfsFACtl::faDel: + return ctlFADel(faCtl, FAttr_Env, einfo); + break; + case XrdSfsFACtl::faGet: + return ctlFAGet(faCtl, FAttr_Env, einfo); + break; + case XrdSfsFACtl::faLst: + return ctlFALst(faCtl, FAttr_Env, einfo); + break; + case XrdSfsFACtl::faSet: + return ctlFASet(faCtl, FAttr_Env, einfo); + break; + default: break; + } + +// The request code is not one we understand +// + return XrdOfsFS->Emsg(epname, einfo, EINVAL, "process fattrs", faCtl.path); +} + +/******************************************************************************/ +/* c t l F A D e l */ +/******************************************************************************/ + +int XrdOfs::ctlFADel(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo) +{ + +// Delete each variable +// + for (unsigned int i = 0; i < faCtl.iNum; i++) + faCtl.info[i].faRC = XrdSysFAttr::Xat->Del(faCtl.info[i].Name,faCtl.path); + +// All done +// + return SFS_OK; +} + +/******************************************************************************/ +/* c t l F A L s t */ +/******************************************************************************/ + +int XrdOfs::ctlFALst(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo) +{ + EPNAME("ctlFALst"); + XrdSysXAttr::AList *alP, *aEnt; + char *nP; + int rc, pfLen, iX = 0, faSize = 0; + int getMsz = (faCtl.opts & XrdSfsFACtl::retvsz) != 0; + bool xPlode = (faCtl.opts & XrdSfsFACtl::xplode) != 0; + +// Get all of the attribute names +// + rc = XrdSysFAttr::Xat->List(&alP, faCtl.path, -1, getMsz); + if (rc < 0) return Emsg(epname, einfo, -rc, "list fattrs", faCtl.path); + +// Count up attributes +// + faCtl.info = 0; + faCtl.iNum = 0; + pfLen = (*faCtl.nPfx ? sizeof(faCtl.nPfx) : 0); + aEnt = alP; + while(aEnt) + {if (aEnt->Nlen) + {if (!pfLen || !strncmp(faCtl.nPfx, aEnt->Name, pfLen)) + {faCtl.iNum++; + faSize += aEnt->Nlen - pfLen + 1; + } else aEnt->Nlen = 0; + } + aEnt = aEnt->Next; + } + +// If there are no attributes of interest, we are done. +// + if (!faCtl.iNum) return SFS_OK; + +// Allocate sufficient memory to hold the complete list +// + if (!GetFABuff(faCtl, faSize)) + {XrdSysFAttr::Xat->Free(alP); + return Emsg(epname, einfo, ENOMEM, "list fattrs", faCtl.path); + } + +// Allocate an info vector if caller wants this exploded +// + if (xPlode) faCtl.info = new XrdSfsFAInfo[faCtl.iNum]; + else faCtl.info = 0; + +// Copy over the names +// + nP = faCtl.fabP->data; + aEnt = alP; + while(aEnt) + {if (aEnt->Nlen) + {strcpy(nP, aEnt->Name+pfLen); + if (xPlode) + {faCtl.info[iX].Name = nP; + faCtl.info[iX].NLen = aEnt->Nlen - pfLen; + faCtl.info[iX].VLen = aEnt->Vlen; + iX++; + } + nP += aEnt->Nlen-pfLen+1; + } + aEnt = aEnt->Next; + } + + {faCtl.info = 0; + faCtl.iNum = 0; + return SFS_OK; + } + faCtl.fabP->dlen = nP - faCtl.fabP->data; + +// Finish up +// + XrdSysFAttr::Xat->Free(alP); + return SFS_OK; +} + +/******************************************************************************/ +/* c t l F A G e t */ +/******************************************************************************/ + +int XrdOfs::ctlFAGet(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo) +{ + char *bP; + int bL; + +// Allocate the initial buffer. We make it big enough to, hopefully get all +// of the attributes, though we may have to reallocate. +// + if (!GetFABuff(faCtl, faSize)) return SetNoMem(faCtl, 0); + +// Setup to retrieve attributes +// + bP = faCtl.fabP->data; + bL = faCtl.fabP->dlen; + +// Get each variable. Unfortunately, we need to allocate a buffer for each +// one as we don't know the size. +// + for (unsigned int i = 0; i < faCtl.iNum; i++) + {if (bL < 8) + {if (!GetFABuff(faCtl, faSize)) return SetNoMem(faCtl, i); + bP = faCtl.fabP->data; + bL = faCtl.fabP->dlen; + } + + if (!GetFAVal(faCtl, bP, bL, i) && !GulpFAVal(faCtl, bP, bL, i)) + return SetNoMem(faCtl, i); + } + + return SFS_OK; +} + +/******************************************************************************/ +/* c t l F A S e t */ +/******************************************************************************/ + +int XrdOfs::ctlFASet(XrdSfsFACtl &faCtl, XrdOucEnv &faEnv, XrdOucErrInfo &einfo) +{ + int isNew = (faCtl.opts & XrdSfsFACtl::newAtr) != 0; + +// Lock this code if we are replacing variables +// + if (!isNew) faMutex.Lock(); + +// Set each variable +// + for (unsigned int i = 0; i < faCtl.iNum; i++) + faCtl.info[i].faRC = XrdSysFAttr::Xat->Set(faCtl.info[i].Name, + faCtl.info[i].Value, + faCtl.info[i].VLen, + faCtl.path, -1, isNew); + +// Unlock the mutex if we locked it +// + if (!isNew) faMutex.UnLock(); + +// All done + return SFS_OK; +} diff --git a/src/XrdOss/XrdOss.hh b/src/XrdOss/XrdOss.hh index 04fda97f8c1..9d2a41d9d0c 100644 --- a/src/XrdOss/XrdOss.hh +++ b/src/XrdOss/XrdOss.hh @@ -151,6 +151,10 @@ int fd; // The associated file descriptor. #define XRDOSS_updtatm 0x0002 #define XRDOSS_preop 0x0004 +// Commands that can be passed to FSctl +// +#define XRDOSS_FSCTLFA 0x0001 + // Class passed to StatVS() // class XrdOssVSInfo diff --git a/src/XrdOss/XrdOssConfig.cc b/src/XrdOss/XrdOssConfig.cc index 889f0c6ce54..b2076fc828e 100644 --- a/src/XrdOss/XrdOssConfig.cc +++ b/src/XrdOss/XrdOssConfig.cc @@ -345,6 +345,7 @@ int XrdOssSys::Configure(const char *configfn, XrdSysError &Eroute, // Export the real path list (for frm et. al.) // XrdOssRPList = &RPList; + if (envP) envP->PutPtr("XrdOssRPList*", &RPList); // All done, close the stream and return the return code. // diff --git a/src/XrdServer.cmake b/src/XrdServer.cmake index b265b8cd43d..a1943856208 100644 --- a/src/XrdServer.cmake +++ b/src/XrdServer.cmake @@ -43,8 +43,9 @@ add_library( XrdXrootd/XrdXrootdTransit.cc XrdXrootd/XrdXrootdTransit.hh XrdXrootd/XrdXrootdTransPend.cc XrdXrootd/XrdXrootdTransPend.hh XrdXrootd/XrdXrootdTransSend.cc XrdXrootd/XrdXrootdTransSend.hh - XrdXrootd/XrdXrootdXeq.cc + XrdXrootd/XrdXrootdXeq.cc XrdXrootd/XrdXrootdXeq.hh XrdXrootd/XrdXrootdXeqAio.cc + XrdXrootd/XrdXrootdXeqFAttr.cc XrdXrootd/XrdXrootdTrace.hh XrdXrootd/XrdXrootdXPath.hh XrdXrootd/XrdXrootdReqID.hh @@ -61,6 +62,7 @@ add_library( XrdOfs/XrdOfsConfigPI.cc XrdOfs/XrdOfsConfigPI.hh XrdOfs/XrdOfsEvr.cc XrdOfs/XrdOfsEvr.hh XrdOfs/XrdOfsEvs.cc XrdOfs/XrdOfsEvs.hh + XrdOfs/XrdOfsFSctl.cc XrdOfs/XrdOfsHandle.cc XrdOfs/XrdOfsHandle.hh XrdOfs/XrdOfsPoscq.cc XrdOfs/XrdOfsPoscq.hh XrdOfs/XrdOfsStats.cc XrdOfs/XrdOfsStats.hh @@ -75,6 +77,7 @@ add_library( #----------------------------------------------------------------------------- XrdSfs/XrdSfsNative.cc XrdSfs/XrdSfsNative.hh XrdSfs/XrdSfsAio.hh + XrdSfs/XrdSfsFAttr.hh XrdSfs/XrdSfsFlags.hh XrdSfs/XrdSfsInterface.hh diff --git a/src/XrdSfs/XrdSfsFAttr.hh b/src/XrdSfs/XrdSfsFAttr.hh new file mode 100644 index 00000000000..741ad8eecf0 --- /dev/null +++ b/src/XrdSfs/XrdSfsFAttr.hh @@ -0,0 +1,101 @@ +#ifndef __SFS_FATTR_H__ +#define __SFS_FATTR_H__ +/******************************************************************************/ +/* */ +/* X r d S f s F A t t r . h h */ +/* */ +/*(c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/*Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Deprtment of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include + +//----------------------------------------------------------------------------- +//! This include file defines control strucres used to drive entended file +//! attribute handling via the fsctl() method. +//----------------------------------------------------------------------------- + +/******************************************************************************/ +/* X r d S f s F A I n f o */ +/******************************************************************************/ + +struct XrdSfsFAInfo +{ +char *Name; //!< Variable name +char *Value; //!< Variable value +int VLen; //!< Variable value length (aligned) +short NLen; //!< Length of name not including null byte +short faRC; //!< Action return code for this element + + XrdSfsFAInfo() : Value(0), VLen(0), NLen(0), faRC(0) {} + ~XrdSfsFAInfo() {} +}; + +/******************************************************************************/ +/* X r d S f s F A B u f f */ +/******************************************************************************/ + +struct XrdSfsFABuff +{ +XrdSfsFABuff *next; +int dlen; //!< Data Length in subsequent buffer +char data[4]; //!< Start of data +}; + +/******************************************************************************/ +/* X r d S f s F A C t l */ +/******************************************************************************/ + +class XrdOucEnv; + +struct XrdSfsFACtl +{ +const char *path; //!< The file path to act on +const char *pcgi; //!< Opaque information (null if none) +XrdSfsFAInfo *info; //!< Pointer to attribute information +XrdOucEnv *envP; //!< Optional environmental information +XrdSfsFABuff *fabP; //!< -> Additional memory that was allocated +char nPfx[2]; //!< The namespace being used +unsigned short iNum; //!< Number of info entries +unsigned char rqst; //!< Type of file attribute request (see below) +unsigned char opts; //!< Request options (see below) + +enum RQST:char {faDel = 0, faGet, faLst, faSet, faFence}; + +static const int accChk = 0x01; //!< Perform access check +static const int newAtr = 0x02; //!< For set the attribute must not exist +static const int xplode = 0x04; //!< Construct an info vec from faList +static const int retvsz = 0x0c; //!< Above plus return size of attr value + + XrdSfsFACtl(const char *p, const char *opq, int anum) + : path(p), pcgi(opq), info(0), envP(0), fabP(0), + iNum(anum), rqst(255), opts(0) + {nPfx[0] = 0; nPfx[1] = 0;} + + ~XrdSfsFACtl() {XrdSfsFABuff *dP, *nP = fabP; + while((dP = nP)) {nP = nP->next; free(dP);} + if (info) delete [] info; + } +}; +#endif diff --git a/src/XrdSfs/XrdSfsInterface.hh b/src/XrdSfs/XrdSfsInterface.hh index e1f903b939a..b8cb50a539b 100644 --- a/src/XrdSfs/XrdSfsInterface.hh +++ b/src/XrdSfs/XrdSfsInterface.hh @@ -92,6 +92,7 @@ #define SFS_FSCTL_STATCC 5 // Return Cluster Config status #define SFS_FSCTL_PLUGIN 8 // Return Implementation Dependent Data #define SFS_FSCTL_PLUGIO 16 // Return Implementation Dependent Data +#define SFS_FSCTL_FATTR 32 // Process extended attributes // Return values for integer & XrdSfsXferSize returning XrdSfs methods // @@ -149,12 +150,12 @@ enum XrdSfsFileExistence class XrdOucTList; -struct XrdSfsFSctl //!< SFS_FSCTL_PLUGIN/PLUGIO parameters +struct XrdSfsFSctl //!< SFS_FSCTL_PLUGIN/PLUGIO parms { - const char *Arg1; //!< PLUGIO & PLUGIN + const char *Arg1; //!< PLUGIO, PLUGIN int Arg1Len; //!< Length int Arg2Len; //!< Length - const char *Arg2; //!< PLUGIN opaque string + const char *Arg2; //!< PLUGIN opaque string }; struct XrdSfsPrep //!< Prepare parameters @@ -316,32 +317,20 @@ virtual void EnvInfo(XrdOucEnv *envP) } //----------------------------------------------------------------------------- -//! Perform a filesystem control operation (version 1) +//! Perform a filesystem control operation (version 2) //! //! @param cmd - The operation to be performed: -//! SFS_FSCTL_LOCATE Locate a file or file servers -//! SFS_FSCTL_STATCC Return cluster config status -//! SFS_FSCTL_STATFS Return physical filesystem information -//! SFS_FSCTL_STATLS Return logical filesystem information -//! SFS_FSCTL_STATXA Return extended attributes +//! SFS_FSCTL_PLUGIN Return Implementation Dependent Data v1 +//! SFS_FSCTL_PLUGIO Return Implementation Dependent Data v2 //! @param args - Arguments specific to cmd. -//! SFS_FSCTL_LOCATE args points to the path to be located -//! "" path is the first exported path -//! "*" return all current servers -//! "*/" return servers exporting path -//! o/w return servers having the path -//! SFS_FSCTL_STATFS Path in the filesystem in question. -//! SFS_FSCTL_STATLS Path in the filesystem in question. -//! SFS_FSCTL_STATXA Path of the file whose xattr is wanted. +//! SFS_FSCTL_PLUGIN path and opaque information. +//! SFS_FSCTL_PLUGIO Unscreened argument string. //! @param eInfo - The object where error info or results are to be returned. //! @param client - Client's identify (see common description). //! //! @return SFS_OK a null response is sent. -//! @return SFS_DATA error.code length of the data to be sent. +//! SFS_DATA error.code length of the data to be sent. //! error.message contains the data to be sent. -//! @return SFS_STARTED Operation started result will be returned via callback. -//! Valid only for for SFS_FSCTL_LOCATE, SFS_FSCTL_STATFS, and -//! SFS_FSCTL_STATXA //! o/w one of SFS_ERROR, SFS_REDIRECT, or SFS_STALL. //----------------------------------------------------------------------------- @@ -355,20 +344,35 @@ virtual int FSctl(const int cmd, } //----------------------------------------------------------------------------- -//! Perform a filesystem control operation (version 2) +//! Perform a filesystem control operation (version 1) //! //! @param cmd - The operation to be performed: -//! SFS_FSCTL_PLUGIN Return Implementation Dependent Data v1 -//! SFS_FSCTL_PLUGIO Return Implementation Dependent Data v2 +//! SFS_FSCTL_FATTR Process extended attributes +//! SFS_FSCTL_LOCATE Locate a file or file servers +//! SFS_FSCTL_STATCC Return cluster config status +//! SFS_FSCTL_STATFS Return physical filesystem information +//! SFS_FSCTL_STATLS Return logical filesystem information +//! SFS_FSCTL_STATXA Return extended attributes //! @param args - Arguments specific to cmd. -//! SFS_FSCTL_PLUGIN path and opaque information. -//! SFS_FSCTL_PLUGIO Unscreened argument string. +//! SFS+FSCTL_FATTR args -> XrdSfsFACtl object. When args +//! is nil then return 0 if fattr supported. +//! SFS_FSCTL_LOCATE args points to the path to be located +//! "" path is the first exported path +//! "*" return all current servers +//! "*/" return servers exporting path +//! o/w return servers having the path +//! SFS_FSCTL_STATFS Path in the filesystem in question. +//! SFS_FSCTL_STATLS Path in the filesystem in question. +//! SFS_FSCTL_STATXA Path of the file whose xattr is wanted. //! @param eInfo - The object where error info or results are to be returned. //! @param client - Client's identify (see common description). //! //! @return SFS_OK a null response is sent. -//! SFS_DATA error.code length of the data to be sent. +//! @return SFS_DATA error.code length of the data to be sent. //! error.message contains the data to be sent. +//! @return SFS_STARTED Operation started result will be returned via callback. +//! Valid only for for SFS_FSCTL_LOCATE, SFS_FSCTL_STATFS, and +//! SFS_FSCTL_STATXA //! o/w one of SFS_ERROR, SFS_REDIRECT, or SFS_STALL. //----------------------------------------------------------------------------- diff --git a/src/XrdXrootd/XrdXrootdConfig.cc b/src/XrdXrootd/XrdXrootdConfig.cc index c4f3b09a26a..63ef77ae750 100644 --- a/src/XrdXrootd/XrdXrootdConfig.cc +++ b/src/XrdXrootd/XrdXrootdConfig.cc @@ -440,6 +440,25 @@ int XrdXrootdProtocol::Configure(char *parms, XrdProtocol_Config *pi) // PidFile(); +// Indicate whether or not we support extened attributes +// + {XrdOucEnv myEnv; + XrdOucErrInfo eInfo("", &myEnv); + char buff[128]; + if (osFS->fsctl(SFS_FSCTL_FATTR, (const char *)0, eInfo, 0) == 0) + {usxMaxNsz = myEnv.GetInt("usxMaxNsz"); + if (usxMaxNsz < 0) usxMaxNsz = 0; + usxMaxVsz = myEnv.GetInt("usxMaxVsz"); + if (usxMaxVsz < 0) usxMaxVsz = 0; + snprintf(buff, sizeof(buff), "%d %d", usxMaxNsz, usxMaxVsz); + usxParms = strdup(buff); + } else { + usxMaxNsz = 0; + usxMaxVsz = 0; + usxParms = strdup("0 0"); + } + } + // Finally, check if we really need to be in bypass mode if it is set // if (OD_Bypass) diff --git a/src/XrdXrootd/XrdXrootdProtocol.cc b/src/XrdXrootd/XrdXrootdProtocol.cc index 1c3677be286..6ad09cda9ef 100644 --- a/src/XrdXrootd/XrdXrootdProtocol.cc +++ b/src/XrdXrootd/XrdXrootdProtocol.cc @@ -127,6 +127,10 @@ int XrdXrootdProtocol::OD_Stall = 33; bool XrdXrootdProtocol::OD_Bypass= false; bool XrdXrootdProtocol::OD_Redir = false; +int XrdXrootdProtocol::usxMaxNsz= kXR_faMaxNlen; +int XrdXrootdProtocol::usxMaxVsz= kXR_faMaxVlen; +char *XrdXrootdProtocol::usxParms = 0; + /******************************************************************************/ /* P r o t o c o l M a n a g e m e n t S t a c k s */ /******************************************************************************/ @@ -533,6 +537,7 @@ int XrdXrootdProtocol::Process2() else break; case kXR_chmod: return do_Chmod(); case kXR_dirlist: return do_Dirlist(); + case kXR_fattr: return do_FAttr(); case kXR_locate: return do_Locate(); case kXR_mkdir: return do_Mkdir(); case kXR_mv: return do_Mv(); diff --git a/src/XrdXrootd/XrdXrootdProtocol.hh b/src/XrdXrootd/XrdXrootdProtocol.hh index 600e820fa53..fa6161c3721 100644 --- a/src/XrdXrootd/XrdXrootdProtocol.hh +++ b/src/XrdXrootd/XrdXrootdProtocol.hh @@ -75,6 +75,7 @@ class XrdOucTrace; class XrdSecProtect; class XrdSecProtector; class XrdSfsDirectory; +class XrdSfsFACtl; class XrdSfsFileSystem; class XrdSecProtocol; class XrdBuffer; @@ -146,6 +147,7 @@ enum RD_func {RD_chmod = 0, RD_chksum, RD_dirlist, RD_locate, RD_mkdir, int do_Dirlist(); int do_DirStat(XrdSfsDirectory *dp, char *pbuff, char *opaque); int do_Endsess(); + int do_FAttr(); int do_Getfile(); int do_Login(); int do_Locate(); @@ -229,6 +231,13 @@ static int xsecl(XrdOucStream &Config); static int xtrace(XrdOucStream &Config); static int xlimit(XrdOucStream &Config); + int ProcFAttr(char *faPath, char *faCgi, char *faArgs, + int faALen, int faCode, bool doAChk); + int XeqFADel(XrdSfsFACtl &ctl, char *faVars, int faVLen); + int XeqFAGet(XrdSfsFACtl &ctl, char *faVars, int faVLen); + int XeqFALst(XrdSfsFACtl &ctl); + int XeqFASet(XrdSfsFACtl &ctl, char *faVars, int faVLen); + static XrdObjectQ ProtStack; XrdObject ProtLink; @@ -293,6 +302,12 @@ static int OD_Stall; static bool OD_Bypass; static bool OD_Redir; +// Extended attributes +// +static int usxMaxNsz; +static int usxMaxVsz; +static char *usxParms; + // async configuration values // static int as_maxperlnk; // Max async requests per link diff --git a/src/XrdXrootd/XrdXrootdXeq.cc b/src/XrdXrootd/XrdXrootdXeq.cc index a5e01c8ad24..6f1eb648d3e 100644 --- a/src/XrdXrootd/XrdXrootdXeq.cc +++ b/src/XrdXrootd/XrdXrootdXeq.cc @@ -59,6 +59,7 @@ #include "XrdXrootd/XrdXrootdProtocol.hh" #include "XrdXrootd/XrdXrootdStats.hh" #include "XrdXrootd/XrdXrootdTrace.hh" +#include "XrdXrootd/XrdXrootdXeq.hh" #include "XrdXrootd/XrdXrootdXPath.hh" #include "XrdVersion.hh" @@ -73,16 +74,6 @@ extern XrdOucTrace *XrdXrootdTrace; /* L o c a l S t r u c t u r e s */ /******************************************************************************/ -struct XrdXrootdFHandle - {kXR_int32 handle; - - void Set(kXR_char *ch) - {memcpy((void *)&handle, (const void *)ch, sizeof(handle));} - XrdXrootdFHandle() {} - XrdXrootdFHandle(kXR_char *ch) {Set(ch);} - ~XrdXrootdFHandle() {} - }; - struct XrdXrootdSessID {unsigned int Sid; int Pid; @@ -111,14 +102,6 @@ struct XrdXrootdWVInfo /* L o c a l D e f i n e s */ /******************************************************************************/ -#define CRED (const XrdSecEntity *)Client - -#define TRACELINK Link - -#define STATIC_REDIRECT(xfnc) \ - if (Route[xfnc].Port[rdType]) \ - return Response.Send(kXR_redirect,Route[xfnc].Port[rdType],\ - Route[xfnc].Host[rdType]) namespace { static const int op_isOpen = 0x00010000; @@ -785,6 +768,12 @@ int XrdXrootdProtocol::do_Endsess() return Response.Send(); } +/******************************************************************************/ +/* d o _ F A t t r */ +/* */ +/* Resides in XrdXrootdXeqFAttr.cc */ +/******************************************************************************/ + /******************************************************************************/ /* d o G e t f i l e */ /******************************************************************************/ @@ -1922,6 +1911,9 @@ int XrdXrootdProtocol::do_Qconf() {const char *nidval = getenv("XRDCMSVNID"); if (!nidval || !(*nidval)) nidval = "vnid"; n = snprintf(bp, bleft, "%s\n", nidval); + } + else if (!strcmp("xattr", val)) + {n = snprintf(bp, bleft, "%s\n", usxParms); bp += n; bleft -= n; } else {n = strlen(val); diff --git a/src/XrdXrootd/XrdXrootdXeq.hh b/src/XrdXrootd/XrdXrootdXeq.hh new file mode 100644 index 00000000000..7d05ddd3d31 --- /dev/null +++ b/src/XrdXrootd/XrdXrootdXeq.hh @@ -0,0 +1,55 @@ +/******************************************************************************/ +/* */ +/* X r d X r o o t d X e q . h h */ +/* */ +/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +/******************************************************************************/ +/* D e f i n e s */ +/******************************************************************************/ + +#define CRED (const XrdSecEntity *)Client + +#define TRACELINK Link + +#define STATIC_REDIRECT(xfnc) \ + if (Route[xfnc].Port[rdType]) \ + return Response.Send(kXR_redirect,Route[xfnc].Port[rdType],\ + Route[xfnc].Host[rdType]) + +/******************************************************************************/ +/* C o m m o n S t r u c t u r e s */ +/******************************************************************************/ + +struct XrdXrootdFHandle + {kXR_int32 handle; + + void Set(kXR_char *ch) + {memcpy((void *)&handle, (const void *)ch, sizeof(handle));} + XrdXrootdFHandle() {} + XrdXrootdFHandle(kXR_char *ch) {Set(ch);} + ~XrdXrootdFHandle() {} + }; diff --git a/src/XrdXrootd/XrdXrootdXeqFAttr.cc b/src/XrdXrootd/XrdXrootdXeqFAttr.cc new file mode 100644 index 00000000000..2952fe556ff --- /dev/null +++ b/src/XrdXrootd/XrdXrootdXeqFAttr.cc @@ -0,0 +1,499 @@ +/******************************************************************************/ +/* */ +/* X r d X r o o t d X e q F A t t r . c c */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include +#include +#include +#include + +#include "XProtocol/XProtocol.hh" +#include "Xrd/XrdBuffer.hh" +#include "Xrd/XrdLink.hh" +#include "XrdOuc/XrdOucErrInfo.hh" +#include "XrdSfs/XrdSfsFAttr.hh" +#include "XrdSfs/XrdSfsInterface.hh" +#include "XrdSec/XrdSecInterface.hh" +#include "XrdXrootd/XrdXrootdFile.hh" +#include "XrdXrootd/XrdXrootdMonData.hh" +#include "XrdXrootd/XrdXrootdProtocol.hh" +#include "XrdXrootd/XrdXrootdXeq.hh" +#include "XrdXrootd/XrdXrootdXPath.hh" + +/******************************************************************************/ +/* L o c a l S t r u c t u r e s */ +/******************************************************************************/ + +namespace +{ +struct faCTL +{ +XrdSfsFAInfo *info; // Pointer to attribute information +char *buff; // Buffer to be decoded +char *bend; // Pointer to last byte of buffer + 1 +int vnsz; // Size of variable name segment +short iNum; // Number of info entries +short iEnd; // Index number of last entry processed +bool verr; // True if a value is in error, otherwise it's the name + + faCTL(char *bp, char *bz, int anum) + : info(new XrdSfsFAInfo[anum]), buff(bp), bend(bz), + vnsz(0), iNum(anum), iEnd(0), verr(false) {} + ~faCTL() {if (info) delete [] info;} +}; + +static const int iovNum = 16; +} + +#define CRED (const XrdSecEntity *)Client + +#define FATTR_NAMESPACE 'U' + +/******************************************************************************/ +/* D e c o d e */ +/******************************************************************************/ + +namespace +{ +XErrorCode Decode(faCTL &ctl, int MaxNsz, int MaxVsz) +{ + char *bP = ctl.buff, *bend = ctl.bend; + int n, vsize; + +// Decode variable names as kXR_unt16 0 || kXR_char var[n] || kXR_char 0 +// + ctl.verr = false; + + for (int i = 0; i < ctl.iNum; i++) + {ctl.iEnd = i; + if (bP+sizeof(kXR_unt16) >= bend) return kXR_ArgMissing; + + // Validate name prefix and force variable into the user namespace + // + if (*bP || *(bP+1)) return kXR_ArgInvalid; + ctl.info[i].Name = bP; + *bP++ = FATTR_NAMESPACE; + *bP++ = '.'; + + // Process the name (null terminated string) + // + n = strlen(bP); + if (!n || n > MaxNsz) + return (n ? kXR_ArgTooLong : kXR_ArgMissing); + ctl.info[i].NLen = n; + bP += n+1; + } + +// If there are no values, then we are done +// + ctl.vnsz = bP - ctl.buff; + if (!MaxVsz) return (bP != bend ? kXR_BadPayload : (XErrorCode)0); + ctl.verr = true; + +// Decode variable values as kXR_int32 n || kXR_char val[n] +// + for (int i = 0; i < ctl.iNum; i++) + {ctl.iEnd = i; + + // Get the length + // + if (bP+sizeof(kXR_int32) > bend) return kXR_ArgInvalid; + memcpy(&vsize, bP, sizeof(kXR_int32)); + vsize = ntohl(vsize); + if (vsize < 0 || vsize > MaxVsz) return kXR_ArgTooLong; + bP += sizeof(kXR_int32); + + // Get the value + // + ctl.info[i].Value = bP; + ctl.info[i].VLen = vsize; + bP += vsize; + if (bP > bend) return kXR_ArgInvalid; + } + +// Make sure nothing remains in the buffer +// + if (bP != bend) return kXR_BadPayload; + return (XErrorCode)0; +} +} + +/******************************************************************************/ +/* F i l l R C */ +/******************************************************************************/ + +namespace +{ +void FillRC(kXR_char *faRC, XrdSfsFAInfo *info, int inum) +{ + kXR_unt16 rc; + int nerrs = 0; + +// Set status code for each element +// + for (int i = 0; i < inum; i++) + {if (info[i].faRC == 0) info[i].Name[0] = info[i].Name[1] = '\0'; + else {nerrs++; + rc = htons(XProtocol::mapError(info[i].faRC)); + memcpy(info[i].Name, &rc, sizeof(rc)); + } + } + +// Complete vector and return length +// + faRC[0] = nerrs; + faRC[1] = inum; +} +} + +/******************************************************************************/ +/* S e n d E r r */ +/******************************************************************************/ + +namespace +{ +int SendErr(XrdXrootdResponse &Resp, faCTL &ctl, XErrorCode eCode) +{ + char eBuff[1024]; + + snprintf(eBuff, sizeof(eBuff), "%s processing fattr %s argument #%d", + XProtocol::errName(eCode), (ctl.verr ? "data" : "name"), ctl.iNum); + + return Resp.Send(eCode, eBuff); +} + + +int SendErr(XrdXrootdResponse &Resp, const char *what, const char *path, int rc) +{ + int eCode = XProtocol::mapError(rc); + char eBuff[2048]; + + snprintf(eBuff, sizeof(eBuff), "%s processing fattr %s %s", + XProtocol::errName(eCode), what, path); + + return Resp.Send((XErrorCode)eCode, eBuff); +} +} + +/******************************************************************************/ +/* d o _ F A t t r */ +/******************************************************************************/ + +int XrdXrootdProtocol::do_FAttr() +{ + const char *eTxt; + char *fn, *fnCgi; + int faCode = static_cast(Request.fattr.subcode); // Is unsigned + int popt, ropt, n, dlen = Request.header.dlen; + bool isRO; + +// Make sure we are configured for extended attributes +// + if (!usxMaxNsz) + return Response.Send(kXR_Unsupported, "fattr request is not supported"); + +// Prevalidate the subcode (it is unsigned) +// + if (faCode > kXR_fatrrMaxSC) + return Response.Send( kXR_ArgInvalid, "fattr subcode is invalid"); + +// Determine whether we will be reading or writing attributes +// + if (faCode == kXR_fattrGet || faCode == kXR_fattrList) + {isRO = true; + eTxt = "Inspecting file attributes"; + } else { + isRO = false; + eTxt = "Modifying file attributes"; + } + +// Make sure we have the right number of arguments +// + if (faCode != kXR_fattrList && !dlen) + return Response.Send(kXR_ArgMissing, + "Required arguments for fattr request not present"); + +// This operation may refer to an open file. Make sure it exists and is +// opened in a compatible mode. Otherwise, verify that the target file +// can be properly accessed by the client. If so, process the request. +// + if (!dlen || argp->buff[0] == 0) + {XrdXrootdFile *fp; + XrdXrootdFHandle fh(Request.fattr.fhandle); + char *theArg = argp->buff; + + if (!FTab || !(fp = FTab->Get(fh.handle))) + return Response.Send(kXR_FileNotOpen, + "fattr does not refer to an open file"); + if (!isRO && fp->FileMode != 'w') + return Response.Send(kXR_InvalidRequest, + "fattr request modifies a file open for reading"); + if (dlen) {dlen--; theArg++;} + + return ProcFAttr(fp->FileKey, 0, theArg, dlen, faCode, false); + } + +// The operation is being targetted to a file path. First, get path length. +// + fn = argp->buff; + n = strlen(argp->buff); // Always ends with a null byte! + +// Prescreen the path and handle any redirects +// + if (rpCheck(fn, &fnCgi)) return rpEmsg(eTxt, fn); + if (!(popt = Squash(fn))) return vpEmsg(eTxt, fn); + if (Route[RD_open1].Host[rdType] && (ropt = RPList.Validate(fn))) + return Response.Send(kXR_redirect, Route[ropt].Port[rdType], + Route[ropt].Host[rdType]); + +// Hand this off to the attribute processor +// + return ProcFAttr(fn, fnCgi, argp->buff+n+1, dlen-n-1, faCode, true); +} + +/******************************************************************************/ +/* P r o c F A t t r */ +/******************************************************************************/ + +int XrdXrootdProtocol::ProcFAttr(char *faPath, char *faCgi, char *faArgs, + int faALen, int faCode, bool doAChk) +{ + int fNumAttr = static_cast(Request.fattr.numattr); + +// Prevalidate the number of attributes (list must have zero) +// + if ((faCode == kXR_fattrList && fNumAttr != 0) + || (faCode != kXR_fattrList && fNumAttr > kXR_faMaxVars)) + return Response.Send( kXR_ArgInvalid, "fattr numattr is invalid"); + +// Allocate an SFS control object now +// + XrdSfsFACtl sfsCtl(faPath, faCgi, fNumAttr); + sfsCtl.nPfx[0] = FATTR_NAMESPACE; + sfsCtl.nPfx[1] = '.'; + if (doAChk) sfsCtl.opts = XrdSfsFACtl::accChk; + +// If this is merely a list then go do it as there is nothing to parse +// + if (faCode == kXR_fattrList) return XeqFALst(sfsCtl); + +// Parse the request buffer as needed +// + faCTL ctl(faArgs, faArgs+faALen, fNumAttr); + XErrorCode rc = + Decode(ctl, usxMaxNsz, (faCode == kXR_fattrSet ? usxMaxVsz : 0)); + if (rc) return SendErr(Response, ctl, rc); + +// Transfer info ownership +// + sfsCtl.info = ctl.info; + ctl.info = 0; + +// Perform the requested action +// + if (faCode == kXR_fattrDel) return XeqFADel(sfsCtl, faArgs, ctl.vnsz); + if (faCode == kXR_fattrGet) return XeqFAGet(sfsCtl, faArgs, ctl.vnsz); + if (faCode == kXR_fattrSet) return XeqFASet(sfsCtl, faArgs, ctl.vnsz); + + return Response.Send(kXR_Unsupported, "fattr request is not supported"); +} + +/******************************************************************************/ +/* X e q F A D e l */ +/******************************************************************************/ + +int XrdXrootdProtocol::XeqFADel(XrdSfsFACtl &ctl, char *faVars, int faVLen) +{ + XrdOucErrInfo eInfo(Link->ID, Monitor.Did, clientPV); + struct iovec iov[3]; + kXR_char faRC[2]; + int rc; + +// Set correct subcode +// + ctl.rqst = XrdSfsFACtl::faDel; + +// Execute the action +// + if ((rc = osFS->fsctl(SFS_FSCTL_FATTR, (const char *)&ctl, eInfo, CRED))) + return fsError(rc, XROOTD_MON_OPENW, eInfo, ctl.path, (char *)ctl.pcgi); + +// Format the response +// + FillRC(faRC, ctl.info, ctl.iNum); + +// Send off the response +// + iov[1].iov_base = faRC; + iov[1].iov_len = sizeof(faRC); + iov[2].iov_base = faVars; + iov[2].iov_len = faVLen; + return Response.Send(iov, 3, sizeof(faRC) + faVLen); +} + +/******************************************************************************/ +/* X e q F A G e t */ +/******************************************************************************/ + +int XrdXrootdProtocol::XeqFAGet(XrdSfsFACtl &ctl, char *faVars, int faVLen) +{ + XrdOucErrInfo eInfo(Link->ID, Monitor.Did, clientPV); + struct iovec iov[iovNum]; + kXR_int32 fasz[iovNum]; + kXR_char faRC[2]; + XResponseType rcode; + int k, rc, dlen; + +// Set correct subcode +// + ctl.rqst = XrdSfsFACtl::faGet; + +// Execute the action +// + if ((rc = osFS->fsctl(SFS_FSCTL_FATTR, (const char *)&ctl, eInfo, CRED))) + return fsError(rc, XROOTD_MON_OPENR, eInfo, ctl.path, (char *)ctl.pcgi); + +// Format the common response +// + FillRC(faRC, ctl.info, ctl.iNum); + +// Prefill the io vector (number of errors, vars, followed the rc-names +// + iov[1].iov_base = faRC; + iov[1].iov_len = sizeof(faRC); + iov[2].iov_base = faVars; + iov[2].iov_len = faVLen; + dlen = sizeof(faRC) + faVLen; + k = 3; + +// Return the value for for each variable, segment the response, if need be +// + for (int i = 0; i < ctl.iNum; i++) + {iov[k ].iov_base = &fasz[i]; + iov[k++].iov_len = sizeof(fasz[0]); + dlen += sizeof(fasz[0]); + if (ctl.info[i].faRC || ctl.info[i].VLen == 0) fasz[i] = 0; + else {fasz[i] = htonl(ctl.info[i].VLen); + iov[k ].iov_base = (void *)ctl.info[i].Value; + iov[k++].iov_len = ctl.info[i].VLen; + dlen += ctl.info[i].VLen; + } + if (k+2 >= iovNum) + {rcode = (i+1 == ctl.iNum ? kXR_ok : kXR_oksofar); + if ((rc = Response.Send(rcode, iov, k, dlen))) return rc; + k = 1; dlen = 0; + } + } + +// Check if we need to send out the last amount of data +// + return (dlen ? Response.Send(iov, k, dlen) : 0); +} + +/******************************************************************************/ +/* X e q F A L s t */ +/******************************************************************************/ + +int XrdXrootdProtocol::XeqFALst(XrdSfsFACtl &ctl) +{ + struct iovec iov[iovNum]; + XrdOucErrInfo eInfo(Link->ID, Monitor.Did, clientPV); + int rc; + +// Set correct subcode +// + ctl.rqst = XrdSfsFACtl::faLst; + +// Execute the action +// + if ((rc = osFS->fsctl(SFS_FSCTL_FATTR, (const char *)&ctl, eInfo, CRED))) + return fsError(rc, XROOTD_MON_OPENR, eInfo, ctl.path, (char *)ctl.pcgi); + +// If there is only a single buffer, hen we can do a simple response +// + if (!ctl.fabP) return Response.Send(); + if (ctl.fabP->next == 0) + return Response.Send(ctl.fabP->data, ctl.fabP->dlen); + +// Send of the response in as many segments as we need +// + int dlen = 0, i = 1; + XrdSfsFABuff *dP = ctl.fabP; + + while(dP) + {iov[i].iov_base = dP->data; + iov[i].iov_len = dP->dlen; + dlen += dP->dlen; + dP = dP->next; + i++; + if (i == iovNum) + {rc = Response.Send((dP ? kXR_oksofar : kXR_ok), iov, i, dlen); + if (rc || dP == 0) break; + } + i = 1; + } + +// All done +// + return rc; +} + +/******************************************************************************/ +/* d o _ F A S e t */ +/******************************************************************************/ + +int XrdXrootdProtocol::XeqFASet(XrdSfsFACtl &ctl, char *faVars, int faVLen) +{ + XrdOucErrInfo eInfo(Link->ID, Monitor.Did, clientPV); + struct iovec iov[3]; + kXR_char faRC[2]; + int rc; + +// Set correct subcode and options +// + ctl.rqst = XrdSfsFACtl::faSet; + if (Request.fattr.options & ClientFattrRequest::isNew) + ctl.opts |= XrdSfsFACtl::newAtr; + +// Execute the action +// + if ((rc = osFS->fsctl(SFS_FSCTL_FATTR, (const char *)&ctl, eInfo, CRED))) + return fsError(rc, XROOTD_MON_OPENW, eInfo, ctl.path, (char *)ctl.pcgi); + +// Format the response +// + FillRC(faRC, ctl.info, ctl.iNum); + +// Send off the response +// + iov[1].iov_base = faRC; + iov[1].iov_len = sizeof(faRC); + iov[2].iov_base = faVars; + iov[2].iov_len = faVLen; + return Response.Send(iov, 3, sizeof(faRC) + faVLen); +}