XRootD
XrdOucNSWalk.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O u c N S W a l k . c c */
4 /* */
5 /* (c) 2009 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cctype>
32 #include <cstring>
33 #include <dirent.h>
34 #include <unistd.h>
35 
36 #include "XrdOuc/XrdOucNSWalk.hh"
37 #include "XrdOuc/XrdOucTList.hh"
38 #include "XrdSys/XrdSysE2T.hh"
39 #include "XrdSys/XrdSysError.hh"
40 #include "XrdSys/XrdSysHeaders.hh"
41 #include "XrdSys/XrdSysPlatform.hh"
42 
43 
44 /******************************************************************************/
45 /* C o n s t r u c t o r */
46 /******************************************************************************/
47 
48 XrdOucNSWalk::XrdOucNSWalk(XrdSysError *erp, const char *dpath,
49  const char *lkfn, int opts,
50  XrdOucTList *xlist)
51 {
52 // Set the required fields
53 //
54  eDest = erp;
55  mPfx = 0;
56  DList = new XrdOucTList(dpath);
57  if (lkfn) LKFn = strdup(lkfn);
58  else LKFn = 0;
59  Opts = opts;
60  DPfd = LKfd = -1;
61  errOK= opts & skpErrs;
62  DEnts= 0;
63  edCB = 0;
64 
65 // Copy the exclude list if one exists
66 //
67  if (!xlist) XList = 0;
68  else while(xlist)
69  {XList = new XrdOucTList(xlist->text,xlist->ival,XList);
70  xlist = xlist->next;
71  }
72 }
73 
74 /******************************************************************************/
75 /* D e s t r u c t o r */
76 /******************************************************************************/
77 
79 {
80  XrdOucTList *tP;
81 
82  if (LKFn) free(LKFn);
83 
84  while((tP = DList)) {DList = tP->next; delete tP;}
85 
86  while((tP = XList)) {XList = tP->next; delete tP;}
87 }
88 
89 /******************************************************************************/
90 /* I n d e x */
91 /******************************************************************************/
92 
93 XrdOucNSWalk::NSEnt *XrdOucNSWalk::Index(int &rc, const char **dPath)
94 {
95  XrdOucTList *tP;
96  NSEnt *eP;
97 
98 // Sequence the directory
99 //
100  rc = 0; *DPath = '\0';
101  while((tP = DList))
102  {setPath(tP->text);
103  DList = tP->next; delete tP;
104  if (LKFn && (rc = LockFile())) break;
105  rc = Build();
106  if (LKfd >= 0) close(LKfd);
107  if (DEnts || (rc && !errOK)) break;
108  if (edCB && isEmpty) edCB->isEmpty(&dStat, DPath, LKFn);
109  }
110 
111 // Return the result
112 //
113  eP = DEnts; DEnts = 0;
114  if (dPath) *dPath = DPath;
115  return eP;
116 }
117 
118 /******************************************************************************/
119 /* P r i v a t e M e t h o d s */
120 /******************************************************************************/
121 /******************************************************************************/
122 /* a d d E n t */
123 /******************************************************************************/
124 
125 void XrdOucNSWalk::addEnt(XrdOucNSWalk::NSEnt *eP)
126 {
127  static const int retIxLO = retIDLO | retIILO;
128 
129 // Complete the entry
130 //
131  if (Opts & noPath) {eP->Path = strdup(File); eP->File = eP->Path;}
132  else {eP->Path = strdup(DPath);
133  eP->File = eP->Path + (File - DPath);
134  }
135  eP->Plen = (eP->File - eP->Path) + strlen(eP->File);
136 
137 // Chain the entry into the list
138 //
139  if (!(Opts & retIxLO)) {eP->Next = DEnts; DEnts = eP;}
140  else {NSEnt *pP = 0, *nP = DEnts;
141  if (Opts & retIDLO)
142  while(nP && eP->Plen < nP->Plen) {pP = nP; nP = nP->Next;}
143  else
144  while(nP && eP->Plen > nP->Plen) {pP = nP; nP = nP->Next;}
145  if (pP) {eP->Next = nP; pP->Next = eP;}
146  else {eP->Next = nP; DEnts = eP;}
147  }
148 }
149 
150 /******************************************************************************/
151 /* B u i l d */
152 /******************************************************************************/
153 
154 int XrdOucNSWalk::Build()
155 {
156  struct Helper {XrdOucNSWalk::NSEnt *P;
157  DIR *D;
158  int F;
159  Helper() : P(0), D(0), F(-1) {}
160  ~Helper() {if (P) delete P;
161  if (D) closedir(D);
162  if (F>0) close(F);
163  }
164  } theEnt;
165  struct dirent *dp;
166  int rc = 0, getLI = Opts & retLink;
167  int nEnt = 0, xLKF = 0, chkED = (edCB != 0) && (LKFn != 0);
168 
169 // Initialize the empty flag prior to doing anything else
170 //
171  isEmpty = 0;
172 
173 // If we can optimize with a directory file descriptor, get one
174 //
175 #ifdef HAVE_FSTATAT
176  if ((DPfd = open(DPath, O_RDONLY)) < 0) rc = errno;
177  else theEnt.F = DPfd;
178 #else
179  DPfd = -1;
180 #endif
181 
182 // Open the directory
183 //
184  if (!(theEnt.D = opendir(DPath)))
185  return Emsg("Build", errno, "open directory", DPath);
186 
187 // Process the entries
188 //
189  errno = 0;
190  while((dp = readdir(theEnt.D)))
191  {if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
192  strcpy(File, dp->d_name); nEnt++;
193  if (!theEnt.P) theEnt.P = new NSEnt();
194  rc = getStat(theEnt.P, getLI);
195  switch(theEnt.P->Type)
196  {case NSEnt::isDir:
197  if (Opts & Recurse && (!getLI || !isSymlink())
198  && (!XList || !inXList(File)))
199  DList = new XrdOucTList(DPath, 0, DList);
200  if (!(Opts & retDir)) continue;
201  break;
202  case NSEnt::isFile:
203  if ((chkED && !xLKF && (xLKF = !strcmp(File, LKFn)))
204  || !(Opts & retFile)) continue;
205  break;
206  case NSEnt::isLink:
207  if ((rc = getLink(theEnt.P)))
208  memset(&theEnt.P->Stat, 0, sizeof(struct stat));
209  else if ((Opts & retStat) && (rc = getStat(theEnt.P)))
210  {theEnt.P->Type = NSEnt::isLink; rc = 0;}
211  break;
212  case NSEnt::isMisc:
213  if (!(Opts & retMisc)) continue;
214  break;
215  default:
216  if (!rc) rc = EINVAL;
217  break;
218  }
219  errno = 0;
220  if (rc) {if (errOK) continue; return rc;}
221  addEnt(theEnt.P); theEnt.P = 0;
222  }
223 
224 // All done, check if we reached EOF or there is an error
225 //
226  *File = '\0';
227  if ((rc = errno) && !errOK)
228  return Emsg("Build", rc, "read directory", DPath);
229 
230 // Check if we need to do a callback for an empty directory
231 //
232  if (edCB && xLKF == nEnt && !DEnts)
233  {if ((DPfd < 0 ? !stat(DPath, &dStat) : !fstat(DPfd, &dStat))) isEmpty=1;
234  else Emsg("Build", errno, "stat directory", DPath);
235  }
236  return 0;
237 }
238 
239 /******************************************************************************/
240 /* E m s g */
241 /******************************************************************************/
242 
243 int XrdOucNSWalk::Emsg(const char *pfx, int rc, const char *txt1,
244  const char *txt2)
245 {
246  if (eDest) eDest->Emsg(pfx, rc, txt1, txt2);
247  else if (mPfx)
248  {const char *etxt = XrdSysE2T(rc);
249  std::cerr <<mPfx <<": Unable to " <<txt1;
250  if (txt2) std::cerr <<' ' <<txt2;
251  std::cerr <<"; " <<(etxt) <<"\n" << std::flush;
252  }
253  return rc;
254 }
255 
256 /******************************************************************************/
257 /* g e t L i n k */
258 /******************************************************************************/
259 
260 int XrdOucNSWalk::getLink(XrdOucNSWalk::NSEnt *eP)
261 {
262  char lnkbuff[2048];
263  int rc;
264 
265  if ((rc = readlink(DPath, lnkbuff, sizeof(lnkbuff))) < 0)
266  return Emsg("getLink", errno, "read link of", DPath);
267 
268  eP->Lksz = rc;
269  eP->Link = (char *)malloc(rc+1);
270  memcpy(eP->Link, lnkbuff, rc);
271  *(eP->Link+rc) = '\0';
272  return 0;
273 }
274 
275 /******************************************************************************/
276 /* g e t S t a t */
277 /******************************************************************************/
278 
279 int XrdOucNSWalk::getStat(XrdOucNSWalk::NSEnt *eP, int doLstat)
280 {
281  int rc;
282 
283 // The following code either uses fstatat() or regular stat()
284 //
285 #ifdef HAVE_FSTATAT
286 do{rc = fstatat(DPfd, File, &(eP->Stat), (doLstat ? AT_SYMLINK_NOFOLLOW : 0));
287 #else
288 do{rc = doLstat ? lstat(DPath, &(eP->Stat)) : stat(DPath, &(eP->Stat));
289 #endif
290  } while(rc && errno == EINTR);
291 
292 // Check for errors
293 //
294  if (rc)
295  {rc = errno;
296  if (rc != ENOENT && rc != ELOOP) Emsg("getStat", rc, "stat", DPath);
297  memset(&eP->Stat, 0, sizeof(struct stat));
298  eP->Type = (rc == ENOENT ? NSEnt::isMisc : NSEnt::isBad);
299  return rc;
300  }
301 
302 // Set appropraite type
303 //
304  if ((eP->Stat.st_mode & S_IFMT) == S_IFDIR) eP->Type = NSEnt::isDir;
305  else if ((eP->Stat.st_mode & S_IFMT) == S_IFREG) eP->Type = NSEnt::isFile;
306  else if ((eP->Stat.st_mode & S_IFMT) == S_IFLNK) eP->Type = NSEnt::isLink;
307  else eP->Type = NSEnt::isMisc;
308 
309  return 0;
310 }
311 
312 /******************************************************************************/
313 /* i n X L i s t */
314 /******************************************************************************/
315 
316 int XrdOucNSWalk::inXList(const char *dName)
317 {
318  XrdOucTList *xTP = XList, *pTP = 0;
319 
320 // Search for the directory entry
321 //
322  while(xTP && strcmp(DPath, xTP->text)) {pTP = xTP; xTP = xTP->next;}
323 
324 // If not found return false. Otherwise, delete the entry and return true.
325 //
326  if (!xTP) return 0;
327  if (pTP) pTP->next = xTP->next;
328  else XList = xTP->next;
329  delete xTP;
330  return 1;
331 }
332 
333 /******************************************************************************/
334 /* i s S y m l i n k */
335 /******************************************************************************/
336 
337 int XrdOucNSWalk::isSymlink()
338 {
339  struct stat buf;
340  int rc;
341 
342 
343 // The following code either uses fstatat() or regular stat()
344 //
345 #ifdef HAVE_FSTATAT
346 do{rc = fstatat(DPfd, File, &buf, AT_SYMLINK_NOFOLLOW);
347 #else
348 do{rc = lstat(DPath, &buf);
349 #endif
350  } while(rc && errno == EINTR);
351 
352 // Check for errors
353 //
354  if (rc) return 0;
355  return (buf.st_mode & S_IFMT) == S_IFLNK;
356 }
357 
358 /******************************************************************************/
359 /* L o c k F i l e */
360 /******************************************************************************/
361 
362 int XrdOucNSWalk::LockFile()
363 {
364  FLOCK_t lock_args;
365  int rc;
366 
367 // Construct the path and open the file
368 //
369  strcpy(File, LKFn);
370  do {LKfd = open(DPath, O_RDWR);} while(LKfd < 0 && errno == EINTR);
371  if (LKfd < 0)
372  {if (errno == ENOENT) {*File = '\0'; return 0;}
373  {*File = '\0';
374  return Emsg("LockFile", errno, "open", DPath);
375  }
376  }
377 
378 // Establish locking options
379 //
380  bzero(&lock_args, sizeof(lock_args));
381  lock_args.l_type = F_WRLCK;
382 
383 // Perform action.
384 //
385  do {rc = fcntl(LKfd,F_SETLKW,&lock_args);}
386  while(rc < 0 && errno == EINTR);
387  if (rc < 0) rc = Emsg("LockFile", errno, "lock", DPath);
388 
389 // All done
390 //
391  *File = '\0';
392  return rc;
393 }
394 
395 /******************************************************************************/
396 /* s e t P a t h */
397 /******************************************************************************/
398 
399 void XrdOucNSWalk::setPath(char *newpath)
400 {
401  int n;
402 
403  strcpy(DPath, newpath);
404  n = strlen(newpath);
405  if (DPath[n-1] != '/')
406  {DPath[n++] = '/'; DPath[n] = '\0';}
407  File = DPath+n;
408 }
struct stat Stat
Definition: XrdCks.cc:49
static XrdSysError eDest(0,"crypto_")
int stat(const char *path, struct stat *buf)
struct dirent * readdir(DIR *dirp)
int open(const char *path, int oflag,...)
int fstat(int fildes, struct stat *buf)
int lstat(const char *path, struct stat *buf)
int fcntl(int fd, int cmd,...)
int closedir(DIR *dirp)
DIR * opendir(const char *path)
#define close(a)
Definition: XrdPosix.hh:43
XrdOucString File
struct myOpts opts
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
if(Avsz)
#define FLOCK_t
virtual void isEmpty(struct stat *dStat, const char *dPath, const char *lkFn)=0
static const int retFile
XrdOucNSWalk(XrdSysError *erp, const char *dname, const char *LKfn=0, int opts=retAll, XrdOucTList *xP=0)
Definition: XrdOucNSWalk.cc:48
static const int retIDLO
static const int retLink
static const int skpErrs
static const int noPath
static const int retStat
static const int retMisc
NSEnt * Index(int &rc, const char **dPath=0)
Definition: XrdOucNSWalk.cc:93
static const int retDir
static const int Recurse
static const int retIILO
XrdOucTList * next
Definition: XrdOucTList.hh:45
char * text
Definition: XrdOucTList.hh:46
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
int Opts
Definition: XrdMpxStats.cc:58
struct NSEnt * Next
Definition: XrdOucNSWalk.hh:48