/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* (C) COPYRIGHT International Business Machines Corp. 2003,2006 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ /*==================================================================== * * tsfindinode command: Recursively traverse the directory structure * to list all files/directories in the file system * that have any of the inode numbers listed. * * Syntax: tsfindinode {-i {inum | inumfile}} [-0] [-t nThreads] [--] * * The "pathname" parameter should be the directory where the root of * the file system is found. * *==================================================================*/ #ifdef GPFS_LINUX /* Use 64 bit version of stat, etc. */ #define _LARGEFILE_SOURCE #define _FILE_OFFSET_BITS 64 typedef long long offset_t; #endif #ifdef GPFS_AIX /* Use 64 bit version of stat, etc. */ #ifndef _LARGE_FILES #define _LARGE_FILES #endif #endif #include #include #include #include #include #include #include #include #include #include extern char *basename(char *); int optcheck = 1; char *RootFsDirP; /* Define the number of threads used to read the directories */ #define DEFAULT_THREADS 16 int nThreads = DEFAULT_THREADS; char *print0 = "\n"; /* Define wait queue for threads */ static pthread_mutex_t QueueMutex; static pthread_cond_t QueueCond; static int NWorkersWaiting = 0; static int NWorkersRunning = 0; typedef struct queue_element { struct queue_element *nextP; ino_t ino; char path[4]; } QueueElement; static QueueElement *WorkQueueP = NULL; /* Define mutex to serialize the output */ static pthread_mutex_t OutputMutex; #define MAX_INODE_INCREMENT 1000 static numInodes = 0; static maxInodes = 0; static ino_t *Inode = NULL; static unsigned char *InodeSeen = NULL; /* error code set while in threads to control final error code */ int exitrc = 0; /* print usage message and exit */ void usage(char *argv0) { fprintf(stderr, "Usage: %s {-i {inum | inumfile}} [-0] [-t nThreads (def=%d)] [--] pathname\n" " (-i can be used multiple times to specify another inode number or file of inode numbers)\n", basename(argv0), DEFAULT_THREADS); exit(1); } void addInum(ino_t inode) { int i, j; int low, high; ino_t *newInode = NULL; if (numInodes >= maxInodes) { newInode = (ino_t *)malloc((maxInodes+MAX_INODE_INCREMENT) * sizeof(ino_t)); if (newInode == NULL) { fprintf(stderr, "Too many inodes (%d)\n", maxInodes); exit(1); } if (Inode != NULL) { /* Copy old list of inodes and delete the old array */ memcpy(newInode, Inode, maxInodes * sizeof(ino_t)); free(Inode); } Inode = newInode; maxInodes += MAX_INODE_INCREMENT; } /* Binary search the list */ i = 0; low = 0; high = numInodes - 1; while (low <= high) { i = (low + high)/2; if (inode == Inode[i]) return; /* ignore duplicate entry */ if (low == high) { if (inode > Inode[i]) i++; break; } if (inode < Inode[i]) high = i - 1; else low = i + 1; } /* move all entries down one slot */ for (j=numInodes; j > i; j--) Inode[j] = Inode[j-1]; /* insert entry in sorted array */ Inode[i] = inode; numInodes++; } void getinodes(char * listorname) { FILE *fd; long inum; char linebuf[256]; memset(linebuf, 0, sizeof(linebuf)); if ((listorname[0]>='0') & (listorname[0]<='9')) { /* If arg starts with a digit, assume it is a number for sscanf to parse */ if (sscanf(listorname, "%ld", &inum) == 1) addInum(inum); } else { /* Otherwise, assume the arg is a filename. Read inode numbers from the file until scanf fails to find a number */ if (listorname[0] == '-') { while (scanf("%ld", &inum) == 1) addInum(inum); } else { fd = fopen(listorname, "r"); if (fd != NULL) { /* while (fscanf(fd, "%ld", &inum) == 1) */ while (fgets(linebuf, sizeof(linebuf), fd) != NULL) { if ((linebuf[0]>='0') & (linebuf[0]<='9')) { if (sscanf(linebuf, "%ld", &inum) == 1) addInum(inum); } else fprintf(stdout, "%s\n", linebuf); /* disregard mesg or comment line */ memset(linebuf, 0, sizeof(linebuf)); } fclose(fd); } else { int rc = errno; fprintf(stderr, "Error opening %s: %s\n", listorname, strerror(rc)); exit(1); } } } } /* If there is no list return 1, or if inode is in the list return its index, else return -1. */ int testinode(ino_t inode) { int low, mid, high; /* Binary search the list */ low = 0; high = numInodes - 1; while (low <= high) { mid = (low + high)/2; if (inode == Inode[mid]) return mid; if (low == high) return -1; if (inode < Inode[mid]) high = mid - 1; else low = mid + 1; } return -1; } /* Add an element to the work queue and signal any thread waiting for work. */ int EnqueueWork(const char *pathP, const char *dirP, ino_t inode) { int rc; int pathLen = strlen(pathP); QueueElement *qP = NULL; /* Allocate a work queue element and space for the path. */ qP = (QueueElement *) malloc((sizeof(*qP) - sizeof(qP->path)) + pathLen + strlen(dirP) + 2); if (qP == NULL) { fprintf(stderr, "Out of memory.\n"); return(ENOMEM); } /* Initialize element */ qP->ino = inode; if (pathLen > 0) { strcpy(qP->path, pathP); if (strlen(dirP) > 0) { strcat(qP->path, "/"); strcat(qP->path, dirP); } } else strcpy(qP->path, dirP); /* Acquire mutex protecting the work queue */ rc = pthread_mutex_lock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc)); return rc; } /* Add work element to the list */ qP->nextP = WorkQueueP; WorkQueueP = qP; /* Signal a waiting worker thread */ if (NWorkersWaiting > 0) { NWorkersWaiting -= 1; rc = pthread_cond_signal(&QueueCond); if (rc != 0) { fprintf(stderr, "Error in pthread_cond_signal: %s\n", strerror(rc)); return rc; } } /* Release mutex before returning */ rc = pthread_mutex_unlock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc)); return rc; } /* Success! */ return 0; } /* Dequeue the next work element from the queue or if the queue is empty, wait for more work to become available */ QueueElement *DequeueWork(void) { int rc; QueueElement *qP; /* Acquire mutex protecting the work queue */ rc = pthread_mutex_lock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; return NULL; } /* One less thread working */ NWorkersRunning -= 1; /* Loop waiting for work to become available */ while ((WorkQueueP == NULL) && (NWorkersRunning > 0)) { NWorkersWaiting += 1; /* One more thread waiting */ rc = pthread_cond_wait(&QueueCond, &QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_cond_wait: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; return NULL; } } /* Check if we are done */ if (WorkQueueP == NULL) { qP = NULL; /* Nothing more to do. */ /* signal any waiting threads */ if (NWorkersWaiting > 0) { NWorkersWaiting = 0; rc = pthread_cond_broadcast(&QueueCond); if (rc != 0) { fprintf(stderr, "Error in pthread_cond_broadcast: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; } } } else { /* Remove the first element from the queue */ qP = WorkQueueP; WorkQueueP = qP->nextP; qP->nextP = NULL; /* One more thread working */ NWorkersRunning += 1; } /* Release mutex before returning */ rc = pthread_mutex_unlock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; return NULL; } return qP; } /* Main body for each thread. Loop until there is no more work to do. */ void* ThreadBody(void *parmP) { int rc; QueueElement *qP; int read_dir(const char *, ino_t); /* Count this thread as a worker thread */ rc = pthread_mutex_lock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; return NULL; } NWorkersRunning += 1; rc = pthread_mutex_unlock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc)); NWorkersRunning -= 1; if (exitrc == 0) exitrc = rc; return NULL; } /* Loop while there is work to do */ do { /* Wait for work */ qP = DequeueWork(); if (qP == NULL) break; /* Read this directory */ rc = read_dir(qP->path, qP->ino); if (exitrc == 0) exitrc = rc; /* Free work queue element */ free(qP); } while (rc == 0); rc = pthread_mutex_lock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; return NULL; } NWorkersRunning -= 1; rc = pthread_mutex_unlock(&QueueMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc)); if (exitrc == 0) exitrc = rc; } return NULL; } /* Read all entries from the current directory */ int read_dir(const char *pathP, ino_t inode) { int rc = 0, ind; const gpfs_direntx_t *direntxP; gpfs_fssnap_handle_t *fsP = NULL; gpfs_ifile_t *dirxP = NULL; char *typeP, *slashP; /* Check if we need a "/" */ if (strlen(pathP) > 0) slashP = "/"; else slashP = ""; /* Open the file system */ fsP = gpfs_get_fssnaphandle_by_path(RootFsDirP); if (fsP == NULL) { rc = errno; fprintf(stderr, "gpfs_get_fssnaphandle_by_path(%s): %s\n", RootFsDirP, strerror(rc)); goto exit; } /* If there is only one inode in the list and is one of the reserved inode it cannot possibly be found, so stop the nonsense now. Calling tsfindinode -i 1 can be a fast test as to whether the filesystem is mounted and ready to use. */ if (numInodes == 1 && Inode[0] < 3) goto exit; /* Open the directory's file */ dirxP = gpfs_iopen(fsP, inode, O_RDONLY, NULL, NULL); if (dirxP == NULL) { rc = errno; fprintf(stderr, "gpfs_iopen(%s/%s): %s\n", RootFsDirP, pathP, strerror(rc)); goto exit; } /* Loop reading the directory entries */ while (1) { rc = gpfs_ireaddir(dirxP, &direntxP); if (rc != 0) { int saveerrno = errno; fprintf(stderr, "gpfs_ireaddir(%s/%s): rc %d %s\n", RootFsDirP, pathP, rc, strerror(saveerrno)); rc = saveerrno; goto exit; } if (direntxP == NULL) break; /* Get directory entry type */ switch (direntxP->d_type) { case GPFS_DE_DIR: /* Skip . and .. entries */ if ((strcmp(direntxP->d_name, ".") == 0) || (strcmp(direntxP->d_name, "..") == 0)) continue; typeP = "DIR"; break; case GPFS_DE_REG: typeP = "REG"; break; case GPFS_DE_LNK: typeP = "LNK"; break; default: typeP = "UNK"; } ind = testinode(direntxP->d_ino); if (ind >= 0) { int plen; /* Print file info on stdout. Serialize output with all other threads */ rc = pthread_mutex_lock(&OutputMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc)); goto exit; } /* Print: inodenumber pathname */ plen = printf("%10d\t%s%s%s%s", direntxP->d_ino, pathP, slashP, direntxP->d_name, print0); InodeSeen[ind] = 1; rc = pthread_mutex_unlock(&OutputMutex); if (rc != 0) { fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc)); goto exit; } if (plen < 0) { rc = errno; goto exit; } } /* Check if this entry is a directory */ if (direntxP->d_type == GPFS_DE_DIR) { /* Add this directory to the work queue */ rc = EnqueueWork(pathP, direntxP->d_name, direntxP->d_ino); if (rc != 0) { if (exitrc == 0) exitrc = rc; goto exit; } } } exit: /* Close open file, if necessary */ if (dirxP) gpfs_iclose(dirxP); if (fsP) gpfs_free_fssnaphandle(fsP); return rc; } /* main */ int main(int argc, char *argv[]) { int i, rc, filearg, ind; struct stat statBuf; pthread_t pThread; /* Scan for options and single directory name */ filearg = 0; for (i = 1; i < argc; i++) { if (0 == strcmp(argv[i], "-i")) { i += 1; if (i < argc) getinodes(argv[i]); else usage(argv[0]); } else if (0 == strcmp(argv[i], "-t")) { i += 1; if (i < argc) nThreads = atoi(argv[i]); else usage(argv[0]); } else if (0 == strcmp(argv[i], "-0")) print0 = "\0"; else if (0 == strcmp(argv[i], "--")) optcheck = 0; else if (optcheck && *argv[i] == '-') usage(argv[0]); else if (filearg == 0) filearg = i; else usage(argv[0]); } /* if no directory was specified, there is nothing to do */ if (filearg == 0 || numInodes == 0) usage(argv[0]); RootFsDirP = argv[filearg]; /* Initialize pthread variables */ rc = pthread_mutex_init(&QueueMutex, NULL); if (rc != 0) { fprintf(stderr, "Error from pthread_mutex_init: %s\n", strerror(rc)); exit(rc); } rc = pthread_cond_init(&QueueCond, NULL); if (rc != 0) { fprintf(stderr, "Error from pthread_cond_init: %s\n", strerror(rc)); exit(rc); } rc = pthread_mutex_init(&OutputMutex, NULL); if (rc != 0) { fprintf(stderr, "Error from pthread_mutex_init: %s\n", strerror(rc)); exit(rc); } /* Get the inode number of the root directory */ rc = stat(RootFsDirP, &statBuf); if (rc) { rc = errno; perror(RootFsDirP); return rc; } /* Verify this is a directory */ if (!S_ISDIR(statBuf.st_mode)) { errno = ENOTDIR; perror(RootFsDirP); return ENOTDIR; } InodeSeen = (unsigned char *) malloc(maxInodes); if (InodeSeen == NULL) { fprintf(stderr, "Out of memory.\n"); return ENOMEM; } memset(InodeSeen, 0, maxInodes); ind = testinode(statBuf.st_ino); if (ind >= 0) { /* Print file info on stdout */ rc = printf("%10d\t%s%s", statBuf.st_ino, RootFsDirP, print0); if (rc < 0) return errno; InodeSeen[ind] = 1; } /* Add the root directory to the work queue */ rc = EnqueueWork(RootFsDirP, "", statBuf.st_ino); if (rc != 0) return rc; /* Start the requested number of threads */ for (i = 1; i < nThreads; i++) { rc = pthread_create(&pThread, NULL, ThreadBody, NULL); if (rc != 0) { fprintf(stderr, "Error from pthread_create(%d): %s\n", i, strerror(rc)); exit(rc); } } /* This thread can work too */ (void) ThreadBody(NULL); for (ind=0; ind < numInodes; ind++) if (!InodeSeen[ind]) fprintf(stderr, "%10d\t(notfound)\n", Inode[ind]); return exitrc; }