~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201210021442

« back to all changes in this revision

Viewing changes to lib/file/file.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
77
77
 *
78
78
 *      XXX - This function invokes access(), which uses the real uid,
79
79
 *      not the effective uid, so it probably does not do what you 
80
 
 *      expect.  Instead it should use FileAttributes(), which calls
81
 
 *      Posix_Stat() and uses the effective uid, but it's too risky 
82
 
 *      to fix right now.  See PR 459242.
 
80
 *      expect.  Instead it should use Posix_EuidAccess(), which 
 
81
 *      uses the effective uid, but it's too risky to fix right now.
 
82
 *      See PR 459242.
83
83
 *
84
84
 * Results:
85
85
 *      TRUE    file is accessible with the process' real uid
355
355
Bool
356
356
File_DeleteEmptyDirectory(ConstUnicode pathName)  // IN:
357
357
{
358
 
   return (FileRemoveDirectory(pathName) == 0) ? TRUE : FALSE;
 
358
   Bool returnValue = TRUE;
 
359
 
 
360
   if (FileRemoveDirectory(pathName) != 0) {
 
361
#if defined(_WIN32)
 
362
      /*
 
363
       * Directory may have read-only bit set. Unset the
 
364
       * read-only bit and try deleting one more time.
 
365
       */
 
366
      if (File_SetFilePermissions(pathName, S_IWUSR)) {
 
367
         if (FileRemoveDirectory(pathName) != 0) {
 
368
            returnValue = FALSE;
 
369
         }
 
370
      } else {
 
371
         returnValue = FALSE;
 
372
      }
 
373
#else
 
374
      returnValue =  FALSE;
 
375
#endif
 
376
   }
 
377
 
 
378
   return returnValue;
359
379
}
360
380
 
361
381
 
816
836
 
817
837
   ASSERT(pathName);
818
838
 
819
 
   pathLen = Unicode_LengthInCodeUnits(pathName);
 
839
   pathLen = Unicode_LengthInCodePoints(pathName);
820
840
 
821
841
   /*
822
842
    * Get volume.
992
1012
   /*
993
1013
    * The volume component may be empty.
994
1014
    */
995
 
   if (Unicode_LengthInCodeUnits(volume) > 0) {
996
 
      Unicode temp;
997
 
 
998
 
      temp = Unicode_Append(volume, *pathName);
 
1015
 
 
1016
   if (!Unicode_IsEmpty(volume)) {
 
1017
      Unicode temp = Unicode_Append(volume, *pathName);
 
1018
 
999
1019
      Unicode_Free(*pathName);
1000
1020
      *pathName = temp;
1001
1021
   }
1005
1025
    * Remove any trailing directory separator characters.
1006
1026
    */
1007
1027
 
1008
 
   len = Unicode_LengthInCodeUnits(*pathName);
 
1028
   len = Unicode_LengthInCodePoints(*pathName);
1009
1029
 
1010
1030
   curLen = len;
1011
1031
 
1015
1035
   }
1016
1036
 
1017
1037
   if (curLen < len) {
1018
 
      Unicode temp;
 
1038
      Unicode temp = Unicode_Substr(*pathName, 0, curLen);
1019
1039
 
1020
 
      temp = Unicode_Substr(*pathName, 0, curLen);
1021
1040
      Unicode_Free(*pathName);
1022
1041
      *pathName = temp;
1023
1042
   }
1087
1106
 
1088
1107
 
1089
1108
/*
 
1109
 *----------------------------------------------------------------------------
 
1110
 *
 
1111
 * FileMakeTempExCreateNameFunc --
 
1112
 *
 
1113
 *      This is a helper function designed for File_MakeTempEx function.
 
1114
 *      Everytime this function is called, this creates a fileName with the
 
1115
 *      format <num><fileName> and returns back to the caller.
 
1116
 *
 
1117
 *      'num' specifies the nth time this function is called.
 
1118
 *
 
1119
 *      'data' specifies the payload that is specified when File_MakeTempEx2()
 
1120
 *      function is called. This points to a Unicode string.
 
1121
 *
 
1122
 * Results:
 
1123
 *      if successful, a dynamically allocated string with the basename of
 
1124
 *      the temp file. NULL otherwise.
 
1125
 *
 
1126
 * Side effects:
 
1127
 *      None
 
1128
 *
 
1129
 *----------------------------------------------------------------------------
 
1130
 */
 
1131
 
 
1132
static Unicode
 
1133
FileMakeTempExCreateNameFunc(int num,               // IN:
 
1134
                             void *data)            // IN:
 
1135
{
 
1136
   Unicode filePath;
 
1137
 
 
1138
   if (data == NULL) {
 
1139
      return NULL;
 
1140
   }
 
1141
 
 
1142
   filePath = Unicode_Format("%s%d", (Unicode) data, num);
 
1143
 
 
1144
   return filePath;
 
1145
}
 
1146
 
 
1147
 
 
1148
/*
1090
1149
 *----------------------------------------------------------------------
1091
1150
 *
1092
1151
 *  File_MakeTempEx --
1115
1174
                ConstUnicode fileName,  // IN:
1116
1175
                Unicode *presult)       // OUT:
1117
1176
{
1118
 
   int fd;
 
1177
   return File_MakeTempEx2(dir,
 
1178
                           TRUE,
 
1179
                           FileMakeTempExCreateNameFunc,
 
1180
                           (void *) fileName,
 
1181
                           presult);
 
1182
 
 
1183
}
 
1184
 
 
1185
 
 
1186
/*
 
1187
 *----------------------------------------------------------------------
 
1188
 *
 
1189
 *  File_MakeTempEx2 --
 
1190
 *
 
1191
 *      Create a temporary file or a directory.
 
1192
 *      If a temporary file is created successfully, then return an open file
 
1193
 *      descriptor to that file.
 
1194
 *
 
1195
 *      'dir' specifies the directory in which to create the file. It
 
1196
 *      must not end in a slash.
 
1197
 *
 
1198
 *      'createTempFile', if TRUE, then a temporary file will be created. If
 
1199
 *      FALSE, then a temporary directory will be created.
 
1200
 *
 
1201
 *      'createNameFunc' specifies the user-specified callback function that
 
1202
 *      will be called to construct the fileName. 'createNameFuncData' will be
 
1203
 *      passed everytime 'createNameFunc' is called. 'createNameFunc'
 
1204
 *      should return the proper fileName.
 
1205
 *
 
1206
 *      Check the documentation for File_MakeTempHelperFunc.
 
1207
 *
 
1208
 * Results:
 
1209
 *      if a temporary file is created, then Open file descriptor or -1;
 
1210
 *      if a temporary directory is created, then 0 or -1;
 
1211
 *      If successful then presult points to a dynamically allocated
 
1212
 *      string with the pathname of the temp file.
 
1213
 *
 
1214
 * Side effects:
 
1215
 *      Creates a file if successful. Errno is set on error
 
1216
 *
 
1217
 *----------------------------------------------------------------------
 
1218
 */
 
