BackupPC-XS
view release on metacpan or search on metacpan
bpc_poolWrite.c view on Meta::CPAN
if ( bpc_poolWrite_createPoolDir(info, &info->digest) ) return;
/*
* If originally present, make sure the zero-length file is still there (and still
* zero-length), and the open slot is still open. If not, it probably means someone
* beat us to it, and we should re-do the whole pool matching to see if the newly
* added pool file now matches.
*/
if ( info->digestExtZeroLen >= 0 ) {
bpc_digest_append_ext(&info->digest, info->digestExtZeroLen);
bpc_digest_md52path(poolPath, info->compress, &info->digest);
if ( stat(poolPath, &st) || st.st_size != 0 ) {
redo = 1;
}
}
if ( !redo ) {
bpc_digest_append_ext(&info->digest, info->digestExtOpen);
bpc_digest_md52path(poolPath, info->compress, &info->digest);
if ( !stat(poolPath, &st) ) {
redo = 1;
}
}
/*
* Try to insert the new file at the zero-length file slot (if present).
*/
if ( !redo && info->digestExtZeroLen >= 0 ) {
char lockFile[BPC_MAXPATHLEN];
int lockFd;
/*
* We can replace a zero-length file, but only via locking to
* avoid race conditions. Since the hardlinking code below doesn't
* use a lock, we can't remove the file and use a hardlink
* because of race conditions - another process might be
* inserting with the same digest and grab the slot.
*
* So we make sure we have exclusive access via a lock file,
* check that the file is still zero-length, and then rename
* the file. If that fails then we redo everything.
*/
bpc_digest_append_ext(&info->digest, info->digestExtZeroLen);
bpc_digest_md52path(poolPath, info->compress, &info->digest);
if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_poolWrite_addToPool: replacing empty pool file %s with %s\n", poolPath, fileName);
snprintf(lockFile, BPC_MAXPATHLEN, "%s.lock", poolPath);
lockFd = bpc_lockRangeFile(lockFile, 0, 1, 1);
/*
* If we don't have the lock, or the file is no longer zero length, or the rename fails,
* then try again.
*/
if ( lockFd < 0 || stat(poolPath, &st) || st.st_size != 0 || rename(fileName, poolPath) ) {
if ( BPC_LogLevel >= 5 ) {
bpc_logMsgf("bpc_poolWrite_addToPool: lock/rename failed: need to repeat write (lockFd = %d, size = %lu, errno = %d)\n",
lockFd, (unsigned long)st.st_size, errno);
}
if ( lockFd >= 0 ) {
bpc_unlockRangeFile(lockFd);
}
unlink(lockFile);
redo = 1;
} else {
chmod(poolPath, 0444);
stat(poolPath, &st);
info->retValue = v3PoolFile ? 2 : 0;
info->poolFileSize = st.st_size;
bpc_unlockRangeFile(lockFd);
unlink(lockFile);
return;
}
}
/*
* Now try to link the file to the new empty slot at the end
*/
if ( !redo ) {
int linkOk, statOk;
ino_t fileIno, poolIno;
/*
* Since this is a new slot, there is no need to do locking since
* the link or open operations below are atomic/exclusive.
*
* First try to hardlink to the empty pool file slot
*/
bpc_digest_append_ext(&info->digest, info->digestExtOpen);
bpc_digest_md52path(poolPath, info->compress, &info->digest);
if ( stat(fileName, &st) ) {
info->errorCnt++;
bpc_logErrf("bpc_poolWrite_addToPool: can't stat %s\n", fileName);
return;
}
fileIno = st.st_ino;
linkOk = !link(fileName, poolPath);
if ( !(statOk = !stat(poolPath, &st)) ) linkOk = 0;
poolIno = st.st_ino;
if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_poolWrite_addToPool: link %s -> %s (linkOk = %d, statOk = %d, ino = %lu/%lu)\n",
poolPath, fileName, linkOk, statOk, (unsigned long)fileIno, (unsigned long)poolIno);
/*
* make sure the link really worked by checking inode numbers
* TODO: test these different cases.
*/
if ( statOk && fileIno == poolIno ) {
/*
* remove the original file and return
*/
unlink(fileName);
chmod(poolPath, 0444);
info->retValue = v3PoolFile ? 2 : 0;
info->poolFileSize = st.st_size;
return;
}
/*
* Something failed. If the stat failed, the hardlink failure wasn't due
* to another file being added by someone else. Perhaps the cpool is
* split across multiple file systems?
*/
if ( !statOk ) {
/*
* The hardlink failed. This could be due to hitting the hardlink
* limit, or the fact that fileName and poolPath are on different
* file systems, or the fileName didn't get written.
* Just copy the file instead (assuming fileName got written).
*/
bpc_poolWrite_copyToPool(info, poolPath, fileName);
return;
}
}
/*
* We need to redo the pool write, since it appears someone else has added
* a pool file with the same digest.
*/
bpc_poolWrite_repeatPoolWrite(info, fileName);
}
/*
* Safely remove the o+x permission that marks a file for future deletion.
* Similar locking is done by BackupPC_refCountUpdate so we can avoid any
* race conditions with the file actually being deleted.
*
* Returns 0 on success.
*/
int bpc_poolWrite_unmarkPendingDelete(char *poolPath)
{
char lockFile[BPC_MAXPATHLEN], *p;
STRUCT_STAT st;
int lockFd;
/*
* The lock file is in the first level of pool sub directories - one level
* up from the full path. So we need to find the 2nd last '/'.
*/
snprintf(lockFile, BPC_MAXPATHLEN, "%s", poolPath);
if ( !(p = strrchr(lockFile, '/')) ) return -1;
*p = '\0';
if ( !(p = strrchr(lockFile, '/')) ) return -1;
snprintf(p + 1, BPC_MAXPATHLEN - (p + 1 - lockFile), "%s", "LOCK");
if ( (lockFd = bpc_lockRangeFile(lockFile, 0, 1, 1)) < 0 ) return -1;
if ( !stat(poolPath, &st) && !chmod(poolPath, st.st_mode & ~S_IXOTH & ~S_IFMT) ) {
if ( BPC_LogLevel >= 7 ) bpc_logMsgf("bpc_poolWrite_unmarkPendingDelete(%s) succeeded\n", poolPath);
bpc_unlockRangeFile(lockFd);
return 0;
} else {
bpc_logErrf("bpc_poolWrite_unmarkPendingDelete(%s) failed; errno = %d\n", poolPath, errno);
bpc_unlockRangeFile(lockFd);
return -1;
}
}
( run in 0.583 second using v1.01-cache-2.11-cpan-39bf76dae61 )