Fix directory creation to use low level methods which consider the presence

of an existing directory to be an error, rather than the NSFileManager methods
that consider it a success.  The logic of distributed locking requires us to
fail if a directory already exists.
This commit is contained in:
Richard Frith-Macdonald 2017-06-17 06:52:05 +01:00
parent a5a2e85423
commit ed721bf4da
4 changed files with 80 additions and 29 deletions

View file

@ -1,3 +1,15 @@
2017-06-16 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSDistributedLock.m: Change code to create lock directory
directly rather than via the NSFileManager methods since Wolfgang
pointed out that those methods consider a pre-existing directory to
have been successfully created, but the distributed locking logic
supposes that an attempt to create an existing directory will fail.
* Tests/base/NSDistributedLock:
* Tests/base/NSDistributedLock/TestInfo:
* Tests/base/NSDistributedLock/basic.m:
Add a few simple testcases for NSDistributedLock.
2017-05-19 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSFileHandle.h:

View file

@ -41,6 +41,14 @@
# include <fcntl.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
static NSFileManager *mgr = nil;
@ -230,37 +238,36 @@ static NSFileManager *mgr = nil;
attributesToSet = [NSMutableDictionary dictionaryWithCapacity: 1];
[attributesToSet setObject: [NSNumber numberWithUnsignedInt: 0755]
forKey: NSFilePosixPermissions];
locked = [mgr createDirectoryAtPath: _lockPath
withIntermediateDirectories: YES
attributes: attributesToSet
error: NULL];
/* We must not use the NSFileManager directory creation methods,
* since they consider the presence of an existing directory a
* success, and we need to know if we can actually create a new
* directory.
*/
#if defined(_WIN32)
{
const unichar *lpath;
lpath = [mgr fileSystemRepresentationWithPath: _lockPath];
locked = (CreateDirectoryW(lpath, 0) != FALSE) ? YES : NO;
}
#else
{
const char *lpath;
lpath = [mgr fileSystemRepresentationWithPath: _lockPath];
locked = (mkdir(lpath, 0777) == 0) ? YES : NO;
}
#endif
if (NO == locked)
{
NSLog(@"Failed to create lock directory '%@' - %@",
_lockPath, [NSError _last]);
}
else
{
BOOL dir;
/* We expect the directory creation to have failed because
* it already exists as another processes lock.
* If the directory doesn't exist, then either the other
* process has removed it's lock (and we can retry)
* or we have a severe problem!
*/
if ([mgr fileExistsAtPath: _lockPath isDirectory: &dir] == NO)
{
locked = [mgr createDirectoryAtPath: _lockPath
withIntermediateDirectories: YES
attributes: attributesToSet
error: NULL];
if (NO == locked)
{
NSLog(@"Failed to create lock directory '%@' - %@",
_lockPath, [NSError _last]);
}
}
}
if (YES == locked)
{
[mgr changeFileAttributes: attributesToSet atPath: _lockPath];
attributes = [mgr fileAttributesAtPath: _lockPath
traverseLink: YES];
if (attributes == nil)

View file

View file

@ -0,0 +1,32 @@
#import "ObjectTesting.h"
#import <Foundation/NSDistributedLock.h>
#import <Foundation/NSFileManager.h>
int main()
{
START_SET("basic")
NSString *path;
NSDistributedLock *lock1;
NSDistributedLock *lock2;
test_NSObject(@"NSDistributedLock",
[NSArray arrayWithObject: [NSDistributedLock new]]);
path = [[NSFileManager defaultManager] currentDirectoryPath];
path = [path stringByAppendingPathComponent: @"MyLock"];
lock1 = [NSDistributedLock lockWithPath: path];
lock2 = [NSDistributedLock lockWithPath: path];
PASS(lock1 != lock2, "locks with the same path differ");
PASS(YES == [lock1 tryLock], "we can lock the first lock");
PASS(NO == [lock2 tryLock], "the locks are exclusive");
[lock1 unlock];
PASS(YES == [lock2 tryLock], "unlocking first lock allows second lock");
END_SET("basic")
return 0;
}