XRootD
Loading...
Searching...
No Matches
XrdXrootdXeq.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d X r o o t d X e q . c c */
4/* */
5/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* Produced by Andrew Hanushevsky for Stanford University under contract */
7/* DE-AC02-76-SFO0515 with the Department of Energy */
8/* */
9/* This file is part of the XRootD software suite. */
10/* */
11/* XRootD is free software: you can redistribute it and/or modify it under */
12/* the terms of the GNU Lesser General Public License as published by the */
13/* Free Software Foundation, either version 3 of the License, or (at your */
14/* option) any later version. */
15/* */
16/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19/* License for more details. */
20/* */
21/* You should have received a copy of the GNU Lesser General Public License */
22/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24/* */
25/* The copyright holder's institutional names and contributor's names may not */
26/* be used to endorse or promote products derived from this software without */
27/* specific prior written permission of the institution or contributor. */
28/******************************************************************************/
29
30#include <cctype>
31#include <cstdio>
32#include <map>
33#include <string>
34#include <sys/time.h>
35
37#include "XrdSfs/XrdSfsFlags.hh"
38#include "XrdSys/XrdSysError.hh"
40#include "XrdSys/XrdSysTimer.hh"
41#include "XrdCks/XrdCksData.hh"
42#include "XrdOuc/XrdOucEnv.hh"
43#include "XrdOuc/XrdOucReqID.hh"
44#include "XrdOuc/XrdOucTList.hh"
48#include "XrdOuc/XrdOucUtils.hh"
52#include "XrdSys/XrdSysE2T.hh"
53#include "Xrd/XrdBuffer.hh"
54#include "Xrd/XrdInet.hh"
55#include "Xrd/XrdLinkCtl.hh"
73
74#include "XrdVersion.hh"
75
76#ifndef ENODATA
77#define ENODATA ENOATTR
78#endif
79
80#ifndef ETIME
81#define ETIME ETIMEDOUT
82#endif
83
84/******************************************************************************/
85/* G l o b a l s */
86/******************************************************************************/
87
89
90/******************************************************************************/
91/* L o c a l S t r u c t u r e s */
92/******************************************************************************/
93
95 {unsigned int Sid;
96 int Pid;
97 int FD;
98 unsigned int Inst;
99
102 };
103
104/******************************************************************************/
105/* L o c a l D e f i n e s */
106/******************************************************************************/
107
108namespace
109{
110
111const char *getTime()
112{
113static char buff[16];
114char tuff[8];
115struct timeval tv;
116struct tm *tmp;
117
118 if (gettimeofday(&tv, 0))
119 {perror("gettimeofday");
120 exit(255);
121 }
122 tmp = localtime(&tv.tv_sec);
123 if (!tmp)
124 {perror("localtime");
125 exit(255);
126 }
127 //012345678901234
128 if (strftime(buff, sizeof(buff), "%y%m%d:%H%M%S. ", tmp) <= 0)
129 {errno = EINVAL;
130 perror("strftime");
131 exit(255);
132 }
133
134 snprintf(tuff, sizeof(tuff), "%d", static_cast<int>(tv.tv_usec/100000));
135 buff[14] = tuff[0];
136 return buff;
137}
138
139// comment out genUEID as it is not used
140//
141
142//int genUEID()
143//{
144// static XrdSysMutex ueidMutex;
145// static int ueidVal = 1;
146// AtomicBeg(ueidMutex);
147// int n = AtomicInc(ueidVal);
148// AtomicEnd(ueidMutex);
149// return n;
150//}
151
152// Startup time
153// 012345670123456
154// yymmdd:hhmmss.t
155static const char *startUP = getTime();
156}
157
158/******************************************************************************/
159/* d o _ A u t h */
160/******************************************************************************/
161
162int XrdXrootdProtocol::do_Auth()
163{
165 XrdSecParameters *parm = 0;
166 XrdOucErrInfo eMsg;
167 const char *eText;
168 int rc, n;
169
170// Ignore authenticate requests if security turned off
171//
172 if (!CIA) return Response.Send();
173 cred.size = Request.header.dlen;
174 cred.buffer = argp->buff;
175
176// If we have no auth protocol or the current protocol is being changed by the
177// client (the client can do so at any time), try to get it. Track number of
178// times we got a protocol object as the read count (we will zero it out later).
179// The credtype change check is always done. While the credtype is consistent,
180// not all protocols provided this information in the past. So, old clients will
181// not necessarily be able to switch protocols mid-stream.
182//
183 if (!AuthProt
184 || strncmp(Entity.prot, (const char *)Request.auth.credtype,
185 sizeof(Request.auth.credtype)))
186 {if (AuthProt) AuthProt->Delete();
187 size_t size = sizeof(Request.auth.credtype);
188 strncpy(Entity.prot, (const char *)Request.auth.credtype, size);
189 if (!(AuthProt = CIA->getProtocol(Link->Host(), *(Link->AddrInfo()),
190 &cred, eMsg)))
191 {eText = eMsg.getErrText(rc);
192 eDest.Emsg("Xeq", "User authentication failed;", eText);
193 return Response.Send(kXR_AuthFailed, eText);
194 }
195 AuthProt->Entity.tident = AuthProt->Entity.pident = Link->ID;
196 numReads++;
197 }
198
199// Now try to authenticate the client using the current protocol
200//
201 if (!(rc = AuthProt->Authenticate(&cred, &parm, &eMsg))
202 && CIA->PostProcess(AuthProt->Entity, eMsg))
203 {rc = Response.Send(); Status &= ~XRD_NEED_AUTH; SI->Bump(SI->LoginAU);
204 AuthProt->Entity.ueid = mySID;
205 Client = &AuthProt->Entity; numReads = 0; strcpy(Entity.prot, "host");
206 if (TRACING(TRACE_AUTH)) Client->Display(eDest);
207 if (DHS) Protect = DHS->New4Server(*AuthProt,clientPV&XrdOucEI::uVMask);
208 if (Monitor.Logins() && Monitor.Auths()) MonAuth();
209 if (!logLogin(true)) return -1;
210 return rc;
211 }
212
213// If we need to continue authentication, tell the client as much
214//
215 if (rc > 0)
216 {TRACEP(LOGIN, "more auth requested; sz=" <<(parm ? parm->size : 0));
217 if (parm) {rc = Response.Send(kXR_authmore, parm->buffer, parm->size);
218 delete parm;
219 return rc;
220 }
221 eDest.Emsg("Xeq", "Security requested additional auth w/o parms!");
222 return Response.Send(kXR_ServerError,"invalid authentication exchange");
223 }
224
225// Authentication failed. We will delete the authentication object and zero
226// out the pointer. We can do this without any locks because this section is
227// single threaded relative to a connection. To prevent guessing attacks, we
228// wait a variable amount of time if there have been 3 or more tries.
229//
230 if (AuthProt) {AuthProt->Delete(); AuthProt = 0;}
231 if ((n = numReads - 2) > 0) XrdSysTimer::Snooze(n > 5 ? 5 : n);
232
233// We got an error, bail out.
234//
235 SI->Bump(SI->AuthBad);
236 eText = eMsg.getErrText(rc);
237 eDest.Emsg("Xeq", "User authentication failed;", eText);
238 return Response.Send(kXR_AuthFailed, eText);
239}
240
241/******************************************************************************/
242/* d o _ B i n d */
243/******************************************************************************/
244
245int XrdXrootdProtocol::do_Bind()
246{
247 XrdXrootdSessID *sp = (XrdXrootdSessID *)Request.bind.sessid;
249 XrdLink *lp;
250 int i, pPid, rc;
251 char buff[64], *cp, *dp;
252
253// Update misc stats count
254//
255 SI->Bump(SI->miscCnt);
256
257// Check if binds need to occur on a TLS connection.
258//
259 if ((doTLS & Req_TLSData) && !isTLS && !Link->hasBridge())
260 return Response.Send(kXR_TLSRequired, "bind requires TLS");
261
262// Find the link we are to bind to
263//
264 if (sp->FD <= 0 || !(lp = XrdLinkCtl::fd2link(sp->FD, sp->Inst)))
265 return Response.Send(kXR_NotFound, "session not found");
266
267// The link may have escaped so we need to hold this link and try again
268//
269 lp->Hold(1);
270 if (lp != XrdLinkCtl::fd2link(sp->FD, sp->Inst))
271 {lp->Hold(0);
272 return Response.Send(kXR_NotFound, "session just closed");
273 }
274
275// Get the protocol associated with the link
276//
277 if (!(pp=dynamic_cast<XrdXrootdProtocol *>(lp->getProtocol()))||lp != pp->Link)
278 {lp->Hold(0);
279 return Response.Send(kXR_ArgInvalid, "session protocol not xroot");
280 }
281
282// Verify that the parent protocol is fully logged in
283//
284 if (!(pp->Status & XRD_LOGGEDIN) || (pp->Status & XRD_NEED_AUTH))
285 {lp->Hold(0);
286 return Response.Send(kXR_ArgInvalid, "session not logged in");
287 }
288
289// Verify that the bind is valid for the requestor
290//
291 if (sp->Pid != myPID || sp->Sid != pp->mySID)
292 {lp->Hold(0);
293 return Response.Send(kXR_ArgInvalid, "invalid session ID");
294 }
295
296// For now, verify that the request is comming from the same host
297//
298 if (strcmp(Link->Host(), lp->Host()))
299 {lp->Hold(0);
300 return Response.Send(kXR_NotAuthorized, "cross-host bind not allowed");
301 }
302
303// We need to hold the parent's stream mutex to prevent inspection or
304// modification of other parallel binds that may occur
305//
306 XrdSysMutexHelper smHelper(pp->streamMutex);
307
308// Find a slot for this path in parent protocol
309//
310 for (i = 1; i < maxStreams && pp->Stream[i]; i++) {}
311 if (i >= maxStreams)
312 {lp->Hold(0);
313 return Response.Send(kXR_NoMemory, "bind limit exceeded");
314 }
315
316// Link this protocol to the parent
317//
318 pp->Stream[i] = this;
319 Stream[0] = pp;
320 PathID = i;
321
322// Construct a login name for this bind session
323//
324 cp = strdup(lp->ID);
325 if ( (dp = rindex(cp, '@'))) *dp = '\0';
326 if (!(dp = rindex(cp, '.'))) pPid = 0;
327 else {*dp++ = '\0'; pPid = strtol(dp, (char **)NULL, 10);}
328 Link->setID(cp, pPid);
329 free(cp);
330 CapVer = pp->CapVer;
332 boundRecycle = new XrdSysSemaphore(0);
333 clientPV = pp->clientPV;
335
336// Check if we need to enable packet marking for this stream
337//
338 if (pp->pmDone)
339 {pmDone = true;
340 if (pp->pmHandle) pmHandle = PMark->Begin(*(Link->AddrInfo()),
341 *(pp->pmHandle), Link->ID);
342 }
343
344// Document the bind
345//
346 smHelper.UnLock();
347 sprintf(buff, "FD %d#%d bound", Link->FDnum(), i);
348 eDest.Log(SYS_LOG_01, "Xeq", buff, lp->ID);
349
350// Get the required number of parallel I/O objects
351//
353
354// There are no errors possible at this point unless the response fails
355//
356 buff[0] = static_cast<char>(i);
357 if (!(rc = Response.Send(kXR_ok, buff, 1))) rc = -EINPROGRESS;
358
359// Return but keep the link disabled
360//
361 lp->Hold(0);
362 return rc;
363}
364
365/******************************************************************************/
366/* d o _ C h k P n t */
367/* */
368/* Resides in XrdXrootdXeqChkPnt.cc */
369/******************************************************************************/
370
371/******************************************************************************/
372/* d o _ c h m o d */
373/******************************************************************************/
374
375int XrdXrootdProtocol::do_Chmod()
376{
377 int mode, rc;
378 char *opaque;
379 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
380
381// Check for static routing
382//
383 STATIC_REDIRECT(RD_chmod);
384
385// Unmarshall the data
386//
387 mode = mapMode((int)ntohs(Request.chmod.mode));
388 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Modifying", argp->buff);
389 if (!Squash(argp->buff)) return vpEmsg("Modifying", argp->buff);
390
391// Preform the actual function
392//
393 rc = osFS->chmod(argp->buff, (XrdSfsMode)mode, myError, CRED, opaque);
394 TRACEP(FS, "chmod rc=" <<rc <<" mode=" <<Xrd::oct1 <<mode <<' ' <<argp->buff);
395 if (SFS_OK == rc) return Response.Send();
396
397// An error occurred
398//
399 return fsError(rc, XROOTD_MON_CHMOD, myError, argp->buff, opaque);
400}
401
402/******************************************************************************/
403/* d o _ C K s u m */
404/******************************************************************************/
405
406int XrdXrootdProtocol::do_CKsum(int canit)
407{
408 char *opaque;
409 char *algT = JobCKT, *args[6];
410 int rc;
411
412// Check for static routing
413//
414 STATIC_REDIRECT(RD_chksum);
415
416// Check if we support this operation
417//
418 if (!JobCKT || (!JobLCL && !JobCKS))
419 return Response.Send(kXR_Unsupported, "query chksum is not supported");
420
421// Prescreen the path
422//
423 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Check summing", argp->buff);
424 if (!Squash(argp->buff)) return vpEmsg("Check summing", argp->buff);
425
426// If this is a cancel request, do it now
427//
428 if (canit)
429 {if (JobCKS) JobCKS->Cancel(argp->buff, &Response);
430 return Response.Send();
431 }
432
433// Check if multiple checksums are supported and if so, pre-process
434//
435 if (JobCKCGI && opaque && *opaque)
436 {char cksT[64];
437 algT = getCksType(opaque, cksT, sizeof(cksT));
438 if (!algT)
439 {char ebuf[1024];
440 snprintf(ebuf, sizeof(ebuf), "%s checksum not supported.", cksT);
441 return Response.Send(kXR_ServerError, ebuf);
442 }
443 }
444
445// If we are allowed to locally query the checksum to avoid computation, do it
446//
447 if (JobLCL && (rc = do_CKsum(algT, argp->buff, opaque)) <= 0) return rc;
448
449// Just make absolutely sure we can continue with a calculation
450//
451 if (!JobCKS)
452 return Response.Send(kXR_ServerError, "Logic error computing checksum.");
453
454// Check if multiple checksums are supported and construct right argument list
455// We make a concession to a wrongly placed setfsuid/gid plugin. Fortunately,
456// it only needs to know user's name but that can come from another plugin.
457//
458 std::string keyval; // Contents will be copied prior to return!
459 if (JobCKCGI > 1 || JobLCL)
460 {args[0] = algT;
461 args[1] = algT;
462 args[2] = argp->buff;
463 args[3] = const_cast<char *>(Client->tident);
464 if (Client->eaAPI->Get(std::string("request.name"), keyval) && !keyval.empty())
465 args[4] = const_cast<char *>(keyval.c_str());
466 else if (Client->name) args[4] = Client->name;
467 else args[4] = 0;
468 args[5] = 0;
469 } else {
470 args[0] = algT;
471 args[1] = argp->buff;
472 args[2] = 0;
473 }
474
475// Preform the actual function
476//
477 return JobCKS->Schedule(argp->buff, (const char **)args, &Response,
478 ((CapVer & kXR_vermask) >= kXR_ver002 ? 0 : JOB_Sync));
479}
480
481/******************************************************************************/
482
483int XrdXrootdProtocol::do_CKsum(char *algT, const char *Path, char *Opaque)
484{
485 static char Space = ' ';
486 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
487 int CKTLen = strlen(algT);
488 int ec, rc = osFS->chksum(XrdSfsFileSystem::csGet, algT, Path,
489 myError, CRED, Opaque);
490 const char *csData = myError.getErrText(ec);
491
492// Diagnose any hard errors
493//
494 if (rc) return fsError(rc, 0, myError, Path, Opaque);
495
496// Return result if it is actually available
497//
498 if (*csData)
499 {if (*csData == '!') return Response.Send(csData+1);
500 struct iovec iov[4] = {{0,0}, {algT, (size_t)CKTLen}, {&Space, 1},
501 {(char *)csData, strlen(csData)+1}};
502 return Response.Send(iov, 4);
503 }
504
505// Diagnose soft errors
506//
507 if (!JobCKS)
508 {const char *eTxt[2] = {JobCKT, " checksum not available."};
509 myError.setErrInfo(0, eTxt, 2);
510 return Response.Send(kXR_ChkSumErr, myError.getErrText());
511 }
512
513// Return indicating that we should try calculating the checksum
514//
515 return 1;
516}
517
518/******************************************************************************/
519/* d o _ C l o s e */
520/******************************************************************************/
521
522int XrdXrootdProtocol::do_Close()
523{
524 static XrdXrootdCallBack closeCB("close", XROOTD_MON_CLOSE);
525 XrdXrootdFile *fp;
526 XrdXrootdFHandle fh(Request.close.fhandle);
527 int rc;
528 bool doDel = true;
529
530// Keep statistics
531//
532 SI->Bump(SI->miscCnt);
533
534// Find the file object
535//
536 if (!FTab || !(fp = FTab->Get(fh.handle)))
537 return Response.Send(kXR_FileNotOpen,
538 "close does not refer to an open file");
539
540// Serialize the file to make sure all references due to async I/O and parallel
541// stream operations have completed.
542//
543 fp->Serialize();
544
545// If the file has a fob then it was subject to pgwrite and if uncorrected
546// checksum errors exist do a forced close. This will trigger POSC or a restore.
547//
548 if (fp->pgwFob && !do_PgClose(fp, rc))
549 {FTab->Del((Monitor.Files() ? Monitor.Agent : 0), fh.handle, true);
550 numFiles--;
551 return rc;
552 }
553
554// Setup the callback to allow close() to return SFS_STARTED so we can defer
555// the response to the close request as it may be a lengthy operation. In
556// this case the argument is the actual file pointer and the link reference
557// is recorded in the file object.
558//
559 fp->cbArg = ReqID.getID();
560 fp->XrdSfsp->error.setErrCB(&closeCB, (unsigned long long)fp);
561
562// Add a reference count to the file in case the close will be deferred. In
563// the deferred case the reference is used to prevent the callback from
564// deleting the file until we have done necessary processing of the object
565// during its removal from the open table.
566//
567 fp->Ref(1);
568
569// Do an explicit close of the file here; check for exceptions. Stall requests
570// leave the file open as there will be a retry. Otherwise, we remove the
571// file from our open table but a "started" return defers the the delete.
572//
573 rc = fp->XrdSfsp->close();
574 TRACEP(FS, " fh=" <<fh.handle <<" close rc=" <<rc);
575 if (rc == SFS_STARTED) doDel = false;
576 else {fp->Ref(-1);
577 if (rc >= SFS_STALL)
578 return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
579 }
580
581// Before we potentially delete the file handle in FTab->Del, generate the
582// appropriate error code (if necessary). Note that we delay the call
583// to Response.Send() in the successful case to avoid holding on to the lock
584// while the response is sent.
585//
586 int retval = 0;
587 if (SFS_OK != rc) retval = fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
588
589// Delete the file from the file table. If the file object is deleted then it
590// will unlock the file In all cases, final monitoring records will be produced.
591//
592 FTab->Del((Monitor.Files() ? Monitor.Agent : 0), fh.handle, doDel);
593 numFiles--;
594 if (!doDel) fp->Ref(-1);
595
596// Send back the right response
597//
598 if (SFS_OK == rc) return Response.Send();
599 return retval;
600}
601
602/******************************************************************************/
603/* d o _ D i r l i s t */
604/******************************************************************************/
605
606int XrdXrootdProtocol::do_Dirlist()
607{
608 int bleft, rc = 0, dlen, cnt = 0;
609 char *opaque, *buff, ebuff[4096];
610 const char *dname;
611 XrdSfsDirectory *dp;
612 bool doDig;
613
614// Check if we are digging for data
615//
616 doDig = (digFS && SFS_LCLROOT(argp->buff));
617
618// Check for static routing
619//
620 if (!doDig) {STATIC_REDIRECT(RD_dirlist);}
621
622// Prescreen the path
623//
624 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Listing", argp->buff);
625 if (!doDig && !Squash(argp->buff))return vpEmsg("Listing", argp->buff);
626
627// Get a directory object
628//
629 if (doDig) dp = digFS->newDir(Link->ID, Monitor.Did);
630 else dp = osFS->newDir(Link->ID, Monitor.Did);
631
632// Make sure we have the object
633//
634 if (!dp)
635 {snprintf(ebuff,sizeof(ebuff)-1,"Insufficient memory to open %s",argp->buff);
636 eDest.Emsg("Xeq", ebuff);
637 return Response.Send(kXR_NoMemory, ebuff);
638 }
639
640// First open the directory
641//
643 if ((rc = dp->open(argp->buff, CRED, opaque)))
644 {rc = fsError(rc, XROOTD_MON_OPENDIR, dp->error, argp->buff, opaque);
645 delete dp;
646 return rc;
647 }
648
649// Check if the caller wants stat information as well
650//
651 if (Request.dirlist.options[0] & (kXR_dstat | kXR_dcksm))
652 return do_DirStat(dp, ebuff, opaque);
653
654// Start retreiving each entry and place in a local buffer with a trailing new
655// line character (the last entry will have a null byte). If we cannot fit a
656// full entry in the buffer, send what we have with an OKSOFAR and continue.
657// This code depends on the fact that a directory entry will never be longer
658// than sizeof( ebuff)-1; otherwise, an infinite loop will result. No errors
659// are allowed to be reflected at this point.
660//
661 dname = 0;
662 do {buff = ebuff; bleft = sizeof(ebuff);
663 while(dname || (dname = dp->nextEntry()))
664 {dlen = strlen(dname);
665 if (dlen > 2 || dname[0] != '.' || (dlen == 2 && dname[1] != '.'))
666 {if ((bleft -= (dlen+1)) < 0) break;
667 strcpy(buff, dname); buff += dlen; *buff = '\n'; buff++; cnt++;
668 }
669 dname = 0;
670 }
671 if (dname) rc = Response.Send(kXR_oksofar, ebuff, buff-ebuff);
672 } while(!rc && dname);
673
674// Send the ending packet if we actually have one to send
675//
676 if (!rc)
677 {if (ebuff == buff) rc = Response.Send();
678 else {*(buff-1) = '\0';
679 rc = Response.Send((void *)ebuff, buff-ebuff);
680 }
681 }
682
683// Close the directory
684//
685 dp->close();
686 delete dp;
687 if (!rc) {TRACEP(FS, "dirlist entries=" <<cnt <<" path=" <<argp->buff);}
688 return rc;
689}
690
691/******************************************************************************/
692/* d o _ D i r S t a t */
693/******************************************************************************/
694
695int XrdXrootdProtocol::do_DirStat(XrdSfsDirectory *dp, char *pbuff,
696 char *opaque)
697{
698 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
699 struct stat Stat;
700 char *buff, *dLoc, *algT = 0;
701 const char *csData, *dname;
702 int bleft, rc = 0, dlen, cnt = 0, statSz = 160;
703 bool manStat;
704 struct {char ebuff[8192]; char epad[512];} XB;
705
706// Preprocess checksum request. If we don't support checksums or if the
707// requested checksum type is not supported, ignore it.
708//
709 if ((Request.dirlist.options[0] & kXR_dcksm) && JobLCL)
710 {char cksT[64];
711 algT = getCksType(opaque, cksT, sizeof(cksT));
712 if (!algT)
713 {char ebuf[1024];
714 snprintf(ebuf, sizeof(ebuf), "%s checksum not supported.", cksT);
715 return Response.Send(kXR_ServerError, ebuf);
716 }
717 statSz += XrdCksData::NameSize + (XrdCksData::ValuSize*2) + 8;
718 }
719
720// We always return stat information, see if we can use autostat
721//
722 manStat = (dp->autoStat(&Stat) != SFS_OK);
723
724// Construct the path to the directory as we will be asking for stat calls
725// if the interface does not support autostat or returning checksums.
726//
727 if (manStat || algT)
728 {strcpy(pbuff, argp->buff);
729 dlen = strlen(pbuff);
730 if (pbuff[dlen-1] != '/') {pbuff[dlen] = '/'; dlen++;}
731 dLoc = pbuff+dlen;
732 } else dLoc = 0;
733
734// The initial leadin is a "dot" entry to indicate to the client that we
735// support the dstat option (older servers will not do that). It's up to the
736// client to issue individual stat requests in that case.
737//
738 memset(&Stat, 0, sizeof(Stat));
739 strcpy(XB.ebuff, ".\n0 0 0 0\n");
740 buff = XB.ebuff+10; bleft = sizeof(XB.ebuff)-10;
741
742// Start retreiving each entry and place in a local buffer with a trailing new
743// line character (the last entry will have a null byte). If we cannot fit a
744// full entry in the buffer, send what we have with an OKSOFAR and continue.
745// This code depends on the fact that a directory entry will never be longer
746// than sizeof( ebuff)-1; otherwise, an infinite loop will result. No errors
747// are allowed to be reflected at this point.
748//
749 dname = 0;
750 do {while(dname || (dname = dp->nextEntry()))
751 {dlen = strlen(dname);
752 if (dlen > 2 || dname[0] != '.' || (dlen == 2 && dname[1] != '.'))
753 {if ((bleft -= (dlen+1)) < 0 || bleft < statSz) break;
754 if (dLoc) strcpy(dLoc, dname);
755 if (manStat)
756 {rc = osFS->stat(pbuff, &Stat, myError, CRED, opaque);
757 if (rc == SFS_ERROR && myError.getErrInfo() == ENOENT)
758 {dname = 0; continue;}
759 if (rc != SFS_OK)
760 return fsError(rc, XROOTD_MON_STAT, myError,
761 argp->buff, opaque);
762 }
763 strcpy(buff, dname); buff += dlen; *buff = '\n'; buff++; cnt++;
764 dlen = StatGen(Stat, buff, sizeof(XB.epad));
765 bleft -= dlen; buff += (dlen-1);
766 if (algT)
767 {int ec = osFS->chksum(XrdSfsFileSystem::csGet, algT,
768 pbuff, myError, CRED, opaque);
769 csData = myError.getErrText();
770 if (ec != SFS_OK || !(*csData) || *csData == '!')
771 csData = "none";
772 int n = snprintf(buff,sizeof(XB.epad)," [ %s:%s ]",
773 algT, csData);
774 buff += n; bleft -= n;
775 }
776 *buff = '\n'; buff++;
777 }
778 dname = 0;
779 }
780 if (dname)
781 {rc = Response.Send(kXR_oksofar, XB.ebuff, buff-XB.ebuff);
782 buff = XB.ebuff; bleft = sizeof(XB.ebuff);
783 TRACEP(FS, "dirstat sofar n=" <<cnt <<" path=" <<argp->buff);
784 }
785 } while(!rc && dname);
786
787// Send the ending packet if we actually have one to send
788//
789 if (!rc)
790 {if (XB.ebuff == buff) rc = Response.Send();
791 else {*(buff-1) = '\0';
792 rc = Response.Send((void *)XB.ebuff, buff-XB.ebuff);
793 }
794 }
795
796// Close the directory
797//
798 dp->close();
799 delete dp;
800 if (!rc) {TRACEP(FS, "dirstat entries=" <<cnt <<" path=" <<argp->buff);}
801 return rc;
802}
803
804/******************************************************************************/
805/* d o _ E n d s e s s */
806/******************************************************************************/
807
808int XrdXrootdProtocol::do_Endsess()
809{
810 XrdXrootdSessID *sp, sessID;
811 int rc;
812
813// Update misc stats count
814//
815 SI->Bump(SI->miscCnt);
816
817// Extract out the FD and Instance from the session ID
818//
819 sp = (XrdXrootdSessID *)Request.endsess.sessid;
820 memcpy((void *)&sessID.Pid, &sp->Pid, sizeof(sessID.Pid));
821 memcpy((void *)&sessID.FD, &sp->FD, sizeof(sessID.FD));
822 memcpy((void *)&sessID.Inst, &sp->Inst, sizeof(sessID.Inst));
823
824// Trace this request
825//
826 TRACEP(LOGIN, "endsess " <<sessID.Pid <<':' <<sessID.FD <<'.' <<sessID.Inst);
827
828// If this session id does not refer to us, ignore the request
829//
830 if (sessID.Pid != myPID) return Response.Send();
831
832// Terminate the indicated session, if possible. This could also be a self-termination.
833//
834 if ((sessID.FD == 0 && sessID.Inst == 0)
835 || !(rc = Link->Terminate(0, sessID.FD, sessID.Inst))) return -1;
836
837// Trace this request
838//
839 TRACEP(LOGIN, "endsess " <<sessID.Pid <<':' <<sessID.FD <<'.' <<sessID.Inst
840 <<" rc=" <<rc <<" (" <<XrdSysE2T(rc < 0 ? -rc : EAGAIN) <<")");
841
842// Return result. We only return obvious problems (exclude ESRCH and EPIPE).
843//
844 if (rc > 0)
845 return (rc = Response.Send(kXR_wait, rc, "session still active")) ? rc:1;
846
847 if (rc == -EACCES)return Response.Send(kXR_NotAuthorized, "not session owner");
848 if (rc == -ETIME) return Response.Send(kXR_Cancelled,"session not ended");
849
850 return Response.Send();
851}
852
853/******************************************************************************/
854/* d o _ F A t t r */
855/* */
856/* Resides in XrdXrootdXeqFAttr.cc */
857/******************************************************************************/
858
859/******************************************************************************/
860/* d o _ g p F i l e */
861/******************************************************************************/
862
863int XrdXrootdProtocol::do_gpFile()
864{
865// int gopts, buffsz;
866
867// Keep Statistics (TO DO: differentiate get vs put)
868//
869 SI->Bump(SI->getfCnt);
870// SI->Bump(SI->putfCnt);
871
872// Check if gpfile need to occur on a TLS connection
873//
874 if ((doTLS & Req_TLSGPFile) && !isTLS && !Link->hasBridge())
875 return Response.Send(kXR_TLSRequired, "gpfile requires TLS");
876
877 return Response.Send(kXR_Unsupported, "gpfile request is not supported");
878}
879
880/******************************************************************************/
881/* d o _ L o c a t e */
882/******************************************************************************/
883
884int XrdXrootdProtocol::do_Locate()
885{
886 static XrdXrootdCallBack locCB("locate", XROOTD_MON_LOCATE);
887 int rc, opts, fsctl_cmd = SFS_FSCTL_LOCATE;
888 char *opaque = 0, *Path, *fn = argp->buff, opt[8], *op=opt;
889 XrdOucErrInfo myError(Link->ID,&locCB,ReqID.getID(),Monitor.Did,clientPV);
890 bool doDig = false;
891
892// Unmarshall the data
893//
894 opts = (int)ntohs(Request.locate.options);
895
896// Map the options
897//
898 if (opts & kXR_nowait) {fsctl_cmd |= SFS_O_NOWAIT; *op++ = 'i';}
899 if (opts & kXR_refresh) {fsctl_cmd |= SFS_O_RESET; *op++ = 's';}
900 if (opts & kXR_force ) {fsctl_cmd |= SFS_O_FORCE; *op++ = 'f';}
901 if (opts & kXR_prefname){fsctl_cmd |= SFS_O_HNAME; *op++ = 'n';}
902 if (opts & kXR_compress){fsctl_cmd |= SFS_O_RAWIO; *op++ = 'u';}
903 if (opts & kXR_4dirlist){fsctl_cmd |= SFS_O_DIRLIST;*op++ = 'D';}
904 *op = '\0';
905 TRACEP(FS, "locate " <<opt <<' ' <<fn);
906
907// Check if this is a non-specific locate
908//
909 if (*fn != '*'){Path = fn;
910 doDig = (digFS && SFS_LCLROOT(Path));
911 }
912 else if (*(fn+1)) {Path = fn+1;
913 doDig = (digFS && SFS_LCLROOT(Path));
914 }
915 else {Path = 0;
916 fn = XPList.Next()->Path();
917 fsctl_cmd |= SFS_O_TRUNC;
918 }
919
920// Check for static routing
921//
922 if (!doDig) {STATIC_REDIRECT(RD_locate);}
923
924// Prescreen the path
925//
926 if (Path)
927 {if (rpCheck(Path, &opaque)) return rpEmsg("Locating", Path);
928 if (!doDig && !Squash(Path))return vpEmsg("Locating", Path);
929 }
930
931// Preform the actual function. For regular Fs add back any opaque info
932//
933 if (doDig) rc = digFS->fsctl(fsctl_cmd, fn, myError, CRED);
934 else {if (opaque)
935 {int n = strlen(argp->buff); argp->buff[n] = '?';
936 if ((argp->buff)+n != opaque-1)
937 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
938 }
939 rc = osFS->fsctl(fsctl_cmd, fn, myError, CRED);
940 }
941 TRACEP(FS, "rc=" <<rc <<" locate " <<fn);
942 return fsError(rc, (doDig ? 0 : XROOTD_MON_LOCATE), myError, Path, opaque);
943}
944
945/******************************************************************************/
946/* d o _ L o g i n */
947/*.x***************************************************************************/
948
949int XrdXrootdProtocol::do_Login()
950{
951 XrdXrootdSessID sessID;
952 XrdNetAddrInfo *addrP;
953 int i, pid, rc, sendSID = 0;
954 char uname[sizeof(Request.login.username)+1];
955
956// Keep Statistics
957//
958 SI->Bump(SI->LoginAT);
959
960// Check if login need to occur on a TLS connection
961//
962 if ((doTLS & Req_TLSLogin) && !isTLS && !Link->hasBridge())
963 {const char *emsg = "login requires TLS be enabled";
964 if (!ableTLS)
965 {emsg = "login requires TLS support";
966 eDest.Emsg("Xeq","login requires TLS but",Link->ID,"is incapable.");
967 }
968 return Response.Send(kXR_TLSRequired, emsg);
969 }
970
971// Unmarshall the pid and construct username using the POSIX.1-2008 standard
972//
973 pid = (int)ntohl(Request.login.pid);
974 strncpy(uname, (const char *)Request.login.username, sizeof(uname)-1);
975 uname[sizeof(uname)-1] = 0;
977
978// Make sure the user is not already logged in
979//
980 if (Status) return Response.Send(kXR_InvalidRequest,
981 "duplicate login; already logged in");
982
983// Establish the ID for this link
984//
985 Link->setID(uname, pid);
986 CapVer = Request.login.capver[0];
987
988// Establish the session ID if the client can handle it (protocol version > 0)
989//
990 if ((i = (CapVer & kXR_vermask)))
991 {sessID.FD = Link->FDnum();
992 sessID.Inst = Link->Inst();
993 sessID.Pid = myPID;
994 mySID = getSID();
995 sessID.Sid = mySID;
996 sendSID = 1;
997 if (!clientPV)
998 { if (i >= kXR_ver004) clientPV = (int)0x0310;
999 else if (i == kXR_ver003) clientPV = (int)0x0300;
1000 else if (i == kXR_ver002) clientPV = (int)0x0290;
1001 else if (i == kXR_ver001) clientPV = (int)0x0200;
1002 else clientPV = (int)0x0100;
1003 }
1005 if (Request.login.ability & kXR_fullurl)
1007 if (Request.login.ability & kXR_lclfile)
1009 if (Request.login.ability & kXR_multipr)
1011 if (Request.login.ability & kXR_readrdok)
1013 if (Request.login.ability & kXR_hasipv64)
1015 if (Request.login.ability & kXR_redirflags)
1017 if (Request.login.ability2 & kXR_ecredir )
1019 }
1020
1021// Mark the client as IPv4 if they came in as IPv4 or mapped IPv4 we can only
1022// return IPv4 addresses. Of course, if the client is dual-stacked then we
1023// simply indicate the client can accept either (the client better be honest).
1024//
1025 addrP = Link->AddrInfo();
1026 if (addrP->isIPType(XrdNetAddrInfo::IPv4) || addrP->isMapped())
1028// WORKAROUND: XrdCl 4.0.x often identifies worker nodes as being IPv6-only.
1029// Rather than breaking a significant number of our dual-stack workers, we
1030// automatically denote IPv6 connections as also supporting IPv4 - regardless
1031// of what the remote client claims. This was fixed in 4.3.x but we can't
1032// tell release differences until 4.5 when we can safely ignore this as we
1033// also don't want to misidentify IPv6-only clients either.
1034 else if (i < kXR_ver004 && XrdInet::GetAssumeV4())
1036
1037// Mark the client as being on a private net if the address is private
1038//
1039 if (addrP->isPrivate()) {clientPV |= XrdOucEI::uPrip; rdType = 1;}
1040 else rdType = 0;
1041
1042// Get the security token for this link. We will either get a token, a null
1043// string indicating host-only authentication, or a null indicating no
1044// authentication. We can then optimize of each case.
1045//
1046 if (CIA)
1047 {const char *pp=CIA->getParms(i, Link->AddrInfo());
1048 if (pp && i ) {if (!sendSID) rc = Response.Send((void *)pp, i);
1049 else {struct iovec iov[3];
1050 iov[1].iov_base = (char *)&sessID;
1051 iov[1].iov_len = sizeof(sessID);
1052 iov[2].iov_base = (char *)pp;
1053 iov[2].iov_len = i;
1054 rc = Response.Send(iov,3,int(i+sizeof(sessID)));
1055 }
1057 }
1058 else {rc = (sendSID ? Response.Send((void *)&sessID, sizeof(sessID))
1059 : Response.Send());
1060 Status = XRD_LOGGEDIN; SI->Bump(SI->LoginUA);
1061 }
1062 }
1063 else {rc = (sendSID ? Response.Send((void *)&sessID, sizeof(sessID))
1064 : Response.Send());
1065 Status = XRD_LOGGEDIN; SI->Bump(SI->LoginUA);
1066 }
1067
1068// We always allow at least host-based authentication. This may be over-ridden
1069// should strong authentication be enabled. Allocation of the protocol object
1070// already supplied the protocol name and the host name. We supply the tident
1071// and the connection details in addrInfo.
1072//
1073 Entity.tident = Entity.pident = Link->ID;
1074 Entity.addrInfo = Link->AddrInfo();
1075 Client = &Entity;
1076
1077// Check if we need to process a login environment
1078//
1079 if (Request.login.dlen > 8)
1080 {XrdOucEnv loginEnv(argp->buff+1, Request.login.dlen-1);
1081 char *rnumb = loginEnv.Get("xrd.rn");
1082 char *cCode = loginEnv.Get("xrd.cc");
1083 char *tzVal = loginEnv.Get("xrd.tz");
1084 char *appXQ = loginEnv.Get("xrd.appname");
1085 char *aInfo = loginEnv.Get("xrd.info");
1086 int tzNum = (tzVal ? atoi(tzVal) : 0);
1087 if (cCode && *cCode && tzNum >= -12 && tzNum <= 14)
1088 {XrdNetAddrInfo::LocInfo locInfo;
1089 locInfo.Country[0] = cCode[0]; locInfo.Country[1] = cCode[1];
1090 locInfo.TimeZone = tzNum & 0xff;
1091 Link->setLocation(locInfo);
1092 }
1093 if (Monitor.Ready() && (appXQ || aInfo))
1094 {char apBuff[1024];
1095 snprintf(apBuff, sizeof(apBuff), "&R=%s&x=%s&y=%s&I=%c",
1096 (rnumb ? rnumb : ""),
1097 (appXQ ? appXQ : ""), (aInfo ? aInfo : ""),
1098 (clientPV & XrdOucEI::uIPv4 ? '4' : '6'));
1099 Entity.moninfo = strdup(apBuff);
1100 }
1101
1102 if (rnumb)
1103 {int majr, minr, pchr;
1104 if (sscanf(rnumb, "v%d.%d.%d", &majr, &minr, &pchr) == 3)
1105 clientRN = (majr<<16) | ((minr<<8) | pchr);
1106 else if (sscanf(rnumb, "v%d-%*x", &majr) == 1) clientRN = -1;
1107 }
1108 if (appXQ) AppName = strdup(appXQ);
1109 }
1110
1111// Allocate a monitoring object, if needed for this connection
1112//
1113 if (Monitor.Ready())
1114 {Monitor.Register(Link->ID, Link->Host(), "xroot", mySID);
1115 if (Monitor.Logins() && (!Monitor.Auths() || !(Status & XRD_NEED_AUTH)))
1116 {Monitor.Report(Entity.moninfo);
1117 if (Entity.moninfo) {free(Entity.moninfo); Entity.moninfo = 0;}
1118 Entity.secMon = &Monitor;
1119 }
1120 }
1121
1122// Complete the rquestID object
1123//
1124 ReqID.setID(Request.header.streamid, Link->FDnum(), Link->Inst());
1125
1126// Document this login
1127//
1128 if (!(Status & XRD_NEED_AUTH) && !logLogin()) return -1;
1129 return rc;
1130}
1131
1132/******************************************************************************/
1133/* d o _ M k d i r */
1134/******************************************************************************/
1135
1136int XrdXrootdProtocol::do_Mkdir()
1137{
1138 int mode, rc;
1139 char *opaque;
1140 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1141
1142// Check for static routing
1143//
1144 STATIC_REDIRECT(RD_mkdir);
1145
1146// Unmarshall the data
1147//
1148 mode = mapMode((int)ntohs(Request.mkdir.mode)) | S_IRWXU;
1149 if (Request.mkdir.options[0] & static_cast<unsigned char>(kXR_mkdirpath))
1150 mode |= SFS_O_MKPTH;
1151 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Creating", argp->buff);
1152 if (!Squash(argp->buff)) return vpEmsg("Creating", argp->buff);
1153
1154// Preform the actual function
1155//
1156 rc = osFS->mkdir(argp->buff, (XrdSfsMode)mode, myError, CRED, opaque);
1157 TRACEP(FS, "rc=" <<rc <<" mkdir " <<Xrd::oct1 <<mode <<' ' <<argp->buff);
1158 if (SFS_OK == rc) return Response.Send();
1159
1160// An error occurred
1161//
1162 return fsError(rc, XROOTD_MON_MKDIR, myError, argp->buff, opaque);
1163}
1164
1165/******************************************************************************/
1166/* d o _ M v */
1167/******************************************************************************/
1168
1169int XrdXrootdProtocol::do_Mv()
1170{
1171 int rc;
1172 char *oldp, *newp, *Opaque, *Npaque;
1173 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1174
1175// Check for static routing
1176//
1177 STATIC_REDIRECT(RD_mv);
1178
1179// Find the space separator between the old and new paths
1180//
1181 oldp = newp = argp->buff;
1182 if (Request.mv.arg1len)
1183 {int n = ntohs(Request.mv.arg1len);
1184 if (n < 0 || n >= Request.mv.dlen || *(argp->buff+n) != ' ')
1185 return Response.Send(kXR_ArgInvalid, "invalid path specification");
1186 *(oldp+n) = 0;
1187 newp += n+1;
1188 } else {
1189 while(*newp && *newp != ' ') newp++;
1190 if (*newp) {*newp = '\0'; newp++;
1191 while(*newp && *newp == ' ') newp++;
1192 }
1193 }
1194
1195// Get rid of relative paths and multiple slashes
1196//
1197 if (rpCheck(oldp, &Opaque)) return rpEmsg("Renaming", oldp);
1198 if (rpCheck(newp, &Npaque)) return rpEmsg("Renaming to", newp);
1199 if (!Squash(oldp)) return vpEmsg("Renaming", oldp);
1200 if (!Squash(newp)) return vpEmsg("Renaming to", newp);
1201
1202// Check if new path actually specified here
1203//
1204 if (*newp == '\0')
1205 Response.Send(kXR_ArgMissing, "new path specified for mv");
1206
1207// Preform the actual function
1208//
1209 rc = osFS->rename(oldp, newp, myError, CRED, Opaque, Npaque);
1210 TRACEP(FS, "rc=" <<rc <<" mv " <<oldp <<' ' <<newp);
1211 if (SFS_OK == rc) return Response.Send();
1212
1213// An error occurred
1214//
1215 return fsError(rc, XROOTD_MON_MV, myError, oldp, Opaque);
1216}
1217
1218/******************************************************************************/
1219/* d o _ O f f l o a d */
1220/******************************************************************************/
1221
1222int XrdXrootdProtocol::do_Offload(int (XrdXrootdProtocol::*Invoke)(),int pathID)
1223{
1224 XrdSysSemaphore isAvail(0);
1226 XrdXrootdPio *pioP;
1227 int rc;
1228 kXR_char streamID[2];
1229
1230// Verify that the path actually exists (note we will have the stream lock)
1231//
1232 if (!(pp = VerifyStream(rc, pathID))) return rc;
1233
1234// Grab the stream ID
1235//
1236 Response.StreamID(streamID);
1237
1238// Try to schedule this operation. In order to maximize the I/O overlap, we
1239// will wait until the stream gets control and will have a chance to start
1240// reading from the network. We handle refs for consistency.
1241//
1242 do{if (!pp->isActive)
1243 {pp->IO = IO;
1244 pp->myBlen = 0;
1245 pp->Resume = &XrdXrootdProtocol::do_OffloadIO;
1246 pp->ResumePio= Invoke;
1247 pp->isActive = true;
1248 pp->newPio = true;
1249 pp->reTry = &isAvail;
1250 pp->Response.Set(streamID);
1251 pp->streamMutex.UnLock();
1252 Link->setRef(1);
1253 IO.File->Ref(1);
1254 Sched->Schedule((XrdJob *)(pp->Link));
1255 isAvail.Wait();
1256 return 0;
1257 }
1258
1259 if ((pioP = pp->pioFree)) break;
1260 pp->reTry = &isAvail;
1261 pp->streamMutex.UnLock();
1262 TRACEP(FSZIO, "busy path " <<pathID <<" offs=" <<IO.Offset);
1263 isAvail.Wait();
1264 TRACEP(FSZIO, "retry path " <<pathID <<" offs=" <<IO.Offset);
1265 pp->streamMutex.Lock();
1266 if (pp->isNOP)
1267 {pp->streamMutex.UnLock();
1268 return Response.Send(kXR_ArgInvalid, "path ID is not connected");
1269 }
1270 } while(1);
1271
1272// Fill out the queue entry and add it to the queue
1273//
1274 pp->pioFree = pioP->Next; pioP->Next = 0;
1275 pioP->Set(Invoke, IO, streamID);
1276 IO.File->Ref(1);
1277 if (pp->pioLast) pp->pioLast->Next = pioP;
1278 else pp->pioFirst = pioP;
1279 pp->pioLast = pioP;
1280 pp->streamMutex.UnLock();
1281 return 0;
1282}
1283
1284/******************************************************************************/
1285/* d o _ O f f l o a d I O */
1286/******************************************************************************/
1287
1288int XrdXrootdProtocol::do_OffloadIO()
1289{
1290 XrdXrootdPio *pioP;
1291 int rc;
1292
1293// Entry implies that we just got scheduled and are marked as active. Hence
1294// we need to post the session thread so that it can pick up the next request.
1295//
1296 streamMutex.Lock();
1297 isLinkWT = false;
1298 if (newPio)
1299 {newPio = false;
1300 if (reTry) {reTry->Post(); reTry = 0;}
1301 TRACEP(FSZIO, "dispatch new I/O path " <<PathID <<" offs=" <<IO.Offset);
1302 }
1303
1304// Perform all I/O operations on a parallel stream
1305//
1306 if (!isNOP)
1307 do {streamMutex.UnLock();
1308 rc = (*this.*ResumePio)();
1309 streamMutex.Lock();
1310
1311 if (rc > 0 && !isNOP)
1312 {ResumePio = Resume;
1313 Resume = &XrdXrootdProtocol::do_OffloadIO;
1314 isLinkWT = true;
1315 streamMutex.UnLock();
1316 return rc;
1317 }
1318
1319 IO.File->Ref(-1); // Note: File was ref'd when request was queued
1320 if (rc || isNOP || !(pioP = pioFirst)) break;
1321 if (!(pioFirst = pioP->Next)) pioLast = 0;
1322
1323 IO = pioP->IO;
1324 ResumePio = pioP->ResumePio;
1325 Response.Set(pioP->StreamID);
1326 pioP->Next = pioFree; pioFree = pioP;
1327 if (reTry) {reTry->Post(); reTry = 0;}
1328 } while(1);
1329 else {rc = -1; IO.File->Ref(-1);}
1330
1331// There are no pending operations or the link died
1332//
1333 if (rc) isNOP = true;
1334 isActive = false;
1335 Stream[0]->Link->setRef(-1);
1336 if (reTry) {reTry->Post(); reTry = 0;}
1337 if (endNote) endNote->Signal();
1338 streamMutex.UnLock();
1339 TRACEP(FSZIO, "offload complete path "<<PathID<<" virt rc=" <<rc);
1340 return (rc ? rc : -EINPROGRESS);
1341}
1342
1343/******************************************************************************/
1344/* d o _ O p e n */
1345/******************************************************************************/
1346
1347namespace
1348{
1349struct OpenHelper
1350 {XrdSfsFile *fp;
1351 XrdXrootdFile *xp;
1352 XrdXrootdFileLock *Locker;
1353 const char *path;
1354 char mode;
1355 bool isOK;
1356
1357 OpenHelper(XrdXrootdFileLock *lkP, const char *fn)
1358 : fp(0), xp(0), Locker(lkP), path(fn), mode(0),
1359 isOK(false) {}
1360
1361 ~OpenHelper()
1362 {if (!isOK)
1363 {if (xp) delete xp; // Deletes fp & unlocks
1364 else {if (fp) delete fp;
1365 if (mode) Locker->Unlock(path,mode);
1366 }
1367 }
1368 }
1369 };
1370}
1371
1372int XrdXrootdProtocol::do_Open()
1373{
1374 static XrdXrootdCallBack openCB("open file", XROOTD_MON_OPENR);
1375 int fhandle;
1376 int rc, mode, opts, openopts, compchk = 0;
1377 int popt, retStat = 0;
1378 char *opaque, usage, ebuff[2048], opC;
1379 bool doDig, doforce = false, isAsync = false;
1380 char *fn = argp->buff, opt[16], *op=opt;
1381 XrdSfsFile *fp;
1382 XrdXrootdFile *xp;
1383 struct stat statbuf;
1384 struct ServerResponseBody_Open myResp;
1385 int resplen = sizeof(myResp.fhandle);
1386 struct iovec IOResp[3]; // Note that IOResp[0] is completed by Response
1387
1388// Keep Statistics
1389//
1390 SI->Bump(SI->openCnt);
1391
1392// Unmarshall the data
1393//
1394 mode = (int)ntohs(Request.open.mode);
1395 opts = (int)ntohs(Request.open.options);
1396
1397// Map the mode and options
1398//
1399 mode = mapMode(mode) | S_IRUSR | S_IWUSR; usage = 'r';
1400 if (opts & kXR_open_read)
1401 {openopts = SFS_O_RDONLY; *op++ = 'r'; opC = XROOTD_MON_OPENR;}
1402 else if (opts & kXR_open_updt)
1403 {openopts = SFS_O_RDWR; *op++ = 'u'; usage = 'w';
1404 opC = XROOTD_MON_OPENW;}
1405 else if (opts & kXR_open_wrto)
1406 {openopts = SFS_O_WRONLY; *op++ = 'o'; usage = 'w';
1407 opC = XROOTD_MON_OPENW;}
1408 else {openopts = SFS_O_RDONLY; *op++ = 'r'; opC = XROOTD_MON_OPENR;}
1409
1410 if (opts & kXR_new)
1411 {openopts |= SFS_O_CREAT; *op++ = 'n'; opC = XROOTD_MON_OPENC;
1412 if (opts & kXR_replica) {*op++ = '+';
1413 openopts |= SFS_O_REPLICA;
1414 }
1415 // Up until 3/28/19 we mistakenly used kXR_mkdir instead of
1416 // kXR_mkpath to allow path creation. That meant, path creation was
1417 // allowed if _mkpath|_async|_refresh|_open_apnd|_replica were set.
1418 // Since the client has always turned on _async that meant that
1419 // path creation was always enabled. We continue this boondogle
1420 // using the correct flag for backward compatibility reasons, sigh.
1421 //
1422 if (opts & (kXR_mkpath | kXR_async))
1423 {*op++ = 'm';
1424 mode |= SFS_O_MKPTH;
1425 }
1426 }
1427 else if (opts & kXR_delete)
1428 {openopts = SFS_O_TRUNC; *op++ = 'd'; opC = XROOTD_MON_OPENW;
1429 if (opts & (kXR_mkpath | kXR_async))
1430 {*op++ = 'm';
1431 mode |= SFS_O_MKPTH;
1432 }
1433 }
1434 if (opts & kXR_compress)
1435 {openopts |= SFS_O_RAWIO; *op++ = 'c'; compchk = 1;}
1436 if (opts & kXR_force) {*op++ = 'f'; doforce = true;}
1437 if ((opts & kXR_async || as_force) && as_aioOK)
1438 {*op++ = 'a'; isAsync = true;}
1439 if (opts & kXR_refresh) {*op++ = 's'; openopts |= SFS_O_RESET;
1440 SI->Bump(SI->Refresh);
1441 }
1442 if (opts & kXR_retstat) {*op++ = 't'; retStat = 1;}
1443 if (opts & kXR_posc) {*op++ = 'p'; openopts |= SFS_O_POSC;}
1444 if (opts & kXR_seqio) {*op++ = 'S'; openopts |= SFS_O_SEQIO;}
1445 *op = '\0';
1446
1447// Do some tracing, avoid exposing any security token in the URL
1448//
1449 if (TRACING(TRACE_FS))
1450 {char* cgiP = index(fn, '?');
1451 if (cgiP) *cgiP = 0;
1452 TRACEP(FS, "open " <<opt <<' ' <<fn);
1453 if (cgiP) *cgiP = '?';
1454 }
1455
1456// Check if opaque data has been provided
1457//
1458 if (rpCheck(fn, &opaque)) return rpEmsg("Opening", fn);
1459
1460// Check if this is a local dig type file
1461//
1462 doDig = (digFS && SFS_LCLPATH(fn));
1463
1464// Validate the path and then check if static redirection applies
1465//
1466 if (doDig) {popt = XROOTDXP_NOLK; opC = 0;}
1467 else {int ropt;
1468 if (!(popt = Squash(fn))) return vpEmsg("Opening", fn);
1469 if (Route[RD_open1].Host[rdType] && (ropt = RPList.Validate(fn)))
1470 return Response.Send(kXR_redirect, Route[ropt].Port[rdType],
1471 Route[ropt].Host[rdType]);
1472 }
1473
1474// Add the multi-write option if this path supports it
1475//
1476 if (popt & XROOTDXP_NOMWCHK) openopts |= SFS_O_MULTIW;
1477
1478// Construct an open helper to release resources should we exit due to an error.
1479//
1480 OpenHelper oHelp(Locker, fn);
1481
1482// Lock this file
1483//
1484 if (!(popt & XROOTDXP_NOLK))
1485 {if ((rc = Locker->Lock(fn, usage, doforce)))
1486 {const char *who;
1487 if (rc > 0) who = (rc > 1 ? "readers" : "reader");
1488 else { rc = -rc;
1489 who = (rc > 1 ? "writers" : "writer");
1490 }
1491 snprintf(ebuff, sizeof(ebuff)-1,
1492 "%s file %s is already opened by %d %s; open denied.",
1493 ('r' == usage ? "Input" : "Output"), fn, rc, who);
1494 eDest.Emsg("Xeq", ebuff);
1495 return Response.Send(kXR_FileLocked, ebuff);
1496 } else oHelp.mode = usage;
1497 }
1498
1499// Get a file object
1500//
1501 if (doDig) fp = digFS->newFile(Link->ID, Monitor.Did);
1502 else fp = osFS->newFile(Link->ID, Monitor.Did);
1503
1504// Make sure we got one
1505//
1506 if (!fp)
1507 {snprintf(ebuff, sizeof(ebuff)-1,"Insufficient memory to open %s",fn);
1508 eDest.Emsg("Xeq", ebuff);
1509 return Response.Send(kXR_NoMemory, ebuff);
1510 }
1511 oHelp.fp = fp;
1512
1513// The open is elegible for a deferred response, indicate we're ok with that
1514//
1515 fp->error.setErrCB(&openCB, ReqID.getID());
1516 fp->error.setUCap(clientPV);
1517
1518// If TPC opens require TLS but this is not a TLS connection, prohibit TPC
1519//
1520 if ((doTLS & Req_TLSTPC) && !isTLS && !Link->hasBridge())
1521 openopts|= SFS_O_NOTPC;
1522
1523// Open the file
1524//
1525 if ((rc = fp->open(fn, (XrdSfsFileOpenMode)openopts,
1526 (mode_t)mode, CRED, opaque)))
1527 {rc = fsError(rc, opC, fp->error, fn, opaque); return rc;}
1528
1529// Obtain a hyper file object
1530//
1531 xp = new XrdXrootdFile(Link->ID, fn, fp, usage, isAsync, &statbuf);
1532 if (!xp)
1533 {snprintf(ebuff, sizeof(ebuff)-1, "Insufficient memory to open %s", fn);
1534 eDest.Emsg("Xeq", ebuff);
1535 return Response.Send(kXR_NoMemory, ebuff);
1536 }
1537 oHelp.xp = xp;
1538
1539// Serialize the link
1540//
1541 Link->Serialize();
1542 *ebuff = '\0';
1543
1544// Create a file table for this link if it does not have one
1545//
1546 if (!FTab) FTab = new XrdXrootdFileTable(Monitor.Did);
1547
1548// Insert this file into the link's file table
1549//
1550 if (!FTab || (fhandle = FTab->Add(xp)) < 0)
1551 {snprintf(ebuff, sizeof(ebuff)-1, "Insufficient memory to open %s", fn);
1552 eDest.Emsg("Xeq", ebuff);
1553 return Response.Send(kXR_NoMemory, ebuff);
1554 }
1555
1556// If the file supports exchange buffering, supply it with the object
1557//
1558 if (fsFeatures & XrdSfs::hasSXIO) fp->setXio(this);
1559
1560// Document forced opens
1561//
1562 if (doforce)
1563 {int rdrs, wrtrs;
1564 Locker->numLocks(fn, rdrs, wrtrs);
1565 if (('r' == usage && wrtrs) || ('w' == usage && rdrs) || wrtrs > 1)
1566 {snprintf(ebuff, sizeof(ebuff)-1,
1567 "%s file %s forced opened with %d reader(s) and %d writer(s).",
1568 ('r' == usage ? "Input" : "Output"), fn, rdrs, wrtrs);
1569 eDest.Emsg("Xeq", ebuff);
1570 }
1571 }
1572
1573// Determine if file is compressed
1574//
1575 memset(&myResp, 0, sizeof(myResp));
1576 if (!compchk) resplen = sizeof(myResp.fhandle);
1577 else {int cpsize;
1578 fp->getCXinfo((char *)myResp.cptype, cpsize);
1579 myResp.cpsize = static_cast<kXR_int32>(htonl(cpsize));
1580 resplen = sizeof(myResp);
1581 }
1582
1583// If client wants a stat in open, return the stat information
1584//
1585 if (retStat)
1586 {retStat = StatGen(statbuf, ebuff, sizeof(ebuff));
1587 IOResp[1].iov_base = (char *)&myResp; IOResp[1].iov_len = sizeof(myResp);
1588 IOResp[2].iov_base = ebuff; IOResp[2].iov_len = retStat;
1589 resplen = sizeof(myResp) + retStat;
1590 }
1591
1592// If we are monitoring, send off a path to dictionary mapping (must try 1st!)
1593//
1594 if (Monitor.Files())
1595 {xp->Stats.FileID = Monitor.MapPath(fn);
1597 Monitor.Agent->Open(xp->Stats.FileID, statbuf.st_size);
1598 }
1599
1600// Since file monitoring is deprecated, a dictid may not have been assigned.
1601// But if fstream monitoring is enabled it will assign the dictid.
1602//
1603 if (Monitor.Fstat())
1604 XrdXrootdMonFile::Open(&(xp->Stats), fn, Monitor.Did, usage == 'w');
1605
1606// Insert the file handle
1607//
1608 memcpy((void *)myResp.fhandle,(const void *)&fhandle,sizeof(myResp.fhandle));
1609 numFiles++;
1610
1611// If packet marking is enabled, notify that we have potentially started data.
1612// We also need to extend the marking to any associated streams.
1613//
1614 int eCode, aCode;
1615 if (PMark && !pmDone)
1616 {streamMutex.Lock();
1617 pmDone = true;
1618 if ((pmHandle = PMark->Begin(*Client, fn, opaque, AppName)))
1619 for (int i = 1; i < maxStreams; i++)
1620 {if (Stream[i] && !(Stream[i]->pmDone))
1621 {Stream[i]->pmDone = true;
1622 Stream[i]->pmHandle =
1623 PMark->Begin(*(Stream[i]->Link->AddrInfo()),
1624 *pmHandle, Stream[i]->Link->ID);
1625 }
1626 }
1627 streamMutex.UnLock();
1628
1629 if (pmHandle && Monitor.Logins() && pmHandle->getEA(eCode, aCode))
1630 Monitor.Report(eCode, aCode);
1631 } else {
1632 if (!pmDone && Monitor.Logins()
1633 && XrdNetPMark::getEA(opaque, eCode, aCode))
1634 {Monitor.Report(eCode, aCode); pmDone = true;}
1635 }
1636
1637// Respond (failure is not an option now)
1638//
1639 oHelp.isOK = true;
1640 if (retStat) return Response.Send(IOResp, 3, resplen);
1641 else return Response.Send((void *)&myResp, resplen);
1642}
1643
1644/******************************************************************************/
1645/* d o _ P i n g */
1646/******************************************************************************/
1647
1648int XrdXrootdProtocol::do_Ping()
1649{
1650
1651// Keep Statistics
1652//
1653 SI->Bump(SI->miscCnt);
1654
1655// This is a basic nop
1656//
1657 return Response.Send();
1658}
1659
1660/******************************************************************************/
1661/* d o _ P r e p a r e */
1662/******************************************************************************/
1663
1664int XrdXrootdProtocol::do_Prepare(bool isQuery)
1665{
1666 static XrdXrootdCallBack prpCB("query", XROOTD_MON_QUERY);
1667
1668 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
1669
1670 XrdOucTokenizer pathlist(argp->buff);
1671 XrdOucTList *pFirst=0, *pP, *pLast = 0;
1672 XrdOucTList *oFirst=0, *oP, *oLast = 0;
1673 XrdOucTListHelper pHelp(&pFirst), oHelp(&oFirst);
1674 XrdXrootdPrepArgs pargs(0, 0);
1675 XrdSfsPrep fsprep;
1676
1677 int rc, pathnum = 0;
1678 char reqid[128], nidbuff[512], *path, *opaque, *prpid = 0;
1679 unsigned short optX = ntohs(Request.prepare.optionX);
1680 char opts;
1681 bool isCancel, isEvict, isPrepare;
1682
1683// Check if this is an evict request (similar to stage)
1684//
1685 isEvict = (optX & kXR_evict) != 0;
1686
1687// Establish what we are really doing here
1688//
1689 if (isQuery)
1690 {opts = 0;
1691 isCancel = false;
1692 } else {
1693 if (Request.prepare.options & kXR_cancel)
1694 {opts = 0;
1695 isCancel = true;
1696 } else {
1697 opts = (isEvict ? 0 : Request.prepare.options);
1698 isCancel = false;
1699 }
1700 }
1701 isPrepare = !(isCancel || isQuery);
1702
1703// Apply prepare limits, as necessary.
1704//
1705 if (isPrepare && (PrepareLimit >= 0) && (++PrepareCount > PrepareLimit)) {
1706 if (LimitError) {
1707 return Response.Send( kXR_overQuota,
1708 "Surpassed this connection's prepare limit.");
1709 } else {
1710 return Response.Send();
1711 }
1712 }
1713
1714// Check for static routing
1715//
1716 if ((opts & kXR_stage) || isCancel) {STATIC_REDIRECT(RD_prepstg);}
1717 STATIC_REDIRECT(RD_prepare);
1718
1719// Prehandle requests that must have a requestID. Otherwise, generate one.
1720// Note that prepare request id's have two formats. The external format is
1721// is qualifiaed by this host while the internal one removes the qualification.
1722// The internal one is only used for the native prepare implementation.
1723// To wit: prpid is the unqualified ID while reqid is the qualified one for
1724// generated id's while prpid is always the specified request id.
1725//
1726 if (isCancel || isQuery)
1727 {if (!(prpid = pathlist.GetLine()))
1728 return Response.Send(kXR_ArgMissing, "Prepare requestid not specified");
1729 fsprep.reqid = prpid;
1730 fsprep.opts = (isCancel ? Prep_CANCEL : Prep_QUERY);
1731 if (!PrepareAlt)
1732 {char hname[256];
1733 int hport;
1734 prpid = PrepID->isMine(prpid, hport, hname, sizeof(hname));
1735 if (!prpid)
1736 {if (!hport) return Response.Send(kXR_ArgInvalid,
1737 "Prepare requestid owned by an unknown server");
1738 TRACEI(REDIR, Response.ID() <<" redirecting prepare to "
1739 << hname <<':' <<hport);
1740 return Response.Send(kXR_redirect, hport, hname);
1741 }
1742 }
1743 } else {
1744 if (opts & kXR_stage)
1745 {prpid = PrepID->ID(reqid, sizeof(reqid));
1746 fsprep.reqid = reqid;
1747 fsprep.opts = Prep_STAGE | (opts & kXR_coloc ? Prep_COLOC : 0);
1748 } else {
1749 reqid[0]='*'; reqid[1]='\0';
1750 fsprep.reqid = prpid = reqid;
1751 fsprep.opts = (isEvict ? Prep_EVICT : 0);
1752 }
1753 }
1754
1755// Initialize the file system prepare arg list
1756//
1757 fsprep.paths = 0;
1758 fsprep.oinfo = 0;
1759 fsprep.notify = 0;
1760
1761// Cycle through all of the paths in the list
1762//
1763 while((path = pathlist.GetLine()))
1764 {if (rpCheck(path, &opaque)) return rpEmsg("Preparing", path);
1765 if (!Squash(path)) return vpEmsg("Preparing", path);
1766 pP = new XrdOucTList(path, pathnum);
1767 (pLast ? (pLast->next = pP) : (pFirst = pP)); pLast = pP;
1768 oP = new XrdOucTList(opaque, 0);
1769 (oLast ? (oLast->next = oP) : (oFirst = oP)); oLast = oP;
1770 pathnum++;
1771 }
1772 fsprep.paths = pFirst;
1773 fsprep.oinfo = oFirst;
1774
1775// We support callbacks but only for alternate prepare processing
1776//
1777 if (PrepareAlt) myError.setErrCB(&prpCB, ReqID.getID());
1778
1779// Process cancel requests here; they are simple at this point.
1780//
1781 if (isCancel)
1782 {if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1783 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1784 rc = Response.Send();
1786 return rc;
1787 }
1788
1789// Process query requests here; they are simple at this point.
1790//
1791 if (isQuery)
1792 {if (PrepareAlt)
1793 {if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1794 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1795 rc = Response.Send();
1796 } else {
1797 char *mBuff = myError.getMsgBuff(rc);
1798 pargs.reqid = prpid;
1799 pargs.user = Link->ID;
1800 pargs.paths = pFirst;
1801 rc = XrdXrootdPrepare::List(pargs, mBuff, rc);
1802 if (rc < 0) rc = Response.Send("No information found.");
1803 else rc = Response.Send(mBuff);
1804 }
1805 return rc;
1806 }
1807
1808// Make sure we have at least one path
1809//
1810 if (!pFirst)
1811 return Response.Send(kXR_ArgMissing, "No prepare paths specified");
1812
1813// Handle evict. We only support the evicts for alternate prepare handlers.
1814//
1815 if (isEvict)
1816 {if (PrepareAlt
1817 && (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED))))
1818 return fsError(rc, XROOTD_MON_PREP, myError, path, 0);
1819 return Response.Send();
1820 }
1821
1822// Handle notification parameter. The notification depends on whether or not
1823// we have a custom prepare handler.
1824//
1825 if (opts & kXR_notify)
1826 {const char *nprot = (opts & kXR_usetcp ? "tcp" : "udp");
1827 fsprep.notify = nidbuff;
1828 if (PrepareAlt)
1829 {if (Request.prepare.port == 0) fsprep.notify = 0;
1830 else snprintf(nidbuff, sizeof(nidbuff), "%s://%s:%d/",
1831 nprot, Link->Host(), ntohs(Request.prepare.port));
1832 } else sprintf(nidbuff, Notify, nprot, Link->FDnum(), Link->ID);
1833 if (fsprep.notify)
1834 fsprep.opts |= (opts & kXR_noerrs ? Prep_SENDAOK : Prep_SENDACK);
1835 }
1836
1837// Complete prepare options
1838//
1839 fsprep.opts |= (opts & kXR_fresh ? Prep_FRESH : 0);
1840 if (opts & kXR_wmode) fsprep.opts |= Prep_WMODE;
1841 if (PrepareAlt)
1842 {switch(Request.prepare.prty)
1843 {case 0: fsprep.opts |= Prep_PRTY0; break;
1844 case 1: fsprep.opts |= Prep_PRTY1; break;
1845 case 2: fsprep.opts |= Prep_PRTY2; break;
1846 case 3: fsprep.opts |= Prep_PRTY3; break;
1847 default: break;
1848 }
1849 } else {
1850 if (Request.prepare.prty == 0) fsprep.opts |= Prep_PRTY0;
1851 else fsprep.opts |= Prep_PRTY1;
1852 }
1853
1854// Issue the prepare request
1855//
1856 if (SFS_OK != (rc = osFS->prepare(fsprep, myError, CRED)))
1857 return fsError(rc, XROOTD_MON_PREP, myError, pFirst->text, oFirst->text);
1858
1859// Perform final processing
1860//
1861 if (!(opts & kXR_stage)) rc = Response.Send();
1862 else {rc = Response.Send(reqid, strlen(reqid));
1863 if (!PrepareAlt)
1864 {pargs.reqid = prpid;
1865 pargs.user = Link->ID;
1866 pargs.paths = pFirst;
1867 XrdXrootdPrepare::Log(pargs);
1868 }
1869 }
1870 return rc;
1871}
1872
1873/******************************************************************************/
1874/* d o _ P r o t o c o l */
1875/******************************************************************************/
1876
1877namespace XrdXrootd
1878{
1879extern char *bifResp[2];
1880extern int bifRLen[2];
1881}
1882
1883int XrdXrootdProtocol::do_Protocol()
1884{
1885 static kXR_int32 verNum = static_cast<kXR_int32>(htonl(kXR_PROTOCOLVERSION));
1886 static kXR_int32 theRle = static_cast<kXR_int32>(htonl(myRole));
1887 static kXR_int32 theRlf = static_cast<kXR_int32>(htonl(myRolf));
1888 static kXR_int32 theRlt = static_cast<kXR_int32>(htonl(myRole|kXR_gotoTLS));
1889
1890 ServerResponseBody_Protocol theResp;
1891 struct iovec ioVec[4] = {{0,0},{&theResp,kXR_ShortProtRespLen},{0,0},{0,0}};
1892
1893 int rc, iovN = 2, RespLen = kXR_ShortProtRespLen;
1894 bool wantTLS = false;
1895
1896// Keep Statistics
1897//
1898 SI->Bump(SI->miscCnt);
1899
1900// Determine which response to provide
1901//
1902 if (Request.protocol.clientpv)
1903 {int cvn = XrdOucEI::uVMask & ntohl(Request.protocol.clientpv);
1904 if (!Status || !(clientPV & XrdOucEI::uVMask))
1905 clientPV = (clientPV & ~XrdOucEI::uVMask) | cvn;
1906 else cvn = (clientPV & XrdOucEI::uVMask);
1907
1908 if (Request.protocol.flags & ClientProtocolRequest::kXR_bifreqs
1909 && XrdXrootd::bifResp[0])
1910 {int k =( Link->AddrInfo()->isPrivate() ? 1 : 0);
1911 ioVec[iovN ].iov_base = XrdXrootd::bifResp[k];
1912 ioVec[iovN++].iov_len = XrdXrootd::bifRLen[k];
1913 RespLen += XrdXrootd::bifRLen[k];
1914 }
1915
1916 if (DHS && cvn >= kXR_PROTSIGNVERSION
1917 && Request.protocol.flags & ClientProtocolRequest::kXR_secreqs)
1918 {int n = DHS->ProtResp(theResp.secreq, *(Link->AddrInfo()), cvn);
1919 ioVec[iovN ].iov_base = (void *)&theResp.secreq;
1920 ioVec[iovN++].iov_len = n;
1921 RespLen += n;
1922 }
1923
1924 if ((myRole & kXR_haveTLS) != 0 && !(Link->hasTLS()))
1925 {wantTLS = (Request.protocol.flags &
1927 ableTLS = wantTLS || (Request.protocol.flags &
1929 if (ableTLS) doTLS = tlsCap;
1930 else doTLS = tlsNot;
1931 if (ableTLS && !wantTLS)
1932 switch(Request.protocol.expect & ClientProtocolRequest::kXR_ExpMask)
1934 wantTLS = (doTLS & Req_TLSData) != 0;
1935 break;
1937 wantTLS = (doTLS & Req_TLSLogin) != 0;
1938 break;
1940 wantTLS = (doTLS & Req_TLSTPC) != 0 ||
1941 (doTLS & Req_TLSLogin) != 0;
1942 break;
1943 default: break;
1944 }
1945 }
1946 theResp.flags = (wantTLS ? theRlt : theRle);
1947 } else {
1948 theResp.flags = theRlf;
1949 doTLS = tlsNot;
1950 }
1951
1952// Send the response
1953//
1954 theResp.pval = verNum;
1955 rc = Response.Send(ioVec, iovN, RespLen);
1956
1957// If the client wants to start using TLS, enable it now. If we fail then we
1958// have no choice but to terminate the connection. Note that incapable clients
1959// don't want TLS but if we require TLS anyway, they will get an error either
1960// pre-login or post-login or on a bind later on.
1961//
1962 if (rc == 0 && wantTLS)
1963 {if (Link->setTLS(true, tlsCtx))
1964 {Link->setProtName("xroots");
1965 isTLS = true;
1966 } else {
1967 eDest.Emsg("Xeq", "Unable to enable TLS for", Link->ID);
1968 rc = -1;
1969 }
1970 }
1971 return rc;
1972}
1973
1974/******************************************************************************/
1975/* d o _ Q c o n f */
1976/******************************************************************************/
1977
1978int XrdXrootdProtocol::do_Qconf()
1979{
1980 static const int fsctl_cmd = SFS_FSCTL_STATCC|SFS_O_LOCAL;
1981 XrdOucTokenizer qcargs(argp->buff);
1982 char *val, buff[4096], *bp=buff;
1983 int n, bleft = sizeof(buff);
1984
1985// Get the first argument
1986//
1987 if (!qcargs.GetLine() || !(val = qcargs.GetToken()))
1988 return Response.Send(kXR_ArgMissing, "query config argument not specified.");
1989
1990// The first item can be xrootd or cmsd to display the config file
1991//
1992 if (!strcmp(val, "cmsd") || !strcmp(val, "xrootd"))
1993 return do_QconfCX(qcargs, val);
1994
1995// Trace this query variable
1996//
1997 do {TRACEP(DEBUG, "query config " <<val);
1998
1999 // Now determine what the user wants to query
2000 //
2001 if (!strcmp("bind_max", val))
2002 {n = snprintf(bp, bleft, "%d\n", maxStreams-1);
2003 bp += n; bleft -= n;
2004 }
2005 else if (!strcmp("chksum", val))
2006 {const char *csList = getenv("XRD_CSLIST");
2007 if (!JobCKT || !csList)
2008 {n = snprintf(bp, bleft, "chksum\n");
2009 bp += n; bleft -= n;
2010 continue;
2011 }
2012 n = snprintf(bp, bleft, "%s\n", csList);
2013 bp += n; bleft -= n;
2014 }
2015 else if (!strcmp("cid", val))
2016 {const char *cidval = getenv("XRDCMSCLUSTERID");
2017 if (!cidval || !(*cidval)) cidval = "cid";
2018 n = snprintf(bp, bleft, "%s\n", cidval);
2019 bp += n; bleft -= n;
2020 }
2021 else if (!strcmp("cms", val))
2022 {XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2023 if (osFS->fsctl(fsctl_cmd, ".", myError, CRED) == SFS_DATA)
2024 n = snprintf(bp, bleft, "%s\n", myError.getErrText());
2025 else n = snprintf(bp, bleft, "%s\n", "cms");
2026 bp += n; bleft -= n;
2027 }
2028 else if (!strcmp("pio_max", val))
2029 {n = snprintf(bp, bleft, "%d\n", maxPio+1);
2030 bp += n; bleft -= n;
2031 }
2032 else if (!strcmp("proxy", val))
2033 {const char* pxyOrigin = "proxy";
2034 if (myRole & kXR_attrProxy)
2035 {pxyOrigin = getenv("XRDXROOTD_PROXY");
2036 if (!pxyOrigin) pxyOrigin = "proxy";
2037 }
2038 n = snprintf(bp,bleft,"%s\n",pxyOrigin);
2039 bp += n; bleft -= n;
2040 }
2041 else if (!strcmp("readv_ior_max", val))
2042 {n = snprintf(bp,bleft,"%d\n",maxReadv_ior);
2043 bp += n; bleft -= n;
2044 }
2045 else if (!strcmp("readv_iov_max", val))
2046 {n = snprintf(bp, bleft, "%d\n", XrdProto::maxRvecsz);
2047 bp += n; bleft -= n;
2048 }
2049 else if (!strcmp("role", val))
2050 {const char *theRole = getenv("XRDROLE");
2051 n = snprintf(bp, bleft, "%s\n", (theRole ? theRole : "none"));
2052 bp += n; bleft -= n;
2053 }
2054 else if (!strcmp("sitename", val))
2055 {const char *siteName = getenv("XRDSITE");
2056 n = snprintf(bp, bleft, "%s\n", (siteName ? siteName : "sitename"));
2057 bp += n; bleft -= n;
2058 }
2059 else if (!strcmp("start", val))
2060 {n = snprintf(bp, bleft, "%s\n", startUP);
2061 bp += n; bleft -= n;
2062 }
2063 else if (!strcmp("sysid", val))
2064 {const char *cidval = getenv("XRDCMSCLUSTERID");
2065 const char *nidval = getenv("XRDCMSVNID");
2066 if (!cidval || !(*cidval) || !nidval || !(*nidval))
2067 {cidval = "sysid"; nidval = "";}
2068 n = snprintf(bp, bleft, "%s %s\n", nidval, cidval);
2069 bp += n; bleft -= n;
2070 }
2071 else if (!strcmp("tpc", val))
2072 {char *tpcval = getenv("XRDTPC");
2073 n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpc"));
2074 bp += n; bleft -= n;
2075 }
2076 else if (!strcmp("tpcdlg", val))
2077 {char *tpcval = getenv("XRDTPCDLG");
2078 n = snprintf(bp, bleft, "%s\n", (tpcval ? tpcval : "tpcdlg"));
2079 bp += n; bleft -= n;
2080 }
2081 else if (!strcmp("tls_port", val) && tlsPort)
2082 {n = snprintf(bp, bleft, "%d\n", tlsPort);
2083 bp += n; bleft -= n;
2084 }
2085 else if (!strcmp("window", val) && Window)
2086 {n = snprintf(bp, bleft, "%d\n", Window);
2087 bp += n; bleft -= n;
2088 }
2089 else if (!strcmp("version", val))
2090 {n = snprintf(bp, bleft, "%s\n", XrdVSTRING);
2091 bp += n; bleft -= n;
2092 }
2093 else if (!strcmp("vnid", val))
2094 {const char *nidval = getenv("XRDCMSVNID");
2095 if (!nidval || !(*nidval)) nidval = "vnid";
2096 n = snprintf(bp, bleft, "%s\n", nidval);
2097 }
2098 else if (!strcmp("fattr", val))
2099 {n = snprintf(bp, bleft, "%s\n", usxParms);
2100 bp += n; bleft -= n;
2101 }
2102 else {n = strlen(val);
2103 if (bleft <= n) break;
2104 strcpy(bp, val); bp +=n; *bp = '\n'; bp++;
2105 bleft -= (n+1);
2106 }
2107 } while(bleft > 0 && (val = qcargs.GetToken()));
2108
2109// Make sure all ended well
2110//
2111 if (val)
2112 return Response.Send(kXR_ArgTooLong, "too many query config arguments.");
2113
2114// All done
2115//
2116 return Response.Send(buff, sizeof(buff) - bleft);
2117}
2118
2119/******************************************************************************/
2120/* d o _ Q c o n f C X */
2121/******************************************************************************/
2122
2123int XrdXrootdProtocol::do_QconfCX(XrdOucTokenizer &qcargs, char *val)
2124{
2125 extern XrdOucString *XrdXrootdCF;
2126 bool isCMSD = (*val == 'c');
2127
2128// Make sure there is nothing else following the token
2129//
2130 if ((val = qcargs.GetToken()))
2131 return Response.Send(kXR_ArgInvalid, "too many query config arguments.");
2132
2133// If this is a cms just return a null for now
2134//
2135 if (isCMSD) return Response.Send((void *)"\n", 2);
2136
2137// Display the xrootd configuration
2138//
2139 if (XrdXrootdCF && isTLS && getenv("XROOTD_QCFOK"))
2140 return Response.Send((void *)XrdXrootdCF->c_str(), XrdXrootdCF->length());
2141
2142// Respond with a null
2143//
2144 return Response.Send((void *)"\n", 2);
2145}
2146
2147/******************************************************************************/
2148/* d o _ Q f h */
2149/******************************************************************************/
2150
2151int XrdXrootdProtocol::do_Qfh()
2152{
2153 static XrdXrootdCallBack qryCB("query", XROOTD_MON_QUERY);
2154 XrdXrootdFHandle fh(Request.query.fhandle);
2155 XrdXrootdFile *fp;
2156 const char *fArg = 0, *qType = "";
2157 int rc;
2158 short qopt = (short)ntohs(Request.query.infotype);
2159
2160// Update misc stats count
2161//
2162 SI->Bump(SI->miscCnt);
2163
2164// Find the file object
2165//
2166 if (!FTab || !(fp = FTab->Get(fh.handle)))
2167 return Response.Send(kXR_FileNotOpen,
2168 "query does not refer to an open file");
2169
2170// The query is elegible for a deferred response, indicate we're ok with that
2171//
2172 fp->XrdSfsp->error.setErrCB(&qryCB, ReqID.getID());
2173
2174// Perform the appropriate query
2175//
2176 switch(qopt)
2177 {case kXR_Qopaqug: qType = "Qopaqug";
2178 fArg = (Request.query.dlen ? argp->buff : 0);
2179 rc = fp->XrdSfsp->fctl(SFS_FCTL_SPEC1,
2180 Request.query.dlen, fArg,
2181 CRED);
2182 break;
2183 case kXR_Qvisa: qType = "Qvisa";
2184 rc = fp->XrdSfsp->fctl(SFS_FCTL_STATV, 0,
2185 fp->XrdSfsp->error);
2186 break;
2187 default: return Response.Send(kXR_ArgMissing,
2188 "Required query argument not present");
2189 }
2190
2191// Preform the actual function
2192//
2193 TRACEP(FS, "fh=" <<fh.handle <<" query " <<qType <<" rc=" <<rc);
2194
2195// Return appropriately
2196//
2197 if (SFS_OK != rc)
2198 return fsError(rc, XROOTD_MON_QUERY, fp->XrdSfsp->error, 0, 0);
2199 return Response.Send();
2200}
2201
2202/******************************************************************************/
2203/* d o _ Q o p a q u e */
2204/******************************************************************************/
2205
2206int XrdXrootdProtocol::do_Qopaque(short qopt)
2207{
2208 static XrdXrootdCallBack qpqCB("query", XROOTD_MON_QUERY);
2209 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2210 XrdSfsFSctl myData;
2211 const char *Act, *AData;
2212 char *opaque;
2213 int fsctl_cmd, rc, dlen = Request.query.dlen;
2214
2215// Process unstructured as well as structured (path/opaque) requests
2216//
2217 if (qopt == kXR_Qopaque)
2218 {myData.Arg1 = argp->buff; myData.Arg1Len = dlen;
2219 myData.Arg2 = 0; myData.Arg2Len = 0;
2220 fsctl_cmd = SFS_FSCTL_PLUGIO;
2221 Act = " qopaque '"; AData = "...";
2222 } else {
2223 // Check for static routing (this falls under stat)
2224 //
2225 STATIC_REDIRECT(RD_stat);
2226
2227 // Prescreen the path
2228 //
2229 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Querying", argp->buff);
2230 if (!Squash(argp->buff)) return vpEmsg("Querying", argp->buff);
2231
2232 // Setup arguments
2233 //
2234 myData.Arg1 = argp->buff;
2235 myData.Arg1Len = (opaque ? opaque - argp->buff - 1 : dlen);
2236 myData.Arg2 = opaque;
2237 myData.Arg2Len = (opaque ? argp->buff + dlen - opaque : 0);
2238 fsctl_cmd = SFS_FSCTL_PLUGIN;
2239 Act = " qopaquf '"; AData = argp->buff;
2240 }
2241// The query is elegible for a deferred response, indicate we're ok with that
2242//
2243 myError.setErrCB(&qpqCB, ReqID.getID());
2244
2245// Preform the actual function using the supplied arguments
2246//
2247 rc = osFS->FSctl(fsctl_cmd, myData, myError, CRED);
2248 TRACEP(FS, "rc=" <<rc <<Act <<AData <<"'");
2249 if (rc == SFS_OK) return Response.Send("");
2250 return fsError(rc, 0, myError, 0, 0);
2251}
2252
2253/******************************************************************************/
2254/* d o _ Q s p a c e */
2255/******************************************************************************/
2256
2257int XrdXrootdProtocol::do_Qspace()
2258{
2259 static const int fsctl_cmd = SFS_FSCTL_STATLS;
2260 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2261 char *opaque;
2262 int n, rc;
2263
2264// Check for static routing
2265//
2266 STATIC_REDIRECT(RD_stat);
2267
2268// Prescreen the path
2269//
2270 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
2271 if (!Squash(argp->buff)) return vpEmsg("Stating", argp->buff);
2272
2273// Add back the opaque info
2274//
2275 if (opaque)
2276 {n = strlen(argp->buff); argp->buff[n] = '?';
2277 if ((argp->buff)+n != opaque-1)
2278 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
2279 }
2280
2281// Preform the actual function using the supplied logical FS name
2282//
2283 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
2284 TRACEP(FS, "rc=" <<rc <<" qspace '" <<argp->buff <<"'");
2285 if (rc == SFS_OK) return Response.Send("");
2286 return fsError(rc, XROOTD_MON_QUERY, myError, argp->buff, opaque);
2287}
2288
2289/******************************************************************************/
2290/* d o _ Q u e r y */
2291/******************************************************************************/
2292
2293int XrdXrootdProtocol::do_Query()
2294{
2295 short qopt = (short)ntohs(Request.query.infotype);
2296
2297// Perform the appropriate query
2298//
2299 switch(qopt)
2300 {case kXR_QStats: return SI->Stats(Response,
2301 (Request.header.dlen ? argp->buff : "a"));
2302 case kXR_Qcksum: return do_CKsum(0);
2303 case kXR_Qckscan: return do_CKsum(1);
2304 case kXR_Qconfig: return do_Qconf();
2305 case kXR_Qspace: return do_Qspace();
2306 case kXR_Qxattr: return do_Qxattr();
2307 case kXR_Qopaque:
2308 case kXR_Qopaquf: return do_Qopaque(qopt);
2309 case kXR_Qopaqug: return do_Qfh();
2310 case kXR_QPrep: return do_Prepare(true);
2311 default: break;
2312 }
2313
2314// Whatever we have, it's not valid
2315//
2316 return Response.Send(kXR_ArgInvalid,
2317 "Invalid information query type code");
2318}
2319
2320/******************************************************************************/
2321/* d o _ Q x a t t r */
2322/******************************************************************************/
2323
2324int XrdXrootdProtocol::do_Qxattr()
2325{
2326 static XrdXrootdCallBack statCB("stat", XROOTD_MON_QUERY);
2327 static const int fsctl_cmd = SFS_FSCTL_STATXA;
2328 int rc;
2329 char *opaque;
2330 XrdOucErrInfo myError(Link->ID,&statCB,ReqID.getID(),Monitor.Did,clientPV);
2331
2332// Check for static routing
2333//
2334 STATIC_REDIRECT(RD_stat);
2335
2336// Prescreen the path
2337//
2338 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
2339 if (!Squash(argp->buff)) return vpEmsg("Stating", argp->buff);
2340
2341// Add back opaque information is present
2342//
2343 if (opaque)
2344 {int n = strlen(argp->buff); argp->buff[n] = '?';
2345 if ((argp->buff)+n != opaque-1)
2346 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
2347 }
2348
2349// Preform the actual function
2350//
2351 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
2352 TRACEP(FS, "rc=" <<rc <<" qxattr " <<argp->buff);
2353 return fsError(rc, XROOTD_MON_QUERY, myError, argp->buff, opaque);
2354}
2355
2356/******************************************************************************/
2357/* d o _ R e a d */
2358/******************************************************************************/
2359
2360int XrdXrootdProtocol::do_Read()
2361{
2362 int pathID, retc;
2363 XrdXrootdFHandle fh(Request.read.fhandle);
2364 numReads++;
2365
2366// We first handle the pre-read list, if any. We do it this way because of
2367// a historical glitch in the protocol. One should really not piggy back a
2368// pre-read on top of a read, though it is allowed.
2369//
2370 if (!Request.header.dlen) pathID = 0;
2371 else if (do_ReadNone(retc, pathID)) return retc;
2372
2373// Unmarshall the data
2374//
2375 IO.IOLen = ntohl(Request.read.rlen);
2376 n2hll(Request.read.offset, IO.Offset);
2377
2378// Find the file object
2379//
2380 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
2381 return Response.Send(kXR_FileNotOpen,
2382 "read does not refer to an open file");
2383
2384// Trace and verify read length is not negative
2385//
2386 TRACEP(FSIO, pathID <<" fh=" <<fh.handle <<" read " <<IO.IOLen
2387 <<'@' <<IO.Offset);
2388 if ( IO.IOLen < 0) return Response.Send(kXR_ArgInvalid,
2389 "Read length is negative");
2390
2391// If we are monitoring, insert a read entry
2392//
2393 if (Monitor.InOut())
2394 Monitor.Agent->Add_rd(IO.File->Stats.FileID, Request.read.rlen,
2395 Request.read.offset);
2396
2397// Short circuit processing if read length is zero
2398//
2399 if (!IO.IOLen) return Response.Send();
2400
2401// There are many competing ways to accomplish a read. Pick the one we
2402// will use and if possible, do a fast dispatch.
2403//
2404 if (IO.File->isMMapped) IO.Mode = XrdXrootd::IOParms::useMMap;
2405 else if (IO.File->sfEnabled && !isTLS && IO.IOLen >= as_minsfsz
2406 && IO.Offset+IO.IOLen <= IO.File->Stats.fSize)
2408 else if (IO.File->AsyncMode && IO.IOLen >= as_miniosz
2409 && IO.Offset+IO.IOLen <= IO.File->Stats.fSize+as_seghalf
2411 {XrdXrootdProtocol *pP;
2412 XrdXrootdNormAio *aioP=0;
2413
2414 if (!pathID) pP = this;
2415 else {if (!(pP = VerifyStream(retc, pathID, false))) return retc;
2416 if (pP->linkAioReq >= as_maxperlnk) pP = 0;
2417 }
2418 if (pP)
2419 {// Use of TmpRsp here is to avoid modying pP. It is built
2420 // to contain the correct streamid for this request and the
2421 // right Link for the pathID. It's used by Alloc to call
2422 // XrdXrootdAioTask::Init which in turn makes a copy of TmpRsp
2423 // to its own response object and also keeps the Link pointer.
2424 XrdXrootdResponse TmpRsp;
2425 TmpRsp = Response;
2426 TmpRsp.Set(pP->Link);
2427 aioP = XrdXrootdNormAio::Alloc(pP,TmpRsp,IO.File);
2428 }
2429 if (aioP)
2430 {if (!IO.File->aioFob) IO.File->aioFob = new XrdXrootdAioFob;
2431 aioP->Read(IO.Offset, IO.IOLen);
2432 return 0;
2433 }
2434 SI->AsyncRej++;
2436 }
2437 else IO.Mode = XrdXrootd::IOParms::useBasic;
2438
2439// See if an alternate path is required, offload the read
2440//
2441 if (pathID) return do_Offload(&XrdXrootdProtocol::do_ReadAll, pathID);
2442
2443// Now read all of the data (do pre-reads first)
2444//
2445 return do_ReadAll();
2446}
2447
2448/******************************************************************************/
2449/* d o _ R e a d A l l */
2450/******************************************************************************/
2451
2452// IO.File = file to be read
2453// IO.Offset = Offset at which to read
2454// IO.IOLen = Number of bytes to read from file and write to socket
2455
2456int XrdXrootdProtocol::do_ReadAll()
2457{
2458 int rc, xframt, Quantum = (IO.IOLen > maxBuffsz ? maxBuffsz : IO.IOLen);
2459 char *buff;
2460
2461// If this file is memory mapped, short ciruit all the logic and immediately
2462// transfer the requested data to minimize latency.
2463//
2464 if (IO.Mode == XrdXrootd::IOParms::useMMap)
2465 {if (IO.Offset >= IO.File->Stats.fSize) return Response.Send();
2466 if (IO.Offset+IO.IOLen <= IO.File->Stats.fSize)
2467 {IO.File->Stats.rdOps(IO.IOLen);
2468 return Response.Send(IO.File->mmAddr+IO.Offset, IO.IOLen);
2469 }
2470 xframt = IO.File->Stats.fSize -IO.Offset;
2471 IO.File->Stats.rdOps(xframt);
2472 return Response.Send(IO.File->mmAddr+IO.Offset, xframt);
2473 }
2474
2475// If we are sendfile enabled, then just send the file if possible
2476//
2477 if (IO.Mode == XrdXrootd::IOParms::useSF)
2478 {IO.File->Stats.rdOps(IO.IOLen);
2479 if (IO.File->fdNum >= 0)
2480 return Response.Send(IO.File->fdNum, IO.Offset, IO.IOLen);
2481 rc = IO.File->XrdSfsp->SendData((XrdSfsDio *)this, IO.Offset, IO.IOLen);
2482 if (rc == SFS_OK)
2483 {if (!IO.IOLen) return 0;
2484 if (IO.IOLen < 0) return -1; // Otherwise retry using read()
2485 } else return fsError(rc, 0, IO.File->XrdSfsp->error, 0, 0);
2486 }
2487
2488// Make sure we have a large enough buffer
2489//
2490 if (!argp || Quantum < halfBSize || Quantum > argp->bsize)
2491 {if ((rc = getBuff(1, Quantum)) <= 0) return rc;}
2492 else if (hcNow < hcNext) hcNow++;
2493 buff = argp->buff;
2494
2495// Now read all of the data. For statistics, we need to record the orignal
2496// amount of the request even if we really do not get to read that much!
2497//
2498 IO.File->Stats.rdOps(IO.IOLen);
2499 do {if ((xframt = IO.File->XrdSfsp->read(IO.Offset, buff, Quantum)) <= 0) break;
2500 if (xframt >= IO.IOLen) return Response.Send(buff, xframt);
2501 if (Response.Send(kXR_oksofar, buff, xframt) < 0) return -1;
2502 IO.Offset += xframt; IO.IOLen -= xframt;
2503 if (IO.IOLen < Quantum) Quantum = IO.IOLen;
2504 } while(IO.IOLen);
2505
2506// Determine why we ended here
2507//
2508 if (xframt == 0) return Response.Send();
2509 return fsError(xframt, 0, IO.File->XrdSfsp->error, 0, 0);
2510}
2511
2512/******************************************************************************/
2513/* d o _ R e a d N o n e */
2514/******************************************************************************/
2515
2516int XrdXrootdProtocol::do_ReadNone(int &retc, int &pathID)
2517{
2518 XrdXrootdFHandle fh;
2519 int ralsz = Request.header.dlen;
2520 struct read_args *rargs=(struct read_args *)(argp->buff);
2521 struct readahead_list *ralsp = (readahead_list *)(rargs+1);
2522
2523// Return the pathid
2524//
2525 pathID = static_cast<int>(rargs->pathid);
2526 if ((ralsz -= sizeof(read_args)) <= 0) return 0;
2527
2528// Make sure that we have a proper pre-read list
2529//
2530 if (ralsz%sizeof(readahead_list))
2531 {Response.Send(kXR_ArgInvalid, "Invalid length for read ahead list");
2532 return 1;
2533 }
2534
2535// Run down the pre-read list
2536//
2537 while(ralsz > 0)
2538 {IO.IOLen = ntohl(ralsp->rlen);
2539 n2hll(ralsp->offset, IO.Offset);
2540 memcpy((void *)&fh.handle, (const void *)ralsp->fhandle,
2541 sizeof(fh.handle));
2542 TRACEP(FSIO, "fh="<<fh.handle<<" read "<<IO.IOLen<<'@'<<IO.Offset);
2543 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
2544 {retc = Response.Send(kXR_FileNotOpen,
2545 "preread does not refer to an open file");
2546 return 1;
2547 }
2548 IO.File->XrdSfsp->read(IO.Offset, IO.IOLen);
2549 ralsz -= sizeof(struct readahead_list);
2550 ralsp++;
2551 numReads++;
2552 };
2553
2554// All done
2555//
2556 return 0;
2557}
2558
2559/******************************************************************************/
2560/* d o _ R e a d V */
2561/******************************************************************************/
2562
2563int XrdXrootdProtocol::do_ReadV()
2564{
2565// This will read multiple buffers at the same time in an attempt to avoid
2566// the latency in a network. The information with the offsets and lengths
2567// of the information to read is passed as a data buffer... then we decode
2568// it and put all the individual buffers in a single one it's up to the
2569// client to interpret it. Code originally developed by Leandro Franco, CERN.
2570// The readv file system code originally added by Brian Bockelman, UNL.
2571//
2572 const int hdrSZ = sizeof(readahead_list);
2573 struct XrdOucIOVec rdVec[XrdProto::maxRvecsz+1];
2574 struct readahead_list *raVec, respHdr;
2575 long long totSZ;
2576 XrdSfsXferSize rdVAmt, rdVXfr, xfrSZ = 0;
2577 int rdVBeg, rdVBreak, rdVNow, rdVNum, rdVecNum;
2578 int currFH, i, k, Quantum, Qleft, rdVecLen = Request.header.dlen;
2579 int rvMon = Monitor.InOut();
2580 int ioMon = (rvMon > 1);
2581 char *buffp, vType = (ioMon ? XROOTD_MON_READU : XROOTD_MON_READV);
2582
2583// Compute number of elements in the read vector and make sure we have no
2584// partial elements.
2585//
2586 rdVecNum = rdVecLen / sizeof(readahead_list);
2587 if ( (rdVecNum <= 0) || (rdVecNum*hdrSZ != rdVecLen) )
2588 return Response.Send(kXR_ArgInvalid, "Read vector is invalid");
2589
2590// Make sure that we can copy the read vector to our local stack. We must impose
2591// a limit on it's size. We do this to be able to reuse the data buffer to
2592// prevent cross-cpu memory cache synchronization.
2593//
2594 if (rdVecNum > XrdProto::maxRvecsz)
2595 return Response.Send(kXR_ArgTooLong, "Read vector is too long");
2596
2597// So, now we account for the number of readv requests and total segments
2598//
2599 numReadV++; numSegsV += rdVecNum;
2600
2601// Run down the list and compute the total size of the read. No individual
2602// read may be greater than the maximum transfer size. We also use this loop
2603// to copy the read ahead list to our readv vector for later processing.
2604//
2605 raVec = (readahead_list *)argp->buff;
2606 totSZ = rdVecLen; Quantum = maxReadv_ior;
2607 for (i = 0; i < rdVecNum; i++)
2608 {totSZ += (rdVec[i].size = ntohl(raVec[i].rlen));
2609 if (rdVec[i].size < 0) return Response.Send(kXR_ArgInvalid,
2610 "Readv length is negative");
2611 if (rdVec[i].size > Quantum) return Response.Send(kXR_NoMemory,
2612 "Single readv transfer is too large");
2613 rdVec[i].offset = ntohll(raVec[i].offset);
2614 memcpy(&rdVec[i].info, raVec[i].fhandle, sizeof(int));
2615 }
2616
2617// Now add an extra dummy element to force flushing of the read vector.
2618//
2619 rdVec[i].offset = -1;
2620 rdVec[i].size = 0;
2621 rdVec[i].info = -1;
2622 rdVBreak = rdVecNum;
2623 rdVecNum++;
2624
2625// We limit the total size of the read to be 2GB for convenience
2626//
2627 if (totSZ > 0x80000000LL)
2628 return Response.Send(kXR_NoMemory, "Total readv transfer is too large");
2629
2630// Calculate the transfer unit which will be the smaller of the maximum
2631// transfer unit and the actual amount we need to transfer.
2632//
2633 Quantum = totSZ < maxTransz ? totSZ : maxTransz;
2634
2635// Now obtain the right size buffer
2636//
2637 if ((Quantum < halfBSize && Quantum > 1024) || Quantum > argp->bsize)
2638 {if ((k = getBuff(1, Quantum)) <= 0) return k;}
2639 else if (hcNow < hcNext) hcNow++;
2640
2641// Check that we really have at least one file open. This needs to be done
2642// only once as this code runs in the control thread.
2643//
2644 if (!FTab) return Response.Send(kXR_FileNotOpen,
2645 "readv does not refer to an open file");
2646
2647// Preset the previous and current file handle to be the handle of the first
2648// element and make sure the file is actually open.
2649//
2650 currFH = rdVec[0].info;
2651 memcpy(respHdr.fhandle, &currFH, sizeof(respHdr.fhandle));
2652 if (!(IO.File = FTab->Get(currFH))) return Response.Send(kXR_FileNotOpen,
2653 "readv does not refer to an open file");
2654
2655// Setup variables for running through the list.
2656//
2657 Qleft = Quantum; buffp = argp->buff; rvSeq++;
2658 rdVBeg = rdVNow = 0; rdVXfr = rdVAmt = 0;
2659
2660// Now run through the elements
2661//
2662 for (i = 0; i < rdVecNum; i++)
2663 {if (rdVec[i].info != currFH)
2664 {xfrSZ = IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2665 if (xfrSZ != rdVAmt) break;
2666 rdVNum = i - rdVBeg; rdVXfr += rdVAmt;
2667 IO.File->Stats.rvOps(rdVXfr, rdVNum);
2668 if (rvMon)
2669 {Monitor.Agent->Add_rv(IO.File->Stats.FileID, htonl(rdVXfr),
2670 htons(rdVNum), rvSeq, vType);
2671 if (ioMon) for (k = rdVBeg; k < i; k++)
2672 Monitor.Agent->Add_rd(IO.File->Stats.FileID,
2673 htonl(rdVec[k].size), htonll(rdVec[k].offset));
2674 }
2675 rdVXfr = rdVAmt = 0;
2676 if (i == rdVBreak) break;
2677 rdVBeg = rdVNow = i; currFH = rdVec[i].info;
2678 memcpy(respHdr.fhandle, &currFH, sizeof(respHdr.fhandle));
2679 if (!(IO.File = FTab->Get(currFH)))
2680 return Response.Send(kXR_FileNotOpen,
2681 "readv does not refer to an open file");
2682 }
2683
2684 if (Qleft < (rdVec[i].size + hdrSZ))
2685 {if (rdVAmt)
2686 {xfrSZ = IO.File->XrdSfsp->readv(&rdVec[rdVNow], i-rdVNow);
2687 if (xfrSZ != rdVAmt) break;
2688 }
2689 if (Response.Send(kXR_oksofar,argp->buff,Quantum-Qleft) < 0)
2690 return -1;
2691 Qleft = Quantum;
2692 buffp = argp->buff;
2693 rdVNow = i; rdVXfr += rdVAmt; rdVAmt = 0;
2694 }
2695
2696 xfrSZ = rdVec[i].size; rdVAmt += xfrSZ;
2697 respHdr.rlen = htonl(xfrSZ);
2698 respHdr.offset = htonll(rdVec[i].offset);
2699 memcpy(buffp, &respHdr, hdrSZ);
2700 rdVec[i].data = buffp + hdrSZ;
2701 buffp += (xfrSZ+hdrSZ); Qleft -= (xfrSZ+hdrSZ);
2702 TRACEP(FSIO,"fh=" <<currFH<<" readV "<< xfrSZ <<'@'<<rdVec[i].offset);
2703 }
2704
2705// Check if we have an error here. This is indicated when rdVAmt is not zero.
2706//
2707 if (rdVAmt)
2708 {if (xfrSZ >= 0)
2709 {xfrSZ = SFS_ERROR;
2710 IO.File->XrdSfsp->error.setErrInfo(-ENODATA,"readv past EOF");
2711 }
2712 return fsError(xfrSZ, 0, IO.File->XrdSfsp->error, 0, 0);
2713 }
2714
2715// All done, return result of the last segment or just zero
2716//
2717 return (Quantum != Qleft ? Response.Send(argp->buff, Quantum-Qleft) : 0);
2718}
2719
2720/******************************************************************************/
2721/* d o _ R m */
2722/******************************************************************************/
2723
2724int XrdXrootdProtocol::do_Rm()
2725{
2726 int rc;
2727 char *opaque;
2728 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2729
2730// Check for static routing
2731//
2732 STATIC_REDIRECT(RD_rm);
2733
2734// Prescreen the path
2735//
2736 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Removing", argp->buff);
2737 if (!Squash(argp->buff)) return vpEmsg("Removing", argp->buff);
2738
2739// Preform the actual function
2740//
2741 rc = osFS->rem(argp->buff, myError, CRED, opaque);
2742 TRACEP(FS, "rc=" <<rc <<" rm " <<argp->buff);
2743 if (SFS_OK == rc) return Response.Send();
2744
2745// An error occurred
2746//
2747 return fsError(rc, XROOTD_MON_RM, myError, argp->buff, opaque);
2748}
2749
2750/******************************************************************************/
2751/* d o _ R m d i r */
2752/******************************************************************************/
2753
2754int XrdXrootdProtocol::do_Rmdir()
2755{
2756 int rc;
2757 char *opaque;
2758 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2759
2760// Check for static routing
2761//
2762 STATIC_REDIRECT(RD_rmdir);
2763
2764// Prescreen the path
2765//
2766 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Removing", argp->buff);
2767 if (!Squash(argp->buff)) return vpEmsg("Removing", argp->buff);
2768
2769// Preform the actual function
2770//
2771 rc = osFS->remdir(argp->buff, myError, CRED, opaque);
2772 TRACEP(FS, "rc=" <<rc <<" rmdir " <<argp->buff);
2773 if (SFS_OK == rc) return Response.Send();
2774
2775// An error occurred
2776//
2777 return fsError(rc, XROOTD_MON_RMDIR, myError, argp->buff, opaque);
2778}
2779
2780/******************************************************************************/
2781/* d o _ S e t */
2782/******************************************************************************/
2783
2784int XrdXrootdProtocol::do_Set()
2785{
2786 XrdOucTokenizer setargs(argp->buff);
2787 char *val, *rest;
2788
2789// Get the first argument
2790//
2791 if (!setargs.GetLine() || !(val = setargs.GetToken(&rest)))
2792 return Response.Send(kXR_ArgMissing, "set argument not specified.");
2793
2794// Trace this set
2795//
2796 TRACEP(DEBUG, "set " <<val <<' ' <<rest);
2797
2798// Now determine what the user wants to set
2799//
2800 if (!strcmp("appid", val))
2801 {while(*rest && *rest == ' ') rest++;
2802 eDest.Emsg("Xeq", Link->ID, "appid", rest);
2803 return Response.Send();
2804 }
2805 else if (!strcmp("monitor", val)) return do_Set_Mon(setargs);
2806 else if (!strcmp("cache", val)) return do_Set_Cache(setargs);
2807
2808// All done
2809//
2810 return Response.Send(kXR_ArgInvalid, "invalid set parameter");
2811}
2812
2813/******************************************************************************/
2814/* d o _ S e t _ C a c h e */
2815/******************************************************************************/
2816
2817// Process: set cache <cmd> <args>
2818
2819int XrdXrootdProtocol::do_Set_Cache(XrdOucTokenizer &setargs)
2820{
2821 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
2822 XrdSfsFSctl myData;
2823 char *cmd, *cargs, *opaque = nullptr;
2824 const char *myArgs[2];
2825
2826// This set is valid only if we implement a cache
2827//
2828 if ((fsFeatures & XrdSfs::hasCACH) == 0)
2829 return Response.Send(kXR_ArgInvalid, "invalid set parameter");
2830
2831// Get the command and argument
2832//
2833 if (!(cmd = setargs.GetToken(&cargs)))
2834 return Response.Send(kXR_ArgMissing,"set cache argument not specified.");
2835
2836// Prescreen the path if the next token starts with a slash
2837//
2838 if (cargs && *cargs == '/')
2839 {if (rpCheck(cargs, &opaque)) return rpEmsg("Setting", cargs);
2840 if (!Squash(cargs)) return vpEmsg("Setting", cargs);
2841 myData.ArgP = myArgs; myData.Arg2Len = -2;
2842 myArgs[0] = cargs;
2843 myArgs[1] = opaque;
2844 } else {
2845 myData.Arg2 = opaque; myData.Arg2Len = (opaque ? strlen(opaque) : 0);
2846 }
2847 myData.Arg1 = cmd; myData.Arg1Len = strlen(cmd);
2848
2849// Preform the actual function using the supplied arguments
2850//
2851 int rc = osFS->FSctl(SFS_FSCTL_PLUGXC, myData, myError, CRED);
2852 TRACEP(FS, "rc=" <<rc <<"set cache " <<myData.Arg1 <<' ' <<cargs);
2853 if (rc == SFS_OK) return Response.Send("");
2854 return fsError(rc, 0, myError, 0, 0);
2855}
2856
2857/******************************************************************************/
2858/* d o _ S e t _ M o n */
2859/******************************************************************************/
2860
2861// Process: set monitor {off | on} {[appid] | info [info]}
2862
2863int XrdXrootdProtocol::do_Set_Mon(XrdOucTokenizer &setargs)
2864{
2865 char *val, *appid;
2866 kXR_unt32 myseq = 0;
2867
2868// Get the first argument
2869//
2870 if (!(val = setargs.GetToken(&appid)))
2871 return Response.Send(kXR_ArgMissing,"set monitor argument not specified.");
2872
2873// For info requests, nothing changes. However, info events must have been
2874// enabled for us to record them. Route the information via the static
2875// monitor entry, since it knows how to forward the information.
2876//
2877 if (!strcmp(val, "info"))
2878 {if (appid && Monitor.Info())
2879 {while(*appid && *appid == ' ') appid++;
2880 if (strlen(appid) > 1024) appid[1024] = '\0';
2881 if (*appid) myseq = Monitor.MapInfo(appid);
2882 }
2883 return Response.Send((void *)&myseq, sizeof(myseq));
2884 }
2885
2886// Determine if on do appropriate processing
2887//
2888 if (!strcmp(val, "on"))
2889 {Monitor.Enable();
2890 if (appid && Monitor.InOut())
2891 {while(*appid && *appid == ' ') appid++;
2892 if (*appid) Monitor.Agent->appID(appid);
2893 }
2894 if (!Monitor.Did && Monitor.Logins()) MonAuth();
2895 return Response.Send();
2896 }
2897
2898// Determine if off and do appropriate processing
2899//
2900 if (!strcmp(val, "off"))
2901 {if (appid && Monitor.InOut())
2902 {while(*appid && *appid == ' ') appid++;
2903 if (*appid) Monitor.Agent->appID(appid);
2904 }
2905 Monitor.Disable();
2906 return Response.Send();
2907 }
2908
2909// Improper request
2910//
2911 return Response.Send(kXR_ArgInvalid, "invalid set monitor argument");
2912}
2913
2914/******************************************************************************/
2915/* d o _ S t a t */
2916/******************************************************************************/
2917
2918int XrdXrootdProtocol::do_Stat()
2919{
2920 static XrdXrootdCallBack statCB("stat", XROOTD_MON_STAT);
2921 static const int fsctl_cmd = SFS_FSCTL_STATFS;
2922 bool doDig;
2923 int rc;
2924 char *opaque, xxBuff[1024];
2925 struct stat buf;
2926 XrdOucErrInfo myError(Link->ID,&statCB,ReqID.getID(),Monitor.Did,clientPV);
2927
2928// Update misc stats count
2929//
2930 SI->Bump(SI->miscCnt);
2931
2932// The stat request may refer to an open file handle. So, screen this out.
2933//
2934 if (!argp || !Request.header.dlen)
2935 {XrdXrootdFile *fp;
2936 XrdXrootdFHandle fh(Request.stat.fhandle);
2937 if (Request.stat.options & kXR_vfs)
2938 {Response.Send(kXR_ArgMissing, "Required argument not present");
2939 return 0;
2940 }
2941 if (!FTab || !(fp = FTab->Get(fh.handle)))
2942 return Response.Send(kXR_FileNotOpen,
2943 "stat does not refer to an open file");
2944 rc = fp->XrdSfsp->stat(&buf);
2945 TRACEP(FS, "fh=" <<fh.handle <<" stat rc=" <<rc);
2946 if (SFS_OK == rc) return Response.Send(xxBuff,
2947 StatGen(buf,xxBuff,sizeof(xxBuff)));
2948 return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
2949 }
2950
2951// Check if we are handling a dig type path
2952//
2953 doDig = (digFS && SFS_LCLROOT(argp->buff));
2954
2955// Check for static routing
2956//
2957 if (!doDig) {STATIC_REDIRECT(RD_stat);}
2958
2959// Prescreen the path
2960//
2961 if (rpCheck(argp->buff, &opaque)) return rpEmsg("Stating", argp->buff);
2962 if (!doDig && !Squash(argp->buff))return vpEmsg("Stating", argp->buff);
2963
2964// Preform the actual function, we may been to add back the opaque info
2965//
2966 if (Request.stat.options & kXR_vfs)
2967 {if (opaque)
2968 {int n = strlen(argp->buff); argp->buff[n] = '?';
2969 if ((argp->buff)+n != opaque-1)
2970 memmove(&argp->buff[n+1], opaque, strlen(opaque)+1);
2971 }
2972 rc = osFS->fsctl(fsctl_cmd, argp->buff, myError, CRED);
2973 TRACEP(FS, "rc=" <<rc <<" statfs " <<argp->buff);
2974 if (rc == SFS_OK) Response.Send("");
2975 } else {
2976 if (doDig) rc = digFS->stat(argp->buff, &buf, myError, CRED, opaque);
2977 else rc = osFS->stat(argp->buff, &buf, myError, CRED, opaque);
2978 TRACEP(FS, "rc=" <<rc <<" stat " <<argp->buff);
2979 if (rc == SFS_OK) return Response.Send(xxBuff,
2980 StatGen(buf,xxBuff,sizeof(xxBuff)));
2981 }
2982 return fsError(rc, (doDig ? 0 : XROOTD_MON_STAT),myError,argp->buff,opaque);
2983}
2984
2985/******************************************************************************/
2986/* d o _ S t a t x */
2987/******************************************************************************/
2988
2989int XrdXrootdProtocol::do_Statx()
2990{
2991 static XrdXrootdCallBack statxCB("xstat", XROOTD_MON_STAT);
2992 int rc;
2993 char *path, *opaque, *respinfo = argp->buff;
2994 mode_t mode;
2995 XrdOucErrInfo myError(Link->ID,&statxCB,ReqID.getID(),Monitor.Did,clientPV);
2996 XrdOucTokenizer pathlist(argp->buff);
2997
2998// Check for static routing
2999//
3000 STATIC_REDIRECT(RD_stat);
3001
3002// Cycle through all of the paths in the list
3003//
3004 while((path = pathlist.GetLine()))
3005 {if (rpCheck(path, &opaque)) return rpEmsg("Stating", path);
3006 if (!Squash(path)) return vpEmsg("Stating", path);
3007 rc = osFS->stat(path, mode, myError, CRED, opaque);
3008 TRACEP(FS, "rc=" <<rc <<" stat " <<path);
3009 if (rc != SFS_OK)
3010 return fsError(rc, XROOTD_MON_STAT, myError, path, opaque);
3011 else {if (mode == (mode_t)-1) *respinfo = (char)kXR_offline;
3012 else if (S_ISDIR(mode)) *respinfo = (char)kXR_isDir;
3013 else *respinfo = (char)kXR_file;
3014 }
3015 respinfo++;
3016 }
3017
3018// Return result
3019//
3020 return Response.Send(argp->buff, respinfo-argp->buff);
3021}
3022
3023/******************************************************************************/
3024/* d o _ S y n c */
3025/******************************************************************************/
3026
3027int XrdXrootdProtocol::do_Sync()
3028{
3029 static XrdXrootdCallBack syncCB("sync", 0);
3030 int rc;
3031 XrdXrootdFile *fp;
3032 XrdXrootdFHandle fh(Request.sync.fhandle);
3033
3034// Keep Statistics
3035//
3036 SI->Bump(SI->syncCnt);
3037
3038// Find the file object
3039//
3040 if (!FTab || !(fp = FTab->Get(fh.handle)))
3041 return Response.Send(kXR_FileNotOpen,"sync does not refer to an open file");
3042
3043// The sync is elegible for a deferred response, indicate we're ok with that
3044//
3045 fp->XrdSfsp->error.setErrCB(&syncCB, ReqID.getID());
3046
3047// Sync the file
3048//
3049 rc = fp->XrdSfsp->sync();
3050 TRACEP(FS, "fh=" <<fh.handle <<" sync rc=" <<rc);
3051 if (SFS_OK != rc) return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
3052
3053// Respond that all went well
3054//
3055 return Response.Send();
3056}
3057
3058/******************************************************************************/
3059/* d o _ T r u n c a t e */
3060/******************************************************************************/
3061
3062int XrdXrootdProtocol::do_Truncate()
3063{
3064 static XrdXrootdCallBack truncCB("trunc", 0);
3065 XrdXrootdFile *fp;
3066 XrdXrootdFHandle fh(Request.truncate.fhandle);
3067 long long theOffset;
3068 int rc;
3069
3070// Unmarshall the data
3071//
3072 n2hll(Request.truncate.offset, theOffset);
3073
3074// Check if this is a truncate for an open file (no path given)
3075//
3076 if (!Request.header.dlen)
3077 {
3078 // Update misc stats count
3079 //
3080 SI->Bump(SI->miscCnt);
3081
3082 // Find the file object
3083 //
3084 if (!FTab || !(fp = FTab->Get(fh.handle)))
3085 return Response.Send(kXR_FileNotOpen,
3086 "trunc does not refer to an open file");
3087
3088 // Truncate the file (it is eligible for async callbacks)
3089 //
3090 fp->XrdSfsp->error.setErrCB(&truncCB, ReqID.getID());
3091 rc = fp->XrdSfsp->truncate(theOffset);
3092 TRACEP(FS, "fh=" <<fh.handle <<" trunc rc=" <<rc <<" sz=" <<theOffset);
3093 if (SFS_OK != rc) return fsError(rc, 0, fp->XrdSfsp->error, 0, 0);
3094
3095 } else {
3096
3097 XrdOucErrInfo myError(Link->ID, Monitor.Did, clientPV);
3098 char *opaque;
3099
3100 // Check for static routing
3101 //
3102 STATIC_REDIRECT(RD_trunc);
3103
3104 // Verify the path and extract out the opaque information
3105 //
3106 if (rpCheck(argp->buff,&opaque)) return rpEmsg("Truncating",argp->buff);
3107 if (!Squash(argp->buff)) return vpEmsg("Truncating",argp->buff);
3108
3109 // Preform the actual function
3110 //
3111 rc = osFS->truncate(argp->buff, (XrdSfsFileOffset)theOffset, myError,
3112 CRED, opaque);
3113 TRACEP(FS, "rc=" <<rc <<" trunc " <<theOffset <<' ' <<argp->buff);
3114 if (SFS_OK != rc)
3115 return fsError(rc, XROOTD_MON_TRUNC, myError, argp->buff, opaque);
3116 }
3117
3118// Respond that all went well
3119//
3120 return Response.Send();
3121}
3122
3123/******************************************************************************/
3124/* d o _ W r i t e */
3125/******************************************************************************/
3126
3127int XrdXrootdProtocol::do_Write()
3128{
3129 int pathID;
3130 XrdXrootdFHandle fh(Request.write.fhandle);
3131 numWrites++;
3132
3133// Unmarshall the data
3134//
3135 IO.IOLen = Request.header.dlen;
3136 n2hll(Request.write.offset, IO.Offset);
3137 pathID = static_cast<int>(Request.write.pathid);
3138
3139// Find the file object. We will drain socket data on the control path only!
3140// .
3141 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
3142 {IO.File = 0;
3143 return do_WriteNone(pathID);
3144 }
3145
3146// Trace and verify that length is not negative
3147//
3148 TRACEP(FSIO, pathID<<" fh="<<fh.handle<<" write "<<IO.IOLen<<'@'<<IO.Offset);
3149 if ( IO.IOLen < 0) return Response.Send(kXR_ArgInvalid,
3150 "Write length is negative");
3151
3152// If we are monitoring, insert a write entry
3153//
3154 if (Monitor.InOut())
3155 Monitor.Agent->Add_wr(IO.File->Stats.FileID, Request.write.dlen,
3156 Request.write.offset);
3157
3158// If zero length write, simply return
3159//
3160 if (!IO.IOLen) return Response.Send();
3161 IO.File->Stats.wrOps(IO.IOLen); // Optimistically correct
3162
3163// If async write allowed and it is a true write request (e.g. not chkpoint) and
3164// current conditions permit async; schedule the write to occur asynchronously
3165//
3166 if (IO.File->AsyncMode && Request.header.requestid == kXR_write
3167 && !as_syncw && IO.IOLen >= as_miniosz && srvrAioOps < as_maxpersrv)
3168 {if (myStalls < as_maxstalls)
3169 {if (pathID) return do_Offload(&XrdXrootdProtocol::do_WriteAio,pathID);
3170 return do_WriteAio();
3171 }
3172 SI->AsyncRej++;
3173 myStalls--;
3174 }
3175
3176// See if an alternate path is required
3177//
3178 if (pathID) return do_Offload(&XrdXrootdProtocol::do_WriteAll, pathID);
3179
3180// Just to the i/o now
3181//
3182 return do_WriteAll();
3183}
3184
3185/******************************************************************************/
3186/* d o _ W r i t e A i o */
3187/******************************************************************************/
3188
3189// IO.File = file to be written
3190// IO.Offset = Offset at which to write
3191// IO.IOLen = Number of bytes to read from socket and write to file
3192
3193int XrdXrootdProtocol::do_WriteAio()
3194{
3195 XrdXrootdNormAio *aioP;
3196
3197// Allocate an aio request object if client hasn't exceeded the link limit
3198//
3200 || !(aioP = XrdXrootdNormAio::Alloc(this, Response, IO.File)))
3201 {SI->AsyncRej++;
3202 if (myStalls > 0) myStalls--;
3203 return do_WriteAll();
3204 }
3205
3206// Issue the write request
3207//
3208 return aioP->Write(IO.Offset, IO.IOLen);
3209}
3210
3211/******************************************************************************/
3212/* d o _ W r i t e A l l */
3213/******************************************************************************/
3214
3215// IO.File = file to be written
3216// IO.Offset = Offset at which to write
3217// IO.IOLen = Number of bytes to read from socket and write to file
3218
3219int XrdXrootdProtocol::do_WriteAll()
3220{
3221 int rc, Quantum = (IO.IOLen > maxBuffsz ? maxBuffsz : IO.IOLen);
3222
3223// Make sure we have a large enough buffer
3224//
3225 if (!argp || Quantum < halfBSize || Quantum > argp->bsize)
3226 {if ((rc = getBuff(0, Quantum)) <= 0) return rc;}
3227 else if (hcNow < hcNext) hcNow++;
3228
3229// Now write all of the data (XrdXrootdProtocol.C defines getData())
3230//
3231 while(IO.IOLen > 0)
3232 {if ((rc = getData("data", argp->buff, Quantum)))
3233 {if (rc > 0)
3234 {Resume = &XrdXrootdProtocol::do_WriteCont;
3235 myBlast = Quantum;
3236 }
3237 return rc;
3238 }
3239 if ((rc = IO.File->XrdSfsp->write(IO.Offset, argp->buff, Quantum)) < 0)
3240 {IO.IOLen = IO.IOLen-Quantum; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3241 return do_WriteNone();
3242 }
3243 IO.Offset += Quantum; IO.IOLen -= Quantum;
3244 if (IO.IOLen < Quantum) Quantum = IO.IOLen;
3245 }
3246
3247// All done
3248//
3249 return Response.Send();
3250}
3251
3252/******************************************************************************/
3253/* d o _ W r i t e C o n t */
3254/******************************************************************************/
3255
3256// IO.File = file to be written
3257// IO.Offset = Offset at which to write
3258// IO.IOLen = Number of bytes to read from socket and write to file
3259// myBlast = Number of bytes already read from the socket
3260
3261int XrdXrootdProtocol::do_WriteCont()
3262{
3263 int rc;
3264
3265// Write data that was finaly finished comming in
3266//
3267 if ((rc = IO.File->XrdSfsp->write(IO.Offset, argp->buff, myBlast)) < 0)
3268 {IO.IOLen = IO.IOLen-myBlast; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3269 return do_WriteNone();
3270 }
3271 IO.Offset += myBlast; IO.IOLen -= myBlast;
3272
3273// See if we need to finish this request in the normal way
3274//
3275 if (IO.IOLen > 0) return do_WriteAll();
3276 return Response.Send();
3277}
3278
3279/******************************************************************************/
3280/* d o _ W r i t e N o n e */
3281/******************************************************************************/
3282
3283int XrdXrootdProtocol::do_WriteNone()
3284{
3285 char *buff, dbuff[4096];
3286 int rlen, blen;
3287
3288// Determine which buffer we will use
3289//
3290 if (argp && argp->bsize > (int)sizeof(dbuff))
3291 {buff = argp->buff;
3292 blen = argp->bsize;
3293 } else {
3294 buff = dbuff;
3295 blen = sizeof(dbuff);
3296 }
3297 if (IO.IOLen < blen) blen = IO.IOLen;
3298
3299// Discard any data being transmitted
3300//
3301 TRACEP(REQ, "discarding " <<IO.IOLen <<" bytes");
3302 while(IO.IOLen > 0)
3303 {rlen = Link->Recv(buff, blen, readWait);
3304 if (rlen < 0) return Link->setEtext("link read error");
3305 IO.IOLen -= rlen;
3306 if (rlen < blen)
3307 {myBlen = 0;
3308 Resume = &XrdXrootdProtocol::do_WriteNone;
3309 return 1;
3310 }
3311 if (IO.IOLen < blen) blen = IO.IOLen;
3312 }
3313
3314// Send final message
3315//
3316 return do_WriteNoneMsg();
3317}
3318
3319/******************************************************************************/
3320
3321int XrdXrootdProtocol::do_WriteNone(int pathID, XErrorCode ec,
3322 const char *emsg)
3323{
3324// We can't recover when the data is arriving on a foriegn bound path as there
3325// no way to properly drain the socket. So, we terminate the connection.
3326//
3327 if (pathID != PathID)
3328 {if (ec && emsg) Response.Send(ec, emsg);
3329 else do_WriteNoneMsg();
3330 return Link->setEtext("write protocol violation");
3331 }
3332
3333// Set error code if present
3334//
3335 if (ec != kXR_noErrorYet)
3336 {IO.EInfo[1] = ec;
3337 if (IO.File)
3338 {if (!emsg) emsg = XProtocol::errName(ec);
3339 IO.File->XrdSfsp->error.setErrInfo(0, emsg);
3340 }
3341 }
3342
3343// Otherwise, continue to darin the socket
3344//
3345 return do_WriteNone();
3346}
3347
3348/******************************************************************************/
3349/* d o _ W r i t e N o n e M s g */
3350/******************************************************************************/
3351
3352int XrdXrootdProtocol::do_WriteNoneMsg()
3353{
3354// Send our the error message and return
3355//
3356 if (!IO.File) return
3357 Response.Send(kXR_FileNotOpen,"write does not refer to an open file");
3358
3359 if (IO.EInfo[1])
3360 return Response.Send((XErrorCode)IO.EInfo[1],
3361 IO.File->XrdSfsp->error.getErrText());
3362
3363 if (IO.EInfo[0]) return fsError(IO.EInfo[0], 0, IO.File->XrdSfsp->error, 0, 0);
3364
3365 return Response.Send(kXR_FSError, IO.File->XrdSfsp->error.getErrText());
3366}
3367
3368/******************************************************************************/
3369/* d o _ W r i t e S p a n */
3370/******************************************************************************/
3371
3373{
3374 int rc;
3375 XrdXrootdFHandle fh(Request.write.fhandle);
3376 numWrites++;
3377
3378// Unmarshall the data
3379//
3380 IO.IOLen = Request.header.dlen;
3381 n2hll(Request.write.offset, IO.Offset);
3382
3383// Find the file object. We will only drain socket data on the control path.
3384// .
3385 if (!FTab || !(IO.File = FTab->Get(fh.handle)))
3386 {IO.IOLen -= myBlast;
3387 IO.File = 0;
3388 return do_WriteNone(Request.write.pathid);
3389 }
3390
3391// If we are monitoring, insert a write entry
3392//
3393 if (Monitor.InOut())
3394 Monitor.Agent->Add_wr(IO.File->Stats.FileID, Request.write.dlen,
3395 Request.write.offset);
3396 IO.File->Stats.wrOps(IO.IOLen); // Optimistically correct
3397
3398// Trace this entry
3399//
3400 TRACEP(FSIO, "fh=" <<fh.handle <<" write " <<IO.IOLen <<'@' <<IO.Offset);
3401
3402// Write data that was already read
3403//
3404 if ((rc = IO.File->XrdSfsp->write(IO.Offset, myBuff, myBlast)) < 0)
3405 {IO.IOLen = IO.IOLen-myBlast; IO.EInfo[0] = rc; IO.EInfo[1] = 0;
3406 return do_WriteNone();
3407 }
3408 IO.Offset += myBlast; IO.IOLen -= myBlast;
3409
3410// See if we need to finish this request in the normal way
3411//
3412 if (IO.IOLen > 0) return do_WriteAll();
3413 return Response.Send();
3414}
3415
3416/******************************************************************************/
3417/* d o _ W r i t e V */
3418/******************************************************************************/
3419
3420int XrdXrootdProtocol::do_WriteV()
3421{
3422// This will write multiple buffers at the same time in an attempt to avoid
3423// the disk latency. The information with the offsets and lengths of the data
3424// to write is passed as a data buffer. We attempt to optimize as best as
3425// possible, though certain combinations may result in multiple writes. Since
3426// socket flushing is nearly impossible when an error occurs, most errors
3427// simply terminate the connection.
3428//
3429 const int wveSZ = sizeof(XrdProto::write_list);
3430 struct trackInfo
3431 {XrdXrootdWVInfo **wvInfo; bool doit;
3432 trackInfo(XrdXrootdWVInfo **wvP) : wvInfo(wvP), doit(true) {}
3433 ~trackInfo() {if (doit && *wvInfo) {free(*wvInfo); *wvInfo = 0;}}
3434 } freeInfo(&wvInfo);
3435
3436 struct XrdProto::write_list *wrLst;
3437 XrdOucIOVec *wrVec;
3438 long long totSZ, maxSZ;
3439 int curFH, k, Quantum, wrVecNum, wrVecLen = Request.header.dlen;
3440
3441// Compute number of elements in the write vector and make sure we have no
3442// partial elements.
3443//
3444 wrVecNum = wrVecLen / wveSZ;
3445 if ( (wrVecLen <= 0) || (wrVecNum*wveSZ != wrVecLen) )
3446 {Response.Send(kXR_ArgInvalid, "Write vector is invalid");
3447 return -1;
3448 }
3449
3450// Make sure that we can make a copy of the read vector. So, we impose a limit
3451// on it's size.
3452//
3453 if (wrVecNum > XrdProto::maxWvecsz)
3454 {Response.Send(kXR_ArgTooLong, "Write vector is too long");
3455 return -1;
3456 }
3457
3458// Create the verctor write information structure sized as needed.
3459//
3460 if (wvInfo) free(wvInfo);
3461 wvInfo = (XrdXrootdWVInfo *)malloc(sizeof(XrdXrootdWVInfo) +
3462 sizeof(XrdOucIOVec)*(wrVecNum-1));
3463 memset(wvInfo, 0, sizeof(XrdXrootdWVInfo) - sizeof(XrdOucIOVec));
3464 wvInfo->wrVec = wrVec = wvInfo->ioVec;
3465
3466// Run down the list and compute the total size of the write. No individual
3467// write may be greater than the maximum transfer size. We also use this loop
3468// to copy the write list to our writev vector for later processing.
3469//
3470 wrLst = (XrdProto::write_list *)argp->buff;
3471 totSZ = 0; maxSZ = 0; k = 0; Quantum = maxTransz; curFH = 0;
3472 for (int i = 0; i < wrVecNum; i++)
3473 {if (wrLst[i].wlen == 0) continue;
3474 memcpy(&wrVec[k].info, wrLst[i].fhandle, sizeof(int));
3475 wrVec[k].size = ntohl(wrLst[i].wlen);
3476 if (wrVec[k].size < 0)
3477 {Response.Send(kXR_ArgInvalid, "Writev length is negtive");
3478 return -1;
3479 }
3480 if (wrVec[k].size > Quantum)
3481 {Response.Send(kXR_NoMemory,"Single writev transfer is too large");
3482 return -1;
3483 }
3484 wrVec[k].offset = ntohll(wrLst[i].offset);
3485 if (wrVec[k].info == curFH) totSZ += wrVec[k].size;
3486 else {if (maxSZ < totSZ) maxSZ = totSZ;
3487 totSZ = wrVec[k].size;
3488 }
3489 k++;
3490 }
3491
3492// Check if we are not actually writing anything, simply return success
3493//
3494 if (maxSZ < totSZ) maxSZ = totSZ;
3495 if (maxSZ == 0) return Response.Send();
3496
3497// So, now we account for the number of writev requests and total segments
3498//
3499 numWritV++; numSegsW += k; wrVecNum = k;
3500
3501// Calculate the transfer unit which will be the smaller of the maximum
3502// transfer unit and the actual amount we need to transfer.
3503//
3504 if (maxSZ > maxTransz) Quantum = maxTransz;
3505 else Quantum = static_cast<int>(maxSZ);
3506
3507// Now obtain the right size buffer
3508//
3509 if ((Quantum < halfBSize && Quantum > 1024) || Quantum > argp->bsize)
3510 {if (getBuff(0, Quantum) <= 0) return -1;}
3511 else if (hcNow < hcNext) hcNow++;
3512
3513// Check that we really have at least the first file open (part of setup)
3514//
3515 if (!FTab || !(IO.File = FTab->Get(wrVec[0].info)))
3516 {Response.Send(kXR_FileNotOpen, "writev does not refer to an open file");
3517 return -1;
3518 }
3519
3520// Setup to do the complete transfer
3521//
3522 wvInfo->curFH = wrVec[0].info;
3523 wvInfo->vBeg = 0;
3524 wvInfo->vPos = 0;
3525 wvInfo->vEnd = wrVecNum;
3526 wvInfo->vMon = 0;
3527 wvInfo->doSync= (Request.writev.options & ClientWriteVRequest::doSync) != 0;
3528 wvInfo->wvMon = Monitor.InOut();
3529 wvInfo->ioMon = (wvInfo->vMon > 1);
3530// wvInfo->vType = (wvInfo->ioMon ? XROOTD_MON_WRITEU : XROOTD_MON_WRITEV);
3531 IO.WVBytes = 0;
3532 IO.IOLen = wrVec[0].size;
3533 myBuff = argp->buff;
3534 myBlast = 0;
3535
3536// Now we simply start the write operations if this is a true writev request.
3537// Otherwise return to the caller for additional processing.
3538//
3539 freeInfo.doit = false;
3540 if (Request.header.requestid == kXR_writev) return do_WriteVec();
3541 return 0;
3542}
3543
3544/******************************************************************************/
3545/* d o _ W r i t e V e c */
3546/******************************************************************************/
3547
3548int XrdXrootdProtocol::do_WriteVec()
3549{
3550 XrdSfsXferSize xfrSZ;
3551 int rc, wrVNum, vNow = wvInfo->vPos;
3552 bool done, newfile;
3553
3554// Read the complete data from the socket for the current element. Note that
3555// should we enter a resume state; upon re-entry all of the data will be read.
3556//
3557do{if (IO.IOLen > 0)
3558 {wvInfo->wrVec[vNow].data = argp->buff + myBlast;
3559 myBlast += IO.IOLen;
3560 if ((rc = getData("data", myBuff, IO.IOLen)))
3561 {if (rc < 0) return rc;
3562 IO.IOLen = 0;
3563 Resume = &XrdXrootdProtocol::do_WriteVec;
3564 return rc;
3565 }
3566 }
3567
3568// Establish the state at this point as this will tell us what to do next.
3569//
3570 vNow++;
3571 done = newfile = false;
3572 if (vNow >= wvInfo->vEnd) done = true;
3573 else if (wvInfo->wrVec[vNow].info != wvInfo->curFH) newfile = true;
3574 else if (myBlast + wvInfo->wrVec[vNow].size <= argp->bsize)
3575 {IO.IOLen = wvInfo->wrVec[vNow].size;
3576 myBuff = argp->buff + myBlast;
3577 wvInfo->vPos = vNow;
3578 continue;
3579 }
3580
3581// We need to write out what we have.
3582//
3583 wrVNum = vNow - wvInfo->vBeg;
3584 xfrSZ = IO.File->XrdSfsp->writev(&(wvInfo->wrVec[wvInfo->vBeg]), wrVNum);
3585 TRACEP(FSIO,"fh=" <<wvInfo->curFH <<" writeV " << xfrSZ <<':' <<wrVNum);
3586 if (xfrSZ != myBlast) break;
3587
3588// Check if we need to do monitoring or a sync with no deferal. Note that
3589// we currently do not support detailed monitoring for vector writes!
3590//
3591 if (done || newfile)
3592 {int monVnum = vNow - wvInfo->vMon;
3593 IO.File->Stats.wvOps(IO.WVBytes, monVnum);
3603 wvInfo->vMon = vNow;
3604 IO.WVBytes = 0;
3605 if (wvInfo->doSync)
3606 {IO.File->XrdSfsp->error.setErrCB(0,0);
3607 xfrSZ = IO.File->XrdSfsp->sync();
3608 if (xfrSZ< 0) break;
3609 }
3610 }
3611
3612// If we are done, the finish up
3613//
3614 if (done)
3615 {if (wvInfo) {free(wvInfo); wvInfo = 0;}
3616 return Response.Send();
3617 }
3618
3619// Sequence to a new file if we need to do so
3620//
3621 if (newfile)
3622 {if (!FTab || !(IO.File = FTab->Get(wvInfo->wrVec[vNow].info)))
3623 {Response.Send(kXR_FileNotOpen,"writev does not refer to an open file");
3624 return -1;
3625 }
3626 wvInfo->curFH = wvInfo->wrVec[vNow].info;
3627 }
3628
3629// Setup to resume transfer
3630//
3631 myBlast = 0;
3632 myBuff = argp->buff;
3633 IO.IOLen = wvInfo->wrVec[vNow].size;
3634 wvInfo->vBeg = vNow;
3635 wvInfo->vPos = vNow;
3636
3637} while(true);
3638
3639// If we got here then there was a write error (file pointer is valid).
3640//
3641 if (wvInfo) {free(wvInfo); wvInfo = 0;}
3642 return fsError((int)xfrSZ, 0, IO.File->XrdSfsp->error, 0, 0);
3643}
3644
3645/******************************************************************************/
3646/* S e n d F i l e */
3647/******************************************************************************/
3648
3650{
3651
3652// Make sure we have some data to send
3653//
3654 if (!IO.IOLen) return 1;
3655
3656// Send off the data
3657//
3658 IO.IOLen = Response.Send(fildes, IO.Offset, IO.IOLen);
3659 return IO.IOLen;
3660}
3661
3662/******************************************************************************/
3663
3665{
3666 int i, xframt = 0;
3667
3668// Make sure we have some data to send
3669//
3670 if (!IO.IOLen) return 1;
3671
3672// Verify the length, it can't be greater than what the client wants
3673//
3674 for (i = 1; i < sfvnum; i++) xframt += sfvec[i].sendsz;
3675 if (xframt > IO.IOLen) return 1;
3676
3677// Send off the data
3678//
3679 if (xframt) IO.IOLen = Response.Send(sfvec, sfvnum, xframt);
3680 else {IO.IOLen = 0; Response.Send();}
3681 return IO.IOLen;
3682}
3683
3684/******************************************************************************/
3685/* S e t F D */
3686/******************************************************************************/
3687
3689{
3690 if (fildes < 0) IO.File->sfEnabled = 0;
3691 else IO.File->fdNum = fildes;
3692}
3693
3694/******************************************************************************/
3695/* U t i l i t y M e t h o d s */
3696/******************************************************************************/
3697/******************************************************************************/
3698/* f s E r r o r */
3699/******************************************************************************/
3700
3701int XrdXrootdProtocol::fsError(int rc, char opC, XrdOucErrInfo &myError,
3702 const char *Path, char *Cgi)
3703{
3704 int ecode, popt, rs;
3705 const char *eMsg = myError.getErrText(ecode);
3706
3707// Process standard errors
3708//
3709 if (rc == SFS_ERROR)
3710 {SI->errorCnt++;
3711 rc = XProtocol::mapError(ecode);
3712
3713 if (Path && (rc == kXR_Overloaded) && (opC == XROOTD_MON_OPENR
3714 || opC == XROOTD_MON_OPENW || opC == XROOTD_MON_OPENC))
3715 {if (myError.extData()) myError.Reset();
3716 return fsOvrld(opC, Path, Cgi);
3717 }
3718
3719 if (Path && (rc == kXR_NotFound) && RQLxist && opC
3720 && (popt = RQList.Validate(Path)))
3723 Route[popt].Host[rdType],
3724 Route[popt].Port[rdType],
3726 if (Cgi) rs = fsRedirNoEnt(eMsg, Cgi, popt);
3727 else rs = Response.Send(kXR_redirect,
3728 Route[popt].Port[rdType],
3729 Route[popt].Host[rdType]);
3730 } else rs = Response.Send((XErrorCode)rc, eMsg);
3731 if (myError.extData()) myError.Reset();
3732 return rs;
3733 }
3734
3735// Process the redirection (error msg is host:port)
3736//
3737 if (rc == SFS_REDIRECT)
3738 {SI->redirCnt++;
3739 // if the plugin set some redirect flags but the client does not
3740 // support them, clear the flags (set -1)
3741 if( ecode < -1 && !( clientPV & XrdOucEI::uRedirFlgs ) )
3742 ecode = -1;
3743 if (XrdXrootdMonitor::Redirect() && Path && opC)
3745 if (TRACING(TRACE_REDIR))
3746 {if (ecode < 0)
3747 {TRACEI(REDIR, Response.ID() <<"redirecting to " << eMsg);}
3748 else {TRACEI(REDIR, Response.ID() <<"redirecting to "
3749 << eMsg <<':' <<ecode);
3750 }
3751 }
3752 if (RedirPI) rs = fsRedirPI(eMsg, ecode, myError.getErrTextLen());
3753 else rs = Response.Send(kXR_redirect, ecode, eMsg,
3754 myError.getErrTextLen());
3755 if (myError.extData()) myError.Reset();
3756 return rs;
3757 }
3758
3759// Process the deferal. We also synchronize sending the deferal response with
3760// sending the actual deferred response by calling Done() in the callback object.
3761// This allows the requestor of he callback know that we actually send the
3762// kXR_waitresp to the end client and avoid violating time causality.
3763//
3764 if (rc == SFS_STARTED)
3765 {SI->stallCnt++;
3766 if (ecode <= 0) ecode = 1800;
3767 TRACEI(STALL, Response.ID() <<"delaying client up to " <<ecode <<" sec");
3768 rc = Response.Send(kXR_waitresp, ecode, eMsg);
3769 if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError);
3770 if (myError.extData()) myError.Reset();
3771 return (rc ? rc : 1);
3772 }
3773
3774// Process the data response
3775//
3776 if (rc == SFS_DATA)
3777 {if (ecode) rs = Response.Send((void *)eMsg, ecode);
3778 else rs = Response.Send();
3779 if (myError.extData()) myError.Reset();
3780 return rs;
3781 }
3782
3783// Process the data response via an iovec
3784//
3785 if (rc == SFS_DATAVEC)
3786 {if (ecode < 2) rs = Response.Send();
3787 else rs = Response.Send((struct iovec *)eMsg, ecode);
3788 if (myError.getErrCB()) myError.getErrCB()->Done(ecode, &myError);
3789 if (myError.extData()) myError.Reset();
3790 return rs;
3791 }
3792
3793// Process the deferal
3794//
3795 if (rc >= SFS_STALL)
3796 {SI->stallCnt++;
3797 TRACEI(STALL, Response.ID() <<"stalling client for " <<rc <<" sec");
3798 rs = Response.Send(kXR_wait, rc, eMsg);
3799 if (myError.extData()) myError.Reset();
3800 return rs;
3801 }
3802
3803// Unknown conditions, report it
3804//
3805 {char buff[32];
3806 SI->errorCnt++;
3807 sprintf(buff, "%d", rc);
3808 eDest.Emsg("Xeq", "Unknown error code", buff, eMsg);
3809 rs = Response.Send(kXR_ServerError, eMsg);
3810 if (myError.extData()) myError.Reset();
3811 return rs;
3812 }
3813}
3814
3815/******************************************************************************/
3816/* f s O v r l d */
3817/******************************************************************************/
3818
3819int XrdXrootdProtocol::fsOvrld(char opC, const char *Path, char *Cgi)
3820{
3821 static const char *prot = "root://";
3822 static int negOne = -1;
3823 static char quest = '?', slash = '/';
3824
3825 struct iovec rdrResp[8];
3826 char *destP=0, dest[512];
3827 int iovNum=0, pOff, port;
3828
3829// If this is a forwarded path and the client can handle full url's then
3830// redirect the client to the destination in the path. Otherwise, if there is
3831// an alternate destination, send client there. Otherwise, stall the client.
3832//
3834 && (pOff = XrdOucUtils::isFWD(Path, &port, dest, sizeof(dest))))
3835 { rdrResp[1].iov_base = (char *)&negOne;
3836 rdrResp[1].iov_len = sizeof(negOne);
3837 rdrResp[2].iov_base = (char *)prot;
3838 rdrResp[2].iov_len = 7; // root://
3839 rdrResp[3].iov_base = (char *)dest;
3840 rdrResp[3].iov_len = strlen(dest); // host:port
3841 rdrResp[4].iov_base = (char *)&slash;
3842 rdrResp[4].iov_len = (*Path == '/' ? 1 : 0); // / or nil for objid
3843 rdrResp[5].iov_base = (char *)(Path+pOff);
3844 rdrResp[5].iov_len = strlen(Path+pOff); // path
3845 if (Cgi && *Cgi)
3846 {rdrResp[6].iov_base = (char *)&quest;
3847 rdrResp[6].iov_len = sizeof(quest); // ?
3848 rdrResp[7].iov_base = (char *)Cgi;
3849 rdrResp[7].iov_len = strlen(Cgi); // cgi
3850 iovNum = 8;
3851 } else iovNum = 6;
3852 destP = dest;
3853 } else if ((destP = Route[RD_ovld].Host[rdType]))
3854 port = Route[RD_ovld].Port[rdType];
3855
3856// If a redirect happened, then trace it.
3857//
3858 if (destP)
3859 {SI->redirCnt++;
3861 XrdXrootdMonitor::Redirect(Monitor.Did, destP, port,
3863 if (iovNum)
3864 {TRACEI(REDIR, Response.ID() <<"redirecting to "<<dest);
3865 return Response.Send(kXR_redirect, rdrResp, iovNum);
3866 } else {
3867 TRACEI(REDIR, Response.ID() <<"redirecting to "<<destP<<':'<<port);
3868 return Response.Send(kXR_redirect, port, destP);
3869 }
3870 }
3871
3872// If there is a stall value, then delay the client
3873//
3874 if (OD_Stall)
3875 {TRACEI(STALL, Response.ID()<<"stalling client for "<<OD_Stall<<" sec");
3876 SI->stallCnt++;
3877 return Response.Send(kXR_wait, OD_Stall, "server is overloaded");
3878 }
3879
3880// We were unsuccessful, return overload as an error
3881//
3882 return Response.Send(kXR_Overloaded, "server is overloaded");
3883}
3884
3885/******************************************************************************/
3886/* f s R e d i r N o E n t */
3887/******************************************************************************/
3888
3889int XrdXrootdProtocol::fsRedirNoEnt(const char *eMsg, char *Cgi, int popt)
3890{
3891 struct iovec ioV[4];
3892 char *tried, *trend, *ptried = 0;
3893 kXR_int32 pnum = htonl(static_cast<kXR_int32>(Route[popt].Port[rdType]));
3894 int tlen;
3895
3896// Try to find the last tried token in the cgi
3897//
3898 if ((trend = Cgi))
3899 {do {if (!(tried = strstr(Cgi, "tried="))) break;
3900 if (tried == trend || *(tried-1) == '&')
3901 {if (!ptried || (*(tried+6) && *(tried+6) != '&')) ptried=tried;}
3902 Cgi = index(tried+6, '&');
3903 } while(Cgi);
3904 }
3905
3906// If we did find a tried, bracket it out with a leading comma (we can modify
3907// the passed cgi string here because this is the last time it will be used.
3908//
3909 if ((tried = ptried))
3910 {tried += 5;
3911 while(*(tried+1) && *(tried+1) == ',') tried++;
3912 trend = index(tried, '&');
3913 if (trend) {tlen = trend - tried; *trend = 0;}
3914 else tlen = strlen(tried);
3915 *tried = ',';
3916 } else tlen = 0;
3917
3918// Check if we are in a redirect loop (i.e. we are listed in the client's cgi).
3919// If so, then treat this and file not found as we've been here before.
3920//
3921 if ((trend = tried) && eMsg)
3922 do {if ((trend = strstr(trend, myCName)))
3923 {if (*(trend+myCNlen) == '\0' || *(trend+myCNlen) == ',')
3924 return Response.Send(kXR_NotFound, eMsg);
3925 trend = index(trend+myCNlen, ',');
3926 }
3927 } while(trend);
3928
3929
3930// If we have not found a tried token or that token far too large to propogate
3931// (i.e. it's likely we have an undetected loop), then do a simple redirect.
3932//
3933 if (!tried || !tlen || tlen > 16384)
3934 return Response.Send(kXR_redirect,
3935 Route[popt].Port[rdType],
3936 Route[popt].Host[rdType]);
3937
3938// We need to append the client's tried list to the one we have to avoid loops
3939//
3940
3941 ioV[1].iov_base = (char *)&pnum;
3942 ioV[1].iov_len = sizeof(pnum);
3943 ioV[2].iov_base = Route[popt].Host[rdType];
3944 ioV[2].iov_len = Route[popt].RDSz[rdType];
3945 ioV[3].iov_base = tried;
3946 ioV[3].iov_len = tlen;
3947
3948// Compute total length
3949//
3950 tlen += sizeof(pnum) + Route[popt].RDSz[rdType];
3951
3952// Send off the redirect
3953//
3954 return Response.Send(kXR_redirect, ioV, 4, tlen);
3955}
3956
3957/******************************************************************************/
3958/* f s R e d i r I P */
3959/******************************************************************************/
3960
3961namespace XrdXrootd
3962{
3963 struct netInfo
3966 char* netID;
3967 time_t expTime = 0;
3969
3970 netInfo(const char*id) : netID(strdup(id)) {}
3971 ~netInfo() {if (netID) free(netID);}
3972 };
3973}
3974
3975XrdXrootd::netInfo* XrdXrootdProtocol::fsRedirIP(const char *netID, int port)
3976{
3977 auto cmpchr = [](const char* a, const char* b) {return strcmp(a,b)<0;};
3978 static std::map<const char*,XrdXrootd::netInfo*,decltype(cmpchr)> niMap(cmpchr);
3979 static XrdSysMutex niMapMtx;
3980
3981 XrdXrootd::netInfo* niP;
3982
3983// First, chek if we have an entry for this item. We need a lock because
3984// some oher thread may be adding a node to the map. Nodes are never deleted.
3985//
3986 niMapMtx.Lock();
3987 auto it = niMap.find(netID);
3988 if (it == niMap.end())
3989 {niP = new XrdXrootd::netInfo(netID);
3990 niMap[niP->netID] = niP;
3991 } else niP = it->second;
3992 niMapMtx.UnLock();
3993 niP->niMutex.Lock();
3994
3995// Validate/initialize the corresponding netaddr object. For newly allocated
3996// objects it is always done. For pre-exiting objects only when they expire.
3997// Note: when expTime is zero then refs must be 1 and this is a 1st time init,
3998//
3999 time_t nowT = time(0);
4000 niP->refs++;
4001 if (niP->expTime <= nowT && niP->refs == 1)
4002 {const char* eTxt = niP->netAddr.Set(netID, port);
4003 if (eTxt)
4004 {if (niP->expTime == 0)
4005 {eDest.Emsg("RedirIP", "Unable to init NetInfo for", netID, eTxt);
4006 niP->refs--;
4007 niP->niMutex.UnLock();
4008 return 0;
4009 }
4010 eDest.Emsg("RedirIP", "Unable to refresh NetInfo for", netID, eTxt);
4011 niP->expTime += 60;
4012 } else niP->expTime = nowT + redirIPHold;
4013 }
4014
4015// We have valid network info on the target
4016//
4017 niP->niMutex.UnLock();
4018 return niP;
4019}
4020
4021/******************************************************************************/
4022/* f s R e d i r P I */
4023/******************************************************************************/
4024
4025int XrdXrootdProtocol::fsRedirPI(const char *trg, int port, int trglen)
4026{
4027 struct THandle
4028 {XrdXrootd::netInfo* Info;
4029 THandle() : Info(0) {}
4030 ~THandle() {if (Info) Info->refs--;}
4031 } T;
4032 std::string Target;
4033 int newPort = port;
4034
4035// Handle the most comman case first - a simple host and port.
4036//
4037 if (port >= 0)
4038 {std::string TDst;
4039 const char* TCgi = index(trg, '?');
4040 if (!TCgi) {TCgi = ""; TDst = trg;}
4041 else TDst.assign(trg, TCgi-trg);
4042 T.Info = fsRedirIP(TDst.c_str(), port);
4043 if (!T.Info) return Response.Send(kXR_redirect, port, trg, trglen);
4044 uint16_t TPort = static_cast<uint16_t>(newPort);
4045 Target = RedirPI->Redirect(TDst.c_str(), TPort, TCgi, T.Info->netAddr,
4046 *(Link->AddrInfo()));
4047 newPort = static_cast<int>(TPort);
4048 } else {
4049
4050// This is a url which requires additional handling. If the url is not
4051// valid we skip calling the plugin as the situation is not salvageable
4052// and the client will complain. We also require that the host and optional
4053// port be atleast two characters long.
4054//
4055 std::string urlHead, TDst, urlPort;
4056 const char* urlTail;
4057 const char* hBeg = strstr(trg, "://");
4058 if (!hBeg)
4059 {eDest.Emsg("RedirPI", "Invalid redirect URL -", trg);
4060 return Response.Send(kXR_redirect, port, trg, trglen);
4061 }
4062 hBeg += 3;
4063 urlHead.assign(trg, hBeg-trg);
4064 urlTail = strstr(hBeg, "/");
4065 if (!urlTail) {urlTail = ""; TDst = hBeg;}
4066 else {if (urlTail-hBeg < 3)
4067 {eDest.Emsg("RedirPI", "Mlalformed URL -", trg);
4068 return Response.Send(kXR_redirect, port, trg, trglen);
4069 }
4070 TDst.assign(hBeg, urlTail-hBeg);
4071 }
4072 T.Info = fsRedirIP(TDst.c_str(), port);
4073 if (!T.Info) return Response.Send(kXR_redirect, port, trg, trglen);
4074 size_t colon = TDst.find(":");
4075 if (colon != std::string::npos)
4076 {urlPort.assign(TDst, colon+1, std::string::npos);
4077 TDst.erase(colon);
4078 }
4079 Target = RedirPI->RedirectURL(urlHead.c_str(), Target.c_str(),
4080 urlPort.c_str(), urlTail, newPort,
4081 T.Info->netAddr, *(Link->AddrInfo()));
4082 if (port == -1 || newPort >= 0) newPort = port;
4083 }
4084
4085// Handle the result of calling the plugin
4086//
4087 if (!Target.size()) return Response.Send(kXR_redirect, port, trg, trglen);
4088
4089 if (Target.front() != '!')
4090 {TRACEI(REDIR, Response.ID() <<"plugin redirects to "
4091 <<Target.c_str() <<" portarg="<<newPort);
4092
4093 return Response.Send(kXR_redirect,port,Target.c_str(),Target.size());
4094 }
4095
4096// The redirect plgin enountered an error, so we bail.
4097//
4098 char mbuff[1024];
4099 snprintf(mbuff,sizeof(mbuff),"Redirect failed; %s",Target.c_str());
4100 eDest.Emsg("Xeq_RedirPI", mbuff);
4101 return Response.Send(kXR_ServerError, mbuff);
4102}
4103
4104/******************************************************************************/
4105/* g e t B u f f */
4106/******************************************************************************/
4107
4108int XrdXrootdProtocol::getBuff(const int isRead, int Quantum)
4109{
4110
4111// Check if we need to really get a new buffer
4112//
4113 if (!argp || Quantum > argp->bsize) hcNow = hcPrev;
4114 else if (Quantum >= halfBSize || hcNow-- > 0) return 1;
4115 else if (hcNext >= hcMax) hcNow = hcMax;
4116 else {int tmp = hcPrev;
4117 hcNow = hcNext;
4118 hcPrev = hcNext;
4119 hcNext = tmp+hcNext;
4120 }
4121
4122// Get a new buffer
4123//
4124 if (argp) BPool->Release(argp);
4125 if ((argp = BPool->Obtain(Quantum))) halfBSize = argp->bsize >> 1;
4126 else return Response.Send(kXR_NoMemory, (isRead ?
4127 "insufficient memory to read file" :
4128 "insufficient memory to write file"));
4129
4130// Success
4131//
4132 return 1;
4133}
4134
4135/******************************************************************************/
4136/* Private: g e t C k s T y p e */
4137/******************************************************************************/
4138
4139char *XrdXrootdProtocol::getCksType(char *opaque, char *cspec, int cslen)
4140{
4141 char *cksT;
4142
4143// Get match for user specified checksum type, if any. Otherwise return default.
4144//
4145 if (opaque && *opaque)
4146 {XrdOucEnv jobEnv(opaque);
4147 if ((cksT = jobEnv.Get("cks.type")))
4148 {XrdOucTList *tP = JobCKTLST;
4149 while(tP && strcasecmp(tP->text, cksT)) tP = tP->next;
4150 if (!tP && cspec) snprintf(cspec, cslen, "%s", cksT);
4151 return (tP ? tP->text : 0);
4152 }
4153 }
4154
4155// Return default
4156//
4157 return JobCKT;
4158}
4159
4160/******************************************************************************/
4161/* Private: l o g L o g i n */
4162/******************************************************************************/
4163
4164bool XrdXrootdProtocol::logLogin(bool xauth)
4165{
4166 const char *uName, *ipName, *tMsg, *zMsg = "";
4167 char lBuff[512], pBuff[512];
4168
4169// Determine ip type
4170//
4172 ipName = (clientPV & XrdOucEI::uIPv64 ? "IP46" : "IPv4");
4173 else ipName = (clientPV & XrdOucEI::uIPv64 ? "IP64" : "IPv6");
4174
4175// Determine client name
4176//
4177 if (xauth) uName = (Client->name ? Client->name : "nobody");
4178 else uName = 0;
4179
4180// Check if TLS was or will be used
4181//
4182 tMsg = Link->verTLS();
4183 if (*tMsg) zMsg = " ";
4184
4185// Format the line
4186//
4187 snprintf(lBuff, sizeof(lBuff), "%s %s %s%slogin%s%s",
4188 (clientPV & XrdOucEI::uPrip ? "pvt" : "pub"), ipName,
4189 tMsg, zMsg,
4190 (xauth ? " as " : ""),
4191 (uName ? uName : ""));
4192
4193// Document the login
4194//
4195 if (Client->tident != Client->pident)
4196 {snprintf(pBuff, sizeof(pBuff), "via %s auth for %s",
4197 Client->prot, Client->pident);
4198 } else *pBuff = 0;
4199 eDest.Log(SYS_LOG_01, "Xeq", Link->ID, lBuff, (*pBuff ? pBuff : 0));
4200
4201// Enable TLS if we need to (note sess setting is off if login setting is on).
4202// If we need to but the client is not TLS capable, send an error and terminate.
4203//
4204 if ((doTLS & Req_TLSSess) && !Link->hasBridge())
4205 {if (ableTLS)
4206 {if (Link->setTLS(true, tlsCtx))
4207 {Link->setProtName("xroots");
4208 isTLS = true;
4209 } else {
4210 eDest.Emsg("Xeq", "Unable to require TLS for", Link->ID);
4211 return false;
4212 }
4213 } else {
4214 eDest.Emsg("Xeq","session requires TLS but",Link->ID,"is incapable.");
4215 Response.Send(kXR_TLSRequired, "session requires TLS support");
4216 return false;
4217 }
4218 }
4219
4220// Record the appname in the final SecEntity object
4221//
4222 if (AppName) Client->eaAPI->Add("xrd.appname", (std::string)AppName);
4223
4224// Assign unique identifier to the final SecEntity object
4225//
4226 Client->ueid = mySID;
4227
4228// Propogate a connect through the whole system
4229//
4230 osFS->Connect(Client);
4231 return true;
4232}
4233
4234/******************************************************************************/
4235/* m a p M o d e */
4236/******************************************************************************/
4237
4238#define Map_Mode(x,y) if (Mode & kXR_ ## x) newmode |= S_I ## y
4239
4240int XrdXrootdProtocol::mapMode(int Mode)
4241{
4242 int newmode = 0;
4243
4244// Map the mode in the obvious way
4245//
4246 Map_Mode(ur, RUSR); Map_Mode(uw, WUSR); Map_Mode(ux, XUSR);
4247 Map_Mode(gr, RGRP); Map_Mode(gw, WGRP); Map_Mode(gx, XGRP);
4248 Map_Mode(or, ROTH); Map_Mode(ox, XOTH);
4249
4250// All done
4251//
4252 return newmode;
4253}
4254
4255/******************************************************************************/
4256/* M o n A u t h */
4257/******************************************************************************/
4258
4260{
4261 char Buff[4096];
4262 const char *bP = Buff;
4263
4264 if (Client == &Entity) bP = Entity.moninfo;
4265 else {snprintf(Buff,sizeof(Buff),
4266 "&p=%s&n=%s&h=%s&o=%s&r=%s&g=%s&m=%s%s&I=%c",
4267 Client->prot,
4268 (Client->name ? Client->name : ""),
4269 (Client->host ? Client->host : ""),
4270 (Client->vorg ? Client->vorg : ""),
4271 (Client->role ? Client->role : ""),
4272 (Client->grps ? Client->grps : ""),
4273 (Client->moninfo ? Client->moninfo : ""),
4274 (Entity.moninfo ? Entity.moninfo : ""),
4275 (clientPV & XrdOucEI::uIPv4 ? '4' : '6')
4276 );
4277 Client->secMon = &Monitor;
4278 }
4279
4280 Monitor.Report(bP);
4281 if (Entity.moninfo) {free(Entity.moninfo); Entity.moninfo = 0;}
4282}
4283
4284/******************************************************************************/
4285/* r p C h e c k */
4286/******************************************************************************/
4287
4288int XrdXrootdProtocol::rpCheck(char *fn, char **opaque)
4289{
4290 char *cp;
4291
4292 if (*fn != '/')
4293 {if (!(XPList.Opts() & XROOTDXP_NOSLASH)) return 1;
4294 if ( XPList.Opts() & XROOTDXP_NOCGI) {*opaque = 0; return 0;}
4295 }
4296
4297 if (!(cp = index(fn, '?'))) *opaque = 0;
4298 else {*cp = '\0'; *opaque = cp+1;
4299 if (!**opaque) *opaque = 0;
4300 }
4301
4302 if (*fn != '/') return 0;
4303
4304 while ((cp = index(fn, '/')))
4305 {fn = cp+1;
4306 if (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\0'))
4307 return 1;
4308 }
4309 return 0;
4310}
4311
4312/******************************************************************************/
4313/* r p E m s g */
4314/******************************************************************************/
4315
4316int XrdXrootdProtocol::rpEmsg(const char *op, char *fn)
4317{
4318 char buff[2048];
4319 snprintf(buff,sizeof(buff)-1,"%s relative path '%s' is disallowed.",op,fn);
4320 buff[sizeof(buff)-1] = '\0';
4321 return Response.Send(kXR_NotAuthorized, buff);
4322}
4323
4324/******************************************************************************/
4325/* S e t S F */
4326/******************************************************************************/
4327
4328int XrdXrootdProtocol::SetSF(kXR_char *fhandle, bool seton)
4329{
4330 XrdXrootdFHandle fh(fhandle);
4331 XrdXrootdFile *theFile;
4332
4333 if (!FTab || !(theFile = FTab->Get(fh.handle))) return -EBADF;
4334
4335// Turn it off or on if so wanted
4336//
4337 if (!seton) theFile->sfEnabled = 0;
4338 else if (theFile->fdNum >= 0) theFile->sfEnabled = 1;
4339
4340// All done
4341//
4342 return 0;
4343}
4344
4345/******************************************************************************/
4346/* S q u a s h */
4347/******************************************************************************/
4348
4349int XrdXrootdProtocol::Squash(char *fn)
4350{
4351 char *ofn, *ifn = fn;
4352
4353 if (*fn != '/') return XPList.Opts();
4354
4355 while(*ifn)
4356 {if (*ifn == '/')
4357 if (*(ifn+1) == '/'
4358 || (*(ifn+1) == '.' && *(ifn+1) && *(ifn+2) == '/')) break;
4359 ifn++;
4360 }
4361
4362 if (!*ifn) return XPList.Validate(fn, ifn-fn);
4363
4364 ofn = ifn;
4365 while(*ifn) {*ofn = *ifn++;
4366 while(*ofn == '/')
4367 {while(*ifn == '/') ifn++;
4368 if (ifn[0] == '.' && ifn[1] == '/') ifn += 2;
4369 else break;
4370 }
4371 ofn++;
4372 }
4373 *ofn = '\0';
4374
4375 return XPList.Validate(fn, ofn-fn);
4376}
4377
4378/******************************************************************************/
4379/* v p E m s g */
4380/******************************************************************************/
4381
4382int XrdXrootdProtocol::vpEmsg(const char *op, char *fn)
4383{
4384 char buff[2048];
4385 snprintf(buff,sizeof(buff)-1,"%s path '%s' is disallowed.",op,fn);
4386 buff[sizeof(buff)-1] = '\0';
4387 return Response.Send(kXR_NotAuthorized, buff);
4388}
XErrorCode
Definition XProtocol.hh:989
@ kXR_ArgInvalid
Definition XProtocol.hh:990
@ kXR_InvalidRequest
Definition XProtocol.hh:996
@ kXR_ArgMissing
Definition XProtocol.hh:991
@ kXR_TLSRequired
@ kXR_AuthFailed
@ kXR_NotAuthorized
@ kXR_NotFound
@ kXR_FileLocked
Definition XProtocol.hh:993
@ kXR_noErrorYet
@ kXR_ChkSumErr
@ kXR_overQuota
@ kXR_FileNotOpen
Definition XProtocol.hh:994
@ kXR_Unsupported
@ kXR_Cancelled
@ kXR_ServerError
@ kXR_Overloaded
@ kXR_ArgTooLong
Definition XProtocol.hh:992
@ kXR_FSError
Definition XProtocol.hh:995
@ kXR_NoMemory
Definition XProtocol.hh:998
@ kXR_ecredir
Definition XProtocol.hh:371
#define kXR_ShortProtRespLen
#define kXR_gotoTLS
#define kXR_haveTLS
#define kXR_PROTSIGNVERSION
Definition XProtocol.hh:74
kXR_char pathid
Definition XProtocol.hh:653
@ kXR_open_wrto
Definition XProtocol.hh:469
@ kXR_compress
Definition XProtocol.hh:452
@ kXR_async
Definition XProtocol.hh:458
@ kXR_delete
Definition XProtocol.hh:453
@ kXR_prefname
Definition XProtocol.hh:461
@ kXR_nowait
Definition XProtocol.hh:467
@ kXR_open_read
Definition XProtocol.hh:456
@ kXR_open_updt
Definition XProtocol.hh:457
@ kXR_mkpath
Definition XProtocol.hh:460
@ kXR_seqio
Definition XProtocol.hh:468
@ kXR_replica
Definition XProtocol.hh:465
@ kXR_posc
Definition XProtocol.hh:466
@ kXR_refresh
Definition XProtocol.hh:459
@ kXR_new
Definition XProtocol.hh:455
@ kXR_force
Definition XProtocol.hh:454
@ kXR_4dirlist
Definition XProtocol.hh:464
@ kXR_retstat
Definition XProtocol.hh:463
@ kXR_waitresp
Definition XProtocol.hh:906
@ kXR_redirect
Definition XProtocol.hh:904
@ kXR_oksofar
Definition XProtocol.hh:900
@ kXR_ok
Definition XProtocol.hh:899
@ kXR_authmore
Definition XProtocol.hh:902
@ kXR_wait
Definition XProtocol.hh:905
@ kXR_dstat
Definition XProtocol.hh:240
@ kXR_dcksm
Definition XProtocol.hh:241
kXR_char fhandle[4]
Definition XProtocol.hh:659
@ kXR_writev
Definition XProtocol.hh:143
@ kXR_write
Definition XProtocol.hh:131
kXR_int32 rlen
Definition XProtocol.hh:660
@ kXR_vermask
Definition XProtocol.hh:377
@ kXR_asyncap
Definition XProtocol.hh:378
#define kXR_attrProxy
#define kXR_PROTOCOLVERSION
Definition XProtocol.hh:70
kXR_int64 offset
Definition XProtocol.hh:661
@ kXR_vfs
Definition XProtocol.hh:763
@ kXR_mkdirpath
Definition XProtocol.hh:410
@ kXR_wmode
Definition XProtocol.hh:591
@ kXR_evict
Definition XProtocol.hh:596
@ kXR_usetcp
Definition XProtocol.hh:594
@ kXR_cancel
Definition XProtocol.hh:587
@ kXR_fresh
Definition XProtocol.hh:593
@ kXR_notify
Definition XProtocol.hh:588
@ kXR_coloc
Definition XProtocol.hh:592
@ kXR_stage
Definition XProtocol.hh:590
@ kXR_noerrs
Definition XProtocol.hh:589
ServerResponseReqs_Protocol secreq
@ kXR_file
@ kXR_isDir
@ kXR_offline
@ kXR_QPrep
Definition XProtocol.hh:616
@ kXR_Qopaqug
Definition XProtocol.hh:625
@ kXR_Qconfig
Definition XProtocol.hh:621
@ kXR_Qopaquf
Definition XProtocol.hh:624
@ kXR_Qckscan
Definition XProtocol.hh:620
@ kXR_Qxattr
Definition XProtocol.hh:618
@ kXR_Qspace
Definition XProtocol.hh:619
@ kXR_Qvisa
Definition XProtocol.hh:622
@ kXR_QStats
Definition XProtocol.hh:615
@ kXR_Qcksum
Definition XProtocol.hh:617
@ kXR_Qopaque
Definition XProtocol.hh:623
@ kXR_ver001
Definition XProtocol.hh:385
@ kXR_ver003
Definition XProtocol.hh:387
@ kXR_ver004
Definition XProtocol.hh:388
@ kXR_ver002
Definition XProtocol.hh:386
@ kXR_readrdok
Definition XProtocol.hh:360
@ kXR_fullurl
Definition XProtocol.hh:358
@ kXR_lclfile
Definition XProtocol.hh:364
@ kXR_multipr
Definition XProtocol.hh:359
@ kXR_redirflags
Definition XProtocol.hh:365
@ kXR_hasipv64
Definition XProtocol.hh:361
int kXR_int32
Definition XPtypes.hh:89
unsigned int kXR_unt32
Definition XPtypes.hh:90
unsigned char kXR_char
Definition XPtypes.hh:65
#define DEBUG(x)
struct stat Stat
Definition XrdCks.cc:49
void usage()
XrdOucTrace * XrdXrootdTrace
#define TRACE_AUTH
#define TRACE_REDIR
#define ENODATA
#define stat(a, b)
Definition XrdPosix.hh:101
XrdSecBuffer XrdSecParameters
XrdSecBuffer XrdSecCredentials
int Mode
XrdOucString Path
#define eMsg(x)
struct myOpts opts
int emsg(int rc, char *msg)
#define Prep_EVICT
int XrdSfsMode
#define SFS_DATAVEC
#define SFS_O_HNAME
#define Prep_FRESH
const char * Arg1
PLUGINO, PLUGION, PLUGXC.
#define SFS_DATA
int Arg2Len
Length or -count of args in extension.
#define Prep_CANCEL
#define SFS_O_RESET
#define SFS_O_DIRLIST
#define SFS_FSCTL_STATFS
#define Prep_QUERY
char * notify
Notification path or 0.
XrdOucTList * paths
List of paths.
XrdOucTList * oinfo
1-to-1 correspondence of opaque info
#define SFS_ERROR
#define Prep_WMODE
#define SFS_LCLROOT(x)
#define SFS_O_SEQIO
#define SFS_O_NOTPC
#define SFS_O_FORCE
#define SFS_O_POSC
#define SFS_FCTL_STATV
#define SFS_REDIRECT
#define Prep_PRTY3
#define Prep_PRTY0
#define SFS_O_MKPTH
#define Prep_PRTY2
#define SFS_STALL
#define SFS_O_RDONLY
#define SFS_STARTED
#define Prep_COLOC
#define SFS_O_MULTIW
#define SFS_FSCTL_STATLS
#define Prep_STAGE
#define SFS_FSCTL_STATCC
char * reqid
Request ID.
#define SFS_O_WRONLY
#define SFS_O_CREAT
#define SFS_FSCTL_STATXA
#define SFS_FSCTL_LOCATE
#define SFS_O_RAWIO
#define SFS_O_RDWR
#define Prep_PRTY1
#define Prep_SENDACK
#define SFS_FSCTL_PLUGIO
#define SFS_O_LOCAL
int XrdSfsFileOpenMode
int Arg1Len
Length.
#define SFS_FCTL_SPEC1
#define SFS_OK
long long XrdSfsFileOffset
#define SFS_LCLPATH(x)
#define SFS_O_NOWAIT
#define SFS_FSCTL_PLUGXC
int opts
Prep_xxx.
#define SFS_O_REPLICA
#define SFS_FSCTL_PLUGIN
#define SFS_O_TRUNC
int XrdSfsXferSize
#define Prep_SENDAOK
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
const int SYS_LOG_01
if(ec< 0) ec
XrdSys::RAtomic< unsigned int > RAtomic_uint
#define TRACING(x)
Definition XrdTrace.hh:70
#define TRACEI(act, x)
Definition XrdTrace.hh:66
XrdOucString * XrdXrootdCF
#define JOB_Sync
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 XRD_BOUNDPATH
#define XRD_LOGGEDIN
#define XRD_NEED_AUTH
#define TRACE_FS
#define TRACEP(act, x)
#define XROOTDXP_NOLK
#define XROOTDXP_NOSLASH
#define XROOTDXP_NOMWCHK
#define XROOTDXP_NOCGI
#define Map_Mode(x, y)
#define STATIC_REDIRECT(xfnc)
#define CRED
static const char * errName(kXR_int32 errCode)
Definition XProtocol.cc:130
static int mapError(int rc)
static const int ValuSize
Definition XrdCksData.hh:42
static const int NameSize
Definition XrdCksData.hh:41
static bool GetAssumeV4()
Definition XrdInet.hh:65
XrdJob(const char *desc="")
Definition XrdJob.hh:51
static XrdLink * fd2link(int fd)
Definition XrdLinkCtl.hh:72
static bool RegisterCloseRequestCb(XrdLink *lp, XrdProtocol *pp, bool(*cb)(void *), void *cbarg)
bool isMapped() 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
XrdOucEICB * getErrCB()
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.
int length() const
const char * c_str() const
XrdOucTList * next
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)
XrdSfsDio()
Constructor and destructor.
Definition XrdSfsDio.hh:103
virtual int autoStat(struct stat *buf)
virtual int open(const char *path, const XrdSecEntity *client=0, const char *opaque=0)=0
XrdOucErrInfo & error
virtual const char * nextEntry()=0
virtual int close()=0
virtual int sync()=0
XrdOucErrInfo & error
virtual int open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client=0, const char *opaque=0)=0
virtual int truncate(XrdSfsFileOffset fsize)=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)
XrdXrootdPgwFob * pgwFob
XrdSfsFile * XrdSfsp
XrdXrootdFileStats Stats
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)
XrdXrootdPio * Next
XrdXrootd::IOParms IO
int(XrdXrootdProtocol::* ResumePio)()
static XrdXrootdPio * Alloc(int n=1)
kXR_char StreamID[2]
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)
XrdSecProtect * Protect
XrdNetPMark::Handle * pmHandle
static XrdNetPMark * PMark
XrdXrootdProtocol * Stream[maxStreams]
XrdXrootd::IOParms IO
static XrdXrootdXPath RPList
static const char Req_TLSGPFile
static bool CloseRequestCb(void *cbarg)
void SetFD(int fildes) override
static const char Req_TLSSess
XrdXrootdWVInfo * wvInfo
XrdSysSemaphore * reTry
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
static const int maxPio
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
XrdXrootdPio * pioFirst
XrdSysCondVar2 * endNote
static XrdSfsFileSystem * osFS
void Set(XrdLink *lp)
static const int maxRvecsz
Definition XProtocol.hh:686
static const int maxWvecsz
Definition XProtocol.hh:838
static const uint64_t hasCACH
Feature: Implements a data cache.
static const uint64_t hasSXIO
Feature: Supports SfsXio.
ssize_t Send(int fd, KernelBuffer &buffer)
char * bifResp[2]
static const kXR_int32 doSync
Definition XProtocol.hh:826
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 uVMask
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
static const int uPrip
long long offset
char * buffer
Pointer to the buffer.
int size
Size of the buffer or length of data in the buffer.
unsigned int Sid
unsigned int Inst
static const int useSF
static const int useBasic
static const int useMMap
netInfo(const char *id)