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