/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* (C) COPYRIGHT International Business Machines Corp. 2002,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 */ /*==================================================================== * * tsreaddir command: Recursively traverse the directory structure * to list all files/directories in the file system. * * Syntax: tsreaddir [-d] [-n] [-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 noDIR = 0; int onlyDIR = 0; int optcheck = 1; char *RootFsDirP; /* Define the number of threads used to read the directories */ #define DEFAULT_THREADS 16 int nThreads = DEFAULT_THREADS; /* 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; /* 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 [-d] [-n] [-t nThreads (def=%d)] [--] pathname\n", basename(argv0), DEFAULT_THREADS); exit(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); 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; const gpfs_direntx_t *direntxP; gpfs_fssnap_handle_t *fsP = NULL; gpfs_ifile_t *dirxP = NULL; char *typeP, *slashP; int doprint; /* 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; } /* 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"; doprint = !noDIR; break; case GPFS_DE_REG: typeP = "REG"; doprint = !onlyDIR; break; case GPFS_DE_LNK: typeP = "LNK"; doprint = !onlyDIR; break; default: typeP = "UNK"; doprint = !onlyDIR; } if (doprint) { 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/generation type pathname (leading 0s on inodenumber/generation allow join command to do character sort to merge output with tstimes.) */ plen = printf("%010d/%010d\t%s\t%s%s%s\n", direntxP->d_ino, direntxP->d_gen, typeP, pathP, slashP, direntxP->d_name); 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; 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], "-n")) noDIR = 1; else if (0 == strcmp(argv[i], "-d")) onlyDIR = 1; 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], "--")) 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) 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; } if (!noDIR) { /* Print file info on stdout */ rc = printf("%010d/%010d\tDIR\t%s\n", statBuf.st_ino, 1, RootFsDirP); if (rc < 0) return errno; } /* Add the root directory to the work queue */ rc = EnqueueWork("", "", 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); return exitrc; }