source: gpfs_3.1_ker2.6.20/lpp/mmfs/samples/util/tsbackup.C @ 16

Last change on this file since 16 was 16, checked in by rock, 16 years ago
File size: 194.1 KB
Line 
1/* IBM_PROLOG_BEGIN_TAG                                                   */
2/* This is an automatically generated prolog.                             */
3/*                                                                        */
4/*                                                                        */
5/*                                                                        */
6/* Licensed Materials - Property of IBM                                   */
7/*                                                                        */
8/* (C) COPYRIGHT International Business Machines Corp. 2004,2006          */
9/* All Rights Reserved                                                    */
10/*                                                                        */
11/* US Government Users Restricted Rights - Use, duplication or            */
12/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
13/*                                                                        */
14/* IBM_PROLOG_END_TAG                                                     */
15#pragma comment (copyright, "@(#)Licensed Materials-Property of IBM")
16static char sccsid[] = "@(#)46  1.5  src/avs/fs/mmfs/samples/util/tsbackup.C, mmfs, avs_rgpfs24, rgpfs24s001a 4/4/06 09:37:12";
17
18/*===========================================================================
19 *
20 * tsbackup:  a utility for backing up a GPFS filesystem to a TSM server
21 *
22 * Syntax:
23 *
24 *   tsbackup Device MountPoint -n ControlFile [-t {full | incremental}]
25 *                                                         [-r IOrate] [-T]
26 *     or
27 *
28 *   tsbackup Device MountPoint -R [-r IOrate] [-T]
29 *
30 *==========================================================================*/
31
32#ifdef GPFS_LINUX
33/* Use 64 bit version of stat, etc. */
34#define _LARGEFILE_SOURCE
35#define _FILE_OFFSET_BITS 64
36
37typedef long long offset_t;
38#endif
39
40#ifdef GPFS_AIX
41/* Use 64 bit version of stat, etc. */
42#ifndef _LARGE_FILES
43#define _LARGE_FILES
44#endif
45#endif
46
47#include <stdio.h>
48#include <stdlib.h>
49#include <errno.h>
50#include <unistd.h>
51#include <string.h>
52#include <fcntl.h>
53#include <assert.h>
54
55// The following includes are for pipeOpen(), pipeClose(), etc.
56#include <signal.h>
57#include <sys/wait.h>
58#include <errno.h>
59#include <sys/stat.h>
60#include <sys/types.h>
61#include <pthread.h>
62
63extern "C"
64{
65#include <time.h>
66#include <ctype.h>
67}
68
69#include <gpfs.h>
70
71#include "tsbackup.h"        // all relevant structures for backup
72
73
74/* forward declarations */
75extern "C" char *basename(const char *);
76
77/* globals for the tsbackup program */
78
79char deviceName[MAX_FILE_NAME];
80char fsName[MAX_FILE_NAME];
81char inputCtrlFile[MAX_FILE_NAME];
82const char* snapshotPathname;
83char snapshotSubdir[MAX_FILE_NAME];
84char sstring[MAX_COMMAND_STRING];
85pid_t msgChildPid = 0;
86gpfs_fssnap_id_t prevSnapId;
87
88static char *transactionCmdExpire = "expire";
89static char *transactionCmdIncremental = "incremental";
90static char *transactionCmdSelective = "selective";
91static char *transactionCmdOption = "filelist";
92
93static char *backupControlName    = ".mmbuControl";
94static char *backupShadowName     = ".mmbuShadow";
95static char *shadowCheckName      = ".mmbuShadowCheck";
96static char *backupFilesizesName  = ".mmbuFilesizes";
97static char *fsSnapshotName_base  = ".mmbuSnapshot";
98static char *transListName        = ".mmbuTrans";
99static char *pendingDeletionsName = ".mmbuPendingDels";
100static char *pendingChangesName   = ".mmbuPendingChgs";
101static char *changesName          = ".mmbuChanges";
102static char *deletionsName        = ".mmbuDeletions";
103
104inodeBitsArray inodeBitsP = NULL;
105inodeBitsArray inodeBits2P = NULL;
106
107char *RootFsDirP;
108
109/* Define the number of threads used to read the directories. */
110#define DEFAULT_THREADS 24
111int nThreads = DEFAULT_THREADS;
112
113/* Define wait queue for threads. */
114static pthread_mutex_t QueueMutex;
115static pthread_cond_t QueueCond;
116static int NWorkersWaiting = 0;
117static int NWorkersRunning = 0;
118
119/* Define mutex to serialize the output. */
120static pthread_mutex_t OutputMutex;
121
122static QueueElement *WorkQueueP = NULL;
123
124
125/*
126 * Define storage for variables declared in tsbackup.h
127 */
128Int32 ioRate = 100;                /* I/O rate */
129Int32 Tracing = 0;                 /* tracing control flag */
130Int32 Full;                        /* backup type full (true or false) */
131Int32 Incremental;                 /* backup type incremental (true or false) */
132Int32 Resume;                      /* resume backup (true or false) */
133char  masterNode[MAX_NAME_CHARS];  /* node on which tsbackup was invoked */
134Int32 masterPID;                   /* pid for master tsbackup process */
135
136char  fsBackupCtrl[MAX_FILE_NAME];
137char  fsSnapshotName[MAX_FILE_NAME];
138char  fsSnapshotPathname[MAX_FILE_NAME];
139gpfs_fssnap_handle_t *fsSnapHandleP = NULL;
140char  filesizesFile[MAX_FILE_NAME];
141Int32 filesizesHandle;
142char  backupShadowFile[MAX_FILE_NAME];
143char  backupShadowCheck[MAX_FILE_NAME];
144char  shadowFile[MAX_BACKUP_CLIENTS][MAX_FILE_NAME];
145Int32 shadowFileHandle[MAX_BACKUP_CLIENTS];
146Int64 shadowFileNumberOfRecords[MAX_BACKUP_CLIENTS];
147char  transactionsList[MAX_FILE_NAME];
148Int32 transactionsListHandle;
149char  clientTransactionsList[MAX_BACKUP_CLIENT_PROCESSES][MAX_FILE_NAME];
150Int32 clientTransactionsListHandle[MAX_BACKUP_CLIENT_PROCESSES];
151char  pendingTransactionsList[MAX_FILE_NAME];
152char  pendingTransactionsListName[MAX_FILE_NAME];
153char  clientPendingTransactionsList[MAX_BACKUP_CLIENT_PROCESSES][MAX_FILE_NAME];
154char  changesFile[MAX_FILE_NAME];
155Int32 fp_chg;
156char  deletionsFile[MAX_FILE_NAME];
157Int32 fp_del;
158Boolean fileDeletions;
159Boolean fileChanges;
160gpfs_backup_control_t *backupControlP;
161
162extern int errno;
163
164#ifndef true
165#define true    1
166#endif
167
168#ifndef false
169#define false   0
170#endif
171
172
173/*
174 * print usage message and exit
175 */
176static void usage(char *argv0)
177{
178  fprintf(stderr, "Usage:\n %s Device MountPoint -n ControlFile [-t {full | incremental}] [-r IOrate]\n    or\n %s Device MountPoint -R [-r IOrate]\n", basename(argv0), basename(argv0));
179  exit(RC_FAIL);
180}
181
182
183#define TSSIZE 128       /* time buffer size */
184
185/*
186 * print trace line for entering a function
187 */
188void traceEntry(char* filen, char* funcn, Int32 linen)
189{
190  time_t nowTime;
191  char timebuf[TSSIZE];
192  struct tm CurrentTime;
193
194  time(&nowTime);
195  strftime(timebuf, TSSIZE, "%X" , localtime_r(&nowTime, &CurrentTime));
196  fprintf(stderr, "Trace: time %s, %s, line %d: enter %s()\n",
197            timebuf, filen, linen, funcn);
198}
199
200
201/*
202 * print trace line for exiting a function
203 */
204void traceExit(char* filen, char* funcn, Int32 linen)
205{
206  time_t nowTime;
207  char timebuf[TSSIZE];
208  struct tm CurrentTime;
209
210  time(&nowTime);
211  strftime(timebuf, TSSIZE, "%X" , localtime_r(&nowTime, &CurrentTime));
212  fprintf(stderr, "Trace: time %s, %s, line %d: exit  %s()\n",
213            timebuf, filen, linen, funcn);
214}
215
216
217/*
218 * print trace line other than a function entry or exit
219 */
220void traceLine(char* filen, char* funcn, Int32 linen)
221{
222  time_t nowTime;
223  char timebuf[TSSIZE];
224  struct tm CurrentTime;
225
226  time(&nowTime);
227  strftime(timebuf, TSSIZE, "%X" , localtime_r(&nowTime, &CurrentTime));
228  fprintf(stderr, "Trace: time %s, %s, line %d, %s()\n",
229            timebuf, filen, linen, funcn);
230}
231
232#if 0
233#ifndef TIMELEN
234#define TIMELEN 26
235#endif
236
237/* Return ptr to buffer passed in with current time
238   filled in the buffer (year removed) */
239char *
240consoleTime(char timeBufP[TIMELEN])
241{
242  time_t TimeNow;
243  char *p;
244
245  time(&TimeNow);
246  ctime_r(&TimeNow, timeBufP);
247  if ((p = strchr(timeBufP, '\n')) != NULL)
248    *p = '\0';
249  return timeBufP;
250}
251#endif
252
253
254/* signal handler for SIGINT */
255void sig_interrupt(int signo)
256{
257  Int32 j, tmp;
258  struct stat statBuf;
259
260  /* Delete any temporary files still lying around. */
261
262  /* Delete any existing client transactions files. */
263  for (j = 0; j < MAX_BACKUP_CLIENT_PROCESSES; j++)
264  {
265    unlink(clientTransactionsList[j]);
266    unlink(clientPendingTransactionsList[j]);
267  }
268
269  /* Delete the original transactions file now that we are done with it. */
270  unlink(transactionsList);
271
272  unlink(changesFile);
273  unlink(filesizesFile);
274  unlink(deletionsFile);
275
276  /* Delete the snapshot if it exists. */
277  if (stat(fsSnapshotPathname, &statBuf) != -1)
278  {
279    /* Delete the snapshot. */
280    sprintf(sstring, "/usr/lpp/mmfs/bin/tsdelsnapshot %s %s > /dev/null",
281              deviceName, fsSnapshotName);
282    tmp = system(sstring);
283
284    /* Check system() return. */
285    if (tmp != 0)
286    {
287      fprintf(stderr, "system(%s) returned rc = %d\n", sstring, tmp);
288    }
289  }
290
291  /* If there is a backup control data structure, set the completion level
292     to none and write the updated backup control data to the control file. */
293  if (backupControlP != 0)
294  {
295    backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_NONE;
296    tmp = createBackupCtrlFile(fsBackupCtrl, backupControlP);
297  }
298
299  exit(RC_FAIL);
300}
301
302
303/*------*
304 * main *
305 *------*/
306
307int main(int argc, char *argv[])
308{
309  char linein[LINE_LENGTH];        /* buffer used in reading a line via fget */
310  Int32 tmp, rc = RC_SUCCESS;
311  Int32 length;
312  Int32 i, j, k;
313  int c;
314  struct stat statBuf;
315#if 0
316  char timeBufP[TIMELEN];
317#endif
318  char timeString[MAX_FILE_NAME];
319  FILE* filePtr = NULL;            /* file pointer for temp files and usage
320                                                                  in popen() */
321  static char *devPrefix            = "/dev/";
322  static char *mpPrefix             = "/";
323  gpfs_direntx_t *shadow_controlP;
324  gpfs_backup_control_t *backupControlLocalP = NULL;
325  backup_control_hdr_t *backupControlHdrP = NULL;
326  char* cp;
327  char* backupType;
328  char* fn = "main";
329
330  /* Set signal handler for SIGINT */
331  if (signal(SIGINT, sig_interrupt) == SIG_ERR)
332  {
333    printf("tsbackup: Unable to set signal handler for SIGINT.\n");
334  }
335
336  /* At least two parms must be passed, since the device and
337     the mountpoint parms are required. */
338  if (argc < 3)
339  {
340    fprintf(stderr, "An incorrect number of parameters was passed.\n");
341    usage(argv[0]);
342  }
343
344  /* Set the backup type flags prior to parsing the command parms. */
345  Incremental = false;
346  Full = false;
347  Resume = false;
348
349  /* Parse the command parameters. */
350
351  /* The first arg is the device parameter, and it is mandatory. */
352  strcpy(deviceName, argv[1]);
353  if ((strncmp(deviceName, devPrefix, strlen(devPrefix)) != 0) ||
354        (strcmp(deviceName, devPrefix) == 0))
355  {
356    fprintf(stderr, "An incorrect parameter (%s) was specified for the device.\nThe value must begin with /dev/.\n", deviceName);
357    usage(argv[0]);
358  }
359
360  /* The second arg is the mountpoint parameter, and it is also mandatory. */
361  strcpy(fsName, argv[2]);
362  if ((strncmp(fsName, mpPrefix, strlen(mpPrefix)) != 0) ||
363        (strcmp(fsName, mpPrefix) == 0))
364  {
365    fprintf(stderr, "An incorrect parameter (%s) was specified for the mountpoint.\nThe value must begin with a /.\n", fsName);
366    usage(argv[0]);
367  }
368
369  /* Clear the input control file string before processing parameters. */
370  *inputCtrlFile = '\0';
371
372  /* Adjust argc and argv now that we've processed the first two parameters. */
373  argc = argc - 2;
374  argv[2] = argv[0];
375  argv = &argv[2];
376
377  /* Parse the remainder of the parameter string. */
378  while ((c = getopt(argc, argv, "n:r:Rt:T")) != EOF)
379  {
380    switch (c)
381    {
382      case 'n':   // control file
383        if (Resume)
384        {
385          fprintf(stderr, "Invalid combination: -n and -R\n");
386          usage(argv[0]);
387        }
388        strncpy(inputCtrlFile, optarg, MAX_FILE_NAME);
389        if (stat(inputCtrlFile, &statBuf) == -1)  // Does the file exist?
390        {
391          fprintf(stderr, "Control file %s does not exist, errno=%d\n",
392                    inputCtrlFile, errno);
393          exit(RC_FAIL);
394        }
395        break;
396
397      case 'r':   // I/O rate
398        ioRate = atoi((const char *) (optarg));
399        if ((ioRate <= 0) || (ioRate > 100))
400        {
401          fprintf(stderr, "An incorrect value (%d) was specified for IOrate.\nSpecify a value between 0 and 100.\n", ioRate);
402          usage(argv[0]);
403        }
404        break;
405
406      case 'R':   // resume backup
407        if (strcmp((const char *) inputCtrlFile, "") != 0)
408        {
409          fprintf(stderr, "Invalid combination: -n and -R\n");
410          usage(argv[0]);
411        }
412        if (Full || Incremental)
413        {
414          fprintf(stderr, "Invalid combination: -t and -R\n");
415          usage(argv[0]);
416        }
417        Resume = true;
418        Incremental = false;
419        Full = false;
420        break;
421
422      case 't':   // backup type (full or incremental)
423        if (Resume)
424        {
425          fprintf(stderr, "Invalid combination: -t and -R\n");
426          usage(argv[0]);
427        }
428        if ( (strcmp((const char *) optarg, "full") == 0) ||
429             (strcmp((const char *) optarg, "Full") == 0) )
430        {
431          Full = true;
432          Incremental = false;
433          Resume = false;
434        }
435        else if ( (strcmp((const char *) optarg, "incremental") == 0) ||
436                  (strcmp((const char *) optarg, "Incremental") == 0) )
437        {
438          Incremental = true;
439          Full = false;
440          Resume = false;
441        }
442        else
443        {
444          fprintf(stderr,
445                    "The value (%s) specified for the -t flag is not valid.\n",
446                    optarg);
447          usage(argv[0]);
448        }
449        break;
450
451      case 'T':   // enable tracing
452        Tracing = true;
453        break;
454
455      default:
456        usage(argv[0]);
457
458    }
459  }
460
461  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
462
463  /* Initializations */
464  for (i = 0; i < MAX_BACKUP_CLIENTS; i++) {
465    shadowFileNumberOfRecords[i] = 0;
466  }
467
468  /* Obtain the pid number and node name for this invocation of tsbackup
469     for later use by the mmexectsmcmd script. */
470  rc = gethostname(masterNode, MAX_NAME_CHARS); 
471  if (rc != 0) {
472    fprintf(stderr,
473     "gethostname() failed; unable to obtain the name of the invoking node.\n");
474    exit(RC_FAIL);
475  }
476  masterPID = getpid();
477
478  /* The names of the following files are based on the mount point
479     of the filesystem. */
480
481  /* Construct the name for the backup control file. */
482  snprintf(fsBackupCtrl, MAX_FILE_NAME, "%s/%s", fsName, backupControlName);
483
484  /* Construct the name for the filesizes file. */
485  snprintf(filesizesFile, MAX_FILE_NAME, "%s/%s", fsName, backupFilesizesName);
486
487  /* Construct the name for the backup shadow file. */
488  snprintf(backupShadowFile, MAX_FILE_NAME, "%s/%s", fsName, backupShadowName);
489
490  /* Construct the name for the prior backup shadow file. */
491  snprintf(backupShadowCheck, MAX_FILE_NAME, "%s/%s",
492             fsName, shadowCheckName);
493
494  /* Create the name of the transactionsList file. */
495  snprintf(transactionsList, MAX_FILE_NAME, "%s/%s", fsName, transListName);
496
497#if 0
498  // BCH - The following code snippet was left here to demonstrate how to
499  // BCH     use the consoleTime routine should it be needed in the future.
500  /* Construct a timestamp-based name for the snapshot
501     to ensure its uniqueness (and thus avoid name collisions). */
502  snprintf(timeString, MAX_FILE_NAME, "%s", consoleTime(timeBufP));
503  while ((cp = strchr(timeString, ' ')) != NULL)   /* replace blanks with */
504  {                                                /*   underscore chars  */
505    *cp = '_';
506  }
507  snprintf(fsSnapshotName, MAX_FILE_NAME, "%s_%s",
508             fsSnapshotName_base, timeString);
509#endif
510
511  /* Construct the name for the snapshot of the filesystem. */
512  snprintf(fsSnapshotName, MAX_FILE_NAME, "%s", fsSnapshotName_base);
513
514  /* Process whatever type of backup was specified. */
515  if (Full)                   /* full backup */
516  {
517    assert((Resume == false) && (Incremental == 0));
518    rc = doFSFullBackup(fsName);
519    switch (rc)
520    {
521      case RC_FAIL:       /* failure */
522        fprintf(stderr, "doFSFullBackup() returned rc = %d\n", rc);
523        fprintf(stderr, "tsbackup: full backup failed, rc = %d\n", rc);
524        break;
525      case RC_PSUCCESS:   /* partial success */
526        fprintf(stderr,
527         "tsbackup: full backup finished with partial success, rc = %d\n", rc);
528        break;
529      case RC_SUCCESS:    /* complete success */
530        printf(
531   "tsbackup: full backup finished with complete success, rc = %d\n", rc);
532        break;
533      default:            /* unexpected rc */
534        fprintf(stderr, "doFSFullBackup() returned rc = %d\n", rc);
535        rc = RC_FAIL;
536        fprintf(stderr, "tsbackup: full backup failed, rc = %d\n", rc);
537        break;
538    }
539  }  /* end full backup */
540
541  else if (Incremental == 1)  /* incremental backup */
542  {
543    /* If an input control file was passed it will be ignored and the
544       information in the backup control file will be used instead. */
545    rc = doFSIncrementalBackup(fsName);
546    switch (rc)
547    {
548      case RC_FAIL:       /* failure */
549        fprintf(stderr, "doFSIncrementalBackup() returned rc = %d\n", rc);
550        fprintf(stderr, "tsbackup: incremental backup failed, rc = %d\n", rc);
551        return(rc);
552      case RC_PSUCCESS:   /* partial success */
553        fprintf(stderr,
554    "tsbackup: incremental backup finished with partial success, rc = %d\n",
555                  rc);
556        break;
557      case RC_SUCCESS:    /* complete success */
558        printf(
559    "tsbackup: incremental backup finished with complete success, rc = %d\n",
560                  rc);
561        break;
562      default:            /* unexpected rc */
563        fprintf(stderr,
564                  "doFSIncrementalBackup() returned unexpected rc = %d\n", rc);
565        rc = RC_FAIL;
566        fprintf(stderr, "tsbackup: incremental backup failed, rc = %d\n", rc);
567    }
568  }  /* end incremental backup */
569
570  else if (Resume)            /* resume backup */
571  {
572    /*
573     * A resume backup may be done if the previous backup operation
574     * finished with partial success.  This information is saved
575     * in the backup control file.  Additionally, with partial success
576     * a file named .PendingTransactions should exist which contains
577     * the names of the files which were not successfully backed up
578     * during the previous backup attempt.
579     * If an input control file was passed, it will be ignored and the
580     * existing information in the backup control file will be used instead.
581     */
582    rc = doFSResumeBackup(fsName, &backupType);
583    switch (rc)
584    {
585      case RC_FAIL:       /* failure */
586        fprintf(stderr, "tsbackup: resume of %s backup failed, rc = %d\n",
587                  backupType, rc);
588        break;
589      case RC_PSUCCESS:   /* partial success */
590        fprintf(stderr,
591    "tsbackup: resume of %s backup finished with partial success, rc = %d\n",
592                  backupType, rc);
593        break;
594      case RC_SUCCESS:    /* complete success */
595        printf(
596    "tsbackup: resume of %s backup finished with complete success, rc = %d\n",
597                 backupType, rc);
598        break;
599      default:            /* unexpected rc */
600        fprintf(stderr,
601                  "doFSResumeBackup() returned unexpected rc = %d\n", rc);
602        rc = RC_FAIL;
603        fprintf(stderr, "tsbackup: resume of %s backup failed, rc = %d\n",
604                  backupType, rc);
605        break;
606    }
607  }  /* end resume backup */
608
609  else
610  {
611    /* We should not get here. */
612    fprintf(stderr, "Invalid parms: specify full, incremental, or resume.\n");
613    rc = RC_FAIL;
614    errno = EINVAL;
615    usage(argv[0]);
616
617  }  /* end if (Full) */
618
619  /* If the the message process was forked, kill it now,
620     since tsbackup is about to exit. */
621  if (msgChildPid != 0)
622  {
623    tmp = kill(msgChildPid, SIGTERM);
624  }
625 
626  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
627
628  exit(rc);
629
630}  /*------ end of main ----------------*/
631
632
633/*
634 * NAME:  doFSFullBackup()
635 *
636 * FUNCTION:
637 *   Do a full backup of a filesystem.
638 *
639 * PARAMETERS:
640 *   fsName:  (IN) The full path name of the mounted filesystem.
641 *
642 * RETURNS:
643 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
644 *
645 * NOTES:
646 *   - Take a new snapshot under the new name.
647 *   - Backup the new snapshot.
648 *   - For each server involved with the previous backup, have a client issue
649 *       the "Delete Filespace" TSM command.
650 */
651int doFSFullBackup(char *fsName)
652{
653  Int32 tmp, keep_rc, rc = RC_SUCCESS;
654  backup_control_hdr_t *backupControlHdrP = NULL;
655  struct stat statBuf;
656  char* fn = "doFSFullBackup";
657
658  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
659
660  /* Check whether there is a backup control file from a previous backup. */
661  rc = checkBackupCtrlFile();
662  if (rc == RC_SUCCESS)
663  {
664    /* Load the global backup control structure with the
665       information from the backup control file. */
666    rc = processBackupCtrlFile(fsName, fsBackupCtrl, &backupControlP);
667    if (rc != RC_SUCCESS)
668    {
669      fprintf(stderr, "processBackupCtrlFile() returned rc = %d\n", rc);
670      if (backupControlP != NULL)  free((void *) backupControlP);
671      return(RC_FAIL);
672    }
673
674    /* If an input control file was specified, load the information
675       in it into the backup control structure just initialized by
676       the call to processBackupCtrlFile().  This will cause the
677       contents of the input control file to override the controls
678       in effect at the time of the last backup. */
679    if (inputCtrlFile != NULL)
680    {
681      rc = processInputCtrlFile(fsName, inputCtrlFile, &backupControlP, false);
682      if (rc != RC_SUCCESS)
683      {
684        fprintf(stderr, "processInputCtrlFile() returned with rc = %d\n", rc);
685        if (backupControlP != NULL)  free((void *) backupControlP);
686        return(RC_FAIL);
687      }
688    }
689  }
690  else
691  {
692    /* Here if there is no backup control file (i.e., this is the first
693       backup).  The user should have specified an input control file. */
694
695    /* If an input control file was specified, use its information
696       to create the backup control structure. */
697    if (inputCtrlFile != NULL)
698    {
699      rc = processInputCtrlFile(fsName, inputCtrlFile, &backupControlP, true);
700      if (rc != RC_SUCCESS)
701      {
702        fprintf(stderr, "processInputCtrlFile() returned with rc = %d\n", rc);
703        if (backupControlP != NULL)  free((void *) backupControlP);
704        return(RC_FAIL);
705      }
706    }
707    else
708    {
709      /* There is no input control file and no prior backup control file.
710         Print an error message and return failure. */
711      fprintf(stderr, "Unable to proceed because no control file was specified and there is no prior backup control file.\n");
712      return(RC_FAIL);
713    }
714  }
715  backupControlHdrP = (backup_control_hdr_t *) backupControlP;
716
717  /* Delete any pending files left over from a prior backup that
718     was partially successful.  Once a full backup is issued,
719     a resume backup is no longer an option. */
720  snprintf(sstring, MAX_FILE_NAME, "%s/%s", fsName, pendingDeletionsName);
721  unlink(sstring);
722  snprintf(sstring, MAX_FILE_NAME, "%s/%s", fsName, pendingChangesName);
723  unlink(sstring);
724
725  /* Create a child process to issue reassuring messages to the user
726     in case the remaining work takes a long time. */
727  forkMsgChild();
728
729  /* Create a snapshot and store it at the mount point of the filesystem. */
730  rc = createBackupSnapshot(deviceName, fsSnapshotName);
731  if (rc != RC_SUCCESS)
732  {
733    fprintf(stderr, "createBackupSnapshot returned with rc = %d.\n", rc);
734    free((void *) backupControlP);
735    return(RC_FAIL);
736  }
737  else
738  {
739    /* Save the name of the snapshot in the gpfs_backup_control_t structure. */
740    strncpy(backupControlHdrP->snapshotName, fsSnapshotName, MAX_FILE_NAME);
741  }
742
743  /* Do a backup of the snapshot and determine the level of success. */
744  rc = doFSBackup(fsName, fsSnapshotPathname);
745  switch (rc)
746  {
747    case RC_SUCCESS:   /* The operation succeeded. */
748      keep_rc = rc;
749      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_FULL;
750      break;
751
752    case RC_PSUCCESS:  /* The operation partially succeeded. */
753      keep_rc = rc;
754      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_PARTIAL;
755      strcpy(backupControlHdrP->backupType, "full");
756      break;
757
758    case RC_FAIL:      /* The operation failed. */
759    default:
760      /* Indicate an unexpected error. */
761      if (rc != RC_FAIL) {
762        fprintf(stderr, "doFSBackup() returned unexpected rc = %d\n", rc);
763      } else {
764        fprintf(stderr, "doFSBackup() returned rc = %d\n", rc);
765      }
766      keep_rc = RC_FAIL;
767      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_NONE;
768      break;
769  }
770
771  /* Write the updated backup control data to the backup control file. */
772  rc = createBackupCtrlFile(fsBackupCtrl, backupControlP);
773  if (rc != RC_SUCCESS)
774  {
775    /* Indicate error to user. */
776    free((void *) backupControlP);
777    return(RC_FAIL);
778  }
779
780  /* Delete the snapshot if the completion level was anything other than
781     partial (we no longer need the snapshot if the completion level was
782     success or failure).  For completion level partial, the snapshot is
783     not deleted so that it is available for a subsequent resume backup. */
784  if (backupControlP->backupHdr.completionLevel != BACKUP_COMPLETION_PARTIAL)
785  {
786    /* Delete the snapshot. */
787    sprintf(sstring, "/usr/lpp/mmfs/bin/tsdelsnapshot %s %s > /dev/null",
788              deviceName, fsSnapshotName);
789    tmp = system(sstring);
790
791    /* Check system() return. */
792    if (tmp != 0)
793    {
794      fprintf(stderr, "system(%s) returned rc = %d\n", sstring, tmp);
795      free((void *) backupControlP);
796      return(RC_FAIL);
797    }
798  }
799
800  /* Release the memory for the backup control structure. */
801  free((void *) backupControlP);
802
803  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
804
805  /* Return to caller with appropriate return code. */
806  return(keep_rc);
807
808}  /*------ end of doFSFullBackup() ----------------*/
809
810
811/*
812 * NAME:  doFSIncrementalBackup()
813 *
814 * FUNCTION:
815 *   Do an incremental backup of a filesystem.
816 *
817 * PARAMETERS:
818 *   fsName:  (IN) The full path name of the mounted filesystem.
819 *
820 * RETURNS:
821 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
822 *
823 * NOTES:
824 *   OPERATION:    BCH - these comments no longer match the code well
825 *     - Take a new snapshot and store it at a directory with the previous name.
826 *     - Handle the deleted files from the FS. (backupControlP is used)
827 *       - Perform an inodescan operation to determine all directories and
828 *         files of the FS.
829 *       - Perform a directory traversal and construct the .backup_shadow file.
830 *       - Compare the .backup_shadow and .backup_shadow_check files to
831 *         determine the list of files which need to be deleted.
832 *         Create a file named .backup_deletions which will contain all
833 *         these file names.
834 *     - Handle the files of the filesystem which have changed or are new.
835 *       - Perform an inodescan operation to determine all directories
836 *         and files of the filesystem which are new or have changed,
837 *         passing the old snapshot_ID.
838 *       - Perform a directory traversal and construct the .backup_shadow file.
839 *       - Extract all the file names from this .backup_shadow file and put
840 *         them into a .backup_changes file.
841 */
842int doFSIncrementalBackup(char *fsName)
843{
844  Int32 tmp, rc, rc1 = RC_SUCCESS, rc2 = RC_SUCCESS;
845  gpfs_backup_control_t *backupControlLocalP = NULL;
846  backup_control_hdr_t *backupControlHdrP = NULL;
847  struct stat statBuf;
848  char* fn = "doFSIncrementalBackup";
849
850  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
851
852  /* Check whether there is a backup control file. */
853  rc = checkBackupCtrlFile();
854  if (rc != RC_SUCCESS)
855  {
856    /* Error:  there is no backup control file. */
857    /* Print error message and return failure. */
858    fprintf(stderr, "A backup type of incremental was specified, but a backup control file\nfrom a previous backup does not exist.\n");
859    return(RC_FAIL);
860  }
861
862  /* Load the local backup control structure with the
863     information from the latest backup control file. */
864  rc = processBackupCtrlFile(fsName, fsBackupCtrl, &backupControlLocalP);
865  if (rc != RC_SUCCESS)
866  {
867    fprintf(stderr, "processBackupCtrlFile() returned rc = %d\n", rc);
868    if (backupControlP != NULL)  free((void *) backupControlP);
869    return(RC_FAIL);
870  }
871
872  /* If the previous backup did not complete successfully, issue a message
873     that it must be completed before an incremental backup will proceed. */
874  if (backupControlLocalP->backupHdr.completionLevel != BACKUP_COMPLETION_FULL)
875  {
876    fprintf(stderr, "An incremental backup cannot be performed because the most recent backup did not complete successfully.\n");  // BCH - msg catalog cand.
877    free((void *) backupControlLocalP);
878    return(RC_FAIL);
879  }
880  backupControlHdrP = (backup_control_hdr_t *) backupControlLocalP;
881
882  /* If an input control file was specified, load the information
883     in it into the backup control structure just initialized by
884     the call to processBackupCtrlFile().  This will cause the
885     contents of the input control file to override the controls
886     in effect at the time of the last backup. */
887  if (inputCtrlFile != NULL)
888  {
889    rc = processInputCtrlFile(fsName, inputCtrlFile,
890                                &backupControlLocalP, false);
891    if (rc != RC_SUCCESS)
892    {
893      fprintf(stderr, "processInputCtrlFile() returned with rc = %d\n", rc);
894      return(RC_FAIL);
895    }
896  }
897
898  /* Set the global variable backupControlP to point to the backupControlLocalP
899     structure for the soon-to-happen call to doFSFileDeletions(). */
900  backupControlP = backupControlLocalP;
901
902  /* Create a child process to issue reassuring messages to the user
903     in case the remaining work takes a long time. */
904  forkMsgChild();
905
906  /* Create a snapshot and store it at the mount point of the filesystem. */
907  rc = createBackupSnapshot(deviceName, fsSnapshotName);
908  if (rc != RC_SUCCESS)
909  {
910    fprintf(stderr, "createBackupSnapshot returned with rc = %d.\n", rc);
911    rc = RC_FAIL;
912    goto release_storage;
913  }
914  else
915  {
916    /* Save the name of the snapshot in the gpfs_backup_control_t structure. */
917    strncpy(backupControlHdrP->snapshotName, fsSnapshotName, MAX_FILE_NAME);
918  }
919
920  /* Save the snapshot id from the previous backup for later use
921     by doFSFileChanges.  The value is saved here because it will
922     be changed by doFSFileDeletions when it calls doInodeScan(). */
923  prevSnapId = backupControlP->backupHdr.snapshotId;
924
925  /* First, process the files that have been deleted
926     from the filesystem since the previous backup. */
927  rc1 = doFSFileDeletions(fsName, fsSnapshotPathname);
928  switch (rc1)
929  {
930    case RC_PSUCCESS:   /* The operation had partial success up to now. */
931    case RC_SUCCESS:    /* The operation had a total success. */
932      break;
933
934    case RC_FAIL:       /* The operation failed. */
935    default:            /* An unexpected error occurred. */
936      /* Indicate error. */
937      if (rc1 != RC_FAIL) {
938        fprintf(stderr, "doFSFileDeletions() returned unexpected rc = %d\n",rc);
939      } else {
940        fprintf(stderr, "doFSFileDeletions() returned rc = %d\n", rc);
941      }
942  }  /* end switch */
943
944  /* Next, update the archive to reflect the new and changed files
945     in the filesystem. */
946  rc2 = doFSFileChanges(fsName, fsSnapshotPathname);
947  switch (rc2)
948  {
949    case RC_PSUCCESS:   /* The operation had partial success up to now. */
950    case RC_SUCCESS:    /* The operation had a total success. */
951      break;
952
953    case RC_FAIL:       /* The operation failed. */
954    default:            /* An unexpected error occurred. */
955      /* Indicate error. */
956      if (rc2 != RC_FAIL) {
957        fprintf(stderr, "doFSFileChanges() returned unexpected rc = %d\n", rc);
958      } else {
959        fprintf(stderr, "doFSFileChanges() returned rc = %d\n", rc);
960      }
961  }  /* end switch */
962
963  // Create rc as a composite of rc1 and rc2.
964  if (rc1 == RC_FAIL || rc2 == RC_FAIL) {
965    rc = RC_FAIL;
966  } else if (rc1 == RC_PSUCCESS || rc2 == RC_PSUCCESS) {
967    rc = RC_PSUCCESS;
968  } else {
969    rc = RC_SUCCESS;
970  }
971
972  /* Process the deletions and changes composite return code. */
973  switch (rc)
974  {
975    case RC_SUCCESS:   /* The operation had total success. */
976      /* Delete any pending transactions file left over
977         from a previous partial success. */
978      unlink(pendingTransactionsList);
979
980      /* Erase the backup control file. */
981      unlink(fsBackupCtrl);
982
983      /* Indicate complete success. */
984      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_FULL;
985      break;
986
987    case RC_PSUCCESS:   /* The operation had partial success. */
988      /* Erase the backup control file. */
989      unlink(fsBackupCtrl);
990
991      /* Indicate partial success and store the backup type. */
992      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_PARTIAL;
993      strcpy(backupControlHdrP->backupType, "incremental");
994      break;
995
996    case RC_FAIL:      /* The operation failed. */
997    default:
998      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_NONE;
999      break;
1000  }
1001
1002delete_snapshot:
1003  /* Delete the snapshot if the completion level was anything other than
1004     partial (we no longer need the snapshot if the completion level was
1005     success or failure).  For completion level partial, the snapshot is
1006     not deleted so that it is available for a subsequent resume backup. */
1007  if (backupControlP->backupHdr.completionLevel != BACKUP_COMPLETION_PARTIAL)
1008  {
1009    /* Delete the snapshot. */
1010    sprintf(sstring, "/usr/lpp/mmfs/bin/tsdelsnapshot %s %s > /dev/null",
1011              deviceName, fsSnapshotName);
1012    tmp = system(sstring);
1013
1014    /* Check system() return. */
1015    if (tmp != 0)
1016    {
1017      fprintf(stderr, "system(%s) returned rc = %d\n", sstring, tmp);
1018      return(RC_FAIL);
1019    }
1020  }
1021
1022  /* Write the updated backup control data to the backup control file. */
1023  tmp = createBackupCtrlFile(fsBackupCtrl, backupControlP);
1024  if (tmp != RC_SUCCESS)
1025  {
1026    /* Indicate error to user. */
1027    free((void *) backupControlP);
1028    return(RC_FAIL);
1029  }
1030
1031release_storage:
1032  free((void *) backupControlP);
1033
1034  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1035
1036  return(rc);
1037
1038}  /*------ end of doFSIncrementalBackup() ----------------*/
1039
1040
1041/*
1042 * NAME:  doFSResumeBackup()
1043 *
1044 * FUNCTION:
1045 *   Resume the latest backup operation of a filesystem.
1046 *
1047 * PARAMETERS:
1048 *   fsName:      (IN)  the full path name of the mounted filesystem
1049 *   backupType:  (OUT) pointer to the type of the backup that was resumed
1050 *
1051 * RETURNS:
1052 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
1053 *
1054 * NOTES:
1055 */
1056int doFSResumeBackup(char *fsName, char **backupType)
1057{
1058  Int32 tmp, rc, rc1 = RC_SUCCESS, rc2  = RC_SUCCESS;
1059  gpfs_backup_control_t *backupControlP = NULL;
1060  Boolean pendingWorkFound = false;
1061  struct stat statBuf;
1062  char* fn = "doFSResumeBackup";
1063
1064  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1065
1066  /* Check whether there is a backup control file. */
1067  rc = checkBackupCtrlFile();
1068  if (rc != RC_SUCCESS)
1069  {
1070    /* Error:  there is no backup control file.
1071       Print error message and return failure. */
1072    fprintf(stderr, "The resume flag was specified, but a backup control file from a\nprevious backup does not exist.\n");
1073    return(RC_FAIL);
1074  }
1075
1076  /* Load the global backup control structure with the
1077     information from the latest backup control file. */
1078  rc = processBackupCtrlFile(fsName, fsBackupCtrl, &backupControlP);
1079  if (rc != RC_SUCCESS)
1080  {
1081    *backupType = "unknown type";
1082    fprintf(stderr, "processBackupCtrlFile() returned rc = %d\n", rc);
1083    return(RC_FAIL);
1084  }
1085
1086  /* Set the pointer to the backup type for use by the caller. */
1087  *backupType = backupControlP->backupHdr.backupType;
1088
1089  /* Verify that the current completion indication is partial completion. */
1090  if (backupControlP->backupHdr.completionLevel != BACKUP_COMPLETION_PARTIAL)
1091  {
1092    fprintf(stderr, "Unable to resume; either the previous backup was completely successful,\nor it did not succeed enough to be resumable.\n");  // BCH - possible candidate for message catalog
1093    free(backupControlP);
1094    return(RC_FAIL);
1095  }
1096
1097  /* Create the full path name for the filesystem snapshot. */
1098  strncpy(fsSnapshotName, backupControlP->backupHdr.snapshotName,
1099            MAX_FILE_NAME);
1100
1101  /* Get the snapshot handle for the snapshot, and then use
1102     the handle to get the full pathname of the snapshot. */
1103  fsSnapHandleP = gpfs_get_fssnaphandle_by_name(deviceName, fsSnapshotName);
1104  if (fsSnapHandleP == NULL)
1105  {
1106    rc = errno;
1107    fprintf(stderr, "%s: gpfs_get_fssnaphandle_by_name(%s, %s): %s\n",
1108              "doFSResumeBackup", fsName, fsSnapshotName, strerror(rc));
1109    return(RC_FAIL);
1110  }
1111  snapshotPathname = gpfs_get_pathname_from_fssnaphandle(fsSnapHandleP);
1112  if (snapshotPathname == NULL)
1113  {
1114    rc = errno;
1115    fprintf(stderr, "%s: gpfs_get_pathname_from_fssnaphandle(%s): %s\n",
1116              "doFSResumeBackup", fsSnapHandleP, strerror(rc));
1117    return(RC_FAIL);
1118  }
1119  strcpy(fsSnapshotPathname, snapshotPathname);
1120 
1121  /* Create a child process to issue reassuring messages to the user
1122     in case the remaining work takes a long time. */
1123  forkMsgChild();
1124 
1125  /* Create the full pathname for the pending deletions file. */
1126  snprintf(pendingTransactionsList, MAX_FILE_NAME, "%s/%s",
1127             fsName, pendingDeletionsName);
1128
1129  /* Check whether the pending deletions file exists. */
1130  tmp = stat(pendingTransactionsList, &statBuf);
1131  if (tmp == -1)
1132  {
1133    if (errno != ENOENT)
1134    {
1135      fprintf(stderr, "Could not stat file '%s', errno=%d\n",
1136                pendingTransactionsList, errno);
1137      return(RC_FAIL);
1138    }
1139  }
1140  else
1141  {
1142    /* Rename the pending deletions file to be the transactions file. */
1143    tmp = rename(pendingTransactionsList, transactionsList);
1144    if (tmp != 0)
1145    {
1146      fprintf(stderr, "rename(%s, %s) returned %d\n",
1147                pendingTransactionsList, transactionsList, rc);
1148      free(backupControlP);
1149      return(RC_FAIL);
1150    }
1151
1152    /* Invoke routine to process the transactions file.  The true flag
1153       that is passed tells the routine to divy the transactions file
1154       among the client processes based on aggregate file size, not by
1155       number of files, which is what makes sense when doing backups. */
1156    rc1 = processFilelist(fsName,
1157                            transactionCmdExpire,
1158                            transactionCmdOption,
1159                            backupControlP,
1160                            true);
1161
1162    // Set flag to indicate something was found and processed.
1163    pendingWorkFound = true;
1164  }
1165
1166  /* Create the full pathname for the pending changes file. */
1167  snprintf(pendingTransactionsList, MAX_FILE_NAME, "%s/%s",
1168             fsName, pendingChangesName);
1169
1170  /* Check whether the pending changes file exists. */
1171  tmp = stat(pendingTransactionsList, &statBuf);
1172  if (tmp == -1)
1173  {
1174    if (errno != ENOENT)
1175    {
1176      fprintf(stderr, "Could not stat file '%s', errno=%d\n",
1177                pendingTransactionsList, errno);
1178      return(RC_FAIL);
1179    }
1180  }
1181  else
1182  {
1183    /* Rename the pending deletions file to be the transactions file. */
1184    tmp = rename(pendingTransactionsList, transactionsList);
1185    if (tmp != 0)
1186    {
1187      fprintf(stderr, "rename(%s, %s) returned %d\n",
1188                pendingTransactionsList, transactionsList, rc);
1189      free(backupControlP);
1190      return(RC_FAIL);
1191    }
1192
1193    /* Invoke routine to process the transactions file.  The true flag
1194       that is passed tells the routine to divy the transactions file
1195       among the client processes based on aggregate file size, not by
1196       number of files, which is what makes sense when doing backups. */
1197    rc2 = processFilelist(fsName,
1198                            transactionCmdSelective,
1199                            transactionCmdOption,
1200                            backupControlP,
1201                            true);
1202
1203    // Set flag to indicate something was found and processed.
1204    pendingWorkFound = true;
1205  }
1206
1207  // If no work was found, issue an error and exit.
1208  if (pendingWorkFound == false)
1209  {
1210    fprintf(stderr,
1211              "Resume was specified, but no pending work files were found.\n");
1212    free(backupControlP);
1213    return(RC_FAIL);
1214  }
1215
1216  // Create rc as a composite of rc1 and rc2.
1217  if (rc1 == RC_FAIL || rc2 == RC_FAIL) {
1218    rc = RC_FAIL;
1219  } else if (rc1 == RC_PSUCCESS || rc2 == RC_PSUCCESS) {
1220    rc = RC_PSUCCESS;
1221  } else {
1222    rc = RC_SUCCESS;
1223  }
1224
1225  /* Process the deletions and changes composite return code. */
1226  switch (rc)
1227  {
1228    case RC_SUCCESS:    /* complete success */
1229      /* Set the backup completion indicator to indicate full. */
1230      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_FULL;
1231      break;
1232
1233    case RC_PSUCCESS:   /* partial success */
1234      /* Set the backup completion indicator to indicate partial success. */
1235      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_PARTIAL;
1236      break;
1237
1238    case RC_FAIL:       /* failure */
1239    default:            /* Unexpected rc */
1240      /* Issue error message. */
1241      fprintf(stderr, "processFilelist() returned rc = %d\n", rc);
1242
1243      /* Set the backup completion indicator to indicate complete failure. */
1244      backupControlP->backupHdr.completionLevel = BACKUP_COMPLETION_NONE;
1245      rc = RC_FAIL;
1246      break;
1247  }
1248
1249  /* Recreate the backup control file with the updated control data. */
1250  tmp = createBackupCtrlFile(fsBackupCtrl, backupControlP);
1251  if (tmp != RC_SUCCESS)
1252  {
1253    /* Indicate error to user. */
1254    fprintf(stderr,
1255              "doFSResumeBackup(): createBackupCtrlFile() returned rc = %d\n",
1256              tmp);
1257    rc = RC_FAIL;
1258  }
1259
1260  /* Delete the snapshot if the completion level was anything other than
1261     partial (we no longer need the snapshot if the completion level was
1262     success or failure).  For completion level partial, the snapshot is
1263     not deleted so that it is available for a subsequent resume backup. */
1264  if (backupControlP->backupHdr.completionLevel != BACKUP_COMPLETION_PARTIAL)
1265  {
1266    /* Delete the snapshot. */
1267    sprintf(sstring, "/usr/lpp/mmfs/bin/tsdelsnapshot %s %s > /dev/null",
1268              deviceName, fsSnapshotName);
1269    tmp = system(sstring);
1270
1271    /* Check system() return. */
1272    if (tmp != 0)
1273    {
1274      fprintf(stderr, "system(%s) returned rc = %d\n", sstring, tmp);
1275      return(RC_FAIL);
1276    }
1277  }
1278
1279  free(backupControlP);
1280
1281  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1282
1283  return(rc);
1284
1285}  /*------ end of doFSResumeBackup() ----------------*/
1286
1287
1288/*
1289 * NAME:  doFSBackup()
1290 *
1291 * FUNCTION:
1292 *   Do a full backup of a filesystem.
1293 *
1294 * PARAMETERS:
1295 *   fsName:              (IN) the full path name of the mounted filesystem
1296 *   fsSnapshotPathName:  (IN) the full path name of the filesystem
1297 *                               snapshot which is to be backed up
1298 *
1299 * RETURNS:
1300 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
1301 *
1302 * NOTES:
1303 */
1304int doFSBackup(char *fsName, char *fsSnapshotPathname)
1305{
1306  Int32 tmp, rc, rc1 = RC_SUCCESS, rc2 = RC_SUCCESS;
1307  Int32 numShadows;
1308  Int32 clientIndex, numClients;
1309  char filenameTemp[MAX_FILE_NAME];
1310  inodeBitsArray *inodeBitsArrayP;
1311  struct stat statBuf;
1312  char* fn = "doFSBackup";
1313
1314  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1315
1316  /* If there is a prior backup (as evidenced by the existence of
1317     a shadow check file), process any files that have been deleted
1318     since that backup was made. */
1319  if (stat(backupShadowCheck, &statBuf) != -1)
1320  {
1321    /* Process the files that have been deleted from the
1322       filesystem since the previous backup. */
1323    rc1 = doFSFileDeletions(fsName, fsSnapshotPathname);
1324    switch (rc1)
1325    {
1326      case RC_PSUCCESS:   /* The operation had partial success up to now. */
1327      case RC_SUCCESS:    /* The operation had a total success. */
1328        break;
1329
1330      case RC_FAIL:       /* The operation failed. */
1331      default:            /* An unexpected error occurred. */
1332        /* Indicate error. */
1333        if (rc1 != RC_FAIL) {
1334          fprintf(stderr,
1335                    "doFSFileDeletions() returned unexpected rc = %d\n", rc1);
1336        } else {
1337          fprintf(stderr, "doFSFileDeletions() returned rc = %d\n", rc1);
1338        }
1339    }  /* end switch */
1340
1341  } else {
1342
1343    /* We come here if there was no prior backup, so doFSFileDeletions
1344       was not called.  As a result, the backup shadow file has not
1345       been created yet.  Proceed to do that now. */
1346
1347    /* Do an inodescan. */
1348    inodeBitsArrayP = &inodeBitsP;
1349    rc2 = doInodeScan(fsSnapshotPathname, inodeBitsArrayP, backupControlP);
1350    if (rc2 != RC_SUCCESS)
1351    {
1352      fprintf(stderr, "doFSBackup(): doInodeScan() returned rc = %d\n", rc2);
1353      return(RC_FAIL);
1354    }
1355
1356    /* Create a backup shadow file, and store it
1357       at the mount point of the filesystem. */
1358    numShadows = 1;
1359    rc2 = createSnapshotShadows(fsName, numShadows, inodeBitsP);
1360    if (rc2 != RC_SUCCESS)
1361    {
1362      fprintf(stderr, "createSnapshotShadows() returned rc = %d\n", rc2);
1363      free(inodeBitsP);
1364      return(RC_FAIL);
1365    }
1366    free(inodeBitsP);
1367
1368    /* Sort the backup shadow file(s) by inode number. */
1369    rc2 = sortShadowfilesByInode(numShadows);
1370    if (rc2 != RC_SUCCESS)
1371    {
1372      fprintf(stderr, "sortShadowfilesByInode() returned rc = %d\n", rc2);
1373      return(RC_FAIL);
1374    }
1375
1376    /* Update the .backup_shadow0 file with the correct
1377       file sizes and erase the filesizes file. */
1378    rc2 = updateShadowfilesFilesizes(numShadows);
1379    if (rc2 != RC_SUCCESS)
1380    {
1381      fprintf(stderr, "updateShadowfilesFilesizes() returned rc = %d\n", rc2);
1382      return(RC_FAIL);
1383    }
1384
1385    /* Sort the backup shadow file into alphabetical order by filename,
1386       and store the results in a single backup shadow file. */
1387    rc2 = sortShadowfilesByFilename();
1388    if (rc2 != RC_SUCCESS)
1389    {
1390      fprintf(stderr, "sortShadowfilesByFilename() returned rc = %d\n", rc2);
1391      return(RC_FAIL);
1392    }
1393  }
1394
1395  /* Create the list of files to be processed. */
1396  rc2 = createFilelist(fsName, backupControlP);
1397  if (rc2 != RC_SUCCESS)
1398  {
1399    fprintf(stderr, "createFilelist() returned rc = %d\n", rc2);
1400    return(RC_FAIL);
1401  }
1402
1403  /* Invoke routine to process the transactions file.  The true flag
1404     that is passed tells the routine to divy the transactions file
1405     among the client processes based on aggregate file size, not by
1406     number of files, which is what makes sense when doing backups. */
1407  rc2 = processFilelist(fsName,
1408                          transactionCmdSelective,
1409                          transactionCmdOption,
1410                          backupControlP,
1411                          true);
1412
1413  // Create rc as a composite of rc1 and rc2.
1414  if (rc1 == RC_FAIL || rc2 == RC_FAIL) {
1415    rc = RC_FAIL;
1416  } else if (rc1 == RC_PSUCCESS || rc2 == RC_PSUCCESS) {
1417    rc = RC_PSUCCESS;
1418  } else {
1419    rc = RC_SUCCESS;
1420  }
1421
1422  /* Process the deletions and changes composite return code. */
1423  switch (rc)
1424  {
1425    case RC_SUCCESS:   /* The operation had total success. */
1426      /* We have complete success; delete any pending transactions file
1427         left over from a previous partial success. */
1428      unlink(pendingTransactionsList);
1429      /* Intentionally fall into the partial success case. */
1430    case RC_PSUCCESS:   /* The operation had partial success. */
1431      /* Rename the backup shadow file to be the old backup shadow file. */
1432      tmp = rename(backupShadowFile, backupShadowCheck);
1433      if (tmp != 0)
1434      {
1435        fprintf(stderr, "rename(%s, %s) returned %d\n",
1436                  backupShadowFile, backupShadowCheck, tmp);
1437        return(RC_FAIL);
1438      }
1439      break;
1440
1441    case RC_FAIL:      /* The operation failed. */
1442    default:
1443      /* The command failed.  Cleanup the various files that were created. */
1444#ifdef DEBUG_BACKUP
1445      /* Leave the files as is for failure analysis. */
1446#else
1447      /* We need to do some cleanup here:
1448         Delete any existing client transactions files. */
1449      numClients = backupControlP->backupHdr.icf.numberOfClients;
1450      for (clientIndex = 0; clientIndex < numClients; clientIndex++)
1451      {
1452        unlink(clientTransactionsList[clientIndex]);
1453      }
1454
1455      /* Delete the backup shadow file. */
1456      unlink(backupShadowFile);
1457#endif  /* DEBUG_BACKUP */
1458
1459      break;
1460  }
1461
1462  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1463
1464  return(rc);
1465
1466}  /*------ end of doFSBackup() ----------------*/
1467
1468
1469/*
1470 * NAME:  checkBackupCtrlFile()
1471 *
1472 * FUNCTION:
1473 *   Check whether a backup control file exists.
1474 *
1475 * PARAMETERS:  none
1476 *
1477 * RETURNS:
1478 *   RC_SUCCESS if file exists, RC_FAIL if it does not.
1479 *
1480 * NOTES:
1481 */
1482int checkBackupCtrlFile()
1483{
1484  Int32 rc;
1485  Int32 handle;                     /* file descriptor for opening a file */
1486  Int32 openFlags;                  /* flags setting in opening files */
1487  char* fn = "checkBackupCtrlFile";
1488
1489  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1490
1491  /* Determine whether the backup control file exists.
1492     The presence of this file indicates the existence of a previous backup. */
1493  openFlags = O_RDONLY;
1494  handle = open(fsBackupCtrl, openFlags, 0666);
1495  if (handle == -1)   // if the open of the backup control file failed
1496  {
1497    rc = RC_FAIL;
1498    if (errno != ENOENT)  /* The file exists but it could not be opened. */
1499    {
1500      fprintf(stderr, "Unable to open file %s\n", fsBackupCtrl);
1501    }
1502  }
1503  else
1504  {
1505    rc = RC_SUCCESS;
1506  }
1507
1508  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1509
1510  return(rc);
1511
1512}  /*------ end of checkBackupCtrlFile() ----------------*/
1513
1514
1515
1516/*
1517 * Utility functions
1518 */
1519
1520/************************************************************************
1521 * NOTE:  The code for these routines is based on equivalent routines
1522 *        included in the GPFS code for classes.
1523 *************************************************************************/
1524
1525
1526# define BITS_PER_WORD 32
1527int calcNWords(Int32 size)
1528{
1529  return (size + BITS_PER_WORD - 1) / BITS_PER_WORD;
1530}
1531
1532
1533/*
1534 * NAME:  Bitmap()
1535 *
1536 * FUNCTION:
1537 *    Construct a bit map of a specified size and return its address.
1538 */
1539int Bitmap(Int32 size, Bit initialValue, UInt32 **inodeBitsP)
1540{
1541  Int32 i, nWords;
1542  UInt32 fillWord, endMask;
1543  Int32 shift;
1544  UInt32* bitsP;
1545
1546  /* Compute size of array of UInts, then allocate and initialize it */
1547  assert(size > 0);
1548  nWords = calcNWords(size);
1549
1550  bitsP = (UInt32 *) malloc(nWords * 4);
1551
1552  if (bitsP == NULL)
1553    return(-1);
1554
1555  if (initialValue == oneBit)
1556    fillWord = 0xFFFFFFFF;
1557  else
1558    fillWord = 0x00000000;
1559
1560  for (i = 0 ; i < nWords ; i++)
1561    bitsP[i] = fillWord;
1562
1563  /* Harbison/Steele C book says:
1564     "The result value is also undefined if the value of the right
1565     operand is greater than or equal to the width (in bit positions)
1566     of the value of the converted left operand."  In other words,
1567     if you shift an int by 32 bits or more, the result is undefined. */
1568  shift = (BITS_PER_WORD - (size%BITS_PER_WORD));
1569
1570  if (shift == BITS_PER_WORD)
1571    endMask = 0xFFFFFFFF;
1572  else
1573    endMask = 0xFFFFFFFF << shift;
1574
1575  bitsP[nWords-1] = fillWord & endMask;
1576  *inodeBitsP = bitsP;
1577
1578  return(0);
1579
1580}  /*------ end of Bitmap() ----------------*/
1581
1582
1583#if 0
1584/*
1585 * NAME:  getValue()
1586 *
1587 * FUNCTION:
1588 *   Return the current value of the bit with a given index.
1589 *   Returns EINVAL if the index is out of range.
1590 */
1591int getValue(UInt32 *inodeBitsP, Int32 index, Bit* bitP)
1592{
1593  UInt32 word;
1594  UInt32* bitsP;
1595
1596  bitsP = inodeBitsP;
1597  if (index < 0)
1598    return EINVAL;
1599
1600  word = bitsP[index/BITS_PER_WORD];
1601
1602  if ((word & (0x80000000 >> (index%BITS_PER_WORD))) == 0x0)
1603    *bitP = zeroBit;
1604  else
1605    *bitP = oneBit;
1606
1607  return 0;
1608
1609}  /*------ end of getValue() ----------------*/
1610#endif
1611
1612
1613/*
1614 * NAME:  testBit()
1615 *
1616 * FUNCTION:
1617 *   Test a bit in a bit array.
1618 *   Returns non-zero if the bit with the given index is a oneBit.
1619 *   Returns 0 if the index is out of range.
1620 */
1621const UInt32 testBit(UInt32* inodeBitsP, Int32 index)
1622{
1623  UInt32 word;
1624  UInt32* bitsP;
1625
1626  bitsP = inodeBitsP;
1627
1628  if (index < 0)
1629  {
1630    return 0;
1631  }
1632
1633  word = bitsP[index/BITS_PER_WORD];
1634
1635  return word & (0x80000000 >> (index%BITS_PER_WORD));
1636
1637}  /*------ end of testBit() ----------------*/
1638
1639
1640/*
1641 * NAME:  setToZero()
1642 *
1643 * FUNCTION:
1644 *   Set the value of a given bit in a bit array to zeroBit.
1645 *   If oldValueP is not null, the old value of the bit is returned.
1646 *   Returns EINVAL if the index is out of range.
1647 */
1648int setToZero(inodeBitsArray inodeBitsP, Int32 index, Bit* oldValueP)
1649{
1650  UInt32 oldWord;
1651  UInt32 mask;
1652  UInt32* bitsP;
1653
1654  bitsP = inodeBitsP;
1655
1656  if (index < 0)
1657  {
1658    return EINVAL;
1659  }
1660
1661  oldWord = bitsP[index/BITS_PER_WORD];
1662
1663  mask = 0x80000000 >> (index%BITS_PER_WORD);
1664
1665  if ((oldWord & mask) == 0x0)
1666  {
1667    if (oldValueP != NULL)
1668    {
1669      *oldValueP = zeroBit;
1670    }
1671  }
1672  else
1673  {
1674    bitsP[index/BITS_PER_WORD] = oldWord & ~mask;
1675    if (oldValueP != NULL)
1676    {
1677      *oldValueP = oneBit;
1678    }
1679  }
1680
1681  return 0;
1682
1683}  /*------ end of setToZero() ----------------*/
1684
1685
1686/*
1687 * NAME:  setToOne()
1688 *
1689 * FUNCTION:
1690 *   Set the value of a given bit in a bit array to oneBit.
1691 *   If oldValueP is not null, the old value of the bit is returned.
1692 *   Returns EINVAL if the index is out of range.
1693 */
1694int setToOne(UInt32* inodeBitsP, Int32 index, Bit* oldValueP)
1695{
1696  UInt32 oldWord;
1697  UInt32 mask;
1698  UInt32* bitsP;
1699
1700  bitsP = inodeBitsP;
1701
1702  if (index < 0)
1703  {
1704    return EINVAL;
1705  }
1706
1707  oldWord = bitsP[index/BITS_PER_WORD];
1708
1709  mask = 0x80000000 >> (index%BITS_PER_WORD);
1710
1711  if ((oldWord & mask) == 0x0)
1712  {
1713    bitsP[index/BITS_PER_WORD] = oldWord | mask;
1714    if (oldValueP != NULL)
1715    {
1716      *oldValueP = zeroBit;
1717    }
1718  }
1719  else
1720  {
1721    if (oldValueP != NULL)
1722    {
1723      *oldValueP = oneBit;
1724    }
1725  }
1726
1727  return 0;
1728
1729}  /*------ end of setToOne() ----------------*/
1730
1731
1732/*
1733 * NAME:  setAllToZero()
1734 *
1735 * FUNCTION:
1736 *   Set all the bits of a bit array to zero.
1737 */
1738void setAllToZero(UInt32* bitsP, Int32 size)
1739{
1740  Int32 i;
1741  Int64 nWords;
1742
1743  nWords = calcNWords(size);
1744
1745  for (i = 0 ; i < nWords ; i++)
1746  {
1747    bitsP[i] = 0x0;
1748  }
1749
1750}  /*------ end of setAllToZero() ----------------*/
1751
1752
1753/*
1754 * NAME:  setAllToOne()
1755 *
1756 * FUNCTION:
1757 *   Set all the bits of a bit array to one.
1758 */
1759void setAllToOne(UInt32* bitsP, Int32 size)
1760{
1761  Int32 i;
1762  Int64 nWords;
1763  UInt32 endMask;
1764  Int32 shift;
1765
1766  nWords = calcNWords(size);
1767
1768  for (i = 0 ; i < nWords ; i++)
1769  {
1770    bitsP[i] = 0xFFFFFFFF;
1771  }
1772
1773  shift = (BITS_PER_WORD - (size%BITS_PER_WORD));
1774  if (shift == BITS_PER_WORD)
1775  {
1776    endMask = 0xFFFFFFFF;
1777  }
1778  else
1779  {
1780    endMask = 0xFFFFFFFF << shift;
1781  }
1782
1783  bitsP[nWords-1] = 0xFFFFFFFF & endMask;
1784
1785}  /*------ end of setAllToOne() ----------------*/
1786
1787
1788#define tst(a,b) (*mode == 'r'? (b) : (a))
1789#define RDR     0
1790#define WTR     1
1791
1792
1793/*
1794 * NAME:  pipeOpen()
1795 *
1796 * FUNCTION:
1797 *   pipeOpen/pipeClose are taken from the AIX popen/pclose
1798 *   equivalent.  The difference is that pipeOpen() clears
1799 *   the signal mask in the forked process.
1800 *
1801 * TODO:  This routine needs to handle SIGCLD in an unobtrusive way
1802 *        so we don't have to set up specific handlers for it
1803 *        in the callers of this routine.
1804 */
1805FILE* pipeOpen(const char *cmd, const char *mode, pid_t *outPidP)
1806{
1807  Int32 p[2];
1808  Int32 myside, yourside;
1809  pid_t pid;
1810  char* fn = "pipeOpen";
1811
1812  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1813
1814  if (pipe(p) < 0)
1815  {
1816    return NULL;
1817  }
1818
1819  myside = tst(p[WTR], p[RDR]);
1820  yourside = tst(p[RDR], p[WTR]);
1821
1822  if ((pid = fork()) == 0)
1823  {
1824    Int32 stdio;
1825    struct stat statBuf;
1826    sigset_t sigMask;
1827
1828    /* An exec inherits the signal mask,
1829     * so we set up not to block anything anymore.
1830     */
1831    sigemptyset(&sigMask);
1832    sigprocmask(SIG_SETMASK, &sigMask, NULL);
1833
1834    /* myside and yourside reverse roles in child */
1835    stdio = tst(0, 1);
1836    close(myside);
1837
1838    if (stdio != yourside)
1839    {
1840      close(stdio);
1841      fcntl(yourside, F_DUPFD, stdio);
1842      close(yourside);
1843    }
1844
1845    if (stat(SHELL_PATH, &statBuf) != 0)
1846    {
1847      exit(127);
1848    }
1849
1850    execl(SHELL_PATH, SHELL, "-c", cmd, (char *) 0);
1851    exit(1);
1852  }
1853
1854  if (pid < 0)
1855  {
1856    return NULL;
1857  }
1858
1859  *outPidP = pid;
1860  close(yourside);
1861
1862  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1863
1864  return fdopen(myside, mode);
1865
1866}  /*------ end of pipeOpen() ----------------*/
1867
1868
1869/*
1870 * NAME:  pipeClose()
1871 *
1872 */
1873int pipeClose(FILE *ptr, pid_t pid)
1874{
1875  Int32 r = 0;
1876  Int32 status;
1877  struct sigaction ointact, oquitact, ohupact, ignact;
1878  char* fn = "pipeClose";
1879
1880  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1881
1882  ignact.sa_handler = SIG_IGN;
1883  sigemptyset(&(ignact.sa_mask));
1884  ignact.sa_flags = 0 ;
1885
1886  fclose(ptr);
1887
1888  sigaction(SIGHUP, &ignact, &ohupact);
1889  sigaction(SIGINT, &ignact, &ointact);
1890  sigaction(SIGQUIT, &ignact, &oquitact);
1891
1892  if (pid == 0)
1893  {
1894    status = -1;
1895  }
1896  else
1897  {
1898    while ((r = waitpid(pid, &status, 0)) == -1)
1899    {
1900      if (errno != EINTR)
1901      {
1902        break;
1903      }
1904    }
1905  }
1906
1907  if (r == -1)
1908  {
1909    status = -1;
1910  }
1911
1912  sigaction(SIGHUP, &ohupact, NULL);
1913  sigaction(SIGINT, &ointact, NULL);
1914  sigaction(SIGQUIT, &oquitact, NULL);
1915
1916  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
1917
1918  return status;
1919
1920}  /*------ end of pipeClose() ----------------*/
1921
1922
1923/*
1924 * NAME:  writeBuffer()
1925 *
1926 * FUNCTION:
1927 *   Write out to a TransactionList file a specified number of bytes
1928 *   of a buffer containing records pertinent to the file.
1929 *
1930 * PARAMETERS:
1931 *   handle:  (IN) The file descriptor indicating the TransactionList file
1932 *   Buffer:  (IN) Points to a buffer area which is to be written out
1933 *                 to the TransactionList file.
1934 *   size:    (IN) The size of the buffer area to be written out.
1935 *
1936 * RETURNS:
1937 *   RC_SUCCESS on success, RC_FAIL on error.
1938 *
1939 * NOTES:
1940 */
1941int writeBuffer(int handle, transactionsListRecord *Buffer, size_t size)
1942{
1943  Int32 rc = RC_SUCCESS;
1944  Int32 bytesWritten;
1945
1946  /* Write the specified number of bytes from the buffer
1947     to the transaction list file. */
1948  bytesWritten = write(handle, Buffer, size);
1949
1950  if (bytesWritten != size)
1951  {
1952    fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
1953              handle, bytesWritten, size);
1954    return(RC_FAIL);
1955  }
1956
1957  return(rc);
1958
1959}  /*------ end of writeBuffer() ----------------*/
1960
1961
1962/*
1963 * NAME:  processBackupCtrlFile()
1964 *
1965 * FUNCTION:
1966 *   Given a filesystem and a backup control file containing information
1967 *   from a previous tsbackup, store the control information in a backup
1968 *   control structure in memory and return a pointer to it.
1969 *
1970 * PARAMETERS:
1971 *   fsNameP              - (IN)  Points to the filesystem name.
1972 *   BackupCtrlFile       - (IN)  Points to the file containing the pertinent
1973 *                                  backup control information.
1974 *   backupControlHeaderP - (OUT) Points to the gpfs_backup_control_t created
1975 *                                  structure.
1976 *
1977 * RETURNS:
1978 *   RC_SUCCESS on success, RC_FAIL on error.
1979 *
1980 * NOTES:
1981 */
1982int processBackupCtrlFile(char *fsName,
1983                          char *BackupCtrlFile,
1984                          gpfs_backup_control_t **backupControlHeaderP)
1985{
1986  Int32 rc = RC_SUCCESS;
1987  Int32 bytesRead, bytesWritten;
1988  char *fsNameP;
1989  char *filename;
1990  Int64 recordInFile;
1991  offset_t nextReadOffset, actualOffset;
1992  gpfs_backup_control_t *backupCtrlP;
1993  Int32 fp;
1994  char* fn = "processBackupCtrlFile";
1995
1996  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
1997
1998  filename = BackupCtrlFile;
1999  fsNameP = fsName;
2000
2001  /* Create gpfs_backup_control structure and initialize it
2002     to null characters. */
2003  backupCtrlP = (gpfs_backup_control_t *) malloc(sizeof(gpfs_backup_control_t));
2004  if (backupCtrlP == NULL)
2005  {
2006    fprintf(stderr, "processBackupCtrlFile(): malloc failure");
2007    *backupControlHeaderP = NULL;   /* Let the caller know the malloc failed. */
2008    return(RC_FAIL);
2009  }
2010
2011  memset((gpfs_backup_control_t *) backupCtrlP, '\0',
2012            sizeof(gpfs_backup_control_t));
2013
2014  /* Open the BackupCtrlFile and process its contents to update the
2015     gpfs_backup_control_t structure. */
2016  fp = open(filename, O_RDONLY, 0664);
2017  if (fp == -1)
2018  {
2019    fprintf(stderr, "processBackupCtrlFile(): open(%s) failure", filename);
2020    return(RC_FAIL);
2021  }
2022
2023  /* Set the file pointer to the beginning of the file. */
2024  recordInFile = 0;
2025  nextReadOffset = recordInFile*sizeof(gpfs_backup_control_t);
2026  actualOffset = lseek(fp, nextReadOffset, SEEK_SET);
2027  if (actualOffset != nextReadOffset)
2028  {
2029    fprintf(stderr, "Cannot seek to %lld in %s\n", nextReadOffset, filename);
2030    return(RC_FAIL);
2031  }
2032
2033  /* Read the control data from the file. */
2034  bytesRead = read(fp, backupCtrlP, sizeof(gpfs_backup_control_t));
2035  if (bytesRead != sizeof(gpfs_backup_control_t))
2036  {
2037    fprintf(stderr, "Error reading file %s\n", filename);
2038    return(RC_FAIL);
2039  }
2040
2041  /* Close the backup control file. */
2042  close(fp);
2043
2044  /* Set the pointer to the data for use by the caller. */
2045  *backupControlHeaderP = backupCtrlP;
2046
2047  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
2048
2049  return(rc);
2050
2051}  /*------ end of processBackupCtrlFile() ----------------*/
2052
2053
2054/*
2055 * NAME:  forkMsgChild()
2056 *
2057 * FUNCTION:
2058 *   Fork a child process that will issue reassuring thoughts periodically
2059 *   until the master process ends.  This is to help the user be patient.
2060 *
2061 * PARAMETERS:
2062 *
2063 * RETURNS:
2064 *   RC_SUCCESS on success, RC_FAIL on error.
2065 *
2066 * NOTES:
2067 */
2068int forkMsgChild()
2069{
2070  Int32 rc = RC_SUCCESS;
2071  char commandBuffer[LINE_LENGTH];  /* cmd to execute */
2072  pid_t pid;
2073  char* fn = "forkMsgChild";
2074
2075  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
2076
2077  /* Create the command to cause the message child to produce messages. */
2078  snprintf(commandBuffer, sizeof(commandBuffer),
2079             "%s/bin/mmexectsmcmd %s %d > /dev/null 2>/dev/null",
2080             INSTALL_DIRECTORY, "givestatus", 150);  // frequency = 2.5 min
2081
2082  /* Create the child process. */
2083  pid = forkit(commandBuffer);
2084
2085  /* Check for complete failure (i.e., check whether nothing came back). */
2086  if (pid < 0)
2087  {
2088    /* The command that was issued failed.
2089       Issue an error message and return a bad return code. */
2090    fprintf(stderr, "forkMsgChild(): forkit() returned pid = %d\n", pid);
2091    rc = RC_FAIL;
2092  }
2093
2094  /* Save the pid of the message process so that tsbackup can
2095     kill the process when he (tsbackup) is about to exit. */
2096  msgChildPid = pid;
2097
2098  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
2099
2100  return(rc);
2101
2102}  /*------ end of forkMsgChild() ----------------*/
2103
2104
2105/*
2106 * NAME:  forkit()
2107 *
2108 * FUNCTION:  fork and exec a passed command
2109 *
2110 */
2111pid_t forkit(const char *cmd)
2112{
2113  pid_t pid;
2114  char* fn = "forkit";
2115
2116  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
2117
2118  if ((pid = fork()) == 0)
2119  {
2120    struct stat statBuf;
2121    sigset_t sigMask;
2122
2123    /* An exec inherits the signal mask,
2124     * so we set up not to block anything anymore.
2125     */
2126    sigemptyset(&sigMask);
2127    sigprocmask(SIG_SETMASK, &sigMask, NULL);
2128
2129    if (stat(SHELL_PATH, &statBuf) != 0)
2130    {
2131      exit(127);
2132    }
2133
2134    execl(SHELL_PATH, SHELL, "-c", cmd, (char *) 0);
2135    exit(1);
2136  }
2137
2138  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
2139
2140  return(pid);
2141
2142}  /*------ end of forkit() ----------------*/
2143
2144
2145/*
2146 * NAME:  createClientFilelists()
2147 *
2148 * FUNCTION:
2149 *   Create the transaction files for the client processes by divying
2150 *   the transactions file into smaller files containing roughly similar
2151 *   amounts of backup work.
2152 *
2153 * PARAMETERS:
2154 *   backupControlHdrP:  (IN) Pointer to a gpfs_backup_control_t structure
2155 *
2156 * RETURNS:
2157 *   RC_SUCCESS on success, RC_FAIL on error.
2158 *
2159 * NOTES:
2160 */
2161int createClientFilelists(gpfs_backup_control_t *backupControlHdrP)
2162{
2163  gpfs_backup_control_t *backupCtrlP;
2164  Int64 clientTransListSize[MAX_BACKUP_CLIENT_PROCESSES];
2165  Int64 targetSizeForProcess;
2166  Int64 currentSizeForProcess= 0;
2167  Int64 sizeOfFilesInBuffer = 0;
2168  Int64 smallestSize; 
2169  Int64 numberOfRecords;
2170  Int32 bufferIndex;
2171  Int32 processIndex;
2172  Int32 numberOfProcesses, np;
2173  Int32 i, j, rc = RC_SUCCESS;
2174  Int32 smallestIndex; 
2175  Int32 handle;
2176  Int32 bytesRead, bytesWritten;
2177  Boolean processHasWork = false;
2178  transactionsListRecord transactionRecord;
2179  transactionsListRecord buffer[NRECS_PER_BUFFER];
2180  transactionsListRecord *dir_buffers[MAX_DIR_BUFFERS];
2181  char filenameP[21];
2182  char suffix[2];
2183  char *testString;
2184  offset_t nextWriteOffset, nextReadOffset, actualOffset;
2185  size_t transactionRecordSize;
2186  struct stat statBuf;
2187  char* fn = "createClientFilelists";
2188
2189  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
2190
2191  /* Initializations */
2192  transactionRecordSize = sizeof(transactionsListRecord);
2193  backupCtrlP = backupControlHdrP;
2194
2195  /* Calculate the number of client processes. */
2196  numberOfProcesses = backupCtrlP->backupHdr.icf.numberOfClients * 
2197                        backupCtrlP->backupHdr.icf.processesPerClient;
2198
2199  /* If there is only one client process (possible, but fairly unlikely),
2200     just rename the transactions file to TransactionsList_0. */
2201  if (numberOfProcesses == 1)
2202  {
2203    /* Create the name of the file for the 0th (and only) client process. */
2204    snprintf(clientTransactionsList[0], MAX_FILE_NAME, "%s%d",
2205               transactionsList, 0);
2206
2207    /* Rename the transactions file. */
2208    rc = rename(transactionsList, clientTransactionsList[0]);
2209    if (rc != 0)
2210    {
2211      fprintf(stderr, "rename(%s, %s) returned rc = %d\n",
2212                transactionsList, clientTransactionsList[0], rc);
2213      return(RC_FAIL);
2214    }
2215  }
2216  else   /* We come here if multiple client processes are needed. */
2217  {
2218    /* Create transaction files for the client processes in append mode.
2219       The name of each created file will be TransactionsList_L, i.e.,
2220       we will concatenate the client process suffix L (where L is the
2221       client process index) to the existing TransactionsList filename. */
2222    for (j = 0; j < numberOfProcesses; j++)
2223    {
2224      /* Create the name of the file for the jth client process. */
2225      snprintf(clientTransactionsList[j], MAX_FILE_NAME, "%s%d",
2226                 transactionsList, j);
2227
2228      /* Create the file in append mode. */
2229      handle = open(clientTransactionsList[j],
2230                      O_CREAT | O_APPEND | O_WRONLY | O_TRUNC, 0644);
2231      if (handle == -1)
2232      {
2233        fprintf(stderr, "Cannot create %s\n", clientTransactionsList[j]);
2234        return(RC_FAIL);
2235      }
2236      else
2237      {
2238        clientTransactionsListHandle[j] = handle;
2239      }
2240
2241      /* Set the size of the newly-created list to zero. */
2242      clientTransListSize[j] = 0;
2243    }
2244  }
2245
2246  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
2247
2248  /* If more than one client process is needed (which is most likely),
2249     divy the transactions file into as many files as there are processes,
2250     each smaller file having a similar total aggregate file size. */
2251  if (numberOfProcesses > 1)
2252  {
2253    /* Determine the number of records in the transactions file. */
2254    rc = stat(transactionsList, &statBuf);
2255    if (rc == -1)
2256    {
2257      fprintf(stderr, "Could not stat file '%s', errno=%d\n",
2258                transactionsList, errno);
2259      return(RC_FAIL);
2260    }
2261    numberOfRecords = statBuf.st_size / transactionRecordSize;
2262
2263    /* Open the transactions file in read mode. */
2264    handle = open(transactionsList, O_RDONLY, 0644);
2265    if (handle == -1)
2266    {
2267      fprintf(stderr, "Cannot open %s\n", transactionsList);
2268      return(RC_FAIL);
2269    }
2270
2271    /* If this is a resume backup, doInodeScan() was not called and
2272       so the total aggregate file size has not been calculated yet.
2273       Calculate it now. */ 
2274    if (Resume) 
2275    {
2276      /* Position the file handle to point to the first record. */
2277      nextReadOffset = 0;
2278      actualOffset = lseek(handle, nextReadOffset, SEEK_SET);
2279      if (actualOffset != nextReadOffset)
2280      {
2281        fprintf(stderr, "Cannot seek to %lld in %s\n", nextReadOffset,
2282                  transactionsList);
2283        return(RC_FAIL);
2284      }
2285
2286      /* Set the aggregate file size accumulator to zero. */
2287      backupCtrlP->backupHdr.totalSizeOfFiles = 0;
2288
2289      /* Loop through the transactions file to calculate the aggregate size. */
2290      while (( bytesRead =
2291                 read(handle, &transactionRecord, transactionRecordSize)) > 0)
2292      {
2293        if (bytesRead != transactionRecordSize)
2294        {
2295          fprintf(stderr, "Error reading file %s\n", transactionsList);
2296          return(RC_FAIL);
2297        }
2298
2299        /* Add the file size to the aggregate file size accumulator. */
2300        backupCtrlP->backupHdr.totalSizeOfFiles += 
2301               strtoll((const char *) transactionRecord.filesize, NULL, 10);
2302      }
2303    }
2304
2305    /* Position the transactions file handle to point to the first record. */
2306    nextReadOffset = 0;
2307    actualOffset = lseek(handle, nextReadOffset, SEEK_SET);
2308    if (actualOffset != nextReadOffset)
2309    {
2310      fprintf(stderr, "Cannot seek to %lld in %s\n", nextReadOffset,
2311                transactionsList);
2312      return(RC_FAIL);
2313    }
2314
2315    /* Calculate the target aggregate files size for each client process. */
2316    targetSizeForProcess =
2317              backupCtrlP->backupHdr.totalSizeOfFiles / numberOfProcesses;
2318
2319    /* Initialize the buffer and process indices. */
2320    bufferIndex = -1;
2321    processIndex = 0;
2322    np = 0;
2323
2324    /*
2325     * Loop until all of the transaction records have been processed:
2326     *  - Read a record from the transaction file.
2327     *  - Handle the current transaction record as appropriate based on
2328     *    how full the current client process's transaction file is.
2329     *    The goal is to give each client process about the same amount
2330     *    of backup work to do.
2331     *  - Point to the next client process when the current client
2332     *    process's transaction file reaches the desired target size.
2333     */
2334    while (numberOfRecords > 0)
2335    {
2336      /* Read the next record from the transaction file. */
2337      bytesRead = read(handle, &transactionRecord, transactionRecordSize);
2338      if (bytesRead != transactionRecordSize)
2339      {
2340        fprintf(stderr, "Error reading file %s\n", transactionsList);
2341        return(RC_FAIL);
2342      }
2343
2344      /* Decrement the number of records. */
2345      numberOfRecords--;
2346
2347      /*
2348       * Handle the transaction record for the current file.
2349       * The record is handled differently based on how the file
2350       * fits into the work allotted to the current client process.
2351       * There are three cases:
2352       *   1. Adding the file would cause the aggregate size to be
2353       *      beyond the allowed size discretion;
2354       *   2. Adding the file would cause the aggregate size to be
2355       *      over the process limit but within the allowed discretion;
2356       *   3. Adding the file would not cause the aggregate size to be
2357       *      over the process limit.
2358       */
2359      if ((currentSizeForProcess + sizeOfFilesInBuffer +
2360             strtoll((const char *) transactionRecord.filesize, NULL, 10)) >
2361             targetSizeForProcess + PROCESS_OVERFLOW_DISCRETION)
2362      {
2363        /*
2364         * We come here if the aggregate size will be beyond the allowed
2365         * discretion.  The following steps are performed:
2366         *   1. If the buffer has something in it, process the buffer:
2367         *     a. Write the contents of the buffer to the
2368         *        client process's transaction file.
2369         *     b. Set flag indicating the current process file
2370         *        has been written to.
2371         *     c. Clear the buffer for use by the next process.
2372         *   2. Copy the new transaction record for the file into the
2373         *        buffer and increment the buffer size accumulator.
2374         *   3. If the current process file has been written to,
2375         *      point to the next client process:
2376         *     a. Increment the client process index.
2377         *     b. Reset the process's size accumulator to 0.
2378         *     c. Reset the flag indicating the current process
2379         *        has been written to.
2380         */
2381
2382        /* If the buffer has something in it, write it to
2383           the client process's transaction file. */
2384        if (bufferIndex > -1)
2385        {
2386          rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2387                            (bufferIndex + 1) * transactionRecordSize);
2388          if (rc != RC_SUCCESS)
2389          {
2390            fprintf(stderr,
2391                 "createClientFilelists(): writeBuffer() returned rc = %d\n",
2392                 rc);
2393            return(RC_FAIL);
2394          }
2395
2396          /* Calculate and store the file size value for this process. */
2397          clientTransListSize[processIndex] =
2398                                currentSizeForProcess + sizeOfFilesInBuffer;
2399
2400          /* Set a flag indicating that the current process file has work. */
2401          processHasWork = true;
2402
2403          /* If this is the highest process index so far, save it
2404             as the number of processes that have work so far. */
2405          if (processIndex > np) {
2406            np = processIndex;
2407          }
2408
2409          /* Clear the buffer in preparation for reuse. */
2410          memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
2411          sizeOfFilesInBuffer = 0;
2412          bufferIndex = -1;
2413        }
2414
2415        /* Copy the new record for the file into the buffer
2416           and increment the buffer size accumulator. */
2417        bufferIndex++;
2418        memcpy((void *) &buffer[bufferIndex],
2419                (const void *) &transactionRecord, transactionRecordSize);
2420        sizeOfFilesInBuffer +=
2421                strtoll((const char *) transactionRecord.filesize, NULL, 10);
2422
2423        if (processHasWork) 
2424        {
2425          /* Select the next process file to add work to.  Pick a process
2426             whose file is empty or the one that has the most room. */
2427          smallestSize = clientTransListSize[0];
2428          for (j = 0; j < numberOfProcesses; j++)
2429          {
2430            if (clientTransListSize[j] == 0) {
2431              smallestSize = 0;
2432              smallestIndex = j;
2433              break;
2434            }
2435            if (clientTransListSize[j] <= smallestSize) {
2436              smallestSize = clientTransListSize[j]; 
2437              smallestIndex = j;
2438            }
2439          }
2440          processIndex = smallestIndex;
2441          currentSizeForProcess = smallestSize;
2442
2443          /* Reset the "process has work" flag. */
2444          processHasWork = false;
2445        }
2446
2447      }
2448      else if ((currentSizeForProcess + sizeOfFilesInBuffer +
2449                strtoll((const char *) transactionRecord.filesize, NULL, 10)) >=
2450                  targetSizeForProcess)
2451      {
2452        /*
2453         * We come here if the aggregate size will be over the process target
2454         * but within the allowed discretion. The following steps are performed:
2455         *   1. Copy the new transaction record for the file into the
2456         *        buffer and increment the buffer size accumulator.
2457         *   2. Process the buffer:
2458         *     a. Write the contents of the buffer to the
2459         *          client process's transaction file.
2460         *     b. Clear the buffer for use by the next process.
2461         *   3. Point to the next client process:
2462         *     a. Increment the client process index.
2463         *     b. Reset the process's size accumulator to 0.
2464         *     c. Reset the flag indicating the current process
2465         *          has been written to.
2466         */
2467
2468        /* Copy the new record for the file into the buffer
2469           and increment the buffer size accumulator. */
2470        bufferIndex++;
2471        memcpy((void *) &buffer[bufferIndex],
2472                (const void *) &transactionRecord, transactionRecordSize);
2473        sizeOfFilesInBuffer +=
2474                strtoll((const char *) transactionRecord.filesize, NULL, 10);
2475
2476        /* Write the contents of the buffer to the process file. */
2477        rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2478                          (bufferIndex + 1) * transactionRecordSize);
2479        if (rc != RC_SUCCESS)
2480        {
2481          fprintf(stderr,
2482                   "createClientFilelists(): writeBuffer() returned rc = %d\n",
2483                   rc);
2484          return(RC_FAIL);
2485        }
2486
2487        /* Calculate and store the file size value for this process. */
2488        clientTransListSize[processIndex] =
2489                                currentSizeForProcess + sizeOfFilesInBuffer;
2490
2491        /* If this is the highest process index so far, save it
2492           as the number of processes that have work so far. */
2493        if (processIndex > np) {
2494          np = processIndex;
2495        }
2496
2497        /* Clear the buffer in preparation for reuse. */
2498        memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
2499        sizeOfFilesInBuffer = 0;
2500        bufferIndex = -1;
2501
2502        /* Select the next process file to add work to.  Pick a process
2503           whose file is empty or the one that has the most room. */
2504        smallestSize = clientTransListSize[0];
2505        for (j = 0; j < numberOfProcesses; j++)
2506        {
2507          if (clientTransListSize[j] == 0) {
2508            smallestSize = 0;
2509            smallestIndex = j;
2510            break;
2511          }
2512          if (clientTransListSize[j] <= smallestSize) {
2513            smallestSize = clientTransListSize[j]; 
2514            smallestIndex = j;
2515          }
2516        }
2517        processIndex = smallestIndex;
2518        currentSizeForProcess = smallestSize;
2519
2520        /* Reset the "process has work" flag. */
2521        processHasWork = false;
2522
2523      }
2524      else  /* We are within the limits allocated for the process. */
2525      {
2526        /*
2527         * We come here if the aggregate size will be within the process
2528         * target limit.  The following steps are performed:
2529         *   1. Copy the new transaction record for the file into the
2530         *        buffer and increment the buffer size accumulator.
2531         *   2. If the buffer is full, process the buffer:
2532         *     a. Write the contents of the buffer to the
2533         *          client process's transaction file.
2534         *     b. Increment the aggregate file size for the process.
2535         *     c. Clear the buffer for use by the next process.
2536         */
2537
2538        /* Copy the current transaction record to the buffer. */
2539        bufferIndex++;
2540        memcpy((void *) &buffer[bufferIndex],
2541                (const void *) &transactionRecord, transactionRecordSize);
2542        sizeOfFilesInBuffer +=
2543                strtoll((const char *) transactionRecord.filesize, NULL, 10);
2544
2545        /* If the buffer is full, write its contents to
2546           the transaction file for the client process. */
2547        if ((bufferIndex + 1) == NRECS_PER_BUFFER)
2548        {
2549          /* Write out the buffer and start a new buffer. */
2550          rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2551                            (bufferIndex + 1) * transactionRecordSize);
2552          if (rc != RC_SUCCESS)
2553          {
2554            fprintf(stderr,
2555                     "createClientFilelists: writeBuffer() returned rc = %d\n",
2556                     rc);
2557            return(RC_FAIL);
2558          }
2559 
2560          /* Set a flag indicating that the current process file has work. */
2561          processHasWork = true;
2562
2563          /* If this is the highest process index so far, save it
2564             as the number of processes that have work so far. */
2565          if (processIndex > np) {
2566            np = processIndex;
2567          }
2568
2569          /* Increment the file size value for this process. */
2570          currentSizeForProcess += sizeOfFilesInBuffer;
2571
2572          /* Store the file size value for this process. */
2573          clientTransListSize[processIndex] = currentSizeForProcess;
2574
2575          /* Clear the buffer in preparation for reuse. */
2576          memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
2577          sizeOfFilesInBuffer = 0;
2578          bufferIndex = -1;
2579        }
2580      }
2581
2582    }  /* end while (numberOfRecords > 0) */
2583
2584    /* We have finished reading the records in the file.
2585       If the buffer has something in it, write it to
2586       the client process's transaction file. */
2587    if (bufferIndex > -1)
2588    {
2589      rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2590                        (bufferIndex + 1) * transactionRecordSize);
2591      if (rc != RC_SUCCESS)
2592      {
2593        fprintf(stderr,
2594                "createClientFilelists: writeBuffer() returned rc = %d\n", rc);
2595        return(RC_FAIL);
2596      }
2597
2598      /* If this is the highest process index so far, save it
2599         as the number of processes that have work so far. */
2600      if (processIndex > np) {
2601        np = processIndex;
2602      }
2603    }
2604
2605  } else {   // Only one process was specified. 
2606
2607    /* Set the process number variable to 0. */
2608    np = 0;
2609
2610  }  /* end if (numberOfProcesses > 1) */
2611
2612  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
2613
2614  /* Set the number of processes to the actual number of processes
2615     for which we created transaction files. */
2616  backupCtrlP->backupHdr.numberOfProcesses = np + 1;
2617
2618  /* If there were multiple processes, close the created client
2619     process transaction files and delete any that are empty. */
2620  if (numberOfProcesses > 1)
2621  {
2622    /* Close the created client process transaction files. */
2623    for (j = 0; j < numberOfProcesses; j++)
2624    {
2625      rc = close(clientTransactionsListHandle[j]);
2626      if (rc != 0)
2627      {
2628        fprintf(stderr, "Cannot close file %s.\n", clientTransactionsList[j]);
2629        return(RC_FAIL);
2630      }
2631    }
2632
2633    /* Delete any created client process transactions files that are empty. */
2634    for (j = np+1; j < numberOfProcesses; j++)
2635    {
2636      /* Delete the empty client process transactions file. */
2637      unlink(clientTransactionsList[j]);
2638    }
2639  }  /* end if (numberOfProcesses > 1) */
2640
2641  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
2642
2643  return(rc);
2644
2645}  /*------ end of createClientFilelists() ----------------*/
2646
2647
2648/*
2649 * NAME:  createClientFilelists2()
2650 *
2651 * FUNCTION:
2652 *   Create the transaction files for the client processes by divying
2653 *   the transactions file into smaller files containing roughly similar
2654 *   numbers of records.
2655 *
2656 * PARAMETERS:
2657 *   backupControlHdrP:  (IN) Pointer to a gpfs_backup_control_t structure
2658 *
2659 * RETURNS:
2660 *   RC_SUCCESS on success, RC_FAIL on error.
2661 *
2662 * NOTES:
2663 */
2664int createClientFilelists2(gpfs_backup_control_t *backupControlHdrP)
2665{
2666  gpfs_backup_control_t *backupCtrlP;
2667  Int64 targetSizeForProcess;
2668  Int64 currentSizeForProcess= 0;
2669  Int64 sizeOfFilesInBuffer = 0;
2670  Int64 numberOfRecords, nrecs;
2671  Int32 bufferIndex;
2672  Int32 processIndex;
2673  Int32 numberOfProcesses, np, numberPerProcess;
2674  Int32 i, j, rc = RC_SUCCESS;
2675  Int32 handle;
2676  Int32 bytesRead, bytesWritten;
2677  Boolean processHasWork = false;
2678  transactionsListRecord transactionRecord;
2679  transactionsListRecord buffer[NRECS_PER_BUFFER];
2680  transactionsListRecord *dir_buffers[MAX_DIR_BUFFERS];
2681  char filenameP[21];
2682  char suffix[2];
2683  char *testString;
2684  offset_t nextWriteOffset, nextReadOffset, actualOffset;
2685  size_t transactionRecordSize;
2686  struct stat statBuf;
2687  char* fn = "createClientFilelists2";
2688
2689  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
2690
2691  /* Initializations */
2692  transactionRecordSize = sizeof(transactionsListRecord);
2693  backupCtrlP = backupControlHdrP;
2694
2695  /* Calculate the number of client processes. */
2696  numberOfProcesses = backupCtrlP->backupHdr.icf.numberOfClients * 
2697                        backupCtrlP->backupHdr.icf.processesPerClient;
2698
2699  /* If there is only one client process (possible, but fairly unlikely),
2700     just rename the transactions file to TransactionsList_0. */
2701  if (numberOfProcesses == 1)
2702  {
2703    /* Create the name of the file for the 0th (and only) client process. */
2704    snprintf(clientTransactionsList[0], MAX_FILE_NAME, "%s%d",
2705               transactionsList, 0);
2706
2707    /* Rename the transactions file. */
2708    rc = rename(transactionsList, clientTransactionsList[0]);
2709    if (rc != 0)
2710    {
2711      fprintf(stderr, "rename(%s, %s) returned rc = %d\n",
2712                transactionsList, clientTransactionsList[0], rc);
2713      return(RC_FAIL);
2714    }
2715  }
2716  else   /* We come here if multiple client processes are needed. */
2717  {
2718    /* Create transaction files for the client processes in append mode.
2719       The name of each created file will be TransactionsList_L, i.e.,
2720       we will concatenate the client process suffix L (where L is the
2721       client process index) to the existing TransactionsList filename. */
2722    for (j = 0; j < numberOfProcesses; j++)
2723    {
2724      /* Create the name of the file for the jth client process. */
2725      snprintf(clientTransactionsList[j], MAX_FILE_NAME, "%s%d",
2726                 transactionsList, j);
2727
2728      /* Create the file in append mode. */
2729      handle = open(clientTransactionsList[j],
2730                      O_CREAT | O_APPEND | O_WRONLY | O_TRUNC, 0644);
2731      if (handle == -1)
2732      {
2733        fprintf(stderr, "Cannot create %s\n", clientTransactionsList[j]);
2734        return(RC_FAIL);
2735      }
2736      else
2737      {
2738        clientTransactionsListHandle[j] = handle;
2739      }
2740    }
2741  }
2742
2743  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
2744
2745  /* If more than one client process is needed (which is most likely),
2746     divy the transactions file into as many files as there are processes,
2747     each smaller file having the same number of files (or nearly so). */
2748  if (numberOfProcesses > 1)
2749  {
2750    /* Determine the number of records in the transactions file. */
2751    rc = stat(transactionsList, &statBuf);
2752    if (rc == -1)
2753    {
2754      fprintf(stderr, "Could not stat file '%s', errno=%d\n",
2755                transactionsList, errno);
2756      return(RC_FAIL);
2757    }
2758    numberOfRecords = statBuf.st_size / transactionRecordSize;
2759    numberPerProcess =
2760                numberOfRecords / (backupCtrlP->backupHdr.numberOfProcesses);
2761
2762    /* Open the transactions file in read mode. */
2763    handle = open(transactionsList, O_RDONLY, 0644);
2764    if (handle == -1)
2765    {
2766      fprintf(stderr, "Cannot open %s\n", transactionsList);
2767      return(RC_FAIL);
2768    }
2769
2770    /* Position the transactions file handle to point to the first record. */
2771    nextReadOffset = 0;
2772    actualOffset = lseek(handle, nextReadOffset, SEEK_SET);
2773    if (actualOffset != nextReadOffset)
2774    {
2775      fprintf(stderr, "Cannot seek to %lld in %s\n", nextReadOffset,
2776                transactionsList);
2777      return(RC_FAIL);
2778    }
2779
2780    /* Initialize the buffer and process indices. */
2781    bufferIndex = -1;
2782    processIndex = 0;
2783    nrecs = 0;
2784
2785    /*
2786     * Loop until all of the transaction records have been processed:
2787     *  - Read a record from the transaction file.
2788     *  - Handle the current transaction record as appropriate based on
2789     *    how full the current client process's transaction file is.
2790     *    The goal is to give each client process about the same amount
2791     *    of backup work to do.
2792     *  - Point to the next client process when the current client
2793     *    process's transaction file reaches the desired target size.
2794     */
2795    while (numberOfRecords > 0)
2796    {
2797      /* Read the next record from the transaction file. */
2798      bytesRead = read(handle, &transactionRecord, transactionRecordSize);
2799      if (bytesRead != transactionRecordSize)
2800      {
2801        fprintf(stderr, "Error reading file %s\n", transactionsList);
2802        return(RC_FAIL);
2803      }
2804
2805      /* Decrement the total number of records and
2806         increment the number of records for the current process. */
2807      numberOfRecords--;
2808      nrecs++;
2809
2810      /* If we are filling the transaction file for the last client process,
2811         go right to the code to write the tranaction record appropriately. */
2812      if (processIndex == numberOfProcesses - 1) {
2813        goto put_record2;
2814      }
2815
2816      /*
2817       * Handle the transaction record for the current file.
2818       * The record is handled differently based on how the file
2819       * fits into work allotted to the current client process.
2820       * There are two cases:
2821       *   1. Adding the file would cause the client file to have
2822       *      more records than the target number of records;
2823       *   2. Adding the file would not cause the client file to have
2824       *      more records than the target number of records;
2825       */
2826      if (nrecs >= numberPerProcess)
2827      {
2828        /*
2829         * We come here if adding the file would cause the client file
2830         * to have more records than the target.  The following steps
2831         * are performed:
2832         *   1. If the buffer has something in it, process the buffer:
2833         *     a. Write the contents of the buffer to the
2834         *        client process's transaction file.
2835         *     b. Set flag indicating the current process file
2836         *        has been written to.
2837         *     c. Clear the buffer for use by the next process.
2838         *   2. Copy the new transaction record for the file into the
2839         *        buffer and increment the buffer size accumulator.
2840         *   3. If the current process file has been written to,
2841         *      point to the next client process:
2842         *     a. Increment the client process index.
2843         *     b. Reset the process's size accumulator to 0.
2844         *     c. Reset the flag indicating the current process
2845         *        has been written to.
2846         */
2847
2848        /* If the buffer has something in it, write it to the client process's
2849           transaction file. */
2850        if (bufferIndex > -1)
2851        {
2852          rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2853                            (bufferIndex + 1) * transactionRecordSize);
2854          if (rc != RC_SUCCESS)
2855          {
2856            fprintf(stderr,
2857                 "createClientFilelists2(): writeBuffer() returned rc = %d\n",
2858                 rc);
2859            return(RC_FAIL);
2860          }
2861
2862          /* Set a flag indicating that the current process file has work. */
2863          processHasWork = true;
2864
2865          /* Save the process index as the number of processes
2866             that have work so far. */
2867          np = processIndex;
2868
2869          /* Clear the buffer in preparation for reuse. */
2870          memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
2871          bufferIndex = -1;
2872        }
2873
2874        /* Copy the new record for the file into the buffer
2875           and increment the buffer size accumulator. */
2876        bufferIndex++;
2877        memcpy((void *) &buffer[bufferIndex],
2878                (const void *) &transactionRecord, transactionRecordSize);
2879
2880        if (processHasWork) 
2881        {
2882          /* Point to the next client process and set its size to zero. */
2883          processIndex++;
2884          nrecs = 0;
2885
2886          /* Reset the "process has work" flag. */
2887          processHasWork = false;
2888        }
2889
2890      }
2891      else  /* We are within the limits of the size allocated for the server. */
2892      {
2893        /*
2894         * We come here if adding the file would not cause the client file
2895         * to have more records than the target.  The following steps
2896         * are performed:
2897         *   1. Copy the new transaction record for the file into the
2898         *        buffer and increment the buffer size accumulator.
2899         *   2. If the buffer is full, process the buffer:
2900         *     a. Write the contents of the buffer to the
2901         *        client process's transaction file.
2902         *     b. Clear the buffer for use by the next process.
2903         */
2904
2905        /* Copy the current transaction record to the buffer. */
2906      put_record2:
2907        bufferIndex++;
2908        memcpy((void *) &buffer[bufferIndex],
2909                (const void *) &transactionRecord, transactionRecordSize);
2910
2911        /* If the buffer is full, write its contents to the transaction file
2912           for the client process. */
2913        if ((bufferIndex + 1) == NRECS_PER_BUFFER)
2914        {
2915          /* Write out the buffer and start a new buffer. */
2916          rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2917                            (bufferIndex + 1) * transactionRecordSize);
2918          if (rc != RC_SUCCESS)
2919          {
2920            fprintf(stderr,
2921                    "createClientFilelists2: writeBuffer() returned rc = %d\n",
2922                    rc);
2923            return(RC_FAIL);
2924          }
2925 
2926          /* Set a flag indicating that the current process file has work. */
2927          processHasWork = true;
2928
2929          /* Save the process index as the number of processes
2930             that have work so far. */
2931          np = processIndex;
2932
2933          /* Clear the buffer in preparation for reuse. */
2934          memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
2935          bufferIndex = -1;
2936        }
2937      }
2938
2939    }  /* end while (numberOfRecords > 0) */
2940
2941    /* We have finished reading the records in the file.
2942     * If the buffer has something in it, write it to the client process's
2943     * transaction file. */
2944    if (bufferIndex > -1)
2945    {
2946      rc = writeBuffer(clientTransactionsListHandle[processIndex], buffer,
2947                        (bufferIndex + 1) * transactionRecordSize);
2948      if (rc != RC_SUCCESS)
2949      {
2950        fprintf(stderr,
2951             "createClientFilelists2: writeBuffer() returned rc = %d\n", rc);
2952        return(RC_FAIL);
2953      }
2954
2955      /* Save the process index as the number of processes
2956         that have work so far. */
2957      np = processIndex;
2958    }
2959
2960  } else {   // Only one process was specified. 
2961
2962    /* Set the process number variable to 0. */
2963    np = 0;
2964
2965  }  /* end if (numberOfProcesses > 1) */
2966
2967  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
2968
2969  /* Set the number of processes to the actual number of processes
2970     for which we created transaction files. */
2971  backupCtrlP->backupHdr.numberOfProcesses = np + 1;
2972
2973  /* If there were multiple processes, close the created client
2974     process transaction files and delete any that are empty. */
2975  if (numberOfProcesses > 1)
2976  {
2977    /* Close the created client process transaction files. */
2978    for (j = 0; j < numberOfProcesses; j++)
2979    {
2980      rc = close(clientTransactionsListHandle[j]);
2981      if (rc != 0)
2982      {
2983        fprintf(stderr, "Cannot close file %s.\n", clientTransactionsList[j]);
2984        return(RC_FAIL);
2985      }
2986    }
2987
2988    /* Delete any created client process transactions files that are empty. */
2989    for (j = np+1; j < numberOfProcesses; j++)
2990    {
2991      /* Delete the empty client process transactions file. */
2992      unlink(clientTransactionsList[j]);
2993    }
2994  }  /* end if (numberOfProcesses > 1) */
2995
2996  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
2997
2998  return(rc);
2999
3000}  /*------ end of createClientFilelists2() ----------------*/
3001
3002
3003/*
3004 * NAME:  createBackupSnapshot()
3005 *
3006 * FUNCTION:
3007 *   Create a snapshot of a GPFS filesystem and store it at
3008 *   the mountpoint of the filesystem.
3009 *
3010 * PARAMETERS:
3011 *   fsName       - (IN) Pointer to the device of the filesystem
3012 *                         for which to get a snapshot
3013 *   snapshotName - (IN) snapshot name
3014 *
3015 * RETURNS:
3016 *   Errno
3017 *
3018 * NOTES:
3019 */
3020int createBackupSnapshot(char *deviceName, char *snapshotName)
3021{
3022  Int32 rc = RC_SUCCESS;
3023  struct stat statBuf;
3024  char* fn = "createBackupSnapshot";
3025
3026  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
3027
3028  /* Obtain a handle to access the active filesystem. */
3029  fsSnapHandleP = gpfs_get_fssnaphandle_by_name(deviceName, NULL);
3030  if (fsSnapHandleP == NULL)
3031  {
3032    rc = errno;
3033    fprintf(stderr, "%s: gpfs_get_fssnaphandle_by_name(%s, %s): %s\n",
3034              fn, deviceName, "NULL", strerror(rc));
3035    return(RC_FAIL);
3036  }
3037
3038  /* Use the handle to obtain the name of the subdirectory
3039     in which snapshots would be created. */
3040  rc = gpfs_get_snapdirname(fsSnapHandleP, snapshotSubdir, MAX_FILE_NAME);
3041  if (rc == -1)
3042  {
3043    rc = errno;
3044    fprintf(stderr, "%s: gpfs_get_snapdirname(%s, %s, %d): %s\n",
3045              fn, fsSnapHandleP, snapshotSubdir, MAX_FILE_NAME, strerror(rc));
3046    return(RC_FAIL);
3047  }
3048
3049  /* Determine whether there would be a name collision if a snapshot
3050     were created.  If so, issue an error message and fail. */
3051  sprintf(sstring, "%s/%s", fsName, snapshotSubdir);
3052  rc = stat(sstring, &statBuf);
3053  if (rc == -1)
3054  {
3055    /* It is acceptable for the snapshot directory to not exist;
3056       Otherwise, issue an error and fail. */
3057    if (errno != ENOENT)
3058    {
3059      fprintf(stderr, "Could not stat directory '%s', errno=%d\n",
3060                sstring, errno);
3061      return(RC_FAIL);
3062    }
3063  }
3064  else
3065  {
3066    /* If we get here, the subdirectory exists.  That is ok if it is the
3067       special subdirectory for snapshots;  to recognize this we use the
3068       fact that the snapshot subdirectory always has an inode number
3069       whose value is 0xFFFF0000 (best test we have right now, per Frank).
3070       Otherwise, issue an error message stating we have a name collision
3071       and then fail the routine. */
3072    if (statBuf.st_ino != 0xFFFF0000)
3073    {
3074      fprintf(stderr,
3075     "tsbackup: Subdirectory %s already exists.  Unable to create snapshot.\n",
3076              sstring);
3077      return(RC_FAIL);
3078    }
3079   
3080    /* Delete the backup snapshot if it exists. */
3081    sprintf(sstring, "%s/%s", sstring, snapshotName);
3082    if (stat(sstring, &statBuf) != -1)
3083    {
3084      /* Delete the snapshot. */
3085      sprintf(sstring, "/usr/lpp/mmfs/bin/tsdelsnapshot %s %s > /dev/null",
3086                deviceName, snapshotName);
3087      rc = system(sstring);
3088
3089      /* Check system() return. */
3090      if (rc != 0)
3091      {
3092        fprintf(stderr, "system(%s) returned rc = %d\n", sstring, rc);
3093        return(RC_FAIL);
3094      }
3095    }
3096  }
3097 
3098  /* Issue the tscrsnapshot command to create a snapshot
3099     and store it at the mount point of the filesystem. */
3100  sprintf(sstring, "/usr/lpp/mmfs/bin/tscrsnapshot %s %s > /dev/null",
3101            deviceName, snapshotName);
3102  rc = system(sstring);
3103
3104  /* check system() return  */
3105  if (rc != 0)
3106  {
3107    fprintf(stderr, "system(%s) returned rc = %d\n", sstring, rc);
3108// BCH - possible candidate for message catalog
3109    fprintf(stderr, "Creation of filesystem snapshot did not succeed.\n");
3110    return(RC_FAIL);
3111  }
3112
3113  /* Create fsSnapshotPathname now that we have a snapshot.
3114     Get the snapshot handle for the snapshot and then
3115     use the handle to get the full pathname of the snapshot. */
3116  fsSnapHandleP = gpfs_get_fssnaphandle_by_name(deviceName, fsSnapshotName);
3117  if (fsSnapHandleP == NULL)
3118  {
3119    rc = errno;
3120    fprintf(stderr, "%s: gpfs_get_fssnaphandle_by_name(%s, %s): %s\n",
3121              fn, deviceName, fsSnapshotName, strerror(rc));
3122    return(RC_FAIL);
3123  }
3124
3125  /* Use the handle to get the full pathname of the snapshot. */
3126  snapshotPathname = gpfs_get_pathname_from_fssnaphandle(fsSnapHandleP);
3127  if (snapshotPathname == NULL)
3128  {
3129    rc = errno;
3130    fprintf(stderr, "%s: gpfs_get_pathname_from_fssnaphandle(%s): %s\n",
3131              fn, fsSnapHandleP, strerror(rc));
3132    return(RC_FAIL);
3133  }
3134  strcpy(fsSnapshotPathname, snapshotPathname);
3135
3136  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
3137
3138  return(rc);
3139
3140}  /*------ end of createBackupSnapshot() ----------------*/
3141
3142
3143/*
3144 * NAME:  processInputCtrlFile()
3145 *
3146 * FUNCTION:
3147 *   Process the input control file (the file passed via the -F parameter
3148 *   of mmbackup) which contains the information for controlling the backup.
3149 *
3150 * PARAMETERS:
3151 *   fsNameP              - (IN)  pointer to the filesystem mount point
3152 *   inputCtrlFile        - (IN)  pointer to the file containing the pertinent
3153 *                                 TSM client and server node information
3154 *   backupControlHeaderP - (OUT) pointer to the gpfs_backup_control_t created
3155 *                                 structure; the header info in this structure
3156 *                                 contains the information regarding the nodes
3157 *
3158 * RETURNS:
3159 *   RC_SUCCESS on success, RC_FAIL on error.
3160 *
3161 * NOTES:
3162 *
3163 *  Input file format:   (comments begin with a # character in the 1st column)
3164 *  ==========================================================================
3165 *  # Input control file for mmbackup
3166 *  # name of Tivoli server (must match name in the TSM client's dsm.opt file)
3167 *  serverName = c164n06.ppd.pok.ibm.com
3168 *  # the following value is used to specify parallelism on each client
3169 *  numberOfProcessesPerClient = 4
3170 *  # backup client nodes
3171 *  clientName = c148n11
3172 *  clientName = c148n13
3173 *  clientName = c148n15
3174 *
3175 */
3176int processInputCtrlFile(char *fsName,
3177                         char *inputCtrlFile,
3178                         gpfs_backup_control_t **backupControlHeaderP,
3179                         Boolean doMalloc)
3180{
3181  Int32 j, rc = RC_SUCCESS;
3182  Int32 numProcessesPerClient;
3183  Int32 clientIndex = 0;
3184  Int32 linectr = 0;
3185  char *fsNameP;
3186  char *filename;
3187  gpfs_backup_control_t *backupCtrlP;
3188  FILE *fp;
3189  char* eq_p;
3190  char linein[LINE_LENGTH];
3191  char numberOfProcessesPerClientString[27] = "numberOfProcessesPerClient";
3192  char serverNameString[11] = "serverName";
3193  char clientNameString[11] = "clientName";
3194  Boolean foundServer = false;
3195  Boolean foundNumberofProcessesPerClient = false;
3196  char* fn = "processInputCtrlFile";
3197
3198  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
3199
3200  filename = inputCtrlFile;
3201  fsNameP = fsName;
3202
3203  if (doMalloc)
3204  {
3205    /* Create gpfs_backup_control structure and initialize it
3206       to null characters. */
3207    backupCtrlP =
3208          (gpfs_backup_control_t *) malloc(sizeof(gpfs_backup_control_t));
3209    if (backupCtrlP == NULL)
3210    {
3211      fprintf(stderr, "processInputCtrlFile(): malloc failure");
3212      *backupControlHeaderP = NULL;   /* Let caller know the malloc failed. */
3213      return(RC_FAIL);
3214    }
3215    memset((gpfs_backup_control_t *) backupCtrlP, '\0',
3216             sizeof(gpfs_backup_control_t));
3217
3218    /* Pass back to the caller a pointer to the backup control data. */
3219    *backupControlHeaderP = backupCtrlP;
3220  }
3221  else
3222  {
3223    /* The control block already exists in storage.
3224       Set our pointer so that we can update it. */
3225    backupCtrlP = *backupControlHeaderP;
3226  }
3227
3228  backupCtrlP->backupHdr.icf.numberOfClients = 0;
3229
3230  /* Open the input control file. */
3231  if ((fp = fopen(filename, "r")) == (FILE *) NULL)
3232  {
3233    fprintf(stderr, "processInputCtrlFile(): fopen(%s) failed\n", filename);
3234    return(RC_FAIL);
3235  }
3236
3237  /* Get the next line of information. */
3238  while (fgets(linein, sizeof(linein), fp) != NULL)
3239  {
3240    /* Increment the line counter. */
3241    linectr++;
3242
3243    /* Check the length of the input line. */
3244    if (strlen(linein) == ((LINE_LENGTH) - 1))
3245    {
3246      fprintf(stderr, "File %s line %d:  Line too long: \n  %s\n",
3247                filename, linectr, linein);
3248      return(RC_FAIL);
3249    }
3250
3251    /* Zap any trailing new line character. */
3252    if (linein[strlen(linein)-1] == '\n') {
3253      linein[strlen(linein)-1] = '\0';
3254    }
3255
3256    /* Skip blank or comment lines. */
3257    if ( (strlen(linein) == 0) || (linein[0] == '\0') || (linein[0] == '#') )
3258    {
3259      continue;
3260    }
3261
3262    /* If no '=' character was found, this is a bogus line. */
3263    if ((eq_p = strchr(linein, inputCtrlSeperatorChar)) == NULL)
3264    {
3265      fprintf(stderr, "File %s line %d:  No %c character in line\n  %s\n",
3266                filename, linectr, inputCtrlSeperatorChar, linein);
3267      return(RC_FAIL);
3268    }
3269    else
3270    {
3271      /* Check for the various allowable types of lines. */
3272
3273      /* Is this line specifying the server? */
3274      if (strncmp(linein, serverNameString, (strlen(serverNameString))) == 0)
3275      {
3276        /* Issue an error if the server name was already specified. */
3277        if (foundServer) 
3278        {
3279          fprintf(stderr,
3280       "File %s line %d:  server name was specified more than once:\n  %s\n",
3281                   filename, linectr, linein);
3282          return(RC_FAIL);
3283        }
3284        foundServer = true;
3285
3286        /* Copy the server name from the input line to the
3287           control structure. */
3288        eq_p++;
3289        strcpy(backupCtrlP->backupHdr.icf.serverName, eq_p);
3290        continue;
3291      }
3292      else
3293      {
3294        /* Is this line specifying the number of processes per client? */
3295        if (strncmp(linein, numberOfProcessesPerClientString,
3296                     (strlen(numberOfProcessesPerClientString))) == 0)
3297        {
3298          /* Issue an error if the number of processes per client
3299             was already specified. */
3300          if (foundNumberofProcessesPerClient == true) 
3301          {
3302            fprintf(stderr,
3303                      "File %s line %d: number of processes per client was specified more than once\n  %s\n", filename, linectr, linein);
3304            return(RC_FAIL);
3305          }
3306          foundNumberofProcessesPerClient = true;
3307
3308          /* Copy the number of processes per client from the input line
3309             to the control structure. */
3310          eq_p++;
3311          numProcessesPerClient = atoi((const char *) eq_p);
3312          backupCtrlP->backupHdr.icf.processesPerClient = numProcessesPerClient;
3313          if ((numProcessesPerClient <= 0) ||
3314               (numProcessesPerClient > MAX_BACKUP_CLIENT_PROCESSES))
3315          {
3316            fprintf(stderr,
3317                      "The %s value (%d) specified in file %s is not valid.\n",
3318                      numberOfProcessesPerClientString, numProcessesPerClient,
3319                      filename);
3320            fclose(fp);
3321            return(RC_FAIL);
3322          }
3323          continue;
3324        }
3325        else
3326        {
3327          /* Is this line specifying a client? */
3328          if (strncmp(linein, clientNameString,
3329                       (strlen(clientNameString))) == 0)
3330          {
3331            /* Copy the client name from the input line
3332               to the control structure. */
3333            eq_p++;
3334            strcpy(backupCtrlP->backupHdr.icf.clients[clientIndex].clientName,
3335                     eq_p);
3336            // BCH - Is the following loc necessary with only 1 server?
3337            backupCtrlP->backupHdr.icf.clients[clientIndex].clientIndex =
3338                                                                  clientIndex;
3339            clientIndex++;
3340            continue;
3341          }
3342          else
3343          {
3344            /* Unrecognized line.  Issue error and return with failure rc. */
3345            fprintf(stderr, "File %s line %d:  Line not recognized:\n  %s\n",
3346                      filename, linectr, linein);
3347            return(RC_FAIL);
3348          }
3349        }
3350      }
3351    }
3352  }  // end while (fgets(linein, sizeof(linein), fp) != NULL)
3353
3354  if (clientIndex == 0)
3355  {
3356    fprintf(stderr, "File %s specified 0 clients.\n", filename);
3357    fclose(fp);
3358    return(RC_FAIL);
3359  }
3360
3361  if (clientIndex > MAX_BACKUP_CLIENTS)
3362  {
3363    fprintf(stderr, "File %s specified %d clients but only %d are allowed.\n",
3364              filename, clientIndex, MAX_BACKUP_CLIENTS);
3365    fclose(fp);
3366    return(RC_FAIL);
3367  }
3368
3369  /* Save the number of clients into the control structure. */
3370  backupCtrlP->backupHdr.icf.numberOfClients = clientIndex;
3371
3372  /* If the number of processes per client was not specified, set it to 1. */
3373  if (foundNumberofProcessesPerClient == false)
3374  {
3375    backupCtrlP->backupHdr.icf.processesPerClient = 1; 
3376  }
3377
3378  /* Close the input control file. */
3379  fclose(fp);
3380
3381  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
3382
3383  return(rc);
3384
3385}  /*------ end of processInputCtrlFile() ----------------*/
3386
3387
3388/*
3389 * NAME:  createBackupCtrlFile()
3390 *
3391 * FUNCTION:
3392 *   Create the .backup_control file and write out its contents.
3393 *   Release the storage of the pertinent gpfs_backup_control_t structure.
3394 *
3395 * PARAMETERS:
3396 *   FileName             - (IN) Name of the file to be created.
3397 *   backupControlHeaderP - (IN) Points to the gpfs_backup_control_t created
3398 *                               structure.  The header info in this structure
3399 *                               contains the information regarding the nodes.
3400 *
3401 * RETURNS:
3402 *   RC_SUCCESS on success, RC_FAIL on error.
3403 *
3404 * NOTES:
3405 */
3406int createBackupCtrlFile(char *fileName,
3407                         gpfs_backup_control_t *backupControlHeaderP)
3408{
3409  Int32 fileHandle, rc = RC_SUCCESS;
3410  char *fileNameP;
3411  Int32 fileIndex;
3412  Int32 bytesRead, bytesWritten;
3413  Int64 nextWriteOffset, actualOffset, recordInFile;
3414  gpfs_backup_control_t *backupCtrlP;
3415  size_t ctrlfileRecordSize = sizeof(gpfs_backup_control_t);
3416  char* fn = "createBackupCtrlFile";
3417
3418  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
3419
3420  fileNameP = fileName;
3421
3422  backupCtrlP = backupControlHeaderP;
3423
3424  /* Create the .backup_control file. */
3425  fileHandle = open(fileNameP, O_CREAT | O_APPEND | O_RDWR | O_TRUNC, 0600);
3426  if (fileHandle == -1)
3427  {
3428    fprintf(stderr, "Cannot open file %s\n", fileNameP);
3429    return(RC_FAIL);
3430  }
3431
3432  recordInFile = 0;
3433  nextWriteOffset = recordInFile*ctrlfileRecordSize;
3434  actualOffset = lseek(fileHandle, nextWriteOffset, SEEK_SET);
3435  if (actualOffset != nextWriteOffset)
3436  {
3437    fprintf(stderr, "Cannot seek to %lld in %s\n",
3438              recordInFile*ctrlfileRecordSize, fileNameP);
3439    return(RC_FAIL);
3440  }
3441
3442  bytesWritten = write(fileHandle, backupCtrlP, ctrlfileRecordSize);
3443  if (bytesWritten != ctrlfileRecordSize)
3444  {
3445    fprintf(stderr, "Error writing file %s\n", fileNameP);
3446    return(RC_FAIL);
3447  }
3448
3449  rc = close(fileHandle);
3450  if (rc != 0)
3451  {
3452    fprintf(stderr, "Cannot close file descriptor fileHandle = %d\n",
3453              fileHandle);
3454    return(RC_FAIL);
3455  }
3456
3457  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
3458
3459  return(rc);
3460
3461}  /*------ end of createBackupCtrlFile() ----------------*/
3462
3463
3464/*
3465 * NAME:  doInodeScan()
3466 *
3467 * FUNCTION:
3468 *   Do an inodescan on a snapshot of a GPFS filesystem,
3469 *   and store the results as follows:
3470 *   a) Create a bit array where an index in the array indicates
3471 *      the inode number and the value whether or not the inode needs
3472 *      to be operated on (a value of 1 indicates a yes answer).
3473 *   b) Capture in a temporary filesizes file, one line per inode,
3474 *      indicating the inode number and the size of its file.
3475 *   c) Determine the total number of inodes used by the filesystem,
3476 *      the maximum inode number used, and the aggregate size of all the
3477 *      files of the filesystem needing to be operated on.
3478 *
3479 * PARAMETERS:
3480 *   fsName            - (IN)  pointer to the filesystem name for which to
3481 *                               do an inodescan
3482 *   inodeBitsArrayP   - (OUT) pointer to the bit map array created
3483 *   backupControlHdrP - (IN)  pointer to the gpfs_backup_control_t created
3484 *                             structure; the header info in this structure
3485 *                             contains the information in (c) above
3486 *
3487 * RETURNS:
3488 *   RC_SUCCESS on success, RC_FAIL on error.
3489 *
3490 * NOTES:
3491 */
3492int doInodeScan(char *fsName,
3493                inodeBitsArray *inodeBitsArrayP,
3494                gpfs_backup_control_t *backupControlHdrP)
3495{
3496  gpfs_iscan_t *backupIscan;
3497  gpfs_ino_t lastInodeNum = 0;
3498  gpfs_ino_t maxInodeNum;
3499  gpfs_backup_control_t *backupCtrlP;
3500  gpfs_fssnap_id_t backup_snapID;
3501  Int32 rc = RC_SUCCESS;
3502  Int32 bytesWritten;
3503  inodeBitsArray InodeP;
3504  const gpfs_iattr_t *iattrP;
3505  backup_filesizes_t inodeSize;
3506  char inode_num[MAX_INT32_CHARS];
3507  char inode_size[MAX_INT64_CHARS];
3508  char* fn = "doInodeScan";
3509
3510  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
3511
3512  backupCtrlP = backupControlHdrP;
3513
3514  /* Create a bit map array for a GPFS filesystem
3515     and initialize it to zero bits. */
3516  rc = Bitmap(MAX_INODES, zeroBit, &InodeP);
3517  if (rc != 0)
3518  {
3519    fprintf(stderr, "Bitmap error rc = %x\n", rc);
3520    return(RC_FAIL);
3521  }
3522
3523  /* Create the filesizes file. */
3524  filesizesHandle = open(filesizesFile,
3525                           O_CREAT | O_APPEND | O_RDWR | O_TRUNC, 0600);
3526  if (filesizesHandle == -1)
3527  {
3528    fprintf(stderr, "Cannot open temp file %s\n", filesizesFile);
3529    free(InodeP);
3530    return(RC_FAIL);
3531  }
3532
3533  /* initializations */
3534  backupCtrlP->backupHdr.inodesTotal = 0;
3535  backupCtrlP->backupHdr.totalSizeOfFiles = 0;
3536
3537  /* Get the snapshot handle for this filesystem. */
3538  fsSnapHandleP = gpfs_get_fssnaphandle_by_path(fsName);
3539  if (fsSnapHandleP == NULL)
3540  {
3541    rc = errno;
3542    fprintf(stderr, "%s: gpfs_get_fssnaphandle_by_path(%s): %s\n",
3543              "doInodeScan", fsName, strerror(rc));
3544    return(RC_FAIL);
3545  }
3546
3547  /* Open the inode scan for this filesystem.  Passing a NULL for the
3548     2nd parameter (previous snapshot id) causes gpfs_open_inodescan()
3549     to return the inodes for all the files in the filesystem. */
3550  backupIscan = gpfs_open_inodescan(fsSnapHandleP, NULL, &maxInodeNum);
3551
3552  /* If gpfs_open_inodescan() failed, issue an error and quit. */
3553  if (backupIscan == NULL)
3554  {
3555    rc = errno;
3556    fprintf(stderr, "%s: gpfs_open_inodescan() for snapshot %s failed: %s\n",
3557              "doInodeScan", fsName, strerror(rc));
3558    free(InodeP);
3559    return(RC_FAIL);
3560  }
3561
3562  /* Get the snapshot id and save it in the backup control file. */
3563  rc = gpfs_get_fssnapid_from_fssnaphandle(fsSnapHandleP, &backup_snapID);
3564  if (rc != 0)
3565  {
3566    rc = errno;
3567    fprintf(stderr,
3568      "%s: gpfs_get_fssnapid_from_fssnaphandle(fsSnapHandleP, &snapID): %s\n",
3569              "doInodeScan", strerror(rc));
3570    return(RC_FAIL);
3571  }
3572  memcpy((void *) &(backupCtrlP->backupHdr.snapshotId),
3573           (const void *) &backup_snapID, sizeof(gpfs_fssnap_id_t));
3574
3575  /* Use the backup/restore by inode API to do an inode scan
3576     and get the list of inodes into the bit array.
3577     Loop doing gpfs_next_inode() which returns gpfs_iattr_t
3578     and put the inode numbers into the allocated array. */
3579
3580  /* Repeated calls to gpfs_next_inode() return inode information
3581     about all existing files and directories in inode number order. */
3582  while (1)
3583  {
3584    rc = gpfs_next_inode(backupIscan, maxInodeNum, &iattrP);
3585    if (rc != 0)
3586    {
3587      rc = errno;
3588      fprintf(stderr, "%s: gpfs_next_inode(%s,%d): %s\n",
3589                "doInodeScan", fsName, lastInodeNum, strerror(rc));
3590      return(RC_FAIL);
3591    }
3592
3593    /* Exit the loop if a null pointer, the indication for the end
3594       of the inode scan, is encountered. */
3595    if (iattrP == NULL) {
3596      break;
3597    }
3598
3599    /* Save the inode number. */
3600    lastInodeNum = iattrP->ia_inode;
3601
3602    /* Set the bit in the bit array. */
3603    rc = setToOne(InodeP, (UInt32) iattrP->ia_inode, NULL);
3604    assert(rc == 0);
3605
3606    /* Construct a record for the backup filesizes file and
3607       append it to the file.  Note the use of sprintf and memcpy
3608       instead of sprintf alone to avoid copying the null at
3609       the end of a string into the record.  This is done because
3610       sort() has trouble handling such embedded nulls. */
3611    memset((void *) &inodeSize, ' ', sizeof(backup_filesizes_t));
3612
3613    sprintf(inode_num, "%d", iattrP->ia_inode);
3614    memcpy((void *) &inodeSize.inodenum, (const void *) &inode_num,
3615             strlen(inode_num));
3616 
3617    sprintf(inode_size, "%lld", iattrP->ia_size);
3618    memcpy((void *) &inodeSize.filesize, (const void *) &inode_size,
3619             strlen(inode_size));
3620    inodeSize.newline_char = '\n';
3621
3622    bytesWritten = write(filesizesHandle, &inodeSize,
3623                           sizeof(backup_filesizes_t));
3624    if (bytesWritten != sizeof(backup_filesizes_t))
3625    {
3626      fprintf(stderr, "Error writing to filesizes file\n");
3627      free(InodeP);
3628      return(RC_FAIL);
3629    }
3630
3631    /* Update pertinent fields in the backup header. */
3632    backupCtrlP->backupHdr.totalSizeOfFiles += (UInt32) iattrP->ia_size;
3633    backupCtrlP->backupHdr.inodeMax = (UInt32) iattrP->ia_inode;
3634    backupCtrlP->backupHdr.inodesTotal++;
3635
3636  }  // end while (1)
3637
3638  rc = close(filesizesHandle);
3639  if (rc != 0)
3640  {
3641    fprintf(stderr, "Error in closing the filesizes file\n");
3642    free(InodeP);
3643    return(RC_FAIL);
3644  }
3645
3646  /* Close the inode scan. */
3647  gpfs_close_inodescan(backupIscan);
3648
3649  /* Free the snapshot handle. */
3650  gpfs_free_fssnaphandle(fsSnapHandleP);
3651
3652  /* Set the bit array pointer being passed back. */
3653  *inodeBitsArrayP = InodeP;
3654
3655  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
3656
3657  return(rc);
3658
3659}  /*------ end of doInodeScan() ----------------*/
3660
3661
3662/*
3663 * NAME:  doIncrInodeScan()
3664 *
3665 * FUNCTION:
3666 *   Do an inodescan on a snapshot of a GPFS filesystem,
3667 *   and capture the results as follows:
3668 *   a) Create a bit array where an index in the array indicates the
3669 *      inode number and the value indicates whether or not the inode is
3670 *      for a file that is new or has changed since the previous snapshot
3671 *   b) Determine the total number of inodes used by the filesystem,
3672 *      and the maximum inode number used.
3673 *
3674 * PARAMETERS:
3675 *   fsName            - (IN)  pointer to the filesystem name for which to
3676 *                               do an inodescan
3677 *   prevSnapIdP       - (IN)  pointer to the snapshot ID of a previous snapshot
3678 *   inodeBitsArray2P  - (OUT) pointer to the bit map array created
3679 *   backupControlHdrP - (IN)  pointer to the gpfs_backup_control_t created
3680 *                               structure; the header info in this structure
3681 *                               contains the information in (b) above
3682 *
3683 * RETURNS:
3684 *   RC_SUCCESS on success, RC_FAIL on error.
3685 *
3686 * NOTES:
3687 */
3688int doIncrInodeScan(char *fsName,
3689                    gpfs_fssnap_id_t *prevSnapIdP,
3690                    inodeBitsArray *inodeBitsArray2P,
3691                    gpfs_backup_control_t *backupControlHdrP)
3692{
3693  gpfs_iscan_t *backupIscan2;
3694  gpfs_ino_t lastInodeNum = 0;
3695  gpfs_ino_t maxInodeNum;
3696  gpfs_backup_control_t *backupCtrlP;
3697  gpfs_fssnap_id_t backup_snapID;
3698  Int32 rc = RC_SUCCESS;
3699  Int32 bytesWritten;
3700  inodeBitsArray inode2P;
3701  const gpfs_iattr_t *iattrP;
3702  char inode_num[MAX_INT32_CHARS];
3703  char inode_size[MAX_INT64_CHARS];
3704  char* fn = "doIncrInodeScan";
3705
3706  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
3707
3708  /* Create a bit map array for a GPFS filesystem
3709     and initialize it to zero bits. */
3710  rc = Bitmap(MAX_INODES, zeroBit, &inode2P);
3711  if (rc != 0)
3712  {
3713    fprintf(stderr, "Bitmap error rc = %x\n", rc);
3714    return(RC_FAIL);
3715  }
3716
3717  /* Get the snapshot handle for this filesystem. */
3718  fsSnapHandleP = gpfs_get_fssnaphandle_by_path(fsName);
3719  if (fsSnapHandleP == NULL)
3720  {
3721    rc = errno;
3722    fprintf(stderr, "%s: gpfs_get_fssnaphandle_by_path(%s): %s\n",
3723              "doIncrInodeScan", fsName, strerror(rc));
3724    return(RC_FAIL);
3725  }
3726
3727  /* Open an incremental inode scan for this filesystem.
3728     Passing the id of the previous snapshot causes gpfs_open_inodescan()
3729     to only return the inodes for files that have been added or changed
3730     since the previous snapshot, plus those that existed in the previous
3731     snapshot but no longer exist now (i.e., those for files which were
3732     deleted from the filesystem since the previous snapshot). */
3733  backupIscan2 = gpfs_open_inodescan(fsSnapHandleP, prevSnapIdP, &maxInodeNum);
3734
3735  /* If gpfs_open_inodescan() failed, issue an error and quit. */
3736  if (backupIscan2 == NULL)
3737  {
3738    rc = errno;
3739    fprintf(stderr, "%s: gpfs_open_inodescan() for snapshot %s failed: %s\n",
3740              "doIncrInodeScan", fsName, strerror(rc));
3741    free(inode2P);
3742    return(RC_FAIL);
3743  }
3744
3745  /* Use the backup/restore by inode API to do an inode scan
3746     and get the list of inodes into the bit array.
3747     Loop doing gpfs_next_inode() which returns gpfs_iattr_t
3748     and put the inode numbers into the allocated array. */
3749
3750  /* Repeated calls to gpfs_next_inode() return inode information
3751     about all existing files and directories in inode number order. */
3752  while (1)
3753  {
3754    rc = gpfs_next_inode(backupIscan2, maxInodeNum, &iattrP);
3755    if (rc != 0)
3756    {
3757      rc = errno;
3758      fprintf(stderr, "%s: gpfs_next_inode(%s,%d): %s\n",
3759                "doIncrInodeScan", fsName, lastInodeNum, strerror(rc));
3760      return(RC_FAIL);
3761    }
3762
3763    /* Exit the loop if a null pointer, the indication
3764       for the end of the inode scan, is encountered. */
3765    if (iattrP == NULL) {
3766      break;
3767    }
3768
3769    /* Save the inode number. */
3770    lastInodeNum = iattrP->ia_inode;
3771
3772    /* If this inode for a file that is still present, flag it.
3773       (We don't flag inodes for deleted files.) */
3774    if (iattrP->ia_nlink != 0)
3775    {
3776      rc = setToOne(inode2P, (UInt32) iattrP->ia_inode, NULL);
3777      assert(rc == 0);
3778    }
3779  }  // end while (1)
3780
3781  /* Close the inode scan. */
3782  gpfs_close_inodescan(backupIscan2);
3783
3784  /* Free the snapshot handle. */
3785  gpfs_free_fssnaphandle(fsSnapHandleP);
3786
3787  /* Set the bit array pointers being passed back. */
3788  *inodeBitsArray2P = inode2P;
3789
3790  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
3791
3792  return(rc);
3793
3794}  /*------ end of doIncrInodeScan() ----------------*/
3795
3796
3797/*
3798 * NAME:  EnqueueWork()
3799 *
3800 * Add an element to the work queue and signal any thread waiting for work.
3801 */
3802int EnqueueWork(const char *pathP, const char *dirP, ino_t inode)
3803{
3804  int rc;
3805  int pathLen = strlen(pathP);
3806  QueueElement *qP = NULL;
3807 
3808  /* Allocate a work queue element and space for the path.  */
3809  qP = (QueueElement *) malloc((sizeof(*qP) - sizeof(qP->path)) 
3810                               + pathLen + strlen(dirP) + 2);
3811  if (qP == NULL)
3812  {
3813    fprintf(stderr, "Out of memory.\n");
3814    return(ENOMEM);
3815  }
3816 
3817  /* Initialize element. */
3818  qP->ino = inode;
3819  if (pathLen > 0) {
3820    snprintf(qP->path, MAX_FILE_NAME, "%s/%s", pathP, dirP);
3821  } else {
3822    strcpy(qP->path, dirP);
3823  }
3824   
3825  /* Acquire mutex protecting the work queue. */
3826  rc = pthread_mutex_lock(&QueueMutex);
3827  if (rc != 0)
3828  {
3829    fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc));
3830    return rc;
3831  }
3832
3833  /* Add work element to the list. */
3834  qP->nextP = WorkQueueP;
3835  WorkQueueP = qP;
3836
3837  /* Signal a waiting worker thread. */
3838  if (NWorkersWaiting > 0)
3839  {
3840    NWorkersWaiting -= 1;
3841    rc = pthread_cond_signal(&QueueCond);
3842    if (rc != 0)
3843    {
3844      fprintf(stderr, "Error in pthread_cond_signal: %s\n", strerror(rc));
3845      return rc;
3846    }
3847  }
3848
3849  /* Release mutex before returning. */
3850  rc = pthread_mutex_unlock(&QueueMutex);
3851  if (rc != 0)
3852  {
3853    fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc));
3854    return rc;
3855  }
3856 
3857  /* Success! */
3858  return 0;
3859
3860}  /*------ end of EnqueueWork() ----------------*/
3861
3862
3863/*
3864 * NAME:  DequeueWork()
3865 *
3866 * Dequeue the next work element from the queue, or if the queue
3867 * is empty, wait for more work to become available.
3868 *
3869 */
3870QueueElement *DequeueWork(void)
3871{
3872  int rc;
3873  QueueElement *qP;
3874 
3875  /* Acquire mutex protecting the work queue. */
3876  rc = pthread_mutex_lock(&QueueMutex);
3877  if (rc != 0)
3878  {
3879    fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc));
3880    return NULL;
3881  }
3882 
3883  /* one less thread working */
3884  NWorkersRunning -= 1;
3885
3886  /* Loop waiting for work to become available. */
3887  while ((WorkQueueP == NULL) && (NWorkersRunning > 0))
3888  {
3889    NWorkersWaiting += 1;       /* one more thread waiting */
3890    rc = pthread_cond_wait(&QueueCond, &QueueMutex);
3891    if (rc != 0)
3892    {
3893      fprintf(stderr, "Error in pthread_cond_wait: %s\n", strerror(rc));
3894      return NULL;
3895    }
3896  }
3897
3898  /* Check whether we are done. */
3899  if (WorkQueueP == NULL)
3900  {
3901    qP = NULL;                  /* Nothing more to do. */
3902   
3903    /* signal any waiting threads */
3904    if (NWorkersWaiting > 0)
3905    {
3906      NWorkersWaiting = 0;
3907      rc = pthread_cond_broadcast(&QueueCond);
3908      if (rc != 0)
3909      {
3910        fprintf(stderr, "Error in pthread_cond_broadcast: %s\n", strerror(rc));
3911      }
3912    }
3913  }
3914  else
3915  {
3916    /* Remove the first element from the queue. */
3917    qP = WorkQueueP;
3918    WorkQueueP = qP->nextP;
3919    qP->nextP = NULL;
3920
3921    /* one more thread working */
3922    NWorkersRunning += 1;
3923  }
3924
3925  /* Release mutex before returning. */
3926  rc = pthread_mutex_unlock(&QueueMutex);
3927  if (rc != 0)
3928  {
3929    fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc));
3930    return NULL;
3931  }
3932 
3933  return qP;
3934 
3935}  /*------ end of DequeueWork() ----------------*/
3936
3937
3938/* Main body for each thread.
3939   Loop until there is no more work to do. */
3940void* ThreadBody(void *parmP)
3941{
3942  int rc;
3943  QueueElement *qP;
3944
3945  /* Count this thread as a worker thread. */
3946  NWorkersRunning += 1;
3947
3948  /* Loop while there is work to do. */
3949  do
3950  {
3951    /* Wait for work. */
3952    qP = DequeueWork();
3953    if (qP == NULL)
3954      break;
3955   
3956    /* Read this directory. */
3957    rc = read_dir(qP->path, qP->ino);
3958
3959    /* Free the work queue element. */
3960    free(qP);
3961
3962  } while (rc == 0);
3963
3964  return 0;
3965 
3966}  /*------ end of ThreadBody() ----------------*/
3967
3968
3969/*
3970 * NAME:  read_dir()
3971 *
3972 * Read all entries from the current directory.
3973 *
3974 */
3975int read_dir(const char *pathP, ino_t inode)
3976{
3977  char fileName[MAX_FILE_NAME];
3978  const gpfs_direntx_t *direntxP;
3979  gpfs_fssnap_handle_t *fsP = NULL;
3980  gpfs_ifile_t *dirxP = NULL;
3981  int rc = 0;
3982
3983  /* Get the handle for the snapshot. */
3984  fsP = gpfs_get_fssnaphandle_by_path(RootFsDirP);
3985  if (fsP == NULL)
3986  {
3987    rc = errno;
3988    fprintf(stderr, "gpfs_get_fssnaphandle_by_path(%s/%s): %s\n",
3989              RootFsDirP, pathP, strerror(rc));
3990    goto exit;
3991  }
3992
3993  /* Open the directory's file. */
3994  dirxP = gpfs_iopen(fsP, inode, O_RDONLY, NULL, NULL);
3995  if (dirxP == NULL)
3996  {
3997    rc = errno;
3998    fprintf(stderr, "gpfs_iopen(%s/%s): %s\n", RootFsDirP, pathP, strerror(rc));
3999    goto exit;
4000  }
4001
4002  /* Loop reading the directory entries. */
4003  while (1)
4004  {
4005    rc = gpfs_ireaddir(dirxP, &direntxP);
4006    if (rc != 0)
4007    {
4008      int saveerrno = errno;
4009      fprintf(stderr, "gpfs_ireaddir(%s/%s): rc %d %s\n",
4010                RootFsDirP, pathP, rc, strerror(saveerrno));
4011      rc = saveerrno;
4012      goto exit;
4013    }
4014    if (direntxP == NULL) {
4015      break;
4016    }
4017
4018    /* Check whether this entry is for a directory. */
4019    if (direntxP->d_type == GPFS_DE_DIR)
4020    {
4021      if (strcmp(direntxP->d_name, "..") != 0)     /* skip parent directory */
4022      {
4023        if ((strcmp(direntxP->d_name, ".") == 0))  /* handle self directory */
4024        {
4025          /* The entry is for the directory itself.
4026             Check the bit map array entry to determine
4027             whether the inode must be backed up. */
4028          if (testBit(inodeBitsP, (UInt32) direntxP->d_ino) != 0)
4029          {
4030            /* Is the directory name too long?  */
4031            if (strlen(pathP)+2 > sizeof(fileName))
4032            {
4033              fprintf(stderr, "read_dir: name %s too long\n", pathP);
4034            }
4035            else
4036            {
4037              /* Call routine to write a record for this inode
4038                 to the shadow file. */
4039              writeToShadowFile(pathP, direntxP->d_ino, 'Y');
4040            }
4041          }
4042        }
4043        else                                    /* handle sub-directory */
4044        {
4045          /* Add this sub-directory to the work queue. */
4046          rc = EnqueueWork(pathP, direntxP->d_name, direntxP->d_ino);
4047          if (rc != 0) {
4048            break;
4049          }
4050        }
4051      }
4052    }
4053    else   /* The entry is for a file. */
4054    {
4055      /* Check the bit map array entry to determine
4056         whether the inode must be backed up. */
4057      if (testBit(inodeBitsP, (UInt32) direntxP->d_ino) != 0)
4058      {
4059        snprintf(fileName, MAX_FILE_NAME, "%s/%s", pathP, direntxP->d_name);
4060        if (strlen(fileName)+2 > sizeof(fileName))
4061        {
4062          fprintf(stderr, "dirInodeWalk: name %s too long\n", fileName);
4063        }
4064        else
4065        {
4066          /* Write a record for this inode to the shadow file. */
4067          writeToShadowFile(fileName, direntxP->d_ino, 'N');
4068        }
4069      }
4070    }
4071  }
4072
4073exit:
4074  /* Close the open file, if necessary. */
4075  if (dirxP) {
4076    gpfs_iclose(dirxP);
4077  }
4078
4079  /* Free the snapshot handle. */
4080  if (fsP) {
4081    gpfs_free_fssnaphandle(fsP);
4082  }
4083
4084  return rc;
4085 
4086}  /*------ end of read_dir() ----------------*/
4087
4088
4089/*
4090 * NAME:  writeToShadowFile()
4091 *
4092 * Create a record for the shadow file using the passed parameters,
4093 * obtain the mutex lock for the shadow file, write the record
4094 * to the file, and release the mutex lock.
4095 *
4096 * Care is taken to not introduce nulls into the shadow record.
4097 * This is done by using sprintf/memcpy instead of sprintf alone.
4098 */
4099int writeToShadowFile(const char *pathP, ino_t inode, const char inodeFlag)
4100{
4101  char name[MAX_FILE_NAME];
4102  char filenameLength[MAX_INT32_CHARS];
4103  Int32 nameLength;
4104  char inode_num[MAX_INT32_CHARS];
4105  backup_shadow_record_t shadowRecord;
4106  Int32 bytesWritten;
4107  int rc = RC_SUCCESS;
4108
4109  /* Build the record to be written to the shadow file. */
4110
4111  /* Set the shadow record to blanks. */
4112  memset((void *) &shadowRecord, ' ', sizeof(backup_shadow_record_t));
4113
4114  /* Copy the full path name into the transaction record.
4115     The name is enclosed with quotes because Tivoli requires this
4116     for filenames that contain an embedded blank.  Rather than
4117     check each filename, we simply always use quotes. */
4118  sprintf(name, "%s%s%s", "\"", pathP, "\"");
4119  nameLength = strlen(name);
4120  memcpy((void *) &shadowRecord.tr.filename, (const void *) &name, nameLength);
4121
4122  /* Save the length of the filename for later use.  This is done
4123     to avoid putting null characters in the shadow file because
4124     the sort command does not handle null characters well. */
4125  sprintf(filenameLength, "%d", nameLength);
4126  memcpy((void *) &shadowRecord.tr.fnLength,
4127           (const void *) &filenameLength, strlen(filenameLength));
4128
4129  /* Copy the inode number as a string to the shadow record. */
4130  sprintf(inode_num, "%d", inode);
4131  memcpy((void *) &shadowRecord.inodenum,
4132           (const void *) &inode_num, strlen(inode_num));
4133
4134  /* Set the directory flag and set the newline character. */
4135  shadowRecord.inodeDir = inodeFlag;
4136  shadowRecord.newline_char = '\n';
4137
4138  /* Serialize the output with all the other threads. */
4139  rc = pthread_mutex_lock(&OutputMutex);
4140  if (rc != 0)
4141  {
4142    fprintf(stderr, "Error in pthread_mutex_lock: %s\n", strerror(rc));
4143    goto exit;
4144  }
4145
4146  /* Write the shadow record to the file. */
4147  bytesWritten = write(shadowFileHandle[0], &shadowRecord,
4148                         sizeof(backup_shadow_record_t));
4149
4150  /* Increment the record count. */
4151  shadowFileNumberOfRecords[0]++;
4152
4153  /* Release the lock. */
4154  rc = pthread_mutex_unlock(&OutputMutex);
4155  if (rc != 0)
4156  {
4157    fprintf(stderr, "Error in pthread_mutex_unlock: %s\n", strerror(rc));
4158    goto exit;
4159  }
4160
4161  /* Issue an error if the write failed. */
4162  // BCH - should we assert or exit with a failure?
4163  if (bytesWritten != sizeof(backup_shadow_record_t))
4164  {
4165    fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
4166              shadowFileHandle[0], bytesWritten,
4167              sizeof(backup_shadow_record_t));
4168  }
4169
4170exit:
4171  return rc;
4172 
4173}  /*------ end of writeToShadowFile() ----------------*/
4174
4175
4176/*
4177 * NAME:  dirInodeWalk()
4178 *
4179 * Thread-based inode directory walker.
4180 */
4181int dirInodeWalk(const char *pathname, inodeBitsArray bit_map)
4182{
4183  char fsSnapshotDir[MAX_FILE_NAME];
4184  int i, rc, filearg;
4185  struct stat statBuf;
4186  pthread_t pThread;
4187
4188  /* Set the root filesystem directory pointer. */
4189  RootFsDirP = (char *) pathname;
4190
4191  /* Initialize pthread variables. */
4192  rc = pthread_mutex_init(&QueueMutex, NULL);
4193  if (rc != 0)
4194  {
4195    fprintf(stderr, "Error from pthread_mutex_init: %s\n", strerror(rc));
4196    exit(rc);
4197  }
4198  rc = pthread_cond_init(&QueueCond, NULL);
4199  if (rc != 0)
4200  {
4201    fprintf(stderr, "Error from pthread_cond_init: %s\n", strerror(rc));
4202    exit(rc);
4203  }
4204  rc = pthread_mutex_init(&OutputMutex, NULL);
4205  if (rc != 0)
4206  {
4207    fprintf(stderr, "Error from pthread_mutex_init: %s\n", strerror(rc));
4208    exit(rc);
4209  }
4210 
4211  /* Get the inode number of the root directory. */
4212  rc = stat(RootFsDirP, &statBuf);
4213  if (rc) 
4214  {
4215    rc = errno;
4216    perror(RootFsDirP);
4217    return rc;
4218  }
4219
4220  /* Verify that this is a directory; return if it is not. */
4221  if (!S_ISDIR(statBuf.st_mode))
4222  {
4223    errno = ENOTDIR;
4224    perror(RootFsDirP);
4225    return ENOTDIR;
4226  }
4227 
4228  /* Add the root directory to the work queue. */
4229  snprintf(fsSnapshotDir, MAX_FILE_NAME, "%s/%s", fsName, snapshotSubdir);
4230  rc = EnqueueWork(fsSnapshotDir, fsSnapshotName, statBuf.st_ino);
4231  if (rc != 0) {
4232    return rc;
4233  }
4234
4235  /* Start the requested number of threads. */
4236  for (i = 1; i < nThreads; i++)
4237  {
4238    rc = pthread_create(&pThread, NULL, ThreadBody, NULL);
4239    if (rc != 0)
4240    {
4241      fprintf(stderr, "Error from pthread_create(%d): %s\n", i, strerror(rc));
4242      exit(rc);
4243    }
4244  }
4245
4246  /* This thread can do work, too. */
4247  (void) ThreadBody(NULL);
4248
4249  return rc;
4250 
4251}  /*------ end of dirInodeWalk() ----------------*/
4252
4253
4254/*
4255 * NAME:  createSnapshotShadows()
4256 *
4257 * FUNCTION:
4258 *   Construct shadow files of a snapshot and store them at the
4259 *   mount point of the filesystem.
4260 *
4261 * PARAMETERS:
4262 *   fsName:              (IN) the name of the filesystem
4263 *   numberOfShadowFiles  (IN) the number of files to be created
4264 *   bit_map              (IN) points to a bit map array to be consulted
4265 *
4266 * RETURNS:
4267 *   RC_SUCCESS on success, RC_FAIL on error.
4268 *
4269 * NOTES:
4270 */
4271int createSnapshotShadows(char *fsName,
4272                          Int32 numberOfShadowFiles,
4273                          inodeBitsArray bit_map)
4274{
4275  Int32 rc = RC_SUCCESS;
4276  Int32 i, handle;
4277  Int32 numShadows;
4278  Int32 numInodes;
4279  char filenameP[18];
4280  char suffix[2];
4281  Int32 *shadow_files[MAX_BACKUP_CLIENTS];
4282  const gpfs_direntx_t *source = NULL;
4283  struct stat statBuf;
4284  char* fn = "createSnapshotShadows";
4285
4286  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4287
4288  numShadows = numberOfShadowFiles;
4289
4290  /* Create shadow files. */
4291  for (i = 0; i < numShadows; i++)
4292  {
4293    /* Create file name. */
4294    memset(suffix, ' ', 2);
4295    sprintf(suffix, "%d", i);
4296    strcpy(filenameP, backupShadowName);
4297    strcat(filenameP, (const char *) suffix);
4298    snprintf(shadowFile[i], MAX_FILE_NAME, "%s/%s", fsName, filenameP);
4299
4300    /* Create file in append mode. */
4301    handle = open(shadowFile[i], O_CREAT | O_APPEND | O_RDWR | O_TRUNC, 0644);
4302    if (handle == -1)
4303    {
4304      fprintf(stderr, "Cannot create %s\n", filenameP);
4305      return(RC_FAIL);
4306    }
4307    else
4308    {
4309      shadowFileHandle[i] = handle;
4310    }
4311  }
4312
4313  /* Stat the snapshot directory to get its inode_number. */
4314  if (stat(fsSnapshotPathname, &statBuf) == -1)
4315  {
4316    fprintf(stderr, "Could not stat file '%s', errno=%d\n",
4317              fsSnapshotPathname, errno);
4318    return(RC_FAIL);
4319  }
4320
4321  /* Update the newly-created backup shadow files. */
4322  dirInodeWalk(fsSnapshotPathname, bit_map);
4323
4324  /* Close the generated files. */
4325  for (i = 0; i < numShadows; i++)
4326  {
4327    rc = close(shadowFileHandle[i]);
4328    if (rc == -1)
4329    {
4330      fprintf(stderr,
4331                "Cannot close file descriptor %d\n", shadowFileHandle[i]);
4332      return(RC_FAIL);
4333    }
4334  }
4335
4336  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
4337
4338  return(rc);
4339
4340}  /*------ end of createSnapshotShadows() ----------------*/
4341
4342
4343/*
4344 * NAME:  sortShadowfilesByInode()
4345 *
4346 * FUNCTION:
4347 *   Sort the shadowFile[MAX_BACKUP_CLIENTS] files by inode number.
4348 *
4349 * PARAMETERS:
4350 *   numShadows:  (IN) the number of shadow files
4351 *
4352 * RETURNS:
4353 *   RC_SUCCESS on success, RC_FAIL on error.
4354 *
4355 * NOTES:
4356 *   The number of shadowFile files is MAX_BACKUP_CLIENTS
4357 *   for the time being.  This may change if needed.
4358 */
4359int sortShadowfilesByInode(Int32 numShadows)
4360{
4361  Int32 rc = RC_SUCCESS;
4362  Int32 i;
4363  char* fn = "sortShadowfilesByInode";
4364
4365  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4366
4367  for (i = 0; i < numShadows; i++)
4368  {
4369    /* Issue a sort command that will perform a numeric sort using
4370       the first field (the file's inode number) of each record. */
4371#ifdef GPFS_AIX
4372    sprintf(sstring, "/usr/bin/sort -b -k 1,1n -o %s %s > /dev/null",
4373              shadowFile[i], shadowFile[i]);
4374#else
4375    sprintf(sstring, "/bin/sort -b -k 1,1n -o %s %s > /dev/null",
4376              shadowFile[i], shadowFile[i]);
4377#endif
4378    rc = system(sstring);
4379
4380    /* Check system() return. */
4381    if (rc != 0)
4382    {
4383      fprintf(stderr, "system(%s) returned %d\n", sstring, rc);
4384      return(RC_FAIL);
4385    }
4386  }
4387
4388  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
4389
4390  return(rc);
4391
4392}  /*------ end of sortShadowfilesByInode() ----------------*/
4393
4394
4395/*
4396 * NAME:  sortShadowfilesByFilename()
4397 *
4398 * FUNCTION:
4399 *   Sort the shadowFile[0] file into alphabetical order by filename
4400 *   and store the results in a single file, backupShadowFile.
4401 *
4402 * PARAMETERS:
4403 *
4404 * RETURNS:
4405 *   RC_SUCCESS on success, RC_FAIL on error.
4406 *
4407 */
4408int sortShadowfilesByFilename()
4409{
4410  Int32 rc = RC_SUCCESS;
4411  Int32 i;
4412  char* fn = "sortShadowfilesByFilename";
4413
4414  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4415
4416  /* Issue a sort command that will perform an alphabetical sort
4417     on the second field (the filename of each file). */
4418#ifdef GPFS_AIX
4419  sprintf(sstring, "/usr/bin/sort -b -k 2,2 -o %s %s > /dev/null",
4420            backupShadowFile, shadowFile[0]);
4421#else
4422  sprintf(sstring, "/bin/sort -b -k 2,2 -o %s %s > /dev/null",
4423            backupShadowFile, shadowFile[0]);
4424#endif
4425  rc = system(sstring);
4426
4427  /* Check system() return. */
4428  if (rc != 0)
4429  {
4430    fprintf(stderr, "system(%s) returned %d\n", sstring, rc);
4431    return(RC_FAIL);
4432  }
4433
4434#ifdef DEBUG_BACKUP
4435  /* Leave the shadow files so that they may get examined. */
4436#else
4437  /* Delete the shadowFile[0] file. */
4438  unlink(shadowFile[0]);
4439#endif /* DEBUG_BACKUP */
4440
4441  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
4442
4443  return(rc);
4444
4445}  /*------ end of sortShadowfilesByFilename() ----------------*/
4446
4447
4448/*
4449 * NAME:  updateShadowfilesFilesizes()
4450 *
4451 * FUNCTION:
4452 *   Update the size field in the shadowFile[MAX_BACKUP_CLIENTS] files
4453 *   from the pertinent information in the file sizes file.
4454 *
4455 * PARAMETERS:
4456 *   numShadows:  (IN) The number of shadow files
4457 *
4458 * RETURNS:
4459 *   RC_SUCCESS on success, RC_FAIL on error.
4460 *
4461 * NOTES:
4462 *   The number of shadowFile files is MAX_BACKUP_CLIENTS for the
4463 *   time being.  This may change if needed.
4464 */
4465int updateShadowfilesFilesizes(Int32 numShadows)
4466{
4467  Int32 offset, inodeDelta, rc1, rc = RC_SUCCESS;
4468  Int32 shadowRecordInode, filesizesRecordInode;
4469  Int32 fileIndex, bytesRead, bytesWritten;
4470  char* eq_p;
4471  Int64 recordsLeft, recordInFilesizes, numberOfFilesizesRecords;
4472  Int64 recordInShadowFile;
4473  offset_t nextWriteOffset, nextReadOffset, actualOffset;
4474  backup_shadow_record_t shadowRecord;
4475  backup_filesizes_t filesizesRecord;
4476  struct stat statBuf;
4477  Boolean readNewRecord = true;
4478  size_t shadowRecordSize = sizeof(backup_shadow_record_t);
4479  size_t filesizesRecordSize = sizeof(backup_filesizes_t);
4480  char* fn = "updateShadowfilesFilesizes";
4481
4482  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4483
4484  /* Open the filesizes file. */
4485  filesizesHandle = open(filesizesFile, O_RDONLY, 0664);
4486  if (filesizesHandle == -1)
4487  {
4488    fprintf(stderr, "Cannot open %s\n", filesizesFile);
4489    return(RC_FAIL);
4490  }
4491
4492  /* Stat the filesizes file. */
4493  if (stat(filesizesFile, &statBuf) == -1)
4494  {
4495    fprintf(stderr, "Could not stat file '%s', errno=%d\n",
4496              filesizesFile, errno);
4497    return(RC_FAIL);
4498  }
4499
4500  /* Determine the number of records in the filesizes file. */
4501  numberOfFilesizesRecords = statBuf.st_size / filesizesRecordSize;
4502
4503  /* Point to the first record in the filesizes file. */
4504  recordInFilesizes = 0;
4505  actualOffset = lseek(filesizesHandle, recordInFilesizes, SEEK_SET);
4506  if (actualOffset != 0)
4507  {
4508    fprintf(stderr, "Cannot seek to %lld in %s\n",
4509              recordInFilesizes, filesizesFile);
4510    return(RC_FAIL);
4511  }
4512
4513  /* Open all the shadowFile[] files. */
4514  for (fileIndex = 0; fileIndex < numShadows; fileIndex++)
4515  {
4516    shadowFileHandle[fileIndex] = open(shadowFile[fileIndex], O_RDWR, 0664);
4517    if (shadowFileHandle[fileIndex] == -1)
4518    {
4519      fprintf(stderr, "Cannot open %s\n", shadowFile[fileIndex]);
4520      return(RC_FAIL);
4521    }
4522  }
4523
4524  /* Update the filesize field in the records of the backup shadow files. */
4525  for (fileIndex = 0; fileIndex < numShadows; fileIndex++)
4526  {
4527    recordInShadowFile = 0;
4528    nextReadOffset = recordInShadowFile*shadowRecordSize;
4529
4530    /* Position the file pointer for the current shadow file. */
4531    actualOffset = lseek(shadowFileHandle[fileIndex],
4532                           recordInShadowFile*shadowRecordSize, SEEK_SET);
4533    if (actualOffset != nextReadOffset)
4534    {
4535      fprintf(stderr, "Cannot seek to %lld in %s\n",
4536                recordInShadowFile*shadowRecordSize, shadowFile[fileIndex]);
4537      return(RC_FAIL);
4538    }
4539
4540    nextWriteOffset = nextReadOffset;
4541    recordsLeft = shadowFileNumberOfRecords[fileIndex];
4542    while (recordsLeft > 0)
4543    {
4544      /* Read the next record from the shadow file. */
4545      bytesRead = read(shadowFileHandle[fileIndex], &shadowRecord,
4546                         shadowRecordSize);
4547      if (bytesRead != shadowRecordSize)
4548      {
4549        fprintf(stderr, "Error reading file %s\n", shadowFile[fileIndex]);
4550        return(RC_FAIL);
4551      }
4552
4553      /* Calculate the next offset and the number of remaining records. */
4554      nextReadOffset += shadowRecordSize;
4555      recordsLeft--;
4556
4557      /* Search the filesizes file for the inode
4558         that matches the shadow record's inode. */
4559      while (recordInFilesizes < numberOfFilesizesRecords)
4560      {
4561        /* If warranted, read a new record from the filesizes file. */
4562        if (readNewRecord)
4563        {
4564          bytesRead = read(filesizesHandle,
4565                             &filesizesRecord, filesizesRecordSize);
4566          if (bytesRead != filesizesRecordSize)
4567          {
4568            fprintf(stderr, "Error reading from file descriptor %d\n",
4569                      filesizesHandle);
4570            return(RC_FAIL);
4571          }
4572        }
4573
4574#if 0
4575        // Historical note:
4576        //   We were failing the test below in the incremental case
4577        //   because there are more records in the filesizes file than in
4578        //   the shadow file - the extra records were the unchanged files,
4579        //   and these had a filesize of 0.  What should be done?
4580        //   This problem did not happen in the past because filesizes
4581        //   were not considered by createClientFilelists, and therefore
4582        //   this routine (updateShadowfilesFilesizes()) was not called
4583        //   in the incremental case.  One possible solution would be to
4584        //   use createClientFilelists2 (the divy-by-numbers-of-files
4585        //   version) rather than createClientFilelists for incremental.
4586        //   In this case we must avoid calling updateShadowfilesFilesizes().
4587        //   A 2nd solution would be to change updateSnapshotFilesizes()
4588        //   to skip over filesizes records for which the filesize is 0.
4589        //   I chose the 1st solution (don't worry about filesizes
4590        //   for incremental backups) for now.  (BCH)
4591        //
4592        // BCH - The above comment may no longer be needed.  We are calling
4593        // BCH   updateShadowfilesFilesizes() in the incremental case
4594        // BCH   without trouble now.  Should we consider going back to using
4595        // BCH   createClientFilelists() instead of createClientFilelists2()
4596        // BCH   in the incremental case again?
4597        //
4598        // BCH - The code has moved on once again, with the test below
4599        // BCH   no longer being used, and the code allowing for the
4600        // BCH   shadow file and the filesizes file to have different numbers
4601        // BCH   of records (this was needed to support hard links, which
4602        // BCH   results in one record in the filesizes file, but two
4603        // BCH   records in the shadow file, one for the file itself,
4604        // BCH   and one for the hardlink).  The net of all this is that
4605        // BCH   someday we may wish to go back to using the same routine
4606        // BCH   (createClientFilelists) for incremental backups that
4607        // BCH   we now use for full backups.   
4608
4609        /* If the inode numbers from the shadow file record and the
4610           filesizes record match, copy the filesize to the shadow file. */
4611        if ((gpfs_ino_t) atoi((const char *) shadowRecord.inodenum) ==
4612              (gpfs_ino_t) atoi((const char *) filesizesRecord.inodenum))
4613#endif
4614        /* Compare the inodes from the shadow file and the filesizes file. */
4615        shadowRecordInode = atoi((const char *) shadowRecord.inodenum);
4616        filesizesRecordInode = atoi((const char *) filesizesRecord.inodenum);
4617        inodeDelta = shadowRecordInode - filesizesRecordInode;
4618        if (inodeDelta > 0)
4619        {
4620          /* We have not yet encountered a file in the filesizes file that
4621             comes lexicographically after the file from the shadow file,
4622             so update the record number and read again. */
4623          recordInFilesizes++;
4624          readNewRecord = true;   // Read a new filesizes record.
4625          continue;
4626        }
4627
4628        if (inodeDelta == 0)
4629        {
4630          /* Since the inode numbers from the shadow file record and the
4631             filesizes record match, copy the filesize to the shadow file. */
4632          memcpy((void *) &shadowRecord.tr.filesize,
4633                  (const void *) &filesizesRecord.filesize,
4634                  sizeof(filesizesRecord.filesize));
4635          nextWriteOffset = nextReadOffset - shadowRecordSize;
4636
4637          /* Reposition the file pointer for the shadow file. */
4638          actualOffset = lseek(shadowFileHandle[fileIndex],
4639                                 nextWriteOffset, SEEK_SET);
4640          if (actualOffset != nextWriteOffset)
4641          {
4642            fprintf(stderr, "Could not seek to offset L%d in file %s\n",
4643                      nextWriteOffset, shadowFile[fileIndex]);
4644            return(RC_FAIL);
4645          }
4646
4647          /* Write the shadow record back to the shadow file. */
4648          bytesWritten = write(shadowFileHandle[fileIndex], &shadowRecord,
4649                                 shadowRecordSize);
4650          if (bytesWritten != shadowRecordSize)
4651          {
4652            fprintf(stderr, "Error writing file %s\n", shadowFile[fileIndex]);
4653            return(RC_FAIL);
4654          }
4655
4656          /* Cause the next record in the shadow file to be read. */
4657          recordInShadowFile++;
4658          readNewRecord = false;   // Don't read a new filesizes record yet.
4659          break;
4660        }
4661
4662        if (inodeDelta < 0)
4663        {
4664          /* We just encountered an inode in the filesizes file that comes
4665             lexicographically after the inode from the shadow file,
4666             so the inode does not exist in the filesizes file.
4667             This should never happen, so issue an error message and quit. */
4668          fprintf(stderr,
4669                   "Inode number %s in file %s could not be found in file %s\n",
4670                    shadowRecord.inodenum,
4671                    shadowFile[fileIndex],
4672                    filesizesFile);
4673          return(RC_FAIL);
4674        }
4675     
4676      }  /* end of while (recordInFilesizes < numberOfFilesizesRecords) */ 
4677
4678    }  /* end of while (recordsLeft > 0) */
4679
4680  }  /* end for (i = 0; i < numShadows; i++) */
4681
4682#ifdef DEBUG_BACKUP
4683  /* Leave the filesizes file so that it may get examined. */
4684#else
4685  /* Delete the filesizes file. */
4686  unlink(filesizesFile);
4687#endif /* DEBUG_BACKUP */
4688
4689  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
4690
4691  return(rc);
4692
4693}  /*------ end of updateShadowfilesFilesizes() ----------------*/
4694
4695
4696/*
4697 * NAME:  createFilelist()
4698 *
4699 * FUNCTION:
4700 *   Create the transactions file.  Update it to include the full
4701 *   path names of the files to be processed (i.e., get backed_up,
4702 *   get restored, etc.) by the server.
4703 *
4704 * PARAMETERS:
4705 *   fsName:             (IN) Points to the filesystem name.
4706 *   backupControlHdrP:  (IN) Pointer to a gpfs_backup_control_t structure
4707 *
4708 * RETURNS:
4709 *   RC_SUCCESS on success, RC_FAIL on error.
4710 *
4711 * NOTES:
4712 */
4713int createFilelist(char *fsName, gpfs_backup_control_t *backupControlHdrP)
4714{
4715  gpfs_backup_control_t *backupCtrlP;
4716  backup_shadow_record_t backupShadowRecord;
4717  Int64 currentServerFilesSize = 0;
4718  Int64 sizeOfFilesInBuffer = 0;
4719  Int64 numberOfInodes;
4720  Int64 recordInFile;
4721  Int32 i, rc = RC_SUCCESS;
4722  Int32 handle;
4723  Int32 backupShadowHandle;
4724  Int32 bufferIndex;
4725  Int32 bytesRead;
4726  transactionsListRecord buffer[NRECS_PER_BUFFER];
4727  offset_t nextReadOffset, actualOffset;
4728  size_t shadowRecordSize;
4729  size_t transactionRecordSize;
4730  char* fn = "createFilelist";
4731
4732  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4733
4734  /* Initializations */
4735  shadowRecordSize = sizeof(backup_shadow_record_t);
4736  transactionRecordSize = sizeof(transactionsListRecord);
4737  backupCtrlP = backupControlHdrP;
4738  numberOfInodes = backupCtrlP->backupHdr.inodesTotal;
4739
4740  /* Create the transaction file in append mode. */
4741  transactionsListHandle =
4742        open(transactionsList, O_CREAT | O_APPEND | O_WRONLY | O_TRUNC, 0644);
4743  if (transactionsListHandle == -1)
4744  {
4745    fprintf(stderr, "Cannot create %s\n", transactionsList);
4746    return(RC_FAIL);
4747  }
4748
4749  /* Open the backup shadow file in READ mode. */
4750  backupShadowHandle = open(backupShadowFile, O_RDONLY, 0644);
4751  if (backupShadowHandle == -1)
4752  {
4753    fprintf(stderr, "Cannot open %s\n", backupShadowFile);
4754    return(RC_FAIL);
4755  }
4756
4757  /* Seek the backup shadow file's first record. */
4758  recordInFile = 0;
4759  nextReadOffset = recordInFile*shadowRecordSize;
4760  actualOffset = lseek(backupShadowHandle,
4761                         recordInFile*shadowRecordSize, SEEK_SET);
4762  if (actualOffset != nextReadOffset)
4763  {
4764    fprintf(stderr, "Cannot seek to %lld in %s\n",
4765              recordInFile*shadowRecordSize, backupShadowFile);
4766    return(RC_FAIL);
4767  }
4768
4769  /*
4770   * Do the following:
4771   * - Read a record from the backup shadow file.
4772   *   The first record read should be for a directory.
4773   * - Write the record to the buffer. 
4774   * - If the buffer is full, write the buffer to the transactions file.
4775   * - Repeat these steps while there are records in the file.
4776   */
4777  bytesRead = read(backupShadowHandle, &backupShadowRecord, shadowRecordSize);
4778  if (bytesRead != shadowRecordSize)
4779  {
4780    fprintf(stderr, "Error reading file %s\n", backupShadowFile);
4781    return(RC_FAIL);
4782  }
4783
4784  /* The first record should be for the snapshot directory which is
4785     located at the mount point of the filesystem.  Assert if it is not. */
4786  // BCH - Should we do something better here than just assert?
4787  // BCH   At the very least I think we should put out an error message first.
4788  assert(strchr((const char *) &(backupShadowRecord.inodeDir), 'Y') != NULL);
4789
4790  /* Copy the backup shadow record's transaction record to the buffer. */
4791  bufferIndex = 0;
4792  buffer[bufferIndex] = backupShadowRecord.tr;
4793  buffer[bufferIndex].delimeter2 = '\n';
4794
4795  /* Increment the aggregate size of the files in the buffer. */
4796  sizeOfFilesInBuffer +=
4797           strtoll((const char *) backupShadowRecord.tr.filesize, NULL, 10);
4798
4799  /* Copy the 1st filename into the anchor name for the server. */
4800  strncpy(backupCtrlP->inode.filename, buffer[bufferIndex].filename,
4801            MAX_FILE_NAME);
4802
4803  /* Decrement the number of inodes now that the first has been processed. */
4804  numberOfInodes--;
4805
4806  /* Loop until the remaining inodes have been processed. */
4807  while (numberOfInodes > 0)
4808  {
4809    /* Read the next record from the backup shadow file. */
4810    bytesRead = read(backupShadowHandle, &backupShadowRecord, shadowRecordSize);
4811    if (bytesRead != shadowRecordSize)
4812    {
4813      fprintf(stderr, "Error reading file %s\n", backupShadowFile);
4814      return(RC_FAIL);
4815    }
4816
4817    /* Decrement the number of inodes and point to the next buffer record. */
4818    numberOfInodes--;
4819    bufferIndex++;
4820
4821    /* Copy the backup shadow record filename to the buffer. */
4822    buffer[bufferIndex] = backupShadowRecord.tr;
4823    buffer[bufferIndex].delimeter2 = '\n';
4824
4825    /* Increment the aggregate size of the files in the buffer. */
4826    sizeOfFilesInBuffer +=
4827             strtoll((const char *) backupShadowRecord.tr.filesize, NULL, 10);
4828
4829    /* Write the buffer to the transactions file if the buffer is full. */
4830    if ((bufferIndex + 1) == NRECS_PER_BUFFER)
4831    {
4832      /* Write the buffer to the file. */
4833      rc = writeBuffer(transactionsListHandle, buffer,
4834                        (bufferIndex + 1) * transactionRecordSize);
4835      if (rc != 0)
4836      {
4837        fprintf(stderr,
4838                  "createFilelist(): writeBuffer() returned rc = %d\n", rc);
4839        return(RC_FAIL);
4840      }
4841
4842      /* Update the size value. */
4843      currentServerFilesSize += sizeOfFilesInBuffer;
4844
4845      /* Clear the buffer for reuse. */
4846      sizeOfFilesInBuffer = 0;
4847      bufferIndex = -1;
4848      memset(buffer, 0, transactionRecordSize*NRECS_PER_BUFFER);
4849    }
4850  }  /* end while (numberOfInodes > 0) */
4851
4852  /* Write the last buffer to the transactions file. */
4853  rc = writeBuffer(transactionsListHandle, buffer,
4854                    (bufferIndex + 1) * transactionRecordSize);
4855  if (rc != 0)
4856  {
4857    fprintf(stderr, "createFilelist(): writeBuffer() returned rc = %d\n", rc);
4858    return(RC_FAIL);
4859  }
4860
4861  /* Update the aggregate size for the server. */
4862  currentServerFilesSize += sizeOfFilesInBuffer;
4863  backupCtrlP->inode.serverFilesSize = currentServerFilesSize;
4864
4865  /* Close the created transactions file. */
4866  rc = close(transactionsListHandle);
4867  if (rc != 0)
4868  {
4869    fprintf(stderr,
4870              "Cannot close file descriptor transactionsListHandle = %d\n",
4871              transactionsListHandle);
4872    return(RC_FAIL);
4873  }
4874
4875  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
4876
4877  return(rc);
4878
4879}  /*------ end of createFilelist() ----------------*/
4880
4881
4882/*
4883 * NAME:  processFilelist()
4884 *
4885 * FUNCTION
4886 *   Given a transactions file containing a list of files to be
4887 *   processed on the backup server, a list of backup clients to do
4888 *   the backup, and a TSM command to run, do the following:
4889 *     a) For each client node process, construct the corresponding
4890 *        filelist file.
4891 *     b) For each client node process, tell the client node to execute
4892 *        the TSM command against its corresponding filelist file.
4893 *     c) Process the return from each client node process regarding
4894 *        the level of success from the command execution.
4895 *
4896 * PARAMETERS:
4897 *   fsName:              (IN) the full path name for the filesystem
4898 *   transactionCmd:      (IN) the command the client node will execute
4899 *                               against a filelist file
4900 *   transactionCmdOptn:  (IN) an option value to be passed to the
4901 *                               transactionCmd
4902 *   backupControlHdrP:   (IN) pointer to a gpfs_backup_control_t structure
4903 *   divyBySize:          (IN) boolean flag indicating whether to divy the
4904 *                               the filelist based on file sizes or not
4905 *
4906 * RETURNS:
4907 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
4908 *
4909 * NOTES:
4910 */
4911int processFilelist(char *fsName,
4912                    char *transactionCmd,
4913                    char *transactionCmdOptn,
4914                    gpfs_backup_control_t *backupControlHdrP,
4915                    Boolean divyBySize)
4916{
4917  Int32 tmp, rc = RC_SUCCESS;
4918  Int32 clientIndex, processIndex;
4919  Int32 numberOfClients, numberOfProcesses;
4920  static char *processCommand = "execTSMcommand";   // mmremote verb
4921  FILE* filePtr[MAX_BACKUP_CLIENT_PROCESSES];
4922  char *cmdReturnString[MAX_BACKUP_CLIENT_PROCESSES];
4923  char *returnStrings;
4924  backup_client_info_t *backupClient[MAX_BACKUP_CLIENTS];
4925  pid_t pid[MAX_BACKUP_CLIENTS];
4926  char processCmdBuffer[MAX_BACKUP_CLIENT_PROCESSES][LINE_LENGTH];
4927  char linebuf[LINE_LENGTH];   /* buffer to store an output line */
4928
4929  gpfs_backup_control_t *backupCtrlP;
4930  Boolean pendingTransactions = false;
4931  Boolean partialSuccess = false;
4932  char* fn = "processFilelist";
4933
4934  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
4935
4936  /* initializations */
4937  backupCtrlP = backupControlHdrP;
4938  numberOfClients = backupCtrlP->backupHdr.icf.numberOfClients;
4939
4940  /* Create the transaction files for the client processes. */
4941  if (divyBySize) {
4942    rc = createClientFilelists(backupCtrlP);
4943  } else {
4944    rc = createClientFilelists2(backupCtrlP);
4945  }
4946
4947  /* Delete the original transactions file now that we are done with it. */
4948  unlink(transactionsList);
4949
4950  /* If the call to createClientFilelists() or createClientFilelists2()
4951     failed, print an error message and return with a bad return code. */
4952  if (rc == RC_FAIL)
4953  {
4954    fprintf(stderr,
4955              "processFilelist(): createClientFilelists() returned %d\n", rc);
4956    return(RC_FAIL);
4957  }
4958
4959  /* Get the number of processes that we will be creating. */
4960  numberOfProcesses = backupCtrlP->backupHdr.numberOfProcesses;
4961
4962  /* Get the client nodes and storage for their command strings. */
4963  returnStrings = (char *) calloc(numberOfProcesses, (LINE_LENGTH));
4964
4965  /* Report error if calloc failed. */
4966  if (returnStrings == NULL)
4967  {
4968    fprintf(stderr, "processFilelist(): calloc failure");
4969    return(RC_FAIL);
4970  }
4971
4972  /* We depend on unused areas of the returnStrings being null chars. */
4973  memset(returnStrings, '\0', (numberOfProcesses * (LINE_LENGTH)));
4974
4975  /* Initialize cmdReturnString array with pointers into returnStrings. */
4976  for (processIndex = 0; processIndex < numberOfProcesses; processIndex++)
4977  {
4978    cmdReturnString[processIndex] = returnStrings+(processIndex*(LINE_LENGTH));
4979  }
4980
4981  /* Initialize the backupClient array. */
4982  for (clientIndex = 0; clientIndex < numberOfClients; clientIndex++)
4983  {
4984    backupClient[clientIndex] =
4985                          &(backupCtrlP->backupHdr.icf.clients[clientIndex]);
4986  }
4987
4988  /* Create the pending transactions filename strings based 
4989     on whether we are processing deletions or changes. */
4990  if (strcmp(transactionCmd, "expire") == 0)
4991  {
4992    strcpy(pendingTransactionsListName, pendingDeletionsName);
4993    snprintf(pendingTransactionsList, MAX_FILE_NAME, "%s/%s",
4994               fsName, pendingDeletionsName);
4995  } else {
4996    strcpy(pendingTransactionsListName, pendingChangesName);
4997    snprintf(pendingTransactionsList, MAX_FILE_NAME, "%s/%s",
4998               fsName, pendingChangesName);
4999  }
5000
5001  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
5002
5003  /*
5004   * The following loop forks processes that do the backups in parallel
5005   * on the TSM clients.  Each process invokes a script on a client node
5006   * which in turn issues the desired TSM backup command.  Each client
5007   * may run multiple processes in parallel.
5008   */
5009  for (processIndex = 0; processIndex < numberOfProcesses; processIndex++)
5010  {
5011    /* Calculate the client index from the process index value. */
5012    clientIndex = processIndex % numberOfClients; 
5013
5014    /* Create the command to be forked. */
5015    snprintf(processCmdBuffer[processIndex],
5016               sizeof(processCmdBuffer[processIndex]),
5017               "%s/bin/mmcommon on1long %s %s %s %s %s %s %s %d %d %s %d",
5018               INSTALL_DIRECTORY,
5019               backupClient[clientIndex]->clientName,
5020               processCommand,
5021               fsName,
5022               transactionCmd,
5023               transactionCmdOptn,
5024               clientTransactionsList[processIndex],
5025               masterNode,
5026               masterPID,
5027               processIndex,
5028               backupCtrlP->backupHdr.icf.serverName,
5029               ioRate);
5030
5031    /* Fork a command to cause the backup to execute as a client process. */
5032    filePtr[processIndex] = pipeOpen(processCmdBuffer[processIndex], "r",
5033                                       &pid[processIndex]);
5034    if (filePtr[processIndex] == NULL)
5035    {
5036      /*
5037       * The client process failed.  Rename its transactions file
5038       * to serve as a pending transactions file and set a flag
5039       * to indicate that there are pending transactions.
5040       */
5041
5042      /* Create the pending transactions file name. */
5043      snprintf(clientPendingTransactionsList[processIndex], MAX_FILE_NAME,
5044                 "%s/%s%d", fsName, pendingTransactionsListName, processIndex);
5045
5046      /* Rename the process's transactions file to a pending trans file. */
5047      rc = rename(clientTransactionsList[processIndex],
5048                    clientPendingTransactionsList[processIndex]);
5049      if (rc != 0)
5050      {
5051        fprintf(stderr, "rename(%s, %s) returned %d\n",
5052                  clientTransactionsList[processIndex],
5053                  clientPendingTransactionsList[processIndex], rc);
5054        free(returnStrings);
5055        return(RC_FAIL);
5056      }
5057
5058      /* Set the pending transactions flag and issue a message. */
5059      pendingTransactions = true;
5060      fprintf(stderr,
5061                "processFilelist(): pipeOpen() returned filePtr[%d] = NULL\n",
5062                processIndex);
5063      rc = RC_FAIL;  // BCH - I believe this may be superfluous
5064    }
5065  }
5066
5067  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
5068
5069  /* Loop through the client processes to get each one's return string. */
5070  /* NOTE:  When at least one process has at least partially successfully
5071            backed up its list of files, the overall operation will be
5072            indicated as having had at least a partial successful completion. */
5073  for (processIndex = 0; processIndex < numberOfProcesses; processIndex++)
5074  {
5075    if (fgets(linebuf, sizeof(linebuf), filePtr[processIndex]) != NULL)
5076    {
5077      strcpy(cmdReturnString[processIndex], linebuf);
5078    }
5079    else
5080    {
5081      /* Handle the case where the client process has failed. */
5082
5083      /* First create the pending transactions file name. */
5084      snprintf(clientPendingTransactionsList[processIndex], MAX_FILE_NAME,
5085                 "%s/%s%d", fsName, pendingTransactionsListName, processIndex);
5086
5087      /* Rename the process's transactions file to a pending trans file. */
5088      rc = rename(clientTransactionsList[processIndex],
5089                    clientPendingTransactionsList[processIndex]);
5090      if (rc != 0)
5091      {
5092        fprintf(stderr, "rename(%s, %s) returned %d\n",
5093                  clientTransactionsList[processIndex],
5094                  clientPendingTransactionsList[processIndex], rc);
5095        free(returnStrings);
5096        return(RC_FAIL);
5097      }
5098
5099      fprintf(stderr,
5100              "processFilelist(): no output received from client process %d\n",
5101              processIndex);
5102      pendingTransactions = true;
5103      rc = RC_FAIL;  // BCH - I believe this may be superfluous
5104    }
5105
5106    /* Close the pipe that was opened for the client process. */
5107    pipeClose(filePtr[processIndex], pid[processIndex]);
5108
5109  }
5110
5111  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
5112
5113  /*
5114   * Check the completion level to determine
5115   * the level of success for the operation.
5116   */
5117  for (processIndex = 0; processIndex < numberOfProcesses; processIndex++)
5118  {
5119    /* Calculate the client index from the process index value. */
5120    clientIndex = processIndex % numberOfClients; 
5121
5122    if (strstr(cmdReturnString[processIndex], "Operation_Success") != NULL)
5123    {
5124      /* In this case all the files were successfully backed up.  Set a flag
5125         indicating at least partial successful operation completion.
5126       */
5127      partialSuccess = true;
5128      if (Tracing) {
5129        fprintf(stderr, "Process %d on client %s successfully processed its list of files.\n", processIndex, backupClient[clientIndex]->clientName);
5130      }
5131
5132#ifdef DEBUG_BACKUP
5133      /* Do not delete the clientTransactionsList[processIndex] file. */
5134#else
5135      /* Delete the clientTransactionsList[processIndex] file. */
5136      unlink(clientTransactionsList[processIndex]);
5137#endif /* DEBUG_BACKUP */
5138
5139    }
5140    else if (strstr(cmdReturnString[processIndex], "Operation_Partial_Success")
5141               != NULL)
5142    {
5143      /* Not all of the files were successfully backed up.  The mmexectsmcmd
5144         script should have created a file named PendingTransactions_L (where
5145         L is the process index) which contains the names of the files that
5146         were not successfully operated on.  Save the name of this file into
5147         clientPendingTransactionsList, the pending transaction files array. */
5148
5149      /* Set flag indicating at least partial successful operation. */
5150      partialSuccess = true;
5151      pendingTransactions = true;
5152      fprintf(stderr, "Process %d on client %s had partial success in processing its list of files.\n", processIndex, backupClient[clientIndex]->clientName);
5153
5154      /* Save the name of the pending transactions file into the
5155         clientPendingTransactionsList array. */
5156      snprintf(clientPendingTransactionsList[processIndex], MAX_FILE_NAME,
5157                 "%s/%s%d", fsName, pendingTransactionsListName, processIndex);
5158
5159#ifdef DEBUG_BACKUP
5160      /* Do not delete the clientTransactionsList[processIndex] file. */
5161#else
5162      /* Delete the clientTransactionsList[processIndex] file. */
5163      unlink(clientTransactionsList[processIndex]);
5164#endif /* DEBUG_BACKUP */
5165
5166    }
5167    else if ((strstr(cmdReturnString[processIndex],"Operation_Failure") != NULL)
5168                ||
5169             (strstr(cmdReturnString[processIndex],"Already_running") != NULL))
5170    {
5171      /* The backup operation on the client process failed.  Rename its
5172         transactions file to clientPendingTransactionsList_L. */
5173
5174      /* Create the process's pending transactions file name. */
5175      snprintf(clientPendingTransactionsList[processIndex], MAX_FILE_NAME,
5176                 "%s/%s%d", fsName, pendingTransactionsListName, processIndex);
5177
5178      /* Rename the process's transactions file to a pending trans file. */
5179      rc = rename(clientTransactionsList[processIndex],
5180                    clientPendingTransactionsList[processIndex]);
5181      if (rc != 0)
5182      {
5183        fprintf(stderr, "rename(%s, %s) returned %d\n",
5184                  clientTransactionsList[processIndex],
5185                  clientPendingTransactionsList[processIndex], rc);
5186        free(returnStrings);
5187        return(RC_FAIL);
5188      }
5189
5190      /* Issue a message to help the user understand what failed
5191         and set the transaction pending flag and a bad return code. */
5192      fprintf(stderr,
5193                "\n%s\n", 2 + strstr(cmdReturnString[processIndex], ":"));
5194      fprintf(stderr,
5195         "Process %d on client %s failed in processing its list of files.\n",
5196         processIndex, backupClient[clientIndex]->clientName);
5197      pendingTransactions = true;
5198      rc = RC_FAIL;  // BCH - I believe this may be superfluous
5199    }
5200    else
5201    {
5202      fprintf(stderr,
5203                "Process %d on client %s returned unexpected results: %s\n",
5204                processIndex, backupClient[clientIndex]->clientName,
5205                cmdReturnString[processIndex]);
5206      free(returnStrings);
5207      return(RC_FAIL);
5208    }
5209  }
5210
5211  if (Tracing) traceLine("tsbackup.C", fn, __LINE__);
5212
5213  /* Return the storage we obtained for the return strings. */
5214  free(returnStrings);
5215
5216  /* Set the return code and do required cleanup based on the outcome. */
5217  if (partialSuccess)
5218  {
5219    if (pendingTransactions)   /* partial success */
5220    {
5221      /* Merge the pendingTransactionsList_L files into a single
5222         pendingTransactionsList file. */
5223      rc = createPendingFile(fsName, backupCtrlP, pendingTransactionsList);
5224      if (rc != 0)
5225      {
5226        fprintf(stderr, "createPendingFile() failed with rc = %d\n", rc);
5227        return(RC_FAIL);
5228      }
5229
5230      /* Set the return code. */
5231      rc = RC_PSUCCESS;
5232    }
5233    else   /* total success */
5234    {
5235      /* Set the return code. */
5236      rc = RC_SUCCESS;
5237    }
5238  }
5239  else     /* total failure */
5240  {
5241    /* Delete any existing client transactions files. */
5242    for (processIndex = 0; processIndex < numberOfProcesses; processIndex++)
5243    {
5244      /* Delete the client transactions file and the
5245         client pending transactions file if either exists. */
5246      unlink(clientTransactionsList[processIndex]);
5247      unlink(clientPendingTransactionsList[processIndex]);
5248    }
5249
5250    /* Set the return code. */
5251    rc = RC_FAIL;
5252
5253  }  // end if (partialSuccess)
5254
5255  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
5256
5257  return(rc);
5258
5259}  /*------ end of processFilelist() ----------------*/
5260
5261
5262/*
5263 * NAME:  createPendingFile()
5264 *
5265 * FUNCTION:
5266 *   Consolidate the pending transactions files from the client processes
5267 *   into a single pending transactions file relating to the server.
5268 *
5269 * PARAMETERS:
5270 *   fsName:             (IN) full path name of filesystem mount point
5271 *   backupControlHdrP:  (IN) pointer to a gpfs_backup_control_t structure
5272 *   pendingFile:        (IN) name of file to be created containing the
5273 *                              pending transactions
5274 *
5275 * RETURNS:
5276 *   RC_SUCCESS on success, RC_FAIL on error.
5277 *
5278 * NOTES:
5279 *   It is assumed that if a client has failed to backup all the files which
5280 *   were to be backed up by that client, a file exists associated with that
5281 *   client which contains all the file names which were not backed up.
5282 */
5283int createPendingFile(char *fsName,
5284                      gpfs_backup_control_t *backupControlHdrP,
5285                      char *pendingFile)
5286{
5287  Int32 numberOfClients, processesPerClient, numberOfProcesses;
5288  Int32 rc = RC_SUCCESS;
5289  Int32 clientIndex, j;
5290  char *filenamesString = NULL;
5291  gpfs_backup_control_t *backupCtrlP;
5292  struct stat statBuf;
5293  Boolean pendingFiles = false;
5294  char* fn = "createPendingFile";
5295
5296  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
5297
5298  /* Initialize some pointers. */
5299  backupCtrlP = backupControlHdrP;
5300
5301  /* Initialize the number of clients and the number of client processes. */
5302  numberOfClients = backupCtrlP->backupHdr.icf.numberOfClients;
5303  processesPerClient = backupCtrlP->backupHdr.icf.processesPerClient;
5304  numberOfProcesses = numberOfClients * processesPerClient;
5305
5306  /* If there is only one client process for the server and the
5307     clientPendingTransactionsList[0] file exists, just rename it
5308     to pendingTransactionsList file. */
5309  if (numberOfProcesses == 1)
5310  {
5311    /* Check the existence of the clientPendingTransactionsList_0 file. */
5312    rc = stat(clientPendingTransactionsList[0], &statBuf);
5313    if (rc == -1)
5314    {
5315      if (errno == ENOENT)
5316      {
5317        fprintf(stderr, "File %s does not exist.\n",
5318                  clientPendingTransactionsList[0]);
5319      }
5320      else
5321      {
5322        fprintf(stderr, "Could not stat file '%s', errno=%d\n",
5323                  clientPendingTransactionsList[0], errno);
5324      }
5325      return(RC_FAIL);
5326    }
5327    else
5328    {
5329      /* Rename the client process pending transactions file to the
5330         desired pending transactions file. */
5331      rc = rename(clientPendingTransactionsList[0], pendingFile);
5332      if (rc != 0)
5333      {
5334        fprintf(stderr, "rename(%s, %s) returned %d\n",
5335                 clientPendingTransactionsList[0], pendingTransactionsList, rc);
5336        return(RC_FAIL);
5337      }
5338    }
5339  }
5340  else  /* We come here if there is more than one client process. */
5341  {
5342    /* For each client process, determine whether a pending transactions
5343       file exists.  If so, put its name into a string of client process
5344       pending transaction files.  If the string contains one or more names,
5345       issue a cat command to have all the files concatenated into one pending
5346       transactions file for the server. */
5347    for (j = 0; j < numberOfProcesses; j++)
5348    {
5349      rc = stat(clientPendingTransactionsList[j], &statBuf);
5350      if (rc == -1)
5351      {
5352        if (errno == ENOENT)
5353        {
5354          /* The clientPendingTransactionsList_j file does not exist. */
5355          continue;
5356        }
5357        else
5358        {
5359          fprintf(stderr, "Could not stat file '%s', errno=%d\n",
5360                    clientPendingTransactionsList[j], errno);
5361          return(RC_FAIL);
5362        }
5363      }
5364      else
5365      {
5366        /* Set a flag to indicate that there are pending files to process. */
5367        pendingFiles = true;
5368
5369        /* Get initial space for string or increase the space as necessary. */
5370        if (filenamesString == NULL)
5371        {
5372          /* Obtain space for the name + 2 extra characters (' ' and '\0'). */
5373          filenamesString = (char*) malloc(
5374                strlen((clientPendingTransactionsList[j]) + 2) * sizeof(char));
5375
5376          if (filenamesString == NULL) 
5377          {
5378            fprintf(stderr, "createPendingFile(): malloc failure\n");
5379            return(RC_FAIL);
5380          }     
5381
5382          /* Zero the storage for the string before using it. */
5383          memset(filenamesString, '\0', sizeof(char));
5384        }
5385        else
5386        {
5387          char* oldNamesString;
5388          oldNamesString = filenamesString;
5389
5390          /* Obtain additional space for the name plus 2 extra characters
5391             (' ' and '\0'). */
5392          filenamesString = (char*) realloc(filenamesString, 
5393      (strlen(filenamesString) + 2 + strlen(clientPendingTransactionsList[j]))
5394                                              * sizeof(char));
5395
5396          if (filenamesString == NULL) 
5397          {
5398            /* free old memory */
5399            free(oldNamesString);
5400            fprintf(stderr, "createPendingFile(): realloc failure\n");
5401            return(RC_FAIL);
5402          }     
5403        }
5404
5405        /* Append the file name to a string consisting of the names of all
5406           client process pending transactions files separated by blanks. */
5407        strcat(filenamesString, clientPendingTransactionsList[j]);
5408        strcat(filenamesString, " ");
5409      }
5410    }  /* for (j = 0;  j < numberOfProcesses; j++) */
5411  } 
5412
5413  /* If there are pending files because one or more of the processes failed,
5414     concatenate the files together into a single pending transactions file. */
5415  if (pendingFiles)
5416  {
5417    /* Create the server's pending transactions file name. */
5418    snprintf(pendingTransactionsList, MAX_FILE_NAME,
5419               "%s/%s", fsName, pendingTransactionsListName);
5420
5421    /* Concatenate the pending transations files from the client processes
5422       into a single file that is the server's pending transactions file. */
5423    // BCH - Recode this to use a loop with open(), read(), write(), close()
5424    // BCH     It will be much more efficient (faster reads and no system() )
5425#ifdef GPFS_AIX
5426    snprintf(sstring, MAX_COMMAND_STRING, "/usr/bin/cat %s > %s",
5427               filenamesString, pendingTransactionsList);
5428#else
5429    snprintf(sstring, MAX_COMMAND_STRING, "/bin/cat %s > %s",
5430               filenamesString, pendingTransactionsList);
5431#endif
5432    rc = system(sstring);
5433
5434    /* Check system() return. */
5435    if (rc != 0)
5436    {
5437      fprintf(stderr, "system(%s) returned %d\n", sstring, rc);
5438      return(RC_FAIL);
5439    }
5440
5441#ifdef DEBUG_BACKUP
5442    /* Do not remove the clientPendingTransactionsList files. */
5443#else
5444    /* Issue the rm command to have the clientPendingTransactionsList files
5445       deleted.  Using rm instead of unlink allows us to delete them all
5446       in one command. */
5447    // BCH - true, but you pay for the overhead of system().  Recode?
5448    snprintf(sstring, MAX_COMMAND_STRING,
5449               "%s %s > /dev/null", RM, filenamesString);
5450    rc = system(sstring);
5451
5452    /* Check system() return. */
5453    if (rc != 0)
5454    {
5455      fprintf(stderr, "system(%s) returned %d\n", sstring, rc);
5456      return(RC_FAIL);
5457    }
5458#endif  /* DEBUG_BACKUP */
5459
5460    /* Free the memory for the string of file names. */
5461    free(filenamesString);
5462    filenamesString = NULL;
5463  }
5464  else
5465  {
5466    rc = RC_SUCCESS;
5467  }
5468
5469  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
5470
5471  return(rc);
5472
5473}  /*------ end of createPendingFile() ----------------*/
5474
5475
5476/*
5477 * NAME:  doFSFileDeletions()
5478 *
5479 * FUNCTION:
5480 *   Determine the files which have been deleted from a filesystem
5481 *   and expire them from the backed up copy of the filesystem.
5482 *
5483 * PARAMETERS:
5484 *   fsName:              (IN) The full path name of the mounted filesystem.
5485 *   fsSnapshotPathname:  (IN) The full path name of a directory name
5486 *                             indicating where a snapshot of the filesystem
5487 *                             is to be stored.
5488 * RETURNS:
5489 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
5490 *
5491 * NOTES:
5492 */
5493int doFSFileDeletions(char *fsName, char *fsSnapshotPathname)
5494{
5495  Int32 tmp, rc = RC_SUCCESS;
5496  Int32 numShadows;
5497  Boolean fileDeletions = false;
5498  inodeBitsArray *inodeBitsArrayP;
5499  char* fn = "doFSFileDeletions";
5500
5501  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
5502
5503  /* Do a regular inode scan.  The inodes for all the files in the
5504     filesystem will be returned. */
5505  inodeBitsArrayP = &inodeBitsP;
5506  rc = doInodeScan(fsSnapshotPathname, inodeBitsArrayP, backupControlP);
5507  if (rc != 0)
5508  {
5509    fprintf(stderr,
5510              "doFSFileDeletions(): doInodeScan() returned rc = %d\n", rc);
5511    return(RC_FAIL);
5512  }
5513
5514  /* Create a backup shadow file, and store it
5515     at the mount point of the filesystem. */
5516  numShadows = 1;
5517  rc = createSnapshotShadows(fsName, numShadows, inodeBitsP);
5518  if (rc != 0)
5519  {
5520    fprintf(stderr, "createSnapshotShadows() returned rc = %d\n", rc);
5521    free(inodeBitsP);
5522    return(RC_FAIL);
5523  }
5524  free(inodeBitsP);
5525
5526  /* Sort the backup shadow file(s) by inode number. */
5527  rc = sortShadowfilesByInode(numShadows);
5528  if (rc != RC_SUCCESS)
5529  {
5530    fprintf(stderr, "sortShadowfilesByInode() returned rc = %d\n", rc);
5531    return(RC_FAIL);
5532  }
5533
5534  /* Update the .backup_shadow0 file with the correct
5535     file sizes and erase the filesizes file. */
5536  rc = updateShadowfilesFilesizes(numShadows);
5537  if (rc != RC_SUCCESS)
5538  {
5539    fprintf(stderr, "updateShadowfilesFilesizes() returned rc = %d\n", rc);
5540    return(RC_FAIL);
5541  }
5542
5543  /* Sort the backup shadow file into alphabetical order by filename,
5544     and store the results in a single backup shadow file. */
5545  rc = sortShadowfilesByFilename();
5546  if (rc != RC_SUCCESS)
5547  {
5548    fprintf(stderr, "sortShadowfilesByFilename() returned rc = %d\n", rc);
5549    return(RC_FAIL);
5550  }
5551
5552  /* Get the list of files to be deleted. */
5553  rc = getFilelistDeletions(backupShadowCheck, backupShadowFile,
5554                              &fileDeletions, (char *) &deletionsFile);
5555  if (rc != 0)
5556  {
5557    fprintf(stderr, "getFilelistDeletions() returned rc = %d\n", rc);
5558    unlink(deletionsFile);
5559    return(RC_FAIL);
5560  }
5561
5562  /* Take an early exit if there are no file deletions. */
5563  if (fileDeletions == false)
5564  {
5565    return(RC_SUCCESS);
5566  }
5567
5568  /* Rename the deletions file to be the transactions file. */
5569  tmp = rename(deletionsFile, transactionsList);
5570  if (tmp != 0)
5571  {
5572    fprintf(stderr, "rename(%s, %s) returned %d\n",
5573              deletionsFile, transactionsList, rc);
5574    return(RC_FAIL);
5575  }
5576
5577  /* Invoke routine to process the transactions file.  The false flag
5578     that is passed tells the routine to divy the transactions file
5579     among the client processes by number of files, not by aggregate
5580     file size, which is what makes sense when doing deletions. */
5581  rc = processFilelist(fsName,
5582                         transactionCmdExpire,
5583                         transactionCmdOption,
5584                         backupControlP,
5585                         false);
5586
5587  /* Process the return code. */
5588  switch (rc)
5589  {
5590    case RC_FAIL:       /* failure */
5591      fprintf(stderr, "processFilelist() returned rc = %d\n", rc);
5592      break;
5593
5594    case RC_PSUCCESS:   /* partial success */
5595    case RC_SUCCESS:    /* complete success */
5596      break;
5597
5598    default:
5599      fprintf(stderr, "processFilelist() returned unexpected rc = %d\n", rc);
5600      rc = RC_FAIL;
5601      break;
5602  }
5603
5604  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
5605
5606  return(rc);
5607
5608}  /*------ end of doFSFileDeletions() ----------------*/
5609
5610
5611/*
5612 * NAME:  getFilelistDeletions()
5613 *
5614 * FUNCTION:
5615 *   Given two shadow files, each one containing the list of files
5616 *   and directories of a filesystem at one point, determine the
5617 *   list of files in the first shadow file which are not in the
5618 *   second shadow file and put that list into a deletions file.
5619 *   Return the full path name of the created file.
5620 *
5621 * PARAMETERS:
5622 *   oldShadowFile:      (IN)  full path name of first shadow file
5623 *   newShadowFile:      (IN)  full path name of second shadow file
5624 *   fileDeletions:      (OUT) flag indicating whether there were any
5625 *                               deleted files; a true value indicates
5626 *                               there were
5627 *   deletionsFilelist:  (OUT) pointer to name of file containing the list
5628 *                               of files which are in oldShadowFile but
5629 *                               are not in newShadowFile
5630 *
5631 * RETURNS:
5632 *   RC_SUCCESS on success, RC_FAIL on error.
5633 *
5634 * NOTES:
5635 */
5636int getFilelistDeletions(const char *oldShadowFile,
5637                         const char *newShadowFile,
5638                         Boolean *fileDeletions,
5639                         char *deletionsFilelist)
5640{
5641  Int32 fp_del, fp_old_shadow, fp_new_shadow;
5642  Int32 j, offset;
5643  Int32 rc1, rc = RC_SUCCESS;
5644  transactionsListRecord transactionRecord;
5645  backup_shadow_record_t oldShadowRecord;
5646  backup_shadow_record_t newShadowRecord;
5647  struct stat statBuf;
5648  Int32 bytesRead, bytesWritten;
5649  Int64 numberOfOldShadowRecords, numberOfNewShadowRecords;
5650  Int64 recordInOldShadow, recordInNewShadow;
5651  offset_t nextReadOldShadowOffset, actualOldShadowOffset;
5652  offset_t nextReadNewShadowOffset, actualNewShadowOffset;
5653  size_t shadowRecordSize = sizeof(backup_shadow_record_t);
5654  size_t transactionRecordSize = sizeof(transactionsListRecord);
5655  char* eq_p;
5656  Boolean shadowDeletions = false;
5657  Boolean searchNeeded = false;
5658  Boolean readNewRecord = true;
5659  char* fn = "getFilelistDeletions";
5660
5661  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
5662
5663  /* Stat the old backup shadow file. */
5664  if (stat(oldShadowFile, &statBuf) == -1)
5665  {
5666    fprintf(stderr, "Could not stat file %s, errno=%d\n", oldShadowFile, errno);
5667    return(RC_FAIL);
5668  }
5669
5670  /* Determine the number of records in the old shadow file. */
5671  numberOfOldShadowRecords = statBuf.st_size / shadowRecordSize;
5672
5673  /* Open the old backup shadow file. */
5674  fp_old_shadow = open(oldShadowFile, O_RDONLY, 0644);
5675  if (fp_old_shadow == -1)
5676  {
5677    fprintf(stderr, "Cannot open file %s\n", oldShadowFile);
5678    return(RC_FAIL);
5679  }
5680
5681  /* Stat the new backup shadow file. */
5682  if (stat(newShadowFile, &statBuf) == -1)
5683  {
5684    fprintf(stderr, "Could not stat file '%s', errno=%d\n",
5685              newShadowFile, errno);
5686    return(RC_FAIL);
5687  }
5688
5689  /* Determine the number of records in the new shadow file. */
5690  numberOfNewShadowRecords = statBuf.st_size / shadowRecordSize;
5691
5692  /* Open the new backup shadow file. */
5693  fp_new_shadow = open(newShadowFile, O_RDONLY, 0644);
5694  if (fp_new_shadow == -1)
5695  {
5696    fprintf(stderr, "Cannot open file %s\n", newShadowFile);
5697    return(RC_FAIL);
5698  }
5699
5700  /* Create the full pathname for the deletions file for the filesystem. */
5701  snprintf(deletionsFile, MAX_FILE_NAME, "%s/%s", fsName, deletionsName);
5702
5703  /* Create and open the deletions file in append mode. */
5704  fp_del = open(deletionsFile, O_CREAT | O_APPEND | O_WRONLY | O_TRUNC, 0600);
5705  if (fp_del == -1)
5706  {
5707    fprintf(stderr, "Cannot open file %s\n", deletionsFile);
5708    return(RC_FAIL);
5709  }
5710
5711  recordInOldShadow = 0;
5712  recordInNewShadow = 0;
5713  nextReadOldShadowOffset = recordInOldShadow*shadowRecordSize;
5714  actualOldShadowOffset = lseek(fp_old_shadow,
5715                                  recordInOldShadow*shadowRecordSize,
5716                                  SEEK_SET);
5717  if (actualOldShadowOffset != nextReadOldShadowOffset)
5718  {
5719    fprintf(stderr, "Cannot seek to %lld in %s\n",
5720              recordInOldShadow*shadowRecordSize, oldShadowFile);
5721    return(RC_FAIL);
5722  }
5723  nextReadNewShadowOffset = recordInNewShadow*shadowRecordSize;
5724  actualNewShadowOffset = lseek(fp_new_shadow,
5725                                  recordInNewShadow*shadowRecordSize,
5726                                  SEEK_SET);
5727  if (actualNewShadowOffset != nextReadNewShadowOffset)
5728  {
5729    fprintf(stderr, "Cannot seek to %lld in %s\n",
5730              recordInNewShadow*shadowRecordSize, newShadowFile);
5731    return(RC_FAIL);
5732  }
5733
5734  /* Loop through the old shadow file. */
5735  while (recordInOldShadow < numberOfOldShadowRecords)
5736  {
5737    /* Read a record from the old shadow file. */
5738    bytesRead = read(fp_old_shadow, &oldShadowRecord, shadowRecordSize);
5739    if (bytesRead != shadowRecordSize)
5740    {
5741      fprintf(stderr, "Error reading from file descriptor %d\n", fp_old_shadow);
5742      return(RC_FAIL);
5743    }
5744
5745    /* Indicate a search for a matching record in the
5746       new shadow file should be done. */
5747    searchNeeded = true;
5748
5749    /* Put a null character at the end of the file name
5750       to make it into a string so we can use strcmp(). */
5751    offset = atoi((const char *) oldShadowRecord.tr.fnLength);
5752    eq_p = oldShadowRecord.tr.filename + offset;
5753    memset(eq_p, '\0', 1);
5754
5755    /* Loop through the new shadow file. */
5756    while (recordInNewShadow < numberOfNewShadowRecords)
5757    {
5758      if (readNewRecord)
5759      {
5760        /* Read a record from the new shadow file. */
5761        bytesRead = read(fp_new_shadow, &newShadowRecord, shadowRecordSize);
5762        if (bytesRead != shadowRecordSize)
5763        {
5764          fprintf(stderr, "Error reading from file descriptor %d\n",
5765                    fp_new_shadow);
5766          return(RC_FAIL);
5767        }
5768
5769        /* Put a null character at the end of the file name
5770           to make it into a string so we can use strcmp(). */
5771        offset = atoi((const char *) newShadowRecord.tr.fnLength);
5772        eq_p = newShadowRecord.tr.filename + offset;
5773        memset(eq_p, '\0', 1);
5774
5775      }  /* end of if (readNewRecord) */
5776
5777      /* Compare the records from the old and new shadow files. */
5778      rc1 = strcmp((const char *) &oldShadowRecord.tr.filename,
5779                     (const char *) &newShadowRecord.tr.filename);
5780      if (rc1 > 0)
5781      {
5782        /* We have not yet encountered a file in the new shadow file
5783           that comes lexicographically after the file from the old
5784           shadow file, so read the next record from the new shadow file. */
5785        recordInNewShadow++;
5786        readNewRecord = true;
5787        continue;
5788      }
5789
5790      if (rc1 == 0)
5791      {
5792        /* The names match. */
5793        recordInOldShadow++;
5794        recordInNewShadow++;
5795        readNewRecord = true;
5796        searchNeeded = false;
5797        break;
5798      }
5799
5800      if (rc1 < 0)
5801      {
5802        /* We just encountered a file in the new shadow file that comes
5803           lexicographically after the file from the old shadow file,
5804           so the file name does not exist in the newShadowFile (i.e.,
5805           it must have been deleted).  Construct a transaction record
5806           for the record from the old shadow file. */
5807        memset((void *) &transactionRecord, ' ', transactionRecordSize);
5808        memcpy((void *) &transactionRecord.filename,
5809                 (void *) &oldShadowRecord.tr.filename,
5810                 strlen((const char *) &oldShadowRecord.tr.filename));
5811        transactionRecord.delimeter2 = '\n';
5812
5813        /* Write the created transactions record to the deletions file. */
5814        bytesWritten = write(fp_del, &transactionRecord, transactionRecordSize);
5815        if (bytesWritten != transactionRecordSize)
5816        {
5817          fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
5818                    fp_del, bytesWritten, transactionRecordSize);
5819          return(RC_FAIL);
5820        }
5821        shadowDeletions = true;
5822        recordInOldShadow++;
5823        readNewRecord = false;
5824        searchNeeded = false;
5825        break;
5826      }  /* end of if (rc1 < 0) */
5827
5828    }  /* end of while (recordInNewShadow < numberOfNewShadowRecords) */
5829
5830    /* Did we exhaust all of the records in the
5831       new shadow file in the course of a search? */
5832    if (searchNeeded && recordInNewShadow == numberOfNewShadowRecords)
5833    {
5834      /* If we get here, it indicates that the old record was
5835         lexicographically greater than all of the records in the new
5836         shadow file.  We will therefore copy the old record and all of
5837         the remaining old records to the deletions file. */
5838
5839      /* First, construct the transaction record for the
5840         current old shadow record. */
5841      memset((void *) &transactionRecord, ' ', transactionRecordSize);
5842      memcpy((void *) &transactionRecord.filename,
5843               (void *) &oldShadowRecord.tr.filename,
5844               strlen((const char *) &oldShadowRecord.tr.filename));
5845      transactionRecord.delimeter2 = '\n';
5846
5847      /* Write the created transaction record to the deletions file. */
5848      bytesWritten = write(fp_del, &transactionRecord, transactionRecordSize);
5849      if (bytesWritten != transactionRecordSize)
5850      {
5851        fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
5852                  fp_del, bytesWritten, transactionRecordSize);
5853        return(RC_FAIL);
5854      }
5855
5856      /* Set a flag to indicate that deletions were found. */
5857      shadowDeletions = true;
5858
5859      /* Copy all of the remaining old shadow file records to the
5860         deletions file and exit the loop. */
5861      for (j = recordInOldShadow+1; j < numberOfOldShadowRecords; j++)
5862      {
5863        /* Read a record from the old shadow file. */
5864        bytesRead = read(fp_old_shadow, &oldShadowRecord, shadowRecordSize);
5865        if (bytesRead != shadowRecordSize)
5866        {
5867          fprintf(stderr,
5868                    "Error reading from file descriptor %d\n", fp_old_shadow);
5869          return(RC_FAIL);
5870        }
5871
5872        /* Put a null character at the end of the file name
5873           to make it into a string so we can use strcmp(). */
5874        offset = atoi((const char *) oldShadowRecord.tr.fnLength);
5875        eq_p = oldShadowRecord.tr.filename + offset;
5876        memset(eq_p, '\0', 1);
5877
5878        /* Construct the transaction record for the shadow record. */
5879        memset((void *) &transactionRecord, ' ', transactionRecordSize);
5880        memcpy((void *) &transactionRecord.filename,
5881                 (void *) &oldShadowRecord.tr.filename,
5882                 strlen((const char *) &oldShadowRecord.tr.filename));
5883        transactionRecord.delimeter2 = '\n';
5884
5885        /* Write the created transactions record to the deletions file. */
5886        bytesWritten = write(fp_del, &transactionRecord, transactionRecordSize);
5887        if (bytesWritten != transactionRecordSize)
5888        {
5889          fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
5890                    fp_del, bytesWritten, transactionRecordSize);
5891          return(RC_FAIL);
5892        }
5893      }  /* end of for loop */
5894      break;   // exit the loop through the old shadow file
5895
5896    }  /* if (recordInNewShadow == numberOfNewShadowRecords) */
5897
5898  }  /* end of while (recordInOldShadow < numberOfOldShadowRecords) */
5899
5900  /* Close the deletions file. */
5901  rc = close(fp_del);
5902  if (rc == -1)
5903  {
5904    fprintf(stderr, "Cannot close file descriptor %d for file %s\n",
5905              fp_del, deletionsFile);
5906    return(RC_FAIL);
5907  }
5908
5909  /* Close the old and new shadow files. */
5910  rc = close(fp_old_shadow);
5911  if (rc == -1)
5912  {
5913    fprintf(stderr, "Cannot close file descriptor %d for file %s\n",
5914              fp_old_shadow, oldShadowFile);
5915    return(RC_FAIL);
5916  }
5917  rc = close(fp_new_shadow);
5918  if (rc == -1)
5919  {
5920    fprintf(stderr, "Cannot close file descriptor %d for file %s\n",
5921              fp_new_shadow, newShadowFile);
5922    return(RC_FAIL);
5923  }
5924
5925  /* If there were deletions, set the deletions flag to true and
5926     pass it and the name of the deletions file back to the caller. */
5927  if (shadowDeletions)
5928  {
5929    *fileDeletions = true;
5930    strncpy(deletionsFilelist, deletionsFile, MAX_FILE_NAME);
5931  }
5932  else
5933  {
5934    /* Indicate no deletions and delete the deletions file. */
5935    *fileDeletions = false;
5936    unlink(deletionsFile);
5937  }
5938
5939  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
5940
5941  return(rc);
5942
5943}  /*------ end of getFilelistDeletions() ----------------*/
5944
5945
5946/*
5947 * NAME:  doFSFileChanges()
5948 *
5949 * FUNCTION:
5950 *   Determine the files which have changed or which have been added
5951 *   to a filesystem and back them up.
5952 *
5953 * PARAMETERS:
5954 *   fsName:              (IN) The full path name of the mounted filesystem.
5955 *   fsSnapshotPathname:  (IN) The full path name of a directory name
5956 *                             indicating where a snapshot of the filesystem
5957 *                             is to be stored.
5958 *
5959 * RETURNS:
5960 *   RC_SUCCESS on success, RC_PSUCCESS on partial success, RC_FAIL on error.
5961 *
5962 * NOTES:
5963 */
5964int doFSFileChanges(char *fsName, char *fsSnapshotPathname)
5965{
5966  Int32 tmp, rc = RC_SUCCESS;
5967  Int32 numShadows;
5968  char filenameTemp[MAX_FILE_NAME];
5969  inodeBitsArray *inodeBitsArray2P;
5970  char* fn = "doFSFileChanges";
5971
5972  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
5973
5974  /* Do an incremental inode scan.  Only the bits for inodes corresponding
5975     to files that have changed since the previous backup will be set. */
5976  inodeBitsArray2P = &inodeBits2P;
5977  rc = doIncrInodeScan(fsSnapshotPathname, &prevSnapId, inodeBitsArray2P,
5978                         backupControlP);
5979  if (rc != 0)
5980  {
5981    fprintf(stderr,
5982              "doFSFileChanges(): doIncrInodeScan() returned rc = %d\n", rc);
5983    return(RC_FAIL);
5984  }
5985
5986  /* Create the full pathname for the changes file for the filesystem. */
5987  snprintf(changesFile, MAX_FILE_NAME, "%s/%s", fsName, changesName);
5988
5989  /* Using the bitmap to identify the changed files, extract the names of the
5990     changed files from the backup shadow file to create the changes file. */
5991  rc = extractChangedFiles(backupShadowFile, *inodeBitsArray2P,
5992                             &fileChanges, changesFile);
5993  if (rc != 0)
5994  {
5995    fprintf(stderr,
5996             "getFSFileChanges(): extractChangedFiles() returned rc = %d\n",
5997             rc);
5998    unlink(changesFile);
5999    return(RC_FAIL);
6000  }
6001
6002  /* Process any changes that were found. */
6003  if (fileChanges == true)
6004  {
6005    /* Rename the changes file to be the transactions file. */
6006    tmp = rename(changesFile, transactionsList);
6007    if (tmp != 0)
6008    {
6009      fprintf(stderr, "rename(%s, %s) returned %d\n",
6010                changesFile, transactionsList, rc);
6011      return(RC_FAIL);
6012    }
6013
6014    /* Invoke routine to process the transactions file.  The false flag
6015       that is passed tells the routine to divy the transactions file
6016       among the client processes by number of files, not by aggregate
6017       file size, which is what makes sense when doing deletions. */
6018    rc = processFilelist(fsName,
6019                           transactionCmdSelective,
6020                           transactionCmdOption,
6021                           backupControlP,
6022                           false);
6023  }
6024  else   // There were no changes to process, so indicate success.
6025  {
6026    rc = RC_SUCCESS;
6027  }
6028
6029  /* Process the return code. */
6030  switch (rc)
6031  {
6032    case RC_FAIL:       /* failure */
6033      fprintf(stderr, "processFilelist() returned rc = %d\n", rc);
6034      break;
6035
6036    case RC_PSUCCESS:   /* partial success */
6037    case RC_SUCCESS:    /* complete success */
6038      /* Rename the backup shadow file to be the old backup shadow file. */
6039      tmp = rename(backupShadowFile, backupShadowCheck);
6040      if (tmp != 0)
6041      {
6042        fprintf(stderr, "rename(%s, %s) returned %d\n",
6043                  backupShadowFile, backupShadowCheck, rc);
6044        return(RC_FAIL);
6045      }
6046      break;
6047
6048    default:
6049      fprintf(stderr, "processFilelist() returned unexpected rc = %d\n", rc);
6050      rc = RC_FAIL;
6051      break;
6052  }
6053
6054  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
6055
6056  return(rc);
6057
6058}  /*------ end of doFSFileChanges() ----------------*/
6059
6060
6061/*
6062 * NAME:  extractChangedFiles()
6063 *
6064 * FUNCTION:
6065 *   Extract the transaction records for the changed files from the
6066 *   input file, returning them in the output file.
6067 *
6068 * PARAMETERS:
6069 *   filein      - (IN)  the name of the file containing the pertinent
6070 *                         backup shadow information
6071 *   bitMap      - (IN)  pointer to the bit map array that indicates
6072 *                         whether a given inode is for a changed file
6073 *   fileChanges - (OUT) flag indicating whether there were any
6074 *                         deleted files; a true value indicates
6075 *                         there were
6076 *   fileout     - (IN)  the name of the file where the extracted
6077 *                         transaction records should be written
6078 *
6079 * RETURNS:
6080 *   RC_SUCCESS on success, RC_FAIL on error.
6081 *
6082 * NOTES:
6083 */
6084int extractChangedFiles(char *filein,
6085                        inodeBitsArray bitMap,
6086                        Boolean *fileChanges,
6087                        char *fileout)
6088{
6089  Int32 rc = RC_SUCCESS;
6090  Int32 bytesRead, bytesWritten;
6091  backup_shadow_record_t shadowRecord;
6092  transactionsListRecord transactionRecord;
6093  Int32 fp_in, fp_out;
6094  Int64 recordInFile;
6095  Int64 numberOfFiles;
6096  Int32 transactionRecordSize, shadowRecordSize;
6097  offset_t nextReadOffset, actualOffset;
6098  struct stat statBuf;
6099  inodeBitsArray myBitMap;
6100  char* fn = "extractChangedFiles";
6101
6102  if (Tracing) traceEntry("tsbackup.C", fn, __LINE__);
6103
6104  /* Initializations */
6105  myBitMap = bitMap;
6106  transactionRecordSize = sizeof(transactionsListRecord);
6107  shadowRecordSize = sizeof(backup_shadow_record_t);
6108
6109  /* Stat and open the filein file. */
6110  if (stat(filein, &statBuf) == -1)
6111  {
6112    fprintf(stderr, "Could not stat file '%s', errno=%d\n", filein, errno);
6113    return(RC_FAIL);
6114  }
6115  numberOfFiles = statBuf.st_size / shadowRecordSize;
6116
6117  fp_in = open(filein, O_RDONLY, 0644);
6118  if (fp_in == -1)
6119  {
6120    fprintf(stderr, "extractChangedFiles(%s, %s): open(%s) failure\n",
6121              filein, fileout, filein);
6122    return(RC_FAIL);
6123  }
6124
6125  /* Locate the first record in the filein file. */
6126  recordInFile = 0;
6127  nextReadOffset = recordInFile*shadowRecordSize;
6128  actualOffset = lseek(fp_in, recordInFile*shadowRecordSize, SEEK_SET);
6129  if (actualOffset != nextReadOffset)
6130  {
6131    fprintf(stderr, "Cannot seek to %lld in %s\n",
6132              recordInFile*shadowRecordSize, filein);
6133    return(RC_FAIL);
6134  }
6135
6136  /* Create and open the fileout file in append mode. */
6137  fp_out = open(fileout, O_CREAT | O_APPEND | O_RDWR | O_TRUNC, 0600);
6138  if (fp_out == -1)
6139  {
6140    fprintf(stderr, "Cannot open file %s\n", fileout);
6141    return(RC_FAIL);
6142  }
6143
6144  /* Read each record from the input file and get the specified file name. */
6145  while (numberOfFiles > 0)
6146  {
6147    bytesRead = read(fp_in, &shadowRecord, shadowRecordSize);
6148    if (bytesRead != shadowRecordSize)
6149    {
6150      fprintf(stderr, "Error reading file %s\n", filein);
6151      return(RC_FAIL);
6152    }
6153
6154    /* Check the bit map array entry to determine
6155       whether the inode must be backed up. */
6156    if (testBit(myBitMap,
6157                 (UInt32) atoi((const char *) shadowRecord.inodenum)) != 0)
6158    {
6159      /* Build the record to be written to the file. */
6160      memset((void *) &transactionRecord, ' ', transactionRecordSize);
6161      memcpy((void *) &transactionRecord.filename,
6162               (void *) &shadowRecord.tr.filename, MAX_FILE_NAME);
6163      transactionRecord.delimeter2 = '\n';
6164
6165      /* Write the record to the file. */
6166      bytesWritten = write(fp_out, &transactionRecord, transactionRecordSize);
6167      if (bytesWritten != transactionRecordSize)
6168      {
6169        fprintf(stderr, "Error writing to %d; wrote %d bytes, not %d\n",
6170                  fp_out, bytesWritten, transactionRecordSize);
6171        return(RC_FAIL);
6172      }
6173      *fileChanges = true;
6174    }
6175    numberOfFiles--;
6176  }
6177
6178  /* Close the input and output files. */
6179  close(fp_in);
6180  close(fp_out);
6181
6182  if (Tracing) traceExit("tsbackup.C", fn, __LINE__);
6183
6184  return(rc);
6185
6186}  /*------ end of extractChangedFiles ----------------*/ 
6187
Note: See TracBrowser for help on using the repository browser.