1219
 
 
1220
int
 
1221
File_MakeTempEx2(ConstUnicode dir,                                // IN:
 
1222
                 Bool createTempFile,                             // IN:
 
1223
                 File_MakeTempCreateNameFunc *createNameFunc,     // IN:
 
1224
                 void *createNameFuncData,                        // IN:
 
1225
                 Unicode *presult)                                // OUT:
 
1226
{
 
1227
   int fd = -1;
1119
1228
   int err;
1120
1229
   uint32 var;
1121
1230
 
1122
1231
   Unicode path = NULL;
1123
 
   Unicode basePath = NULL;
1124
1232
 
1125
 
   if ((dir == NULL) || (fileName == NULL)) {
 
1233
   if ((dir == NULL) || (createNameFunc == NULL)) {
1126
1234
      errno = EFAULT;
1127
1235
      return -1;
1128
1236
   }
1131
1239
 
1132
1240
   *presult = NULL;
1133
1241
 
1134
 
   /* construct base full pathname to use */
1135
 
   basePath = Unicode_Join(dir, DIRSEPS, fileName, NULL);
1136
 
 
1137
1242
   for (var = 0; var < 0xFFFFFFFF; var++) {
1138
 
      Unicode temp;
 
1243
      Unicode fileName = NULL;
1139
1244
 
1140
1245
      /* construct suffixed pathname to use */
1141
1246
      Unicode_Free(path);
1142
 
 
1143
 
      temp = Unicode_Format("%d", var);
1144
 
      ASSERT_MEM_ALLOC(temp);
1145
 
      path = Unicode_Append(basePath, temp);
1146
 
      Unicode_Free(temp);
1147
 
 
1148
 
      fd = Posix_Open(path, O_CREAT | O_EXCL | O_BINARY | O_RDWR, 0600);
 
1247
      path = NULL;
 
1248
 
 
1249
      fileName = (*createNameFunc)(var, createNameFuncData);
 
1250
 
 
1251
      if (fileName == NULL) {
 
1252
         Msg_Append(MSGID(file.maketemp.helperFuncFailed)
 
1253
                  "Failed to construct the filename.\n");
 
1254
         errno = EFAULT;
 
1255
         goto exit;
 
1256
      }
 
1257
 
 
1258
      /* construct base full pathname to use */
 
1259
      path = Unicode_Join(dir, DIRSEPS,  fileName, NULL);
 
1260
 
 
1261
      Unicode_Free(fileName);
 
1262
 
 
1263
      if (createTempFile) {
 
1264
         fd = Posix_Open(path, O_CREAT | O_EXCL | O_BINARY | O_RDWR, 0600);
 
1265
#if defined(_WIN32)
 
1266
         /*
 
1267
          * On windows, Posix_Open() fails with EACCES if there is any
 
1268
          * access violation while creating the file. Also, EACCES is returned
 
1269
          * if a directory already exists with the same name. In such case,
 
1270
          * we need to check if a file already exists and ignore EACCES error.
 
1271
          */
 
1272
         if ((fd == -1) && (errno == EACCES) && (File_Exists(path))) {
 
1273
            continue;
 
1274
         }
 
1275
#endif
 
1276
      } else {
 
1277
         fd = Posix_Mkdir(path, 0600);
 
1278
      }
1149
1279
 
1150
1280
      if (fd != -1) {
1151
1281
         *presult = path;
1173
1303
 
1174
1304
  exit:
1175
1305
   err = errno;
1176
 
   Unicode_Free(basePath);
1177
1306
   Unicode_Free(path);
1178
1307
   errno = err;
1179
1308
 
1256
1385
 *      end of the 'src' file to the current position in the 'dst' file
1257
1386
 *
1258
1387
 * Results:
1259
 
 *      TRUE on success
1260
 
 *      FALSE on failure
 
1388
 *      TRUE   success
 
1389
 *      FALSE  failure
1261
1390
 *
1262
1391
 * Side effects:
1263
1392
 *      The current position in the 'src' file and the 'dst' file are modified
1269
1398
File_CopyFromFdToFd(FileIODescriptor src,  // IN:
1270
1399
                    FileIODescriptor dst)  // IN:
1271
1400
{
 
1401
   Err_Number err;
1272
1402
   FileIOResult fretR;
1273
1403
 
1274
1404
   do {
1278
1408
 
1279
1409
      fretR = FileIO_Read(&src, buf, sizeof(buf), &actual);
1280
1410
      if (!FileIO_IsSuccess(fretR) && (fretR != FILEIO_READ_ERROR_EOF)) {
 
1411
         err = Err_Errno();
 
1412
 
1281
1413
         Msg_Append(MSGID(File.CopyFromFdToFd.read.failure)
1282
1414
                               "Read error: %s.\n\n", FileIO_MsgError(fretR));
1283
1415
 
 
1416
         Err_SetErrno(err);
 
1417
 
1284
1418
         return FALSE;
1285
1419
      }
1286
1420
 
1287
1421
      fretW = FileIO_Write(&dst, buf, actual, NULL);
1288
1422
      if (!FileIO_IsSuccess(fretW)) {
 
1423
         err = Err_Errno();
 
1424
 
1289
1425
         Msg_Append(MSGID(File.CopyFromFdToFd.write.failure)
1290
1426
                              "Write error: %s.\n\n", FileIO_MsgError(fretW));
1291
1427
 
 
1428
         Err_SetErrno(err);
 
1429
 
1292
1430
         return FALSE;
1293
1431
      }
1294
1432
   } while (fretR != FILEIO_READ_ERROR_EOF);
1325
1463
                      ConstUnicode dstName,  // IN:
1326
1464
                      int dstDispose)        // IN:
1327
1465
{
 
1466
   Bool success;
 
1467
   Err_Number err;
 
1468
   FileIOResult fret;
1328
1469
   FileIODescriptor dst;
1329
 
   FileIOResult fret;
1330
 
   Bool result;
1331
1470
 
1332
1471
   ASSERT(dstName);
1333
1472
 
1335
1474
 
1336
1475
   fret = File_CreatePrompt(&dst, dstName, 0, dstDispose);
1337
1476
   if (!FileIO_IsSuccess(fret)) {
 
1477
      err = Err_Errno();
 
1478
 
1338
1479
      if (fret != FILEIO_CANCELLED) {
1339
1480
         Msg_Append(MSGID(File.CopyFromFdToName.create.failure)
1340
1481
                    "Unable to create a new '%s' file: %s.\n\n",
1341
1482
                    UTF8(dstName), FileIO_MsgError(fret));
1342
1483
      }
1343
1484
 
 
1485
      Err_SetErrno(err);
 
1486
 
1344
1487
      return FALSE;
1345
1488
   }
1346
1489
 
1347
 
   result = File_CopyFromFdToFd(src, dst);
 
1490
   success = File_CopyFromFdToFd(src, dst);
 
1491
 
 
1492
   err = Err_Errno();
1348
1493
 
1349
1494
   if (FileIO_Close(&dst) != 0) {
 
1495
      if (success) {  // Report close failure when there isn't another error
 
1496
         err =  Err_Errno();
 
1497
      }
 
1498
 
1350
1499
      Msg_Append(MSGID(File.CopyFromFdToName.close.failure)
1351
1500
                 "Unable to close the '%s' file: %s.\n\n", UTF8(dstName),
1352
1501
                 Msg_ErrString());
1353
 
      result = FALSE;
 
1502
 
 
1503
      success = FALSE;
1354
1504
   }
1355
1505
 
1356
 
   if (result == FALSE) {
 
1506
   if (!success) {
1357
1507
      /* The copy failed: ensure the destination file is removed */
1358
1508
      File_Unlink(dstName);
1359
1509
   }
1360
1510
 
1361
 
   return result;
 
1511
   Err_SetErrno(err);
 
1512
 
 
1513
   return success;
1362
1514
}
1363
1515
 
1364
1516
 
1388
1540
                  int access,              // IN:
1389
1541
                  int prompt)              // IN:
1390
1542
{
 
1543
   FileIOResult fret;
1391
1544
   FileIOOpenAction action;
1392
 
   FileIOResult fret;
1393
1545
 
1394
1546
   ASSERT(file);
1395
1547
   ASSERT(pathName);
1399
1551
   while ((fret = FileIO_Open(file, pathName, FILEIO_OPEN_ACCESS_WRITE | access,
1400
1552
                             action)) == FILEIO_OPEN_ERROR_EXIST) {
1401
1553
      static Msg_String const buttons[] = {
1402
 
         {BUTTONID(file.create.retry) "Retry"},
 
1554
         {BUTTONID(file.create.retry)     "Retry"},
1403
1555
         {BUTTONID(file.create.overwrite) "Overwrite"},
1404
 
         {BUTTONID(cancel) "Cancel"},
 
1556
         {BUTTONID(cancel)                "Cancel"},
1405
1557
         {NULL}
1406
1558
      };
1407
1559
      int answer;
 
1560
      Err_Number err = Err_Errno();
1408
1561
 
1409
1562
      answer = (prompt != -1) ? prompt : Msg_Question(buttons, 2,
1410
1563
         MSGID(File.CreatePrompt.question)
1414
1567
         "to another location, select Retry.\n"
1415
1568
         "To cancel the operation, select Cancel.\n",
1416
1569
         UTF8(pathName));
 
1570
 
 
1571
      Err_SetErrno(err);
 
1572
 
1417
1573
      if (answer == 2) {
1418
1574
         fret = FILEIO_CANCELLED;
1419
1575
         break;
1456
1612
                        ConstUnicode dstName,  // IN:
1457
1613
                        int dstDispose)        // IN:
1458
1614
{
 
1615
   Bool success;
 
1616
   Err_Number err;
 
1617
   FileIOResult fret;
1459
1618
   FileIODescriptor src;
1460
 
   FileIOResult fret;
1461
 
   Bool result;
1462
1619
 
1463
1620
   ASSERT(srcName);
1464
1621
   ASSERT(dstName);
1467
1624
 
1468
1625
   fret = FileIO_Open(&src, srcName, FILEIO_OPEN_ACCESS_READ, FILEIO_OPEN);
1469
1626
   if (!FileIO_IsSuccess(fret)) {
 
1627
      err = Err_Errno();
 
1628
 
1470
1629
      Msg_Append(MSGID(File.CopyFromNameToName.open.failure)
1471
1630
                 "Unable to open the '%s' file for read access: %s.\n\n",
1472
1631
                 UTF8(srcName), FileIO_MsgError(fret));
1473
1632
 
 
1633
      Err_SetErrno(err);
 
1634
 
1474
1635
      return FALSE;
1475
1636
   }
1476
1637
 
1477
 
   result = File_CopyFromFdToName(src, dstName, dstDispose);
 
1638
   success = File_CopyFromFdToName(src, dstName, dstDispose);
 
1639
 
 
1640
   err = Err_Errno();
1478
1641
   
1479
1642
   if (FileIO_Close(&src) != 0) {
 
1643
      if (success) {  // Report close failure when there isn't another error
 
1644
         err =  Err_Errno();
 
1645
      }
 
1646
 
1480
1647
      Msg_Append(MSGID(File.CopyFromNameToName.close.failure)
1481
1648
                 "Unable to close the '%s' file: %s.\n\n", UTF8(srcName),
1482
1649
                 Msg_ErrString());
1483
 
      result = FALSE;
1484
 
   }
1485
 
 
1486
 
   return result;
1487
 
}
 
1650
 
 
1651
      success = FALSE;
 
1652
   }
 
1653
 
 
1654
   Err_SetErrno(err);
 
1655
 
 
1656
   return success;
 
1657
}
 
1658
 
 
1659
 
 
1660
/*
 
1661
 *-----------------------------------------------------------------------------
 
1662
 *
 
1663
 * FileCopyTree --
 
1664
 *
 
1665
 *      Recursively copies all files from a source path to a destination,
 
1666
 *      optionally overwriting any files. This does the actual work
 
1667
 *      for File_CopyTree.
 
1668
 *
 
1669
 * Results:
 
1670
 *      TRUE on success
 
1671
 *      FALSE on failure: Error messages are appended.
 
1672
 *
 
1673
 * Side effects:
 
1674
 *      None.
 
1675
 *
 
1676
 *-----------------------------------------------------------------------------
 
1677
 */
 
1678
 
 
1679
static Bool
 
1680
FileCopyTree(ConstUnicode srcName,    // IN:
 
1681
             ConstUnicode dstName,    // IN:
 
1682
             Bool overwriteExisting,  // IN:
 
1683
             Bool followSymlinks)     // IN:
 
1684
{
 
1685
   int err;
 
1686
   Bool success = TRUE;
 
1687
   int numFiles;
 
1688
   int i;
 
1689
   Unicode *fileList = NULL;
 
1690
 
 
1691
   numFiles = File_ListDirectory(srcName, &fileList);
 
1692
 
 
1693
   if (numFiles == -1) {
 
1694
      err = Err_Errno();
 
1695
      Msg_Append(MSGID(File.CopyTree.walk.failure)
 
1696
                 "Unable to access '%s' when copying files.\n\n",
 
1697
                 UTF8(srcName));
 
1698
      Err_SetErrno(err);
 
1699
 
 
1700
      return FALSE;
 
1701
   }
 
1702
 
 
1703
   File_EnsureDirectory(dstName);
 
1704
 
 
1705
   for (i = 0; i < numFiles && success; i++) {
 
1706
      struct stat sb;
 
1707
      Unicode name;
 
1708
      Unicode srcFilename;
 
1709
 
 
1710
      name = Unicode_Alloc(fileList[i], STRING_ENCODING_DEFAULT);
 
1711
      srcFilename = File_PathJoin(srcName, name);
 
1712
 
 
1713
      if (followSymlinks) {
 
1714
         success = (Posix_Stat(srcFilename, &sb) == 0);
 
1715
      } else {
 
1716
         success = (Posix_Lstat(srcFilename, &sb) == 0);
 
1717
      }
 
1718
 
 
1719
      if (success) {
 
1720
         Unicode dstFilename = File_PathJoin(dstName, name);
 
1721
 
 
1722
         switch (sb.st_mode & S_IFMT) {
 
1723
         case S_IFDIR:
 
1724
            success = FileCopyTree(srcFilename, dstFilename, overwriteExisting,
 
1725
                                   followSymlinks);
 
1726
            break;
 
1727
 
 
1728
#if !defined(_WIN32)
 
1729
         case S_IFLNK:
 
1730
            if (Posix_Symlink(Posix_ReadLink(srcFilename), dstFilename) != 0) {
 
1731
               err = Err_Errno();
 
1732
               Msg_Append(MSGID(File.CopyTree.symlink.failure)
 
1733
                          "Unable to symlink '%s' to '%s': %s\n\n",
 
1734
                          UTF8(Posix_ReadLink(srcFilename)),
 
1735
                          UTF8(dstFilename),
 
1736
                          Err_Errno2String(err));
 
1737
               Err_SetErrno(err);
 
1738
               success = FALSE;
 
1739
            }
 
1740
            break;
 
1741
#endif
 
1742
 
 
1743
         default:
 
1744
            if (!File_Copy(srcFilename, dstFilename, overwriteExisting)) {
 
1745
               err = Err_Errno();
 
1746
               Msg_Append(MSGID(File.CopyTree.copy.failure)
 
1747
                          "Unable to copy '%s' to '%s': %s\n\n",
 
1748
                          UTF8(srcFilename), UTF8(dstFilename),
 
1749
                          Err_Errno2String(err));
 
1750
               Err_SetErrno(err);
 
1751
               success = FALSE;
 
1752
            }
 
1753
 
 
1754
            break;
 
1755
         }
 
1756
 
 
1757
         Unicode_Free(dstFilename);
 
1758
      } else {
 
1759
         err = Err_Errno();
 
1760
         Msg_Append(MSGID(File.CopyTree.stat.failure)
 
1761
                    "Unable to get information on '%s' when copying files.\n\n",
 
1762
                    UTF8(srcFilename));
 
1763
         Err_SetErrno(err);
 
1764
      }
 
1765
 
 
1766
      Unicode_Free(srcFilename);
 
1767
      Unicode_Free(name);
 
1768
   }
 
1769
 
 
1770
   for (i = 0; i < numFiles; i++) {
 
1771
      Unicode_Free(fileList[i]);
 
1772
   }
 
1773
 
 
1774
   free(fileList);
 
1775
 
 
1776
   return success;
 
1777
}
 
1778
 
 
1779
 
 
1780
/*
 
1781
 *-----------------------------------------------------------------------------
 
1782
 *
 
1783
 * File_CopyTree --
 
1784
 *
 
1785
 *      Recursively copies all files from a source path to a destination,
 
1786
 *      optionally overwriting any files.
 
1787
 *
 
1788
 * Results:
 
1789
 *      TRUE on success
 
1790
 *      FALSE on failure: Error messages are appended.
 
1791
 *
 
1792
 * Side effects:
 
1793
 *      None.
 
1794
 *
 
1795
 *-----------------------------------------------------------------------------
 
1796
 */
 
1797
 
 
1798
Bool
 
1799
File_CopyTree(ConstUnicode srcName,    // IN:
 
1800
              ConstUnicode dstName,    // IN:
 
1801
              Bool overwriteExisting,  // IN:
 
1802
              Bool followSymlinks)     // IN:
 
1803
{
 
1804
   int err;
 
1805
 
 
1806
   ASSERT(srcName);
 
1807
   ASSERT(dstName);
 
1808
 
 
1809
   if (!File_IsDirectory(srcName)) {
 
1810
      err = Err_Errno();
 
1811
      Msg_Append(MSGID(File.CopyTree.source.notDirectory)
 
1812
                 "The source path '%s' is not a directory.\n\n",
 
1813
                 UTF8(srcName));
 
1814
      Err_SetErrno(err);
 
1815
      return FALSE;
 
1816
   }
 
1817
 
 
1818
   if (!File_IsDirectory(dstName)) {
 
1819
      err = Err_Errno();
 
1820
      Msg_Append(MSGID(File.CopyTree.dest.notDirectory)
 
1821
                 "The destination path '%s' is not a directory.\n\n",
 
1822
                 UTF8(dstName));
 
1823
      Err_SetErrno(err);
 
1824
      return FALSE;
 
1825
   }
 
1826
 
 
1827
   return FileCopyTree(srcName, dstName, overwriteExisting, followSymlinks);
 
1828
}
 
1829
 
1488
1830
 
1489
1831
/*
1490
1832
 *----------------------------------------------------------------------
1510
1852
                ConstUnicode dstName,     // IN:
1511
1853
                Bool overwriteExisting)   // IN:
1512
1854
{
 
1855
   Bool success;
 
1856
   Err_Number err;
 
1857
   FileIOResult fret;
1513
1858
   FileIODescriptor dst;
1514
1859
   FileIOOpenAction action;
1515
 
   FileIOResult fret;
1516
 
   Bool result;
1517
1860
 
1518
1861
   ASSERT(dstName);
1519
1862
 
1524
1867
 
1525
1868
   fret = FileIO_Open(&dst, dstName, FILEIO_OPEN_ACCESS_WRITE, action);
1526
1869
   if (!FileIO_IsSuccess(fret)) {
 
1870
      err = Err_Errno();
 
1871
 
1527
1872
      Msg_Append(MSGID(File.CopyFromFdToName.create.failure)
1528
1873
                 "Unable to create a new '%s' file: %s.\n\n", UTF8(dstName),
1529
1874
                 FileIO_MsgError(fret));
1530
1875
 
 
1876
      Err_SetErrno(err);
 
1877
 
1531
1878
      return FALSE;
1532
1879
   }
1533
1880
 
1534
 
   result = File_CopyFromFdToFd(src, dst);
 
1881
   success = File_CopyFromFdToFd(src, dst);
 
1882
 
 
1883
   err = Err_Errno();
1535
1884
 
1536
1885
   if (FileIO_Close(&dst) != 0) {
 
1886
      if (success) {  // Report close failure when there isn't another error
 
1887
         err =  Err_Errno();
 
1888
      }
 
1889
 
1537
1890
      Msg_Append(MSGID(File.CopyFromFdToName.close.failure)
1538
1891
                 "Unable to close the '%s' file: %s.\n\n", UTF8(dstName),
1539
1892
                 Msg_ErrString());
1540
 
      result = FALSE;
 
1893
 
 
1894
      success = FALSE;
1541
1895
   }
1542
1896
 
1543
 
   if (result == FALSE) {
 
1897
   if (!success) {
1544
1898
      /* The copy failed: ensure the destination file is removed */
1545
1899
      File_Unlink(dstName);
1546
1900
   }
1547
1901
 
1548
 
   return result;
 
1902
   Err_SetErrno(err);
 
1903
 
 
1904
   return success;
1549
1905
}
1550
1906
 
1551
1907
 
1574
1930
          ConstUnicode dstName,    // IN:
1575
1931
          Bool overwriteExisting)  // IN:
1576
1932
{
 
1933
   Bool success;
 
1934
   Err_Number err;
 
1935
   FileIOResult fret;
1577
1936
   FileIODescriptor src;
1578
 
   FileIOResult fret;
1579
 
   Bool result;
1580
1937
 
1581
1938
   ASSERT(srcName);
1582
1939
   ASSERT(dstName);
1585
1942
 
1586
1943
   fret = FileIO_Open(&src, srcName, FILEIO_OPEN_ACCESS_READ, FILEIO_OPEN);
1587
1944
   if (!FileIO_IsSuccess(fret)) {
 
1945
      err = Err_Errno();
 
1946
 
1588
1947
      Msg_Append(MSGID(File.Copy.open.failure)
1589
1948
                 "Unable to open the '%s' file for read access: %s.\n\n",
1590
1949
                 UTF8(srcName), FileIO_MsgError(fret));
1591
1950
 
 
1951
      Err_SetErrno(err);
 
1952
 
1592
1953
      return FALSE;
1593
1954
   }
1594
1955
 
1595
 
   result = File_CopyFromFd(src, dstName, overwriteExisting);
 
1956
   success = File_CopyFromFd(src, dstName, overwriteExisting);
 
1957
 
 
1958
   err = Err_Errno();
1596
1959
   
1597
1960
   if (FileIO_Close(&src) != 0) {
 
1961
      if (success) {  // Report close failure when there isn't another error
 
1962
         err =  Err_Errno();
 
1963
      }
 
1964
 
1598
1965
      Msg_Append(MSGID(File.Copy.close.failure)
1599
1966
                 "Unable to close the '%s' file: %s.\n\n", UTF8(srcName),
1600
1967
                 Msg_ErrString());
1601
 
      result = FALSE;
 
1968
 
 
1969
      success = FALSE;
1602
1970
   }
1603
1971
 
1604
 
   return result;
 
1972
   Err_SetErrno(err);
 
1973
 
 
1974
   return success;
1605
1975
}
1606
1976
 
 
1977
 
1607
1978
/*
1608
1979
 *----------------------------------------------------------------------
1609
1980
 *
1610
 
 * File_Rename --
 
1981
 * File_Move --
1611
1982
 *
1612
 
 *      Renames a source to a destination file.
1613
 
 *      Will copy the file if necessary
 
1983
 *      Moves a file from one place to the other as efficiently as possible.
 
1984
 *      This can be used to rename a file but, since file copying may be
 
1985
 *      necessary, there is no assurance of atomicity. For efficiency
 
1986
 *      purposes copying only results if the native rename ability fails.
1614
1987
 *
1615
1988
 * Results:
1616
 
 *      TRUE if succeeded FALSE otherwise
 
1989
 *      TRUE   succeeded
 
1990
 *      FALSE  otherwise
1617
1991
 *      
1618
1992
 * Side effects:
1619
1993
 *      src file is no more, but dst file exists
1622
1996
 */
1623
1997
 
1624
1998
Bool 
1625
 
File_Rename(ConstUnicode oldFile,  // IN:
1626
 
            ConstUnicode newFile)  // IN:
 
1999
File_Move(ConstUnicode oldFile,  // IN:
 
2000
          ConstUnicode newFile,  // IN:
 
2001
          Bool *asRename)        // OUT: result occurred due to rename/copy
1627
2002
{
1628
 
   Bool ret = TRUE;
1629
 
 
1630
 
   if (FileRename(oldFile, newFile) != 0) {
1631
 
      /* overwrite the file if it exists */
1632
 
      if (File_Copy(oldFile, newFile, TRUE)) {
1633
 
         File_Unlink(oldFile);
 
2003
   Bool ret;
 
2004
   Bool duringRename;
 
2005
 
 
2006
   if (FileRename(oldFile, newFile) == 0) {
 
2007
      duringRename = TRUE;
 
2008
      ret = TRUE;
 
2009
      Err_SetErrno(0);
 
2010
   } else {
 
2011
      duringRename = FALSE;
 
2012
 
 
2013
      if (File_Copy(oldFile, newFile, TRUE)) {  // Allow overwrite
 
2014
         File_Unlink(oldFile);  // Explicitly ignore errors
 
2015
         ret = TRUE;
 
2016
         Err_SetErrno(0);
1634
2017
      } else {
1635
2018
         ret = FALSE;
1636
2019
      }
1637
2020
   }
1638
2021
 
 
2022
   if (asRename) {
 
2023
      *asRename = duringRename;
 
2024
   }
 
2025
 
1639
2026
   return ret;
1640
2027
}
1641
2028
 
1675
2062
 *
1676
2063
 * File_GetSize --
1677
2064
 *
1678
 
 *      Get size of file.
 
2065
 *      Get size of file. Try File_GetSizeEx to get size of directory/symlink.
1679
2066
 *
1680
2067
 * Results:
1681
2068
 *      Size of file or -1.
1776
2163
      /*
1777
2164
       * If the prefix matches on a DIRSEPS boundary, or the prefix is the
1778
2165
       * whole string, replace it.
 
2166
       *
1779
2167
       * If we don't insist on matching a whole directory name, we could
1780
2168
       * mess things of if one directory is a substring of another.
 
2169
       *
 
2170
       * Perform a case-insensitive compare on Windows. (There are
 
2171
       * case-insensitive filesystems on MacOS also, but the problem
 
2172
       * is more acute with Windows because of frequent drive-letter
 
2173
       * case mismatches. So in lieu of actually asking the
 
2174
       * filesystem, let's just go with a simple ifdef for now.)
1781
2175
       */
1782
2176
 
1783
2177
      if ((oldPathLen >= oldPrefixLen) &&
1784
 
          (memcmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
 
2178
#ifdef _WIN32
 
2179
          (Str_Strncasecmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
 
2180
#else
 
2181
          (Str_Strncmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
 
2182
#endif
1785
2183
          (strchr(VALID_DIRSEPS, oldPath[oldPrefixLen]) ||
1786
2184
              (oldPath[oldPrefixLen] == '\0'))) {
1787
2185
         size_t newPrefixLen = strlen(newPrefix);
1816
2214
/*
1817
2215
 *----------------------------------------------------------------------------
1818
2216
 *
 
2217
 * File_GetSizeEx --
 
2218
 *
 
2219
 *      Get size of file or directory or symlink. File_GetSize can only get
 
2220
 *      size of file.
 
2221
 *
 
2222
 * Results:
 
2223
 *      Size of file/directory/symlink or -1.
 
2224
 *
 
2225
 * Side effects:
 
2226
 *      None.
 
2227
 *
 
2228
 *----------------------------------------------------------------------------
 
2229
 */
 
2230
 
 
2231
int64
 
2232
File_GetSizeEx(ConstUnicode pathName) // IN
 
2233
{
 
2234
   int numFiles;
 
2235
   int i;
 
2236
   Unicode *fileList = NULL;
 
2237
   struct stat sb;
 
2238
   int64 totalSize = 0;
 
2239
 
 
2240
   if (pathName == NULL) {
 
2241
      return -1;
 
2242
   }
 
2243
 
 
2244
   if (-1 == Posix_Lstat(pathName, &sb)) {
 
2245
      return -1;
 
2246
   }
 
2247
 
 
2248
   if (S_IFDIR != (sb.st_mode & S_IFMT)) {
 
2249
      return sb.st_size;
 
2250
   }
 
2251
 
 
2252
   numFiles = File_ListDirectory(pathName, &fileList);
 
2253
 
 
2254
   if (-1 == numFiles) {
 
2255
      return -1;
 
2256
   }
 
2257
 
 
2258
   for (i = 0; i < numFiles; i++) {
 
2259
      Unicode name;
 
2260
      Unicode fileName;
 
2261
      int64 fileSize;
 
2262
 
 
2263
      name = Unicode_Alloc(fileList[i], STRING_ENCODING_DEFAULT);
 
2264
      fileName = File_PathJoin(pathName, name);
 
2265
 
 
2266
      fileSize = File_GetSizeEx(fileName);
 
2267
 
 
2268
      Unicode_Free(fileName);
 
2269
      Unicode_Free(name);
 
2270
 
 
2271
      if (-1 == fileSize) {
 
2272
         totalSize = -1;
 
2273
         break;
 
2274
      } else {
 
2275
         totalSize += fileSize;
 
2276
      }
 
2277
   }
 
2278
 
 
2279
   if (numFiles >= 0) {
 
2280
      Unicode_FreeList(fileList, numFiles);
 
2281
   }
 
2282
 
 
2283
   return totalSize;
 
2284
}
 
2285
 
 
2286
 
 
2287
/*
 
2288
 *----------------------------------------------------------------------------
 
2289
 *
1819
2290
 * File_GetSizeByPath --
1820
2291
 *
1821
2292
 *      Get size of a file without opening it.
1863
2334
      return TRUE;
1864
2335
   }
1865
2336
 
1866
 
   length = Unicode_LengthInCodeUnits(pathName);
 
2337
   length = Unicode_LengthInCodePoints(pathName);
1867
2338
 
1868
2339
   if (length == 0) {
1869
2340
      return TRUE;
1875
2346
 
1876
2347
   File_SplitName(pathName, &volume, NULL, NULL);
1877
2348
 
1878
 
   index = Unicode_LengthInCodeUnits(volume);
 
2349
   index = Unicode_LengthInCodePoints(volume);
1879
2350
 
1880
2351
   Unicode_Free(volume);
1881
2352
 
1936
2407
{
1937
2408
   int i;
1938
2409
   int numFiles;
 
2410
   int err = 0;
1939
2411
   Unicode base;
1940
2412
 
1941
2413
   Unicode *fileList = NULL;
1942
2414
   Bool sawFileError = FALSE;
1943
2415
 
1944
 
   switch (FileAttributes(pathName, NULL)) {
1945
 
   case 0:
1946
 
      break;
1947
 
   case ENOENT:
1948
 
   case ENOTDIR:
1949
 
      /* path does not exist or is inaccessible */
1950
 
      return TRUE;
1951
 
   default:
1952
 
      return FALSE;
 
2416
   if (Posix_EuidAccess(pathName, F_OK) != 0) {
 
2417
      /*
 
2418
       * If Posix_EuidAccess failed with errno == ENOSYS, then fall back
 
2419
       * to FileAttributes.
 
2420
       */
 
2421
      if (errno == ENOSYS) {
 
2422
         /* FileAttributes returns the error code instead of setting errno. */
 
2423
         err = FileAttributes(pathName, NULL);
 
2424
      } else {
 
2425
         /* Use the error value that was set by Posix_EuidAccess. */
 
2426
         err = errno;
 
2427
      }
 
2428
   }
 
2429
 
 
2430
   switch (err) {
 
2431
      case ENOENT:
 
2432
      case ENOTDIR:
 
2433
         /* path does not exist or is inaccessible */
 
2434
         return TRUE;
 
2435
      default:
 
2436
         break;
1953
2437
   }
1954
2438
 
1955
2439
   /* get list of files in current directory */
2011
2495
 
2012
2496
   Unicode_Free(base);
2013
2497
 
2014
 
   /* delete the now-empty directory */
2015
 
   if (!File_DeleteEmptyDirectory(pathName)) {
 
2498
   /*
 
2499
    * Call File_DeleteEmptyDirectory() only if there is no prior error
 
2500
    * while deleting the children.
 
2501
    */
 
2502
   if (!sawFileError && !File_DeleteEmptyDirectory(pathName)) {
2016
2503
      sawFileError = TRUE;
2017
2504
   }
2018
2505
 
2066
2553
      char *next = Str_Strchr(path, sep);
2067
2554
      size_t len = next ? next - path : strlen(path);
2068
2555
 
2069
 
      if (len == n && Str_Strncmp(path, elem, len) == 0) {
 
2556
      if ((len == n) && (Str_Strncmp(path, elem, len) == 0)) {
2070
2557
         if (next) {
2071
2558
            memmove(path, next + 1, strlen(next + 1) + 1);
2072
2559
         } else {
2130
2617
      cur = Str_SafeAsprintf(NULL, "%s%s%s", cwd, DIRSEPS, fileIn);
2131
2618
   }
2132
2619
 
2133
 
   if (FileAttributes(cur, NULL) == 0) {
 
2620
   if (Posix_EuidAccess(cur, F_OK) == 0) {
 
2621
      goto done;
 
2622
   }
 
2623
   if (errno == ENOSYS && FileAttributes(cur, NULL) == 0) {
2134
2624
      goto done;
2135
2625
   }
2136
2626
 
2162
2652
         }
2163
2653
      }
2164
2654
 
2165
 
      if (FileAttributes(cur, NULL) == 0) {
 
2655
      if (Posix_EuidAccess(cur, F_OK) == 0) {
 
2656
         break;
 
2657
      }
 
2658
 
 
2659
      if ((errno == ENOSYS) && (FileAttributes(cur, NULL) == 0)) {
2166
2660
         break;
2167
2661
      }
2168
2662
 
2294
2788
 
2295
2789
 
2296
2790
/*
 
2791
 *-----------------------------------------------------------------------------
 
2792
 *
 
2793
 * File_RemoveExtension --
 
2794
 *
 
2795
 *      Return a copy of the given path name with the extension
 
2796
 *      removed. We ASSERT that the given path does have an extension.
 
2797
 *
 
2798
 * Results:
 
2799
 *      A newly allocated buffer with the modified string. The caller
 
2800
 *      is responsible to free it when they are done with it.
 
2801
 *
 
2802
 * Side effects:
 
2803
 *      None.
 
2804
 *
 
2805
 *-----------------------------------------------------------------------------
 
2806
 */
 
2807
 
 
2808
Unicode
 
2809
File_RemoveExtension(ConstUnicode pathName)  // IN:
 
2810
{
 
2811
   UnicodeIndex index;
 
2812
 
 
2813
   ASSERT(pathName);
 
2814
 
 
2815
   index = Unicode_FindLast(pathName, ".");
 
2816
   ASSERT(index != UNICODE_INDEX_NOT_FOUND);
 
2817
 
 
2818
   return Unicode_Truncate(pathName, index);
 
2819
}
 
2820
 
 
2821
 
 
2822
/*
2297
2823
 *----------------------------------------------------------------------
2298
2824
 *
2299
2825
 * File_ExpandAndCheckDir --
2431
2957
   return msecActualSleepTime;
2432
2958
}
2433
2959
#endif // N_PLAT_NLM
 
2960
 
 
2961
 
 
2962
/*
 
2963
 *----------------------------------------------------------------------
 
2964
 *
 
2965
 * FileRotateByRename --
 
2966
 *
 
2967
 *      The oldest indexed file should be removed so that the consequent
 
2968
 *      rename succeeds.
 
2969
 *
 
2970
 *      The last dst is 'fileName' and should not be deleted.
 
2971
 *
 
2972
 * Results:
 
2973
 *      If newFileName is non-NULL: the new path is returned to *newFileName
 
2974
 *      if the rotation succeeded, otherwise NULL is returned in *newFileName.
 
2975
 *      The caller is responsible for freeing the string returned in
 
2976
 *      *newFileName.
 
2977
 *
 
2978
 * Side effects:
 
2979
 *      Rename backup old files kept so far.
 
2980
 *
 
2981
 *----------------------------------------------------------------------
 
2982
 */
 
2983
 
 
2984
static void
 
2985
FileRotateByRename(const char *fileName,  // IN: full path to file
 
2986
                   const char *baseName,  // IN: filename w/o extension.
 
2987
                   const char *ext,       // IN: extension
 
2988
                   int n,                 // IN: number of old files to keep
 
2989
                   char **newFileName)    // OUT/OPT: new path to file
 
2990
{
 
2991
   char *src = NULL;
 
2992
   char *dst = NULL;
 
2993
   int i;
 
2994
   int result;
 
2995
 
 
2996
   for (i = n; i >= 0; i--) {
 
2997
      src = (i == 0) ? (char *) fileName :
 
2998
                       Str_SafeAsprintf(NULL, "%s-%d%s", baseName, i - 1, ext);
 
2999
 
 
3000
      if (dst == NULL) {
 
3001
         result = File_UnlinkIfExists(src);
 
3002
 
 
3003
         if (result == -1) {
 
3004
            Log(LGPFX" %s: failed to remove %s: %s\n", __FUNCTION__,
 
3005
                src, Msg_ErrString());
 
3006
         }
 
3007
      } else {
 
3008
         result = Posix_Rename(src, dst);
 
3009
 
 
3010
         if (result == -1) {
 
3011
            int error = Err_Errno();
 
3012
 
 
3013
            if (error != ENOENT) {
 
3014
               Log(LGPFX" %s: failed to rename %s -> %s: %s\n", src, dst,
 
3015
                   __FUNCTION__, Err_Errno2String(error));
 
3016
            }
 
3017
         }
 
3018
      }
 
3019
 
 
3020
      if ((src == fileName) && (newFileName != NULL)) {
 
3021
         *newFileName = result == -1 ? NULL : strdup(dst);
 
3022
      }
 
3023
 
 
3024
      ASSERT(dst != fileName);
 
3025
      free(dst);
 
3026
      dst = src;
 
3027
   }
 
3028
}
 
3029
 
 
3030
 
 
3031
/*
 
3032
 *----------------------------------------------------------------------
 
3033
 *
 
3034
 * FileNumberCompare --
 
3035
 *
 
3036
 *      Helper function for comparing the contents of two
 
3037
 *      uint32 pointers a and b, suitable for use by qsort
 
3038
 *      to order an array of file numbers.
 
3039
 *
 
3040
 * Results:
 
3041
 *      The contents of 'a' minus the contents of 'b'.
 
3042
 *
 
3043
 * Side effects:
 
3044
 *      None.
 
3045
 */
 
3046
 
 
3047
static int
 
3048
FileNumberCompare(const void *a,  // IN:
 
3049
                  const void *b)  // IN:
 
3050
{
 
3051
   return *(uint32 *) a - *(uint32 *) b;
 
3052
}
 
3053
 
 
3054
 
 
3055
/*
 
3056
 *----------------------------------------------------------------------
 
3057
 *
 
3058
 * FileRotateByRenumber --
 
3059
 *
 
3060
 *      File rotation scheme optimized for vmfs:
 
3061
 *        1) find highest numbered file (maxNr)
 
3062
 *        2) rename <base>.<ext> to <base>-<maxNr + 1>.<ext>
 
3063
 *        3) delete (nFound - numToKeep) lowest numbered files.
 
3064
 *
 
3065
 *        Wrap around is handled incorrectly.
 
3066
 *
 
3067
 * Results:
 
3068
 *      If newFilePath is non-NULL: the new path is returned to *newFilePath
 
3069
 *      if the rotation succeeded, otherwise NULL is returned in *newFilePath.
 
3070
 *      The caller is responsible for freeing the string returned in
 
3071
 *      *newFilePath.
 
3072
 *
 
3073
 * Side effects:
 
3074
 *      Files renamed / deleted.
 
3075
 *
 
3076
 *----------------------------------------------------------------------
 
3077
 */
 
3078
 
 
3079
static void
 
3080
FileRotateByRenumber(const char *filePath,       // IN: full path to file
 
3081
                     const char *filePathNoExt,  // IN: filename w/o extension.
 
3082
                     const char *ext,            // IN: extension
 
3083
                     int n,                      // IN: number old files to keep
 
3084
                     char **newFilePath)         // OUT/OPT: new path to file
 
3085
{
 
3086
   char *baseDir = NULL, *fmtString = NULL, *baseName = NULL, *tmp;
 
3087
   char *fullPathNoExt = NULL;
 
3088
   uint32 maxNr = 0;
 
3089
   int i, nrFiles, nFound = 0;
 
3090
   char **fileList = NULL;
 
3091
   uint32 *fileNumbers = NULL;
 
3092
   int result;
 
3093
 
 
3094
   fullPathNoExt = File_FullPath(filePathNoExt);
 
3095
   if (fullPathNoExt == NULL) {
 
3096
      Log(LGPFX" %s: failed to get full path for '%s'.\n", __FUNCTION__,
 
3097
          filePathNoExt);
 
3098
      goto cleanup;
 
3099
   }
 
3100
 
 
3101
   File_GetPathName(fullPathNoExt, &baseDir, &baseName);
 
3102
   if ((baseDir[0] == '\0') || (baseName[0] == '\0')) {
 
3103
      Log(LGPFX" %s: failed to get base dir for path '%s'.\n", __FUNCTION__,
 
3104
          filePathNoExt);
 
3105
      goto cleanup;
 
3106
   }
 
3107
 
 
3108
   fmtString = Str_SafeAsprintf(NULL, "%s-%%d%s%%n", baseName, ext);
 
3109
 
 
3110
   nrFiles = File_ListDirectory(baseDir, &fileList);
 
3111
   if (nrFiles == -1) {
 
3112
      Log(LGPFX" %s: failed to read the directory '%s'.\n", __FUNCTION__,
 
3113
          baseDir);
 
3114
      goto cleanup;
 
3115
   }
 
3116
 
 
3117
   fileNumbers = Util_SafeCalloc(nrFiles, sizeof(uint32));
 
3118
 
 
3119
   for (i = 0; i < nrFiles; i++) {
 
3120
      uint32 curNr;
 
3121
      int bytesProcessed = 0;
 
3122
 
 
3123
      /*
 
3124
       * Make sure the whole file name matched what we expect for the file.
 
3125
       */
 
3126
 
 
3127
      if ((sscanf(fileList[i], fmtString, &curNr, &bytesProcessed) >= 1) &&
 
3128
          (bytesProcessed == strlen(fileList[i]))) {
 
3129
         fileNumbers[nFound++] = curNr;
 
3130
      }
 
3131
 
 
3132
      free(fileList[i]);
 
3133
   }
 
3134
 
 
3135
   if (nFound > 0) {
 
3136
      qsort(fileNumbers, nFound, sizeof(uint32), FileNumberCompare);
 
3137
      maxNr = fileNumbers[nFound - 1];
 
3138
   }
 
3139
 
 
3140
   /* rename the existing file to the next number */
 
3141
   tmp = Str_SafeAsprintf(NULL, "%s/%s-%d%s", baseDir, baseName,
 
3142
                          maxNr + 1, ext);
 
3143
 
 
3144
   result = Posix_Rename(filePath, tmp);
 
3145
 
 
3146
   if (result == -1) {
 
3147
      int error = Err_Errno();
 
3148
 
 
3149
      if (error != ENOENT) {
 
3150
         Log(LGPFX" %s: failed to rename %s -> %s failed: %s\n", __FUNCTION__,
 
3151
             filePath, tmp, Err_Errno2String(error));
 
3152
      }
 
3153
   }
 
3154
 
 
3155
   if (newFilePath != NULL) {
 
3156
      if (result == -1) {
 
3157
         *newFilePath = NULL;
 
3158
         free(tmp);
 
3159
      } else {
 
3160
         *newFilePath = tmp;
 
3161
      }
 
3162
   }
 
3163
 
 
3164
   if (nFound >= n) {
 
3165
      /* Delete the extra files. */
 
3166
      for (i = 0; i <= nFound - n; i++) {
 
3167
         tmp = Str_SafeAsprintf(NULL, "%s/%s-%d%s", baseDir, baseName,
 
3168
                                fileNumbers[i], ext);
 
3169
 
 
3170
         if (Posix_Unlink(tmp) == -1) {
 
3171
            Log(LGPFX" %s: failed to remove %s: %s\n", __FUNCTION__, tmp,
 
3172
                Msg_ErrString());
 
3173
         }
 
3174
         free(tmp);
 
3175
      }
 
3176
   }
 
3177
 
 
3178
  cleanup:
 
3179
   free(fileNumbers);
 
3180
   free(fileList);
 
3181
   free(fmtString);
 
3182
   free(baseDir);
 
3183
   free(baseName);
 
3184
   free(fullPathNoExt);
 
3185
}
 
3186
 
 
3187
 
 
3188
/*
 
3189
 *----------------------------------------------------------------------
 
3190
 *
 
3191
 * File_Rotate --
 
3192
 *
 
3193
 *      Rotate old files. The 'noRename' option is useful for filesystems
 
3194
 *      where rename is hideously expensive (*cough* vmfs).
 
3195
 *
 
3196
 * Results:
 
3197
 *      If newFileName is non-NULL: the new path is returned to
 
3198
 *      *newFileName if the rotation succeeded, otherwise NULL
 
3199
 *      is returned in *newFileName.  The caller is responsible
 
3200
 *      for freeing the string returned in *newFileName.
 
3201
 *
 
3202
 * Side effects:
 
3203
 *      Files are renamed / deleted.
 
3204
 *
 
3205
 *----------------------------------------------------------------------
 
3206
 */
 
3207
 
 
3208
void
 
3209
File_Rotate(const char *fileName,  // IN: original file
 
3210
            int n,                 // IN: number of backup files
 
3211
            Bool noRename,         // IN: don't rename all files
 
3212
            char **newFileName)    // OUT/OPT: new path to file
 
3213
{
 
3214
   const char *ext;
 
3215
   size_t baseLen;
 
3216
   char *baseName;
 
3217
 
 
3218
   if ((ext = Str_Strrchr(fileName, '.')) == NULL) {
 
3219
      ext = fileName + strlen(fileName);
 
3220
   }
 
3221
   baseLen = ext - fileName;
 
3222
 
 
3223
   /*
 
3224
    * Backup base of file name.
 
3225
    *
 
3226
    * Since the Str_Asprintf(...) doesn't like format of %.*s and crashes
 
3227
    * in Windows 2000. (Daniel Liu)
 
3228
    */
 
3229
 
 
3230
   baseName = Util_SafeStrdup(fileName);
 
3231
   baseName[baseLen] = '\0';
 
3232
 
 
3233
   if (noRename) {
 
3234
      FileRotateByRenumber(fileName, baseName, ext, n, newFileName);
 
3235
   } else {
 
3236
      FileRotateByRename(fileName, baseName, ext, n, newFileName);
 
3237
   }
 
3238
 
 
3239
   free(baseName);
 
3240
}