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
#define SFS_DATA
#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
#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.
const char * Arg1
PLUGINO, PLUGION, PLUGXC.
int Arg2Len
Length or -count of args in extension.
int Arg1Len
Length.
unsigned int Sid
unsigned int Inst
static const int useSF
static const int useBasic
static const int useMMap
netInfo(const char *id)