77#include "XrdVersion.hh"
80#define ENODATA ENOATTR
84#define ETIME ETIMEDOUT
121 if (gettimeofday(&tv, 0))
122 {perror(
"gettimeofday");
125 tmp = localtime(&tv.tv_sec);
127 {perror(
"localtime");
131 if (strftime(buff,
sizeof(buff),
"%y%m%d:%H%M%S. ", tmp) <= 0)
137 snprintf(tuff,
sizeof(tuff),
"%d",
static_cast<int>(tv.tv_usec/100000));
158static const char *startUP = getTime();
165int XrdXrootdProtocol::do_Auth()
188 sizeof(
Request.auth.credtype)))
190 size_t size =
sizeof(
Request.auth.credtype);
191 strncpy(
Entity.prot, (
const char *)
Request.auth.credtype, size);
194 {eText =
eMsg.getErrText(rc);
195 eDest.Emsg(
"Xeq",
"User authentication failed;", eText);
212 if (!logLogin(
true))
return -1;
219 {
TRACEP(LOGIN,
"more auth requested; sz=" <<(parm ? parm->
size : 0));
224 eDest.Emsg(
"Xeq",
"Security requested additional auth w/o parms!");
238 SI->Bump(
SI->AuthBad);
239 eText =
eMsg.getErrText(rc);
240 eDest.Emsg(
"Xeq",
"User authentication failed;", eText);
248int XrdXrootdProtocol::do_Bind()
250 XrdXrootdSessID *sp = (XrdXrootdSessID *)
Request.bind.sessid;
254 char buff[64], *cp, *dp;
258 SI->Bump(
SI->miscCnt);
301 if (strcmp(
Link->Host(), lp->
Host()))
328 if ( (dp = rindex(cp,
'@'))) *dp =
'\0';
329 if (!(dp = rindex(cp,
'.'))) pPid = 0;
330 else {*dp++ =
'\0'; pPid = strtol(dp, (
char **)NULL, 10);}
331 Link->setID(cp, pPid);
350 sprintf(buff,
"FD %d#%d bound",
Link->FDnum(), i);
359 buff[0] =
static_cast<char>(i);
378int XrdXrootdProtocol::do_Chmod()
390 mode = mapMode((
int)ntohs(
Request.chmod.mode));
391 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Modifying",
argp->buff);
392 if (!Squash(
argp->buff))
return vpEmsg(
"Modifying",
argp->buff);
409int XrdXrootdProtocol::do_CKsum(
int canit)
412 char *algT =
JobCKT, *args[6];
426 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Check summing",
argp->buff);
427 if (!Squash(
argp->buff))
return vpEmsg(
"Check summing",
argp->buff);
440 algT = getCksType(opaque, cksT,
sizeof(cksT));
443 snprintf(ebuf,
sizeof(ebuf),
"%s checksum not supported.", cksT);
450 if (
JobLCL && (rc = do_CKsum(algT,
argp->buff, opaque)) <= 0)
return rc;
465 args[2] =
argp->buff;
466 args[3] =
const_cast<char *
>(
Client->tident);
467 if (
Client->eaAPI->Get(std::string(
"request.name"), keyval) && !keyval.empty())
468 args[4] =
const_cast<char *
>(keyval.c_str());
474 args[1] =
argp->buff;
486int XrdXrootdProtocol::do_CKsum(
char *algT,
const char *
Path,
char *Opaque)
488 static char Space =
' ';
490 int CKTLen = strlen(algT);
492 myError,
CRED, Opaque);
493 const char *csData = myError.getErrText(
ec);
497 if (rc)
return fsError(rc, 0, myError,
Path, Opaque);
502 {
if (*csData ==
'!')
return Response.Send(csData+1);
503 struct iovec iov[4] = {{0,0}, {algT, (size_t)CKTLen}, {&Space, 1},
504 {(
char *)csData, strlen(csData)+1}};
511 {
const char *eTxt[2] = {
JobCKT,
" checksum not available."};
512 myError.setErrInfo(0, eTxt, 2);
525int XrdXrootdProtocol::do_Clone()
527 XrdXrootdFHandle fh(
Request.clone.fhandle);
529 XrdSfsFile *dstFile, *srcFile = 0;
531 int clVecNum, clVecLen =
Request.header.dlen;
541 if (!
FTab || !(fP =
FTab->Get(fh.handle)))
543 "clone does not refer to an open dest file");
549 clVecNum = clVecLen /
sizeof(XrdProto::clone_list);
550 if ( (clVecNum <= 0) ||
551 (clVecNum*(
int)
sizeof(XrdProto::clone_list) != clVecLen) )
563 std::vector<XrdOucCloneSeg> clVec(clVecNum);
567 XrdProto::clone_list* clList = (XrdProto::clone_list *)
argp->buff;
571 for (
int i = 0; i < clVecNum; i++)
572 {fh.Set(clList[i].srcFH);
573 if (!srcFile || currFH != fh.handle)
575 if (!(fP =
FTab->Get(currFH)))
577 "clone does not refer to an open src file");
584 const char *
eMsg = myError.getErrText(ecode);
588 else fdNum = myError.getErrInfo();
592 "clone does not refer to an open src file");
594 clVec[i].srcFD = fdNum;
595 n2hll(clList[i].srcOffs, clVec[i].srcOffs);
596 n2hll(clList[i].srcLen, clVec[i].srcLen);
597 n2hll(clList[i].dstOffs, clVec[i].dstOffs);
602 int rc = dstFile->
Clone(clVec);
603 if (
SFS_OK != rc)
return fsError(rc, 0, dstFile->
error, 0, 0);
612int XrdXrootdProtocol::do_Close()
616 XrdXrootdFHandle fh(
Request.close.fhandle);
622 SI->Bump(
SI->miscCnt);
626 if (!
FTab || !(fp =
FTab->Get(fh.handle)))
628 "close does not refer to an open file");
638 if (fp->
pgwFob && !do_PgClose(fp, rc))
649 fp->cbArg =
ReqID.getID();
663 rc = fp->XrdSfsp->close();
664 TRACEP(FS,
" fh=" <<fh.handle <<
" close rc=" <<rc);
668 return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
677 if (
SFS_OK != rc) retval = fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
684 if (!doDel) fp->Ref(-1);
696int XrdXrootdProtocol::do_Dirlist()
698 int bleft, rc = 0, dlen, cnt = 0;
699 char *opaque, *buff, ebuff[4096];
714 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Listing",
argp->buff);
715 if (!doDig && !Squash(
argp->buff))
return vpEmsg(
"Listing",
argp->buff);
725 {snprintf(ebuff,
sizeof(ebuff)-1,
"Insufficient memory to open %s",
argp->buff);
726 eDest.Emsg(
"Xeq", ebuff);
742 return do_DirStat(dp, ebuff, opaque);
752 do {buff = ebuff; bleft =
sizeof(ebuff);
753 while(dname || (dname = dp->
nextEntry()))
754 {dlen = strlen(dname);
755 if (dlen > 2 || dname[0] !=
'.' || (dlen == 2 && dname[1] !=
'.'))
756 {
if ((bleft -= (dlen+1)) < 0)
break;
757 strcpy(buff, dname); buff += dlen; *buff =
'\n'; buff++; cnt++;
762 }
while(!rc && dname);
767 {
if (ebuff == buff) rc =
Response.Send();
768 else {*(buff-1) =
'\0';
769 rc =
Response.Send((
void *)ebuff, buff-ebuff);
777 if (!rc) {
TRACEP(FS,
"dirlist entries=" <<cnt <<
" path=" <<
argp->buff);}
790 char *buff, *dLoc, *algT = 0;
791 const char *csData, *dname;
792 int bleft, rc = 0, dlen, cnt = 0, statSz = 160;
794 struct {
char ebuff[8192];
char epad[512];} XB;
801 algT = getCksType(opaque, cksT,
sizeof(cksT));
804 snprintf(ebuf,
sizeof(ebuf),
"%s checksum not supported.", cksT);
818 {strcpy(pbuff,
argp->buff);
819 dlen = strlen(pbuff);
820 if (pbuff[dlen-1] !=
'/') {pbuff[dlen] =
'/'; dlen++;}
829 strcpy(XB.ebuff,
".\n0 0 0 0\n");
830 buff = XB.ebuff+10; bleft =
sizeof(XB.ebuff)-10;
840 do {
while(dname || (dname = dp->
nextEntry()))
841 {dlen = strlen(dname);
842 if (dlen > 2 || dname[0] !=
'.' || (dlen == 2 && dname[1] !=
'.'))
843 {
if ((bleft -= (dlen+1)) < 0 || bleft < statSz)
break;
844 if (dLoc) strcpy(dLoc, dname);
847 if (rc ==
SFS_ERROR && myError.getErrInfo() == ENOENT)
848 {dname = 0;
continue;}
853 strcpy(buff, dname); buff += dlen; *buff =
'\n'; buff++; cnt++;
854 dlen = StatGen(
Stat, buff,
sizeof(XB.epad));
855 bleft -= dlen; buff += (dlen-1);
858 pbuff, myError,
CRED, opaque);
859 csData = myError.getErrText();
860 if (
ec !=
SFS_OK || !(*csData) || *csData ==
'!')
862 int n = snprintf(buff,
sizeof(XB.epad),
" [ %s:%s ]",
864 buff += n; bleft -= n;
866 *buff =
'\n'; buff++;
872 buff = XB.ebuff; bleft =
sizeof(XB.ebuff);
873 TRACEP(FS,
"dirstat sofar n=" <<cnt <<
" path=" <<
argp->buff);
875 }
while(!rc && dname);
880 {
if (XB.ebuff == buff) rc =
Response.Send();
881 else {*(buff-1) =
'\0';
882 rc =
Response.Send((
void *)XB.ebuff, buff-XB.ebuff);
890 if (!rc) {
TRACEP(FS,
"dirstat entries=" <<cnt <<
" path=" <<
argp->buff);}
898int XrdXrootdProtocol::do_Endsess()
900 XrdXrootdSessID *sp, sessID;
905 SI->Bump(
SI->miscCnt);
909 sp = (XrdXrootdSessID *)
Request.endsess.sessid;
910 memcpy((
void *)&sessID.
Pid, &sp->
Pid,
sizeof(sessID.
Pid));
911 memcpy((
void *)&sessID.
FD, &sp->
FD,
sizeof(sessID.
FD));
912 memcpy((
void *)&sessID.
Inst, &sp->
Inst,
sizeof(sessID.
Inst));
916 TRACEP(LOGIN,
"endsess " <<sessID.
Pid <<
':' <<sessID.
FD <<
'.' <<sessID.
Inst);
924 if ((sessID.
FD == 0 && sessID.
Inst == 0)
925 || !(rc =
Link->Terminate(0, sessID.
FD, sessID.
Inst)))
return -1;
929 TRACEP(LOGIN,
"endsess " <<sessID.
Pid <<
':' <<sessID.
FD <<
'.' <<sessID.
Inst
930 <<
" rc=" <<rc <<
" (" <<
XrdSysE2T(rc < 0 ? -rc : EAGAIN) <<
")");
953int XrdXrootdProtocol::do_gpFile()
959 SI->Bump(
SI->getfCnt);
974int XrdXrootdProtocol::do_Locate()
978 char *opaque = 0, *
Path, *fn =
argp->buff, opt[8], *op=opt;
995 TRACEP(FS,
"locate " <<opt <<
' ' <<fn);
999 if (*fn !=
'*'){
Path = fn;
1002 else if (*(fn+1)) {
Path = fn+1;
1006 fn =
XPList.Next()->Path();
1017 {
if (rpCheck(
Path, &opaque))
return rpEmsg(
"Locating",
Path);
1018 if (!doDig && !Squash(
Path))
return vpEmsg(
"Locating",
Path);
1023 if (doDig) rc =
digFS->fsctl(fsctl_cmd, fn, myError,
CRED);
1025 {
int n = strlen(
argp->buff);
argp->buff[n] =
'?';
1026 if ((
argp->buff)+n != opaque-1)
1027 memmove(&
argp->buff[n+1], opaque, strlen(opaque)+1);
1029 rc =
osFS->fsctl(fsctl_cmd, fn, myError,
CRED);
1031 TRACEP(FS,
"rc=" <<rc <<
" locate " <<fn);
1039int XrdXrootdProtocol::do_Login()
1041 XrdXrootdSessID sessID;
1042 XrdNetAddrInfo *addrP;
1043 int i, pid, rc, sendSID = 0;
1044 char uname[
sizeof(
Request.login.username)+1];
1048 SI->Bump(
SI->LoginAT);
1053 {
const char *
emsg =
"login requires TLS be enabled";
1055 {
emsg =
"login requires TLS support";
1056 eDest.Emsg(
"Xeq",
"login requires TLS but",
Link->ID,
"is incapable.");
1063 pid = (int)ntohl(
Request.login.pid);
1064 strncpy(uname, (
const char *)
Request.login.username,
sizeof(uname)-1);
1065 uname[
sizeof(uname)-1] = 0;
1071 "duplicate login; already logged in");
1075 Link->setID(uname, pid);
1081 {sessID.
FD =
Link->FDnum();
1115 addrP =
Link->AddrInfo();
1137 {
const char *pp=
CIA->getParms(i,
Link->AddrInfo());
1138 if (pp && i ) {
if (!sendSID) rc =
Response.Send((
void *)pp, i);
1139 else {
struct iovec iov[3];
1140 iov[1].iov_base = (
char *)&sessID;
1141 iov[1].iov_len =
sizeof(sessID);
1142 iov[2].iov_base = (
char *)pp;
1144 rc =
Response.Send(iov,3,
int(i+
sizeof(sessID)));
1148 else {rc = (sendSID ?
Response.Send((
void *)&sessID,
sizeof(sessID))
1153 else {rc = (sendSID ?
Response.Send((
void *)&sessID,
sizeof(sessID))
1170 {XrdOucEnv loginEnv(
argp->buff+1,
Request.login.dlen-1);
1171 char *rnumb = loginEnv.Get(
"xrd.rn");
1172 char *cCode = loginEnv.Get(
"xrd.cc");
1173 char *tzVal = loginEnv.Get(
"xrd.tz");
1174 char *appXQ = loginEnv.Get(
"xrd.appname");
1175 char *aInfo = loginEnv.Get(
"xrd.info");
1176 int tzNum = (tzVal ? atoi(tzVal) : 0);
1177 if (cCode && *cCode && tzNum >= -12 && tzNum <= 14)
1178 {XrdNetAddrInfo::LocInfo locInfo;
1181 Link->setLocation(locInfo);
1183 if (
Monitor.Ready() && (appXQ || aInfo))
1185 snprintf(apBuff, sizeof(apBuff),
"&R=%s&x=%s&y=%s&I=%c",
1186 (rnumb ? rnumb :
""),
1187 (appXQ ? appXQ :
""), (aInfo ? aInfo :
""),
1188 (clientPV & XrdOucEI::uIPv4 ?
'4' :
'6'));
1189 Entity.moninfo = strdup(apBuff);
1193 {
int majr, minr, pchr;
1194 if (sscanf(rnumb,
"v%d.%d.%d", &majr, &minr, &pchr) == 3)
1195 clientRN = (majr<<16) | ((minr<<8) | pchr);
1196 else if (sscanf(rnumb,
"v%d-%*x", &majr) == 1)
clientRN = -1;
1198 if (appXQ)
AppName = strdup(appXQ);
1226int XrdXrootdProtocol::do_Mkdir()
1238 mode = mapMode((
int)ntohs(
Request.mkdir.mode)) | S_IRWXU;
1241 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Creating",
argp->buff);
1242 if (!Squash(
argp->buff))
return vpEmsg(
"Creating",
argp->buff);
1259int XrdXrootdProtocol::do_Mv()
1262 char *oldp, *newp, *Opaque, *Npaque;
1271 oldp = newp =
argp->buff;
1273 {
int n = ntohs(
Request.mv.arg1len);
1274 if (n < 0 || n >=
Request.mv.dlen || *(
argp->buff+n) !=
' ')
1279 while(*newp && *newp !=
' ') newp++;
1280 if (*newp) {*newp =
'\0'; newp++;
1281 while(*newp && *newp ==
' ') newp++;
1287 if (rpCheck(oldp, &Opaque))
return rpEmsg(
"Renaming", oldp);
1288 if (rpCheck(newp, &Npaque))
return rpEmsg(
"Renaming to", newp);
1289 if (!Squash(oldp))
return vpEmsg(
"Renaming", oldp);
1290 if (!Squash(newp))
return vpEmsg(
"Renaming to", newp);
1299 rc =
osFS->rename(oldp, newp, myError,
CRED, Opaque, Npaque);
1300 TRACEP(FS,
"rc=" <<rc <<
" mv " <<oldp <<
' ' <<newp);
1314 XrdSysSemaphore isAvail(0);
1335 pp->
Resume = &XrdXrootdProtocol::do_OffloadIO;
1339 pp->
reTry = &isAvail;
1349 if ((pioP = pp->
pioFree))
break;
1350 pp->
reTry = &isAvail;
1352 TRACEP(FSZIO,
"busy path " <<pathID <<
" offs=" <<
IO.Offset);
1354 TRACEP(FSZIO,
"retry path " <<pathID <<
" offs=" <<
IO.Offset);
1365 pioP->
Set(Invoke,
IO, streamID);
1378int XrdXrootdProtocol::do_OffloadIO()
1391 TRACEP(FSZIO,
"dispatch new I/O path " <<
PathID <<
" offs=" <<
IO.Offset);
1401 if (rc > 0 && !
isNOP)
1403 Resume = &XrdXrootdProtocol::do_OffloadIO;
1419 else {rc = -1;
IO.File->Ref(-1);}
1423 if (rc)
isNOP =
true;
1425 Stream[0]->Link->setRef(-1);
1429 TRACEP(FSZIO,
"offload complete path "<<
PathID<<
" virt rc=" <<rc);
1430 return (rc ? rc : -EINPROGRESS);
1442 XrdXrootdFileLock *Locker;
1447 OpenHelper(XrdXrootdFileLock *lkP,
const char *fn)
1448 : fp(0), xp(0), Locker(lkP), path(fn), mode(0),
1454 else {
if (fp)
delete fp;
1455 if (mode) Locker->Unlock(path,mode);
1462int XrdXrootdProtocol::do_Open()
1466 int rc, mode,
opts, optt, openopts, compchk = 0;
1468 char *opaque,
usage, ebuff[2048], opC;
1469 bool doDig, doforce =
false, isAsync =
false, doClone =
false;
1470 char *fn =
argp->buff, opt[24], *op=opt;
1472 XrdXrootdFile *xp, *sameFS = 0;
1473 struct stat statbuf;
1474 struct ServerResponseBody_Open myResp;
1475 int resplen =
sizeof(myResp.fhandle);
1476 struct iovec IOResp[3];
1481 SI->Bump(
SI->openCnt);
1485 mode = (int)ntohs(
Request.open.mode);
1487 optt = (int)ntohs(
Request.open.optiont);
1495 mode = mapMode(mode) | S_IRUSR | S_IWUSR;
usage =
'r';
1531 {openopts |=
SFS_O_RAWIO; *op++ =
'c'; compchk = 1;}
1534 {*op++ =
'a'; isAsync =
true;}
1536 SI->Bump(
SI->Refresh);
1542 {XrdXrootdFHandle fh(
Request.open.fhtemplt);
1545 "file cloning is not supported" :
1546 "colocating with a specified file is not supported");
1549 "cloned file is not being opened R/W");
1550 {*op++ =
'K'; doClone =
true;}
1553 "file must be opened as a new file in order to colocate");
1556 if (!
FTab || !(sameFS =
FTab->Get(fh.handle)))
1558 "file template does not refer to an open file");
1566 {
char* cgiP = index(fn,
'?');
1567 if (cgiP) *cgiP = 0;
1568 TRACEP(FS,
"open " <<opt <<
' ' <<fn);
1569 if (cgiP) *cgiP =
'?';
1574 if (rpCheck(fn, &opaque))
return rpEmsg(
"Opening", fn);
1583 else {
int ropt = -1;
1584 if (!(popt = Squash(fn)))
return vpEmsg(
"Opening", fn);
1586 ropt =
RPList.Validate(fn);
1603 OpenHelper oHelp(
Locker, fn);
1610 if (rc > 0) who = (rc > 1 ?
"readers" :
"reader");
1612 who = (rc > 1 ?
"writers" :
"writer");
1614 snprintf(ebuff,
sizeof(ebuff)-1,
1615 "%s file %s is already opened by %d %s; open denied.",
1616 (
'r' ==
usage ?
"Input" :
"Output"), fn, rc, who);
1617 eDest.Emsg(
"Xeq", ebuff);
1619 }
else oHelp.mode =
usage;
1630 {snprintf(ebuff,
sizeof(ebuff)-1,
"Insufficient memory to open %s",fn);
1631 eDest.Emsg(
"Xeq", ebuff);
1652 std::string oinfo(opaque ? opaque :
"");
1656 oinfo += (!oinfo.empty() ?
"&" :
"") + coloc;
1662 (mode_t)mode,
CRED, oinfo.c_str())))
1663 return fsError(rc, opC, fp->
error, fn, opaque);
1668 return fsError(rc, opC, fp->
error, fn, opaque);
1672 xp =
new XrdXrootdFile(
Link->ID, fn, fp,
usage, isAsync, &statbuf);
1674 {snprintf(ebuff,
sizeof(ebuff)-1,
"Insufficient memory to open %s", fn);
1675 eDest.Emsg(
"Xeq", ebuff);
1691 if (!
FTab || (fhandle =
FTab->Add(xp)) < 0)
1692 {snprintf(ebuff,
sizeof(ebuff)-1,
"Insufficient memory to open %s", fn);
1693 eDest.Emsg(
"Xeq", ebuff);
1705 Locker->numLocks(fn, rdrs, wrtrs);
1706 if ((
'r' ==
usage && wrtrs) || (
'w' ==
usage && rdrs) || wrtrs > 1)
1707 {snprintf(ebuff,
sizeof(ebuff)-1,
1708 "%s file %s forced opened with %d reader(s) and %d writer(s).",
1709 (
'r' ==
usage ?
"Input" :
"Output"), fn, rdrs, wrtrs);
1710 eDest.Emsg(
"Xeq", ebuff);
1716 memset(&myResp, 0,
sizeof(myResp));
1717 if (!compchk) resplen =
sizeof(myResp.fhandle);
1719 fp->
getCXinfo((
char *)myResp.cptype, cpsize);
1720 myResp.cpsize =
static_cast<kXR_int32>(htonl(cpsize));
1721 resplen =
sizeof(myResp);
1727 {retStat = StatGen(statbuf, ebuff,
sizeof(ebuff));
1728 IOResp[1].iov_base = (
char *)&myResp; IOResp[1].iov_len =
sizeof(myResp);
1729 IOResp[2].iov_base = ebuff; IOResp[2].iov_len = retStat;
1730 resplen =
sizeof(myResp) + retStat;
1749 memcpy((
void *)myResp.fhandle,(
const void *)&fhandle,
sizeof(myResp.fhandle));
1762 {
Stream[i]->pmDone =
true;
1781 if (retStat)
return Response.Send(IOResp, 3, resplen);
1782 else return Response.Send((
void *)&myResp, resplen);
1789int XrdXrootdProtocol::do_Ping()
1794 SI->Bump(
SI->miscCnt);
1805int XrdXrootdProtocol::do_Prepare(
bool isQuery)
1811 XrdOucTokenizer pathlist(
argp->buff);
1812 XrdOucTList *pFirst=0, *pP, *pLast = 0;
1813 XrdOucTList *oFirst=0, *oP, *oLast = 0;
1814 XrdOucTListHelper pHelp(&pFirst), oHelp(&oFirst);
1815 XrdXrootdPrepArgs pargs(0, 0);
1818 int rc, pathnum = 0;
1819 char reqid[128], nidbuff[512], *path, *opaque, *prpid = 0;
1820 unsigned short optX = ntohs(
Request.prepare.optionX);
1822 bool isCancel, isEvict, isPrepare;
1842 isPrepare = !(isCancel || isQuery);
1849 "Surpassed this connection's prepare limit.");
1867 if (isCancel || isQuery)
1868 {
if (!(prpid = pathlist.GetLine()))
1870 fsprep.
reqid = prpid;
1875 prpid =
PrepID->isMine(prpid, hport, hname,
sizeof(hname));
1878 "Prepare requestid owned by an unknown server");
1880 << hname <<
':' <<hport);
1886 {prpid =
PrepID->ID(reqid,
sizeof(reqid));
1887 fsprep.
reqid = reqid;
1890 reqid[0]=
'*'; reqid[1]=
'\0';
1891 fsprep.
reqid = prpid = reqid;
1904 while((path = pathlist.GetLine()))
1905 {
if (rpCheck(path, &opaque))
return rpEmsg(
"Preparing", path);
1906 if (!Squash(path))
return vpEmsg(
"Preparing", path);
1907 pP =
new XrdOucTList(path, pathnum);
1908 (pLast ? (pLast->next = pP) : (pFirst = pP)); pLast = pP;
1909 oP =
new XrdOucTList(opaque, 0);
1910 (oLast ? (oLast->next = oP) : (oFirst = oP)); oLast = oP;
1913 fsprep.
paths = pFirst;
1914 fsprep.
oinfo = oFirst;
1938 char *mBuff = myError.getMsgBuff(rc);
1939 pargs.reqid = prpid;
1940 pargs.user =
Link->ID;
1941 pargs.paths = pFirst;
1943 if (rc < 0) rc =
Response.Send(
"No information found.");
1971 else snprintf(nidbuff,
sizeof(nidbuff),
"%s://%s:%d/",
1972 nprot,
Link->Host(), ntohs(
Request.prepare.port));
2003 else {rc =
Response.Send(reqid, strlen(reqid));
2005 {pargs.reqid = prpid;
2006 pargs.user =
Link->ID;
2007 pargs.paths = pFirst;
2024int XrdXrootdProtocol::do_Protocol()
2031 ServerResponseBody_Protocol theResp;
2035 bool wantTLS =
false;
2039 SI->Bump(
SI->miscCnt);
2043 if (
Request.protocol.clientpv)
2051 {
int k =(
Link->AddrInfo()->isPrivate() ? 1 : 0);
2059 {
int n =
DHS->ProtResp(theResp.
secreq, *(
Link->AddrInfo()), cvn);
2060 ioVec[iovN ].iov_base = (
void *)&theResp.
secreq;
2061 ioVec[iovN++].iov_len = n;
2066 {wantTLS = (
Request.protocol.flags &
2087 theResp.
flags = (wantTLS ? theRlt : theRle);
2089 theResp.
flags = theRlf;
2095 theResp.
pval = verNum;
2096 rc =
Response.Send(ioVec, iovN, RespLen);
2103 if (rc == 0 && wantTLS)
2105 {
Link->setProtName(
"xroots");
2108 eDest.Emsg(
"Xeq",
"Unable to enable TLS for",
Link->ID);
2119int XrdXrootdProtocol::do_Qconf()
2122 XrdOucTokenizer qcargs(
argp->buff);
2123 char *val, buff[4096], *bp=buff;
2124 int n, bleft =
sizeof(buff);
2128 if (!qcargs.GetLine() || !(val = qcargs.GetToken()))
2133 if (!strcmp(val,
"cmsd") || !strcmp(val,
"xrootd"))
2134 return do_QconfCX(qcargs, val);
2142 if (!strcmp(
"bind_max", val))
2143 {n = snprintf(bp, bleft,
"%d\n",
maxStreams-1);
2144 bp += n; bleft -= n;
2146 else if (!strcmp(
"chksum", val))
2147 {
const char *csList = getenv(
"XRD_CSLIST");
2149 {n = snprintf(bp, bleft,
"chksum\n");
2150 bp += n; bleft -= n;
2153 n = snprintf(bp, bleft,
"%s\n", csList);
2154 bp += n; bleft -= n;
2156 else if (!strcmp(
"cid", val))
2157 {
const char *cidval = getenv(
"XRDCMSCLUSTERID");
2158 if (!cidval || !(*cidval)) cidval =
"cid";
2159 n = snprintf(bp, bleft,
"%s\n", cidval);
2160 bp += n; bleft -= n;
2162 else if (!strcmp(
"cms", val))
2165 n = snprintf(bp, bleft,
"%s\n", myError.getErrText());
2166 else n = snprintf(bp, bleft,
"%s\n",
"cms");
2167 bp += n; bleft -= n;
2169 else if (!strcmp(
"pio_max", val))
2170 {n = snprintf(bp, bleft,
"%d\n",
maxPio+1);
2171 bp += n; bleft -= n;
2173 else if (!strcmp(
"proxy", val))
2174 {
const char* pxyOrigin =
"proxy";
2176 {pxyOrigin = getenv(
"XRDXROOTD_PROXY");
2177 if (!pxyOrigin) pxyOrigin =
"proxy";
2179 n = snprintf(bp,bleft,
"%s\n",pxyOrigin);
2180 bp += n; bleft -= n;
2182 else if (!strcmp(
"readv_ior_max", val))
2184 bp += n; bleft -= n;
2186 else if (!strcmp(
"readv_iov_max", val))
2188 bp += n; bleft -= n;
2190 else if (!strcmp(
"role", val))
2191 {
const char *theRole = getenv(
"XRDROLE");
2192 n = snprintf(bp, bleft,
"%s\n", (theRole ? theRole :
"none"));
2193 bp += n; bleft -= n;
2195 else if (!strcmp(
"sitename", val))
2196 {
const char *siteName = getenv(
"XRDSITE");
2197 n = snprintf(bp, bleft,
"%s\n", (siteName ? siteName :
"sitename"));
2198 bp += n; bleft -= n;
2200 else if (!strcmp(
"start", val))
2201 {n = snprintf(bp, bleft,
"%s\n", startUP);
2202 bp += n; bleft -= n;
2204 else if (!strcmp(
"sysid", val))
2205 {
const char *cidval = getenv(
"XRDCMSCLUSTERID");
2206 const char *nidval = getenv(
"XRDCMSVNID");
2207 if (!cidval || !(*cidval) || !nidval || !(*nidval))
2208 {cidval =
"sysid"; nidval =
"";}
2209 n = snprintf(bp, bleft,
"%s %s\n", nidval, cidval);
2210 bp += n; bleft -= n;
2212 else if (!strcmp(
"tpc", val))
2213 {
char *tpcval = getenv(
"XRDTPC");
2214 n = snprintf(bp, bleft,
"%s\n", (tpcval ? tpcval :
"tpc"));
2215 bp += n; bleft -= n;
2217 else if (!strcmp(
"tpcdlg", val))
2218 {
char *tpcval = getenv(
"XRDTPCDLG");
2219 n = snprintf(bp, bleft,
"%s\n", (tpcval ? tpcval :
"tpcdlg"));
2220 bp += n; bleft -= n;
2222 else if (!strcmp(
"tls_port", val) &&
tlsPort)
2223 {n = snprintf(bp, bleft,
"%d\n",
tlsPort);
2224 bp += n; bleft -= n;
2226 else if (!strcmp(
"window", val) &&
Window)
2227 {n = snprintf(bp, bleft,
"%d\n",
Window);
2228 bp += n; bleft -= n;
2230 else if (!strcmp(
"version", val))
2231 {n = snprintf(bp, bleft,
"%s\n", XrdVSTRING);
2232 bp += n; bleft -= n;
2234 else if (!strcmp(
"vnid", val))
2235 {
const char *nidval = getenv(
"XRDCMSVNID");
2236 if (!nidval || !(*nidval)) nidval =
"vnid";
2237 n = snprintf(bp, bleft,
"%s\n", nidval);
2239 else if (!strcmp(
"fattr", val))
2240 {n = snprintf(bp, bleft,
"%s\n",
usxParms);
2241 bp += n; bleft -= n;
2243 else {n = strlen(val);
2244 if (bleft <= n)
break;
2245 strcpy(bp, val); bp +=n; *bp =
'\n'; bp++;
2248 }
while(bleft > 0 && (val = qcargs.GetToken()));
2257 return Response.Send(buff,
sizeof(buff) - bleft);
2267 bool isCMSD = (*val ==
'c');
2276 if (isCMSD)
return Response.Send((
void *)
"\n", 2);
2285 return Response.Send((
void *)
"\n", 2);
2292int XrdXrootdProtocol::do_Qfh()
2295 XrdXrootdFHandle fh(
Request.query.fhandle);
2297 const char *fArg = 0, *qType =
"";
2299 short qopt = (short)ntohs(
Request.query.infotype);
2303 SI->Bump(
SI->miscCnt);
2307 if (!
FTab || !(fp =
FTab->Get(fh.handle)))
2309 "query does not refer to an open file");
2335 "Required query argument not present");
2340 TRACEP(FS,
"fh=" <<fh.handle <<
" query " <<qType <<
" rc=" <<rc);
2353int XrdXrootdProtocol::do_Qopaque(
short qopt)
2358 const char *Act, *AData;
2360 int fsctl_cmd, rc, dlen =
Request.query.dlen;
2366 myData.Arg2 = 0; myData.
Arg2Len = 0;
2368 Act =
" qopaque '"; AData =
"...";
2376 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Querying",
argp->buff);
2377 if (!Squash(
argp->buff))
return vpEmsg(
"Querying",
argp->buff);
2382 myData.
Arg1Len = (opaque ? opaque -
argp->buff - 1 : dlen);
2383 myData.Arg2 = opaque;
2384 myData.
Arg2Len = (opaque ?
argp->buff + dlen - opaque : 0);
2397 myError.setErrCB(&qpqCB,
ReqID.getID());
2401 rc =
osFS->FSctl(fsctl_cmd, myData, myError,
CRED);
2402 TRACEP(FS,
"rc=" <<rc <<Act <<AData <<
"'");
2404 return fsError(rc, 0, myError, 0, 0);
2411int XrdXrootdProtocol::do_Qspace()
2424 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Stating",
argp->buff);
2425 if (!Squash(
argp->buff))
return vpEmsg(
"Stating",
argp->buff);
2430 {n = strlen(
argp->buff);
argp->buff[n] =
'?';
2431 if ((
argp->buff)+n != opaque-1)
2432 memmove(&
argp->buff[n+1], opaque, strlen(opaque)+1);
2437 rc =
osFS->fsctl(fsctl_cmd,
argp->buff, myError,
CRED);
2438 TRACEP(FS,
"rc=" <<rc <<
" qspace '" <<
argp->buff <<
"'");
2447int XrdXrootdProtocol::do_Query()
2449 short qopt = (short)ntohs(
Request.query.infotype);
2467 case kXR_QPrep:
return do_Prepare(
true);
2474 "Invalid information query type code");
2481int XrdXrootdProtocol::do_Qxattr()
2495 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Stating",
argp->buff);
2496 if (!Squash(
argp->buff))
return vpEmsg(
"Stating",
argp->buff);
2501 {
int n = strlen(
argp->buff);
argp->buff[n] =
'?';
2502 if ((
argp->buff)+n != opaque-1)
2503 memmove(&
argp->buff[n+1], opaque, strlen(opaque)+1);
2508 rc =
osFS->fsctl(fsctl_cmd,
argp->buff, myError,
CRED);
2509 TRACEP(FS,
"rc=" <<rc <<
" qxattr " <<
argp->buff);
2517int XrdXrootdProtocol::do_Read()
2520 XrdXrootdFHandle fh(
Request.read.fhandle);
2527 if (!
Request.header.dlen) pathID = 0;
2528 else if (do_ReadNone(retc, pathID))
return retc;
2537 if (!
FTab || !(
IO.File =
FTab->Get(fh.handle)))
2539 "read does not refer to an open file");
2543 TRACEP(FSIO, pathID <<
" fh=" <<fh.handle <<
" read " <<
IO.IOLen
2546 "Read length is negative");
2563 &&
IO.Offset+
IO.IOLen <=
IO.File->Stats.fSize)
2569 XrdXrootdNormAio *aioP=0;
2571 if (!pathID) pP =
this;
2572 else {
if (!(pP =
VerifyStream(retc, pathID,
false)))
return retc;
2581 XrdXrootdResponse TmpRsp;
2587 {
if (!
IO.File->aioFob)
IO.File->aioFob =
new XrdXrootdAioFob;
2598 if (pathID)
return do_Offload(&XrdXrootdProtocol::do_ReadAll, pathID);
2602 return do_ReadAll();
2613int XrdXrootdProtocol::do_ReadAll()
2622 {
if (
IO.Offset >=
IO.File->Stats.fSize)
return Response.Send();
2623 if (
IO.Offset+
IO.IOLen <=
IO.File->Stats.fSize)
2624 {
IO.File->Stats.rdOps(
IO.IOLen);
2627 xframt =
IO.File->Stats.fSize -
IO.Offset;
2628 IO.File->Stats.rdOps(xframt);
2629 return Response.Send(
IO.File->mmAddr+
IO.Offset, xframt);
2635 {
IO.File->Stats.rdOps(
IO.IOLen);
2636 if (
IO.File->fdNum >= 0)
2638 rc =
IO.File->XrdSfsp->SendData((
XrdSfsDio *)
this,
IO.Offset,
IO.IOLen);
2640 {
if (!
IO.IOLen)
return 0;
2641 if (
IO.IOLen < 0)
return -1;
2642 }
else return fsError(rc, 0,
IO.File->XrdSfsp->error, 0, 0);
2647 if (!
argp || Quantum < halfBSize || Quantum >
argp->bsize)
2648 {
if ((rc = getBuff(1, Quantum)) <= 0)
return rc;}
2655 IO.File->Stats.rdOps(
IO.IOLen);
2656 do {
if ((xframt =
IO.File->XrdSfsp->read(
IO.Offset, buff, Quantum)) <= 0)
break;
2657 if (xframt >=
IO.IOLen)
return Response.Send(buff, xframt);
2659 IO.Offset += xframt;
IO.IOLen -= xframt;
2660 if (
IO.IOLen < Quantum) Quantum =
IO.IOLen;
2665 if (xframt == 0)
return Response.Send();
2666 return fsError(xframt, 0,
IO.File->XrdSfsp->error, 0, 0);
2673int XrdXrootdProtocol::do_ReadNone(
int &retc,
int &pathID)
2675 XrdXrootdFHandle fh;
2676 int ralsz =
Request.header.dlen;
2677 struct read_args *rargs=(
struct read_args *)(
argp->buff);
2678 struct readahead_list *ralsp = (readahead_list *)(rargs+1);
2682 pathID =
static_cast<int>(rargs->
pathid);
2683 if ((ralsz -=
sizeof(read_args)) <= 0)
return 0;
2687 if (ralsz%
sizeof(readahead_list))
2695 {
IO.IOLen = ntohl(ralsp->
rlen);
2702 "preread does not refer to an open file");
2705 IO.File->XrdSfsp->read(
IO.Offset,
IO.IOLen);
2706 ralsz -=
sizeof(
struct readahead_list);
2720int XrdXrootdProtocol::do_ReadV()
2729 const int hdrSZ =
sizeof(readahead_list);
2730 struct XrdOucIOVec rdVec[XrdProto::maxRvecsz+1];
2731 struct readahead_list *raVec, respHdr;
2734 int rdVBeg, rdVBreak, rdVNow, rdVNum, rdVecNum;
2735 int currFH, i, k, Quantum, Qleft, rdVecLen =
Request.header.dlen;
2737 int ioMon = (rvMon > 1);
2738 char *buffp, vType = (ioMon ? XROOTD_MON_READU : XROOTD_MON_READV);
2743 rdVecNum = rdVecLen / sizeof(readahead_list);
2744 if ( (rdVecNum <= 0) || (rdVecNum*hdrSZ != rdVecLen) )
2745 return Response.Send(kXR_ArgInvalid, "Read vector is invalid");
2751 if (rdVecNum > XrdProto::maxRvecsz)
2752 return
Response.
Send(kXR_ArgTooLong, "Read vector is too long");
2762 raVec = (readahead_list *)
argp->buff;
2764 for (i = 0; i < rdVecNum; i++)
2765 {totSZ += (rdVec[i].size = ntohl(raVec[i].rlen));
2766 if (rdVec[i].size < 0) return Response.Send(kXR_ArgInvalid,
2767 "Readv length is negative");
2768 if (rdVec[i].size > Quantum) return Response.Send(kXR_NoMemory,
2769 "Single readv transfer is too large");
2770 rdVec[i].offset = ntohll(raVec[i].offset);
2771 memcpy(&rdVec[i].info, raVec[i].fhandle, sizeof(int));
2776 rdVec[i].offset = -1;
2779 rdVBreak = rdVecNum;
2784 if (totSZ > 0x80000000LL)
2785 return
Response.
Send(kXR_NoMemory, "Total readv transfer is too large");
2790 Quantum = totSZ < maxTransz ? totSZ : maxTransz;
2794 if ((Quantum < halfBSize && Quantum > 1024) || Quantum > argp->bsize)
2795 {
if ((k = getBuff(1, Quantum)) <= 0)
return k;}
2802 "readv does not refer to an open file");
2807 currFH = rdVec[0].info;
2808 memcpy(respHdr.fhandle, &currFH,
sizeof(respHdr.fhandle));
2810 "readv does not refer to an open file");
2814 Qleft = Quantum; buffp =
argp->buff;
rvSeq++;
2815 rdVBeg = rdVNow = 0; rdVXfr = rdVAmt = 0;
2819 for (i = 0; i < rdVecNum; i++)
2820 {
if (rdVec[i].info != currFH)
2821 {xfrSZ =
IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2822 if (xfrSZ != rdVAmt)
break;
2823 rdVNum = i - rdVBeg; rdVXfr += rdVAmt;
2824 IO.File->Stats.rvOps(rdVXfr, rdVNum);
2826 {
Monitor.Agent->Add_rv(
IO.File->Stats.FileID, htonl(rdVXfr),
2827 htons(rdVNum),
rvSeq, vType);
2828 if (ioMon)
for (k = rdVBeg; k < i; k++)
2829 Monitor.Agent->Add_rd(
IO.File->Stats.FileID,
2830 htonl(rdVec[k].size), htonll(rdVec[k].offset));
2832 rdVXfr = rdVAmt = 0;
2833 if (i == rdVBreak)
break;
2834 rdVBeg = rdVNow = i; currFH = rdVec[i].info;
2835 memcpy(respHdr.fhandle, &currFH,
sizeof(respHdr.fhandle));
2836 if (!(
IO.File =
FTab->Get(currFH)))
2838 "readv does not refer to an open file");
2841 if (Qleft < (rdVec[i].size + hdrSZ))
2843 {xfrSZ =
IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2844 if (xfrSZ != rdVAmt)
break;
2850 rdVNow = i; rdVXfr += rdVAmt; rdVAmt = 0;
2853 xfrSZ = rdVec[i].size; rdVAmt += xfrSZ;
2854 respHdr.rlen = htonl(xfrSZ);
2855 respHdr.offset = htonll(rdVec[i].offset);
2856 memcpy(buffp, &respHdr, hdrSZ);
2857 rdVec[i].data = buffp + hdrSZ;
2858 buffp += (xfrSZ+hdrSZ); Qleft -= (xfrSZ+hdrSZ);
2859 TRACEP(FSIO,
"fh=" <<currFH<<
" readV "<< xfrSZ <<
'@'<<rdVec[i].offset);
2867 IO.File->XrdSfsp->error.setErrInfo(-
ENODATA,
"readv past EOF");
2869 return fsError(xfrSZ, 0,
IO.File->XrdSfsp->error, 0, 0);
2874 return (Quantum != Qleft ?
Response.Send(
argp->buff, Quantum-Qleft) : 0);
2881int XrdXrootdProtocol::do_Rm()
2893 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Removing",
argp->buff);
2894 if (!Squash(
argp->buff))
return vpEmsg(
"Removing",
argp->buff);
2899 TRACEP(FS,
"rc=" <<rc <<
" rm " <<
argp->buff);
2911int XrdXrootdProtocol::do_Rmdir()
2923 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Removing",
argp->buff);
2924 if (!Squash(
argp->buff))
return vpEmsg(
"Removing",
argp->buff);
2929 TRACEP(FS,
"rc=" <<rc <<
" rmdir " <<
argp->buff);
2941int XrdXrootdProtocol::do_Set()
2943 XrdOucTokenizer setargs(
argp->buff);
2948 if (!setargs.GetLine() || !(val = setargs.GetToken(&rest)))
2957 if (!strcmp(
"appid", val))
2958 {
while(*rest && *rest ==
' ') rest++;
2959 eDest.Emsg(
"Xeq",
Link->ID,
"appid", rest);
2962 else if (!strcmp(
"monitor", val))
return do_Set_Mon(setargs);
2963 else if (!strcmp(
"cache", val))
return do_Set_Cache(setargs);
2980 char *cmd, *cargs, *opaque =
nullptr;
2981 const char *myArgs[2];
2990 if (!(cmd = setargs.
GetToken(&cargs)))
2995 if (cargs && *cargs ==
'/')
2996 {
if (rpCheck(cargs, &opaque))
return rpEmsg(
"Setting", cargs);
2997 if (!Squash(cargs))
return vpEmsg(
"Setting", cargs);
2998 myData.ArgP = myArgs; myData.
Arg2Len = -2;
3002 myData.Arg2 = opaque; myData.
Arg2Len = (opaque ? strlen(opaque) : 0);
3009 TRACEP(FS,
"rc=" <<rc <<
"set cache " <<myData.
Arg1 <<
' ' <<cargs);
3011 return fsError(rc, 0, myError, 0, 0);
3027 if (!(val = setargs.
GetToken(&appid)))
3034 if (!strcmp(val,
"info"))
3036 {
while(*appid && *appid ==
' ') appid++;
3037 if (strlen(appid) > 1024) appid[1024] =
'\0';
3038 if (*appid) myseq =
Monitor.MapInfo(appid);
3040 return Response.Send((
void *)&myseq,
sizeof(myseq));
3045 if (!strcmp(val,
"on"))
3048 {
while(*appid && *appid ==
' ') appid++;
3049 if (*appid)
Monitor.Agent->appID(appid);
3057 if (!strcmp(val,
"off"))
3058 {
if (appid &&
Monitor.InOut())
3059 {
while(*appid && *appid ==
' ') appid++;
3060 if (*appid)
Monitor.Agent->appID(appid);
3075int XrdXrootdProtocol::do_Stat()
3081 char *opaque, xxBuff[1024];
3087 SI->Bump(
SI->miscCnt);
3093 XrdXrootdFHandle fh(
Request.stat.fhandle);
3100 "stat does not refer to an open file");
3104 StatGen(buf,xxBuff,
sizeof(xxBuff)));
3118 if (rpCheck(
argp->buff, &opaque))
return rpEmsg(
"Stating",
argp->buff);
3119 if (!doDig && !Squash(
argp->buff))
return vpEmsg(
"Stating",
argp->buff);
3125 {
int n = strlen(
argp->buff);
argp->buff[n] =
'?';
3126 if ((
argp->buff)+n != opaque-1)
3127 memmove(&
argp->buff[n+1], opaque, strlen(opaque)+1);
3129 rc =
osFS->fsctl(fsctl_cmd,
argp->buff, myError,
CRED);
3130 TRACEP(FS,
"rc=" <<rc <<
" statfs " <<
argp->buff);
3133 if (doDig) rc =
digFS->stat(
argp->buff, &buf, myError,
CRED, opaque);
3134 else rc =
osFS->stat(
argp->buff, &buf, myError,
CRED, opaque);
3135 TRACEP(FS,
"rc=" <<rc <<
" stat " <<
argp->buff);
3137 StatGen(buf,xxBuff,
sizeof(xxBuff)));
3146int XrdXrootdProtocol::do_Statx()
3150 char *path, *opaque, *respinfo =
argp->buff;
3153 XrdOucTokenizer pathlist(
argp->buff);
3161 while((path = pathlist.GetLine()))
3162 {
if (rpCheck(path, &opaque))
return rpEmsg(
"Stating", path);
3163 if (!Squash(path))
return vpEmsg(
"Stating", path);
3164 rc =
osFS->stat(path, mode, myError,
CRED, opaque);
3165 TRACEP(FS,
"rc=" <<rc <<
" stat " <<path);
3168 else {
if (mode == (mode_t)-1) *respinfo = (char)
kXR_offline;
3169 else if (S_ISDIR(mode)) *respinfo = (char)
kXR_isDir;
3184int XrdXrootdProtocol::do_Sync()
3186 static XrdXrootdCallBack syncCB(
"sync", 0);
3189 XrdXrootdFHandle fh(
Request.sync.fhandle);
3193 SI->Bump(
SI->syncCnt);
3219int XrdXrootdProtocol::do_Truncate()
3221 static XrdXrootdCallBack truncCB(
"trunc", 0);
3223 XrdXrootdFHandle fh(
Request.truncate.fhandle);
3224 long long theOffset;
3229 n2hll(
Request.truncate.offset, theOffset);
3237 SI->Bump(
SI->miscCnt);
3243 "trunc does not refer to an open file");
3249 TRACEP(FS,
"fh=" <<fh.
handle <<
" trunc rc=" <<rc <<
" sz=" <<theOffset);
3263 if (rpCheck(
argp->buff,&opaque))
return rpEmsg(
"Truncating",
argp->buff);
3264 if (!Squash(
argp->buff))
return vpEmsg(
"Truncating",
argp->buff);
3270 TRACEP(FS,
"rc=" <<rc <<
" trunc " <<theOffset <<
' ' <<
argp->buff);
3284int XrdXrootdProtocol::do_Write()
3287 XrdXrootdFHandle fh(
Request.write.fhandle);
3294 pathID =
static_cast<int>(
Request.write.pathid);
3300 return do_WriteNone(pathID);
3305 TRACEP(FSIO, pathID<<
" fh="<<fh.
handle<<
" write "<<
IO.IOLen<<
'@'<<
IO.Offset);
3307 "Write length is negative");
3318 IO.File->Stats.wrOps(
IO.IOLen);
3326 {
if (pathID)
return do_Offload(&XrdXrootdProtocol::do_WriteAio,pathID);
3327 return do_WriteAio();
3335 if (pathID)
return do_Offload(&XrdXrootdProtocol::do_WriteAll, pathID);
3339 return do_WriteAll();
3350int XrdXrootdProtocol::do_WriteAio()
3352 XrdXrootdNormAio *aioP;
3360 return do_WriteAll();
3365 return aioP->
Write(
IO.Offset,
IO.IOLen);
3376int XrdXrootdProtocol::do_WriteAll()
3382 if (!
argp || Quantum < halfBSize || Quantum >
argp->bsize)
3383 {
if ((rc = getBuff(0, Quantum)) <= 0)
return rc;}
3391 {
Resume = &XrdXrootdProtocol::do_WriteCont;
3396 if ((rc =
IO.File->XrdSfsp->write(
IO.Offset,
argp->buff, Quantum)) < 0)
3397 {
IO.IOLen =
IO.IOLen-Quantum;
IO.EInfo[0] = rc;
IO.EInfo[1] = 0;
3398 return do_WriteNone();
3400 IO.Offset += Quantum;
IO.IOLen -= Quantum;
3401 if (
IO.IOLen < Quantum) Quantum =
IO.IOLen;
3418int XrdXrootdProtocol::do_WriteCont()
3424 if ((rc =
IO.File->XrdSfsp->write(
IO.Offset,
argp->buff,
myBlast)) < 0)
3426 return do_WriteNone();
3432 if (
IO.IOLen > 0)
return do_WriteAll();
3440int XrdXrootdProtocol::do_WriteNone()
3442 char *buff, dbuff[4096];
3447 if (
argp &&
argp->bsize > (
int)
sizeof(dbuff))
3452 blen =
sizeof(dbuff);
3454 if (
IO.IOLen < blen) blen =
IO.IOLen;
3458 TRACEP(REQ,
"discarding " <<
IO.IOLen <<
" bytes");
3461 if (rlen < 0)
return Link->setEtext(
"link read error");
3465 Resume = &XrdXrootdProtocol::do_WriteNone;
3468 if (
IO.IOLen < blen) blen =
IO.IOLen;
3473 return do_WriteNoneMsg();
3478int XrdXrootdProtocol::do_WriteNone(
int pathID,
XErrorCode ec,
3486 else do_WriteNoneMsg();
3487 return Link->setEtext(
"write protocol violation");
3496 IO.File->XrdSfsp->error.setErrInfo(0,
emsg);
3502 return do_WriteNone();
3509int XrdXrootdProtocol::do_WriteNoneMsg()
3513 if (!
IO.File)
return
3518 IO.File->XrdSfsp->error.getErrText());
3520 if (
IO.EInfo[0])
return fsError(
IO.EInfo[0], 0,
IO.File->XrdSfsp->error, 0, 0);
3545 return do_WriteNone(
Request.write.pathid);
3553 IO.File->Stats.wrOps(
IO.IOLen);
3563 return do_WriteNone();
3569 if (
IO.IOLen > 0)
return do_WriteAll();
3577int XrdXrootdProtocol::do_WriteV()
3590 ~trackInfo() {
if (doit && *wvInfo) {free(*wvInfo); *wvInfo = 0;}}
3593 struct XrdProto::write_list *wrLst;
3595 long long totSZ, maxSZ;
3596 int curFH, k, Quantum, wrVecNum, wrVecLen =
Request.header.dlen;
3601 wrVecNum = wrVecLen / wveSZ;
3602 if ( (wrVecLen <= 0) || (wrVecNum*wveSZ != wrVecLen) )
3618 wvInfo = (XrdXrootdWVInfo *)malloc(
sizeof(XrdXrootdWVInfo) +
3619 sizeof(XrdOucIOVec)*(wrVecNum-1));
3620 memset(
wvInfo, 0,
sizeof(XrdXrootdWVInfo) -
sizeof(XrdOucIOVec));
3627 wrLst = (XrdProto::write_list *)
argp->buff;
3628 totSZ = 0; maxSZ = 0; k = 0; Quantum =
maxTransz; curFH = 0;
3629 for (
int i = 0; i < wrVecNum; i++)
3630 {
if (wrLst[i].wlen == 0)
continue;
3631 memcpy(&wrVec[k].info, wrLst[i].fhandle,
sizeof(
int));
3632 wrVec[k].
size = ntohl(wrLst[i].wlen);
3633 if (wrVec[k].size < 0)
3637 if (wrVec[k].size > Quantum)
3641 wrVec[k].
offset = ntohll(wrLst[i].offset);
3642 if (wrVec[k].info == curFH) totSZ += wrVec[k].
size;
3643 else {
if (maxSZ < totSZ) maxSZ = totSZ;
3644 totSZ = wrVec[k].
size;
3651 if (maxSZ < totSZ) maxSZ = totSZ;
3652 if (maxSZ == 0)
return Response.Send();
3662 else Quantum =
static_cast<int>(maxSZ);
3666 if ((Quantum < halfBSize && Quantum > 1024) || Quantum >
argp->bsize)
3667 {
if (getBuff(0, Quantum) <= 0)
return -1;}
3672 if (!
FTab || !(
IO.File =
FTab->Get(wrVec[0].info)))
3689 IO.IOLen = wrVec[0].
size;
3696 freeInfo.doit =
false;
3705int XrdXrootdProtocol::do_WriteVec()
3708 int rc, wrVNum, vNow =
wvInfo->vPos;
3718 {
if (rc < 0)
return rc;
3720 Resume = &XrdXrootdProtocol::do_WriteVec;
3728 done = newfile =
false;
3729 if (vNow >=
wvInfo->vEnd) done =
true;
3730 else if (
wvInfo->wrVec[vNow].info !=
wvInfo->curFH) newfile =
true;
3732 {
IO.IOLen =
wvInfo->wrVec[vNow].size;
3740 wrVNum = vNow -
wvInfo->vBeg;
3741 xfrSZ =
IO.File->XrdSfsp->writev(&(
wvInfo->wrVec[
wvInfo->vBeg]), wrVNum);
3742 TRACEP(FSIO,
"fh=" <<
wvInfo->curFH <<
" writeV " << xfrSZ <<
':' <<wrVNum);
3748 if (done || newfile)
3749 {
int monVnum = vNow -
wvInfo->vMon;
3750 IO.File->Stats.wvOps(
IO.WVBytes, monVnum);
3763 {
IO.File->XrdSfsp->error.setErrCB(0,0);
3764 xfrSZ =
IO.File->XrdSfsp->sync();
3765 if (xfrSZ< 0)
break;
3790 IO.IOLen =
wvInfo->wrVec[vNow].size;
3799 return fsError((
int)xfrSZ, 0,
IO.File->XrdSfsp->error, 0, 0);
3811 if (!
IO.IOLen)
return 1;
3827 if (!
IO.IOLen)
return 1;
3831 for (i = 1; i < sfvnum; i++) xframt += sfvec[i].sendsz;
3832 if (xframt >
IO.IOLen)
return 1;
3836 if (xframt)
IO.IOLen =
Response.Send(sfvec, sfvnum, xframt);
3847 if (fildes < 0)
IO.File->sfEnabled = 0;
3848 else IO.File->fdNum = fildes;
3858int XrdXrootdProtocol::fsError(
int rc,
char opC,
XrdOucErrInfo &myError,
3859 const char *
Path,
char *Cgi)
3861 int ecode, popt, rs;
3873 return fsOvrld(opC,
Path, Cgi);
3883 if (Cgi) rs = fsRedirNoEnt(
eMsg, Cgi, popt);
3906 <<
eMsg <<
':' <<ecode);
3923 if (ecode <= 0) ecode = 1800;
3924 TRACEI(STALL,
Response.ID() <<
"delaying client up to " <<ecode <<
" sec");
3928 return (rc ? rc : 1);
3943 {
if (ecode < 2) rs =
Response.Send();
3954 TRACEI(STALL,
Response.ID() <<
"stalling client for " <<rc <<
" sec");
3964 sprintf(buff,
"%d", rc);
3965 eDest.Emsg(
"Xeq",
"Unknown error code", buff,
eMsg);
3976int XrdXrootdProtocol::fsOvrld(
char opC,
const char *
Path,
char *Cgi)
3978 static const char *prot =
"root://";
3979 static int negOne = -1;
3980 static char quest =
'?', slash =
'/';
3982 struct iovec rdrResp[8];
3983 char *destP=0, dest[512];
3984 int iovNum=0, pOff, port;
3992 { rdrResp[1].iov_base = (
char *)&negOne;
3993 rdrResp[1].iov_len =
sizeof(negOne);
3994 rdrResp[2].iov_base = (
char *)prot;
3995 rdrResp[2].iov_len = 7;
3996 rdrResp[3].iov_base = (
char *)dest;
3997 rdrResp[3].iov_len = strlen(dest);
3998 rdrResp[4].iov_base = (
char *)&slash;
3999 rdrResp[4].iov_len = (*
Path ==
'/' ? 1 : 0);
4000 rdrResp[5].iov_base = (
char *)(
Path+pOff);
4001 rdrResp[5].iov_len = strlen(
Path+pOff);
4003 {rdrResp[6].iov_base = (
char *)?
4004 rdrResp[6].iov_len =
sizeof(quest);
4005 rdrResp[7].iov_base = (
char *)Cgi;
4006 rdrResp[7].iov_len = strlen(Cgi);
4024 TRACEI(REDIR,
Response.ID() <<
"redirecting to "<<destP<<
':'<<port);
4046int XrdXrootdProtocol::fsRedirNoEnt(
const char *
eMsg,
char *Cgi,
int popt)
4048 struct iovec ioV[4];
4049 char *tried, *trend, *ptried = 0;
4056 {
do {
if (!(tried = strstr(Cgi,
"tried=")))
break;
4057 if (tried == trend || *(tried-1) ==
'&')
4058 {
if (!ptried || (*(tried+6) && *(tried+6) !=
'&')) ptried=tried;}
4059 Cgi = index(tried+6,
'&');
4066 if ((tried = ptried))
4068 while(*(tried+1) && *(tried+1) ==
',') tried++;
4069 trend = index(tried,
'&');
4070 if (trend) {tlen = trend - tried; *trend = 0;}
4071 else tlen = strlen(tried);
4078 if ((trend = tried) &&
eMsg)
4079 do {
if ((trend = strstr(trend,
myCName)))
4082 trend = index(trend+
myCNlen,
',');
4090 if (!tried || !tlen || tlen > 16384)
4098 ioV[1].iov_base = (
char *)&pnum;
4099 ioV[1].iov_len =
sizeof(pnum);
4102 ioV[3].iov_base = tried;
4103 ioV[3].iov_len = tlen;
4134 auto cmpchr = [](
const char* a,
const char* b) {
return strcmp(a,b)<0;};
4135 static std::map<
const char*,XrdXrootd::netInfo*,
decltype(cmpchr)> niMap(cmpchr);
4136 static XrdSysMutex niMapMtx;
4138 XrdXrootd::netInfo* niP;
4144 auto it = niMap.find(netID);
4145 if (it == niMap.end())
4146 {niP =
new XrdXrootd::netInfo(netID);
4147 niMap[niP->
netID] = niP;
4148 }
else niP = it->second;
4156 time_t nowT = time(0);
4159 {
const char* eTxt = niP->
netAddr.
Set(netID, port);
4162 {
eDest.Emsg(
"RedirIP",
"Unable to init NetInfo for", netID, eTxt);
4167 eDest.Emsg(
"RedirIP",
"Unable to refresh NetInfo for", netID, eTxt);
4182int XrdXrootdProtocol::fsRedirPI(
const char *trg,
int port,
int trglen)
4185 {XrdXrootd::netInfo*
Info;
4186 THandle() :
Info(0) {}
4187 ~THandle() {
if (Info)
Info->refs--;}
4196 const char* TCgi = index(trg,
'?');
4197 if (!TCgi) {TCgi =
""; TDst = trg;}
4198 else TDst.assign(trg, TCgi-trg);
4199 T.Info = fsRedirIP(TDst.c_str(), port);
4201 uint16_t TPort =
static_cast<uint16_t
>(newPort);
4202 Target =
RedirPI->Redirect(TDst.c_str(), TPort, TCgi, T.Info->netAddr,
4203 *(
Link->AddrInfo()));
4204 newPort =
static_cast<int>(TPort);
4212 std::string urlHead, TDst, urlPort;
4213 const char* urlTail;
4214 const char* hBeg = strstr(trg,
"://");
4216 {
eDest.Emsg(
"RedirPI",
"Invalid redirect URL -", trg);
4220 urlHead.assign(trg, hBeg-trg);
4221 urlTail = strstr(hBeg,
"/");
4222 if (!urlTail) {urlTail =
""; TDst = hBeg;}
4223 else {
if (urlTail-hBeg < 3)
4224 {
eDest.Emsg(
"RedirPI",
"Mlalformed URL -", trg);
4227 TDst.assign(hBeg, urlTail-hBeg);
4229 T.Info = fsRedirIP(TDst.c_str(), port);
4231 size_t colon = TDst.find(
":");
4232 if (colon != std::string::npos)
4233 {urlPort.assign(TDst, colon+1, std::string::npos);
4236 Target =
RedirPI->RedirectURL(urlHead.c_str(), Target.c_str(),
4237 urlPort.c_str(), urlTail, newPort,
4238 T.Info->netAddr, *(
Link->AddrInfo()));
4239 if (port == -1 || newPort >= 0) newPort = port;
4246 if (Target.front() !=
'!')
4248 <<Target.c_str() <<
" portarg="<<newPort);
4256 snprintf(mbuff,
sizeof(mbuff),
"Redirect failed; %s",Target.c_str());
4257 eDest.Emsg(
"Xeq_RedirPI", mbuff);
4265int XrdXrootdProtocol::getBuff(
const int isRead,
int Quantum)
4284 "insufficient memory to read file" :
4285 "insufficient memory to write file"));
4296char *XrdXrootdProtocol::getCksType(
char *opaque,
char *cspec,
int cslen)
4302 if (opaque && *opaque)
4303 {XrdOucEnv jobEnv(opaque);
4304 if ((cksT = jobEnv.Get(
"cks.type")))
4306 while(tP && strcasecmp(tP->
text, cksT)) tP = tP->
next;
4307 if (!tP && cspec) snprintf(cspec, cslen,
"%s", cksT);
4308 return (tP ? tP->
text : 0);
4321bool XrdXrootdProtocol::logLogin(
bool xauth)
4323 const char *uName, *ipName, *tMsg, *zMsg =
"";
4324 char lBuff[512], pBuff[512];
4334 if (xauth) uName = (
Client->name ?
Client->name :
"nobody");
4339 tMsg =
Link->verTLS();
4340 if (*tMsg) zMsg =
" ";
4344 snprintf(lBuff,
sizeof(lBuff),
"%s %s %s%slogin%s%s",
4347 (xauth ?
" as " :
""),
4348 (uName ? uName :
""));
4353 {snprintf(pBuff,
sizeof(pBuff),
"via %s auth for %s",
4364 {
Link->setProtName(
"xroots");
4367 eDest.Emsg(
"Xeq",
"Unable to require TLS for",
Link->ID);
4371 eDest.Emsg(
"Xeq",
"session requires TLS but",
Link->ID,
"is incapable.");
4395#define Map_Mode(x,y) if (Mode & kXR_ ## x) newmode |= S_I ## y
4397int XrdXrootdProtocol::mapMode(
int Mode)
4419 const char *bP = Buff;
4422 else {snprintf(Buff,
sizeof(Buff),
4423 "&p=%s&n=%s&h=%s&o=%s&r=%s&g=%s&m=%s%s&I=%c",
4445int XrdXrootdProtocol::rpCheck(
char *fn,
char **opaque)
4454 if (!(cp = index(fn,
'?'))) *opaque = 0;
4455 else {*cp =
'\0'; *opaque = cp+1;
4456 if (!**opaque) *opaque = 0;
4459 if (*fn !=
'/')
return 0;
4461 while ((cp = index(fn,
'/')))
4463 if (fn[0] ==
'.' && fn[1] ==
'.' && (fn[2] ==
'/' || fn[2] ==
'\0'))
4473int XrdXrootdProtocol::rpEmsg(
const char *op,
char *fn)
4476 snprintf(buff,
sizeof(buff)-1,
"%s relative path '%s' is disallowed.",op,fn);
4477 buff[
sizeof(buff)-1] =
'\0';
4495 else if (theFile->fdNum >= 0) theFile->
sfEnabled = 1;
4506int XrdXrootdProtocol::Squash(
char *fn)
4508 char *ofn, *ifn = fn;
4515 || (*(ifn+1) ==
'.' && *(ifn+1) && *(ifn+2) ==
'/'))
break;
4519 if (!*ifn)
return XPList.Validate(fn, ifn-fn);
4522 while(*ifn) {*ofn = *ifn++;
4524 {
while(*ifn ==
'/') ifn++;
4525 if (ifn[0] ==
'.' && ifn[1] ==
'/') ifn += 2;
4532 return XPList.Validate(fn, ofn-fn);
4539int XrdXrootdProtocol::vpEmsg(
const char *op,
char *fn)
4542 snprintf(buff,
sizeof(buff)-1,
"%s path '%s' is disallowed.",op,fn);
4543 buff[
sizeof(buff)-1] =
'\0';
#define kXR_ShortProtRespLen
#define kXR_PROTSIGNVERSION
#define kXR_PROTOCOLVERSION
ServerResponseReqs_Protocol secreq
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
int emsg(int rc, char *msg)
const char * Arg1
PLUGFS, PLUGIN, PLUGIO, PLUGXC.
int Arg2Len
Length or -count of args in extension.
char * notify
Notification path or 0.
XrdOucTList * paths
List of paths.
XrdOucTList * oinfo
1-to-1 correspondence of opaque info
long long XrdSfsFileOffset
const char * XrdSysE2T(int errcode)
XrdSys::RAtomic< unsigned int > RAtomic_uint
XrdSysTrace XrdXrootdTrace
XrdOucString * XrdXrootdCF
const kXR_char XROOTD_MON_OPENW
const kXR_char XROOTD_MON_STAT
const kXR_char XROOTD_MON_REDLOCAL
const kXR_char XROOTD_MON_PREP
const kXR_char XROOTD_MON_OPENC
const kXR_char XROOTD_MON_TRUNC
const kXR_char XROOTD_MON_CLOSE
const kXR_char XROOTD_MON_CHMOD
const kXR_char XROOTD_MON_LOCATE
const kXR_char XROOTD_MON_OPENR
const kXR_char XROOTD_MON_MV
const kXR_char XROOTD_MON_RMDIR
const kXR_char XROOTD_MON_RM
const kXR_char XROOTD_MON_OPENDIR
const kXR_char XROOTD_MON_QUERY
const kXR_char XROOTD_MON_MKDIR
#define STATIC_REDIRECT(xfnc)
static const char * errName(kXR_int32 errCode)
static int mapError(int rc)
static const int ValuSize
static const int NameSize
static bool GetAssumeV4()
XrdJob(const char *desc="")
static XrdLink * fd2link(int fd)
static bool RegisterCloseRequestCb(XrdLink *lp, XrdProtocol *pp, bool(*cb)(void *), void *cbarg)
char * ID
Pointer to the client's link identity.
XrdProtocol * getProtocol()
Obtain current protocol object pointer.
const char * Host() const
bool isIPType(IPType ipType) const
const char * Set(const char *hSpec, int pNum=PortInSpec)
static bool getEA(const char *cgi, int &ecode, int &acode)
virtual void Done(int &Result, XrdOucErrInfo *eInfo, const char *Path=0)=0
void setErrCB(XrdOucEICB *cb, unsigned long long cbarg=0)
const char * getErrText()
void setUCap(int ucval)
Set user capabilties.
void Reset()
Reset object to no message state. Call this method to release appendages.
const char * c_str() const
char * GetToken(char **rest=0, int lowcase=0)
static void Sanitize(char *instr, char subc='_')
static int isFWD(const char *path, int *port=0, char *hBuff=0, int hBLen=0, bool pTrim=false)
static std::string UrlEncode(const std::string &input)
XrdSfsDio()
Constructor and destructor.
virtual int autoStat(struct stat *buf)
virtual int open(const char *path, const XrdSecEntity *client=0, const char *opaque=0)=0
virtual const char * nextEntry()=0
virtual int open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client=0, const char *opaque=0)=0
virtual int Clone(XrdSfsFile &srcFile)
virtual int truncate(XrdSfsFileOffset fsize)=0
virtual const char * FName()=0
virtual int getCXinfo(char cxtype[4], int &cxrsz)=0
virtual int stat(struct stat *buf)=0
virtual void setXio(XrdSfsXio *xioP)
virtual int fctl(const int cmd, const char *args, XrdOucErrInfo &eInfo)=0
static void Snooze(int seconds)
static void Open(XrdXrootdFileStats *fsP, const char *Path, unsigned int uDID, bool isRW)
int Write(long long offs, int dlen) override
void Read(long long offs, int dlen) override
static XrdXrootdNormAio * Alloc(XrdXrootdProtocol *protP, XrdXrootdResponse &resp, XrdXrootdFile *fP)
int(XrdXrootdProtocol::* ResumePio)()
static XrdXrootdPio * Alloc(int n=1)
void Set(int(XrdXrootdProtocol::*Invoke)(), XrdXrootd::IOParms &io, const kXR_char *theSID)
static int List(XrdXrootdPrepArgs &pargs, char *resp, int resplen)
static void Log(XrdXrootdPrepArgs &pargs)
static void Logdel(char *reqid)
static XrdXrootdStats * SI
int SendFile(int fildes) override
XrdXrootdProtocol * VerifyStream(int &rc, int pID, bool lok=true)
static XrdSfsFileSystem * digFS
int SetSF(kXR_char *fhandle, bool seton=false)
XrdNetPMark::Handle * pmHandle
static XrdNetPMark * PMark
XrdXrootdProtocol * Stream[maxStreams]
static XrdXrootdXPath RPList
static const char Req_TLSGPFile
static bool CloseRequestCb(void *cbarg)
void SetFD(int fildes) override
static const char Req_TLSSess
XrdXrootdFileTable * FTab
static XrdXrootdJob * JobCKS
static XrdSysError & eDest
static unsigned int getSID()
XrdSecProtocol * AuthProt
int getData(gdCallBack *gdcbP, const char *dtype, char *buff, int blen)
XrdXrootdMonitor::User Monitor
static XrdXrootdRedirPI * RedirPI
static const char * myCName
static const char Req_TLSData
static XrdXrootdFileLock * Locker
int(XrdXrootdProtocol::* Resume)()
static const char Req_TLSTPC
static XrdTlsContext * tlsCtx
static XrdXrootdXPath XPList
static XrdScheduler * Sched
static const char Req_TLSLogin
XrdXrootdResponse Response
int(XrdXrootdProtocol::* ResumePio)()
static const int maxStreams
static XrdOucTList * JobCKTLST
static XrdXrootdXPath RQList
static struct XrdXrootdProtocol::RD_Table Route[RD_Num]
static XrdSecProtector * DHS
static XrdBuffManager * BPool
XrdSysSemaphore * boundRecycle
static XrdSecService * CIA
static RAtomic_int srvrAioOps
static uint64_t fsFeatures
static XrdOucReqID * PrepID
static XrdSfsFileSystem * osFS
static const int maxRvecsz
static const int maxClonesz
static const int maxWvecsz
static const uint64_t hasCACH
Feature: Implements a data cache.
static const uint64_t hasSXIO
Feature: Supports SfsXio.
static const uint64_t hasFICL
Feature: Supports file cloning and samefs.
ssize_t Send(int fd, KernelBuffer &buffer)
static const kXR_int32 doSync
char TimeZone
+/- hours from GMT (-128 if not set)
unsigned char Country[2]
Two letter TLD country code.
static const int uRedirFlgs
ucap: Client supports "file://"
static const int uUrlOK
ucap: Supports async responses
static const int uIPv64
ucap: Supports only IPv4 info
static const int uReadR
ucap: Supports multiple protocols
static const int uEcRedir
ucap: Client supports redirect flags
static const int uMProt
ucap: Supports url redirects
static const int uLclF
ucap: Client is on a private net
static const int uAsync
ucap: Extract protocol version
static const int uIPv4
ucap: Supports read redirects
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.
static const int useBasic