Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
John McEleney
/
mailutils
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit
e8674181
...
e86741813eb537dc19491a9f69f8ebc12815ac0e
authored
2007-03-07 19:56:26 +0000
by
Sergey Poznyakoff
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Rewrite
1 parent
525ec621
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
437 additions
and
320 deletions
include/mailutils/locker.h
mailbox/locker.c
include/mailutils/locker.h
View file @
e867418
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2005 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001, 2005
, 2007
Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
...
...
@@ -47,34 +47,53 @@ enum mu_locker_set_mode
/* mu_locker_create() flags */
#define MU_LOCKER_SIMPLE 0x00
/* Locker types */
#define MU_LOCKER_TYPE_DOTLOCK 0
#define MU_LOCKER_TYPE_EXTERNAL 1
/* Use an external program to lock the file. This is necessary
for programs having permission to access a file, but do not
have write permission on the directory that contains that file. */
#define MU_LOCKER_TYPE_KERNEL 2
/* Use kernel locking (flock, lockf or ioctl) */
#define MU_LOCKER_TYPE_NULL 3
/* Special locker type: means no lock. This is to be used with
temporary mailboxes stored in memory. */
#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
#define MU_LOCKER_IS_TYPE(f,t) (MU_LOCKER_FLAG_TO_TYPE(f) == (t))
#define MU_LOCKER_SET_TYPE(f,t) ((f) = MU_LOCKER_TYPE_TO_FLAG(t) | MU_LOCKER_OPTIONS(f))
#define MU_LOCKER_TYPE_MASK 0xff00
#define MU_LOCKER_OPTION_MASK 0x00ff
#define MU_LOCKER_OPTIONS(f) ((f) & MU_LOCKER_OPTION_MASK)
#define MU_LOCKER_NULL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_NULL)
#define MU_LOCKER_DOTLOCK MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_DOTLOCK)
#define MU_LOCKER_EXTERNAL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_EXTERNAL)
#define MU_LOCKER_KERNEL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_KERNEL)
/* Options */
#define MU_LOCKER_SIMPLE 0x0000
/* Just try and dotlock the file, not the default because its usually
better to retry. */
#define MU_LOCKER_RETRY 0x01
#define MU_LOCKER_RETRY 0x0
00
1
/* This requests that we loop retries times, sleeping retry_sleep
seconds in between trying to obtain the lock before failing with
MU_LOCK_CONFLICT. */
#define MU_LOCKER_TIME 0x02
#define MU_LOCKER_TIME 0x0
00
2
/* This mode checks the last update time of the lock, then removes
it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this,
then the servers better periodically update the lock on the
file... do they? */
#define MU_LOCKER_PID 0x04
#define MU_LOCKER_PID 0x0
00
4
/* PID locking is only useful for programs that aren't using
an external dotlocker, non-setgid programs will use a dotlocker,
which locks and exits imediately. This is a protection against
a server crashing, it's not generally useful. */
#define MU_LOCKER_EXTERNAL 0x08
/* Use an external program to lock the file. This is necessary
for programs having permission to access a file, but do not
have write permission on the directory that contains that file. */
#define MU_LOCKER_NULL 0x10
/* Special locker type: means no lock. This is to be used with
temporary mailboxes stored in memory. */
#define MU_LOCKER_KERNEL 0x20
/* Use kernel locking (flock, lockf or ioctl) */
#define MU_LOCKER_DEFAULT (MU_LOCKER_RETRY)
#define MU_LOCKER_DEFAULT (MU_LOCKER_
DOTLOCK | MU_LOCKER_
RETRY)
/* Use these flags for as the default locker flags (the default defaults
* to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the
...
...
@@ -104,6 +123,14 @@ extern int mu_locker_get_retries (mu_locker_t, int*);
extern
int
mu_locker_get_retry_sleep
(
mu_locker_t
,
int
*
);
extern
int
mu_locker_get_external
(
mu_locker_t
,
char
**
);
enum
mu_locker_mode
{
mu_lck_shr
,
/* Shared (advisory) lock */
mu_lck_exc
,
/* Exclusive lock */
mu_lck_opt
/* Optional lock = shared, if the locker supports it, no
locking otherwise */
};
extern
int
mu_locker_lock
(
mu_locker_t
);
extern
int
mu_locker_touchlock
(
mu_locker_t
);
extern
int
mu_locker_unlock
(
mu_locker_t
);
...
...
mailbox/locker.c
View file @
e867418
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2005, 2006 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001, 2005, 2006,
2007 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
...
...
@@ -49,7 +50,8 @@
struct
_mu_locker
{
int
refcnt
;
unsigned
refcnt
;
/* Number of times mu_locker_lock was called */
enum
mu_locker_mode
mode
;
/* Current locking mode (if refcnt > 0) */
char
*
file
;
int
flags
;
...
...
@@ -73,20 +75,121 @@ struct _mu_locker
}
data
;
};
/* Assert that we're managing the refcnt and fd correctly, either
* we have a lock, and the fd is valid, or refcnt is 0 and fd is -1.
* And refcnt can never be less than 0.
*/
#define INVARIANT(l) assert((l)->refcnt >= 0);
static
void
expire_stale_lock
(
mu_locker_t
lock
);
static
int
stat_check
(
const
char
*
file
,
int
fd
,
int
links
);
static
int
check_file_permissions
(
const
char
*
file
);
static
int
lock_external
(
mu_locker_t
l
,
int
lock
);
static
int
_locker_lock_dotlock
(
mu_locker_t
lock
);
static
int
_locker_unlock_dotlock
(
mu_locker_t
lock
);
static
int
_locker_lock_kernel
(
mu_locker_t
lock
);
static
int
_locker_unlock_kernel
(
mu_locker_t
lock
);
#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
struct
locker_tab
{
int
(
*
init
)
(
mu_locker_t
);
void
(
*
destroy
)
(
mu_locker_t
);
int
(
*
prelock
)
(
mu_locker_t
);
int
(
*
lock
)
(
mu_locker_t
,
enum
mu_locker_mode
);
int
(
*
unlock
)
(
mu_locker_t
);
};
static
int
init_dotlock
(
mu_locker_t
);
static
void
destroy_dotlock
(
mu_locker_t
);
static
int
lock_dotlock
(
mu_locker_t
,
enum
mu_locker_mode
);
static
int
unlock_dotlock
(
mu_locker_t
);
static
int
init_external
(
mu_locker_t
);
static
void
destroy_external
(
mu_locker_t
);
static
int
lock_external
(
mu_locker_t
,
enum
mu_locker_mode
);
static
int
unlock_external
(
mu_locker_t
);
static
int
init_kernel
(
mu_locker_t
);
static
int
lock_kernel
(
mu_locker_t
,
enum
mu_locker_mode
);
static
int
unlock_kernel
(
mu_locker_t
);
static
int
prelock_common
(
mu_locker_t
);
static
struct
locker_tab
locker_tab
[]
=
{
{
init_dotlock
,
destroy_dotlock
,
prelock_common
,
lock_dotlock
,
unlock_dotlock
},
{
init_external
,
destroy_external
,
prelock_common
,
lock_external
,
unlock_external
},
{
init_kernel
,
NULL
,
NULL
,
lock_kernel
,
unlock_kernel
},
{
NULL
,
NULL
,
NULL
,
NULL
,
NULL
}
};
#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
static
int
stat_check
(
const
char
*
file
,
int
fd
,
int
links
)
{
struct
stat
fn_stat
;
struct
stat
fd_stat
;
int
err
=
0
;
int
localfd
=
-
1
;
if
(
fd
==
-
1
)
{
localfd
=
open
(
file
,
O_RDONLY
);
if
(
localfd
==
-
1
)
return
errno
;
fd
=
localfd
;
}
/* We should always be able to stat a valid fd, so this
is an error condition. */
if
(
lstat
(
file
,
&
fn_stat
)
||
fstat
(
fd
,
&
fd_stat
))
err
=
errno
;
else
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
#define CHK(X) if(X) err = EINVAL
CHK
(
!
S_ISREG
(
fn_stat
.
st_mode
));
CHK
(
!
S_ISREG
(
fd_stat
.
st_mode
));
CHK
(
fn_stat
.
st_nlink
!=
links
);
CHK
(
fn_stat
.
st_dev
!=
fd_stat
.
st_dev
);
CHK
(
fn_stat
.
st_ino
!=
fd_stat
.
st_ino
);
CHK
(
fn_stat
.
st_mode
!=
fd_stat
.
st_mode
);
CHK
(
fn_stat
.
st_nlink
!=
fd_stat
.
st_nlink
);
CHK
(
fn_stat
.
st_uid
!=
fd_stat
.
st_uid
);
CHK
(
fn_stat
.
st_gid
!=
fd_stat
.
st_gid
);
CHK
(
fn_stat
.
st_rdev
!=
fd_stat
.
st_rdev
);
#undef CHK
}
if
(
localfd
!=
-
1
)
close
(
localfd
);
return
err
;
}
static
int
check_file_permissions
(
const
char
*
file
)
{
int
fd
=
-
1
;
int
err
=
0
;
if
((
fd
=
open
(
file
,
O_RDONLY
))
==
-
1
)
return
errno
==
ENOENT
?
0
:
errno
;
err
=
stat_check
(
file
,
fd
,
1
);
close
(
fd
);
fd
=
-
1
;
if
(
err
)
{
if
(
err
==
EINVAL
)
err
=
MU_ERR_LOCK_BAD_FILE
;
return
err
;
}
return
0
;
}
static
int
prelock_common
(
mu_locker_t
locker
)
{
/* Check if we are trying to lock a regular file, with a link count
of 1, that we have permission to read, etc., or don't lock it. */
return
check_file_permissions
(
locker
->
file
);
}
static
int
mu_locker_default_flags
=
MU_LOCKER_DEFAULT
;
static
time_t
mu_locker_retry_timeout
=
MU_LOCKER_RETRY_SLEEP
;
...
...
@@ -143,115 +246,36 @@ mu_locker_set_default_external_program (char *path)
}
int
mu_locker_
create
(
mu_locker_t
*
plocker
,
const
char
*
filename_
,
int
flags
)
mu_locker_
set_flags
(
mu_locker_t
locker
,
int
flags
)
{
mu_locker_t
l
;
char
filename
[
_POSIX_PATH_MAX
];
int
err
=
0
;
unsigned
otype
,
ntype
;
if
(
plocker
==
NULL
)
return
MU_ERR_
OUT_PT
R_NULL
;
if
(
!
locker
)
return
MU_ERR_
LOCKE
R_NULL
;
if
(
filename_
==
NULL
)
otype
=
MU_LOCKER_TYPE
(
locker
);
if
(
otype
>=
MU_LOCKER_NTYPES
)
return
EINVAL
;
ntype
=
MU_LOCKER_FLAG_TO_TYPE
(
flags
);
if
(
ntype
>=
MU_LOCKER_NTYPES
)
return
EINVAL
;
if
((
err
=
mu_unroll_symlink
(
filename
,
sizeof
(
filename
),
filename_
)))
return
err
;
l
=
calloc
(
1
,
sizeof
(
*
l
));
if
(
l
==
NULL
)
return
ENOMEM
;
l
->
file
=
strdup
(
filename
);
if
(
l
->
file
==
NULL
)
{
free
(
l
);
return
ENOMEM
;
}
if
(
strcmp
(
filename
,
"/dev/null"
)
==
0
)
l
->
flags
=
MU_LOCKER_NULL
;
else
if
(
flags
)
l
->
flags
=
flags
;
else
l
->
flags
=
mu_locker_default_flags
;
l
->
expire_time
=
mu_locker_expire_timeout
;
l
->
retries
=
mu_locker_retry_count
;
l
->
retry_sleep
=
mu_locker_retry_timeout
;
/* Initialize locker-type-specific data */
if
(
l
->
flags
&
MU_LOCKER_EXTERNAL
)
{
if
(
!
(
l
->
data
.
external
.
name
=
strdup
(
mu_locker_external_program
?
mu_locker_external_program
:
MU_LOCKER_EXTERNAL_PROGRAM
)))
if
(
ntype
!=
otype
)
{
mu_locker_destroy
(
&
l
);
return
ENOMEM
;
}
}
else
if
(
!
(
l
->
flags
&
MU_LOCKER_KERNEL
))
{
l
->
data
.
dot
.
dotlock
=
malloc
(
strlen
(
l
->
file
)
+
5
/*strlen(".lock")*/
+
1
);
int
rc
;
if
(
!
l
->
data
.
dot
.
dotlock
)
if
(
locker_tab
[
otype
].
destroy
)
locker_tab
[
otype
].
destroy
(
locker
);
locker
->
flags
=
flags
;
if
(
locker_tab
[
otype
].
init
)
{
free
(
l
->
file
);
free
(
l
);
return
ENOMEM
;
rc
=
locker_tab
[
otype
].
init
(
locker
);
if
(
rc
)
locker
->
flags
=
MU_LOCKER_NULL
;
return
rc
;
}
sprintf
(
l
->
data
.
dot
.
dotlock
,
"%s.lock"
,
l
->
file
);
}
INVARIANT
(
l
);
*
plocker
=
l
;
return
0
;
}
void
_locker_destroy_private
(
mu_locker_t
locker
)
{
if
(
locker
)
{
if
(
locker
->
flags
&
MU_LOCKER_EXTERNAL
)
free
(
locker
->
data
.
external
.
name
);
else
if
(
locker
->
flags
&
MU_LOCKER_KERNEL
)
/* nothing */
;
else
{
free
(
locker
->
data
.
dot
.
dotlock
);
locker
->
data
.
dot
.
dotlock
=
NULL
;
free
(
locker
->
data
.
dot
.
nfslock
);
locker
->
data
.
dot
.
nfslock
=
NULL
;
}
}
}
void
mu_locker_destroy
(
mu_locker_t
*
plocker
)
{
if
(
plocker
&&
*
plocker
)
{
_locker_destroy_private
(
*
plocker
);
free
((
*
plocker
)
->
file
);
free
(
*
plocker
);
*
plocker
=
NULL
;
}
}
int
mu_locker_set_flags
(
mu_locker_t
locker
,
int
flags
)
{
if
(
!
locker
)
return
MU_ERR_LOCKER_NULL
;
locker
->
flags
=
flags
;
return
0
;
...
...
@@ -306,7 +330,7 @@ mu_locker_set_external (mu_locker_t locker, const char* program)
if
(
!
locker
)
return
MU_ERR_LOCKER_NULL
;
if
(
!
(
locker
->
flags
&
MU_LOCKER_EXTERNAL
)
)
if
(
MU_LOCKER_TYPE
(
locker
)
!=
MU_LOCKER_TYPE_EXTERNAL
)
return
EINVAL
;
/* program can be NULL */
...
...
@@ -329,7 +353,7 @@ mu_locker_get_flags (mu_locker_t locker, int *flags)
if
(
!
locker
)
return
MU_ERR_LOCKER_NULL
;
if
(
!
flags
)
if
(
!
flags
)
return
EINVAL
;
*
flags
=
locker
->
flags
;
...
...
@@ -380,144 +404,162 @@ mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
}
int
mu_locker_
lock
(
mu_locker_t
lock
)
mu_locker_
create
(
mu_locker_t
*
plocker
,
const
char
*
fname
,
int
flags
)
{
int
rc
;
int
retries
=
1
;
unsigned
type
;
mu_locker_t
l
;
char
filename
[
_POSIX_PATH_MAX
];
int
err
=
0
;
if
(
lock
==
NULL
)
if
(
plocker
==
NULL
)
return
MU_ERR_OUT_PTR_NULL
;
if
(
fname
==
NULL
)
return
EINVAL
;
if
(
lock
->
flags
==
MU_LOCKER_NULL
)
return
0
;
if
(
(
err
=
mu_unroll_symlink
(
filename
,
sizeof
(
filename
),
fname
))
)
return
err
;
INVARIANT
(
lock
)
/* Is the lock already applied? */
if
(
lock
->
refcnt
>
0
)
l
=
calloc
(
1
,
sizeof
(
*
l
));
if
(
l
==
NULL
)
return
ENOMEM
;
l
->
file
=
strdup
(
filename
);
if
(
l
->
file
==
NULL
)
{
lock
->
refcnt
++
;
return
0
;
free
(
l
)
;
return
ENOMEM
;
}
if
(
access
(
lock
->
file
,
W_OK
))
if
(
strcmp
(
filename
,
"/dev/null"
)
==
0
)
l
->
flags
=
MU_LOCKER_NULL
;
else
if
(
flags
)
l
->
flags
=
flags
;
else
l
->
flags
=
mu_locker_default_flags
;
l
->
expire_time
=
mu_locker_expire_timeout
;
l
->
retries
=
mu_locker_retry_count
;
l
->
retry_sleep
=
mu_locker_retry_timeout
;
type
=
MU_LOCKER_TYPE
(
l
);
if
(
type
>=
MU_LOCKER_NTYPES
)
{
/* There is no use trying to lock the file if we are not
allowed to write to it */
_locker_destroy_private
(
lock
);
lock
->
flags
|=
MU_LOCKER_NULL
;
return
0
;
free
(
l
->
file
);
return
EINVAL
;
}
/* Check we are trying to lock a regular file, with a link count
of 1, that we have permission to read, etc., or don't lock it. */
if
((
rc
=
check_file_permissions
(
lock
->
file
)))
return
rc
;
/* Do the lock with an external program, if requested. */
if
(
lock
->
flags
&
MU_LOCKER_EXTERNAL
)
return
lock_external
(
lock
,
1
);
else
if
(
!
(
lock
->
flags
&
MU_LOCKER_KERNEL
))
/* Initialize locker-type-specific data */
err
=
locker_tab
[
type
].
init
?
locker_tab
[
type
].
init
(
l
)
:
0
;
if
(
err
)
{
char
*
tmp
,
*
p
;
mu_locker_destroy
(
&
l
);
return
err
;
}
tmp
=
strdup
(
lock
->
file
);
if
(
!
tmp
)
return
ENOMEM
;
*
plocker
=
l
;
strcpy
(
tmp
,
lock
->
file
);
p
=
strrchr
(
tmp
,
'/'
);
if
(
!
p
)
return
0
;
}
void
mu_locker_destroy
(
mu_locker_t
*
plocker
)
{
if
(
plocker
&&
*
plocker
)
{
free
(
tmp
);
tmp
=
strdup
(
"."
);
if
(
!
tmp
)
return
ENOMEM
;
unsigned
type
=
MU_LOCKER_TYPE
(
*
plocker
);
if
(
type
<
MU_LOCKER_NTYPES
)
{
if
(
locker_tab
[
type
].
destroy
)
locker_tab
[
type
].
destroy
(
*
plocker
);
free
((
*
plocker
)
->
file
);
free
(
*
plocker
);
*
plocker
=
NULL
;
}
else
*
p
=
0
;
}
}
if
(
access
(
tmp
,
W_OK
))
int
_mu_locker_lock
(
mu_locker_t
lock
,
enum
mu_locker_mode
mode
)
{
int
rc
;
unsigned
type
;
unsigned
retries
=
1
;
if
(
lock
==
NULL
||
(
type
=
MU_LOCKER_TYPE
(
lock
))
>=
MU_LOCKER_NTYPES
)
return
EINVAL
;
if
(
locker_tab
[
type
].
prelock
&&
(
rc
=
locker_tab
[
type
].
prelock
(
lock
)))
return
rc
;
/* Is the lock already applied? */
if
(
lock
->
refcnt
>
0
)
{
/* Fallback to kernel locking */
_locker_destroy_private
(
lock
);
lock
->
flags
|=
MU_LOCKER_KERNEL
;
}
free
(
tmp
);
lock
->
refcnt
++
;
if
(
mode
==
lock
->
mode
)
return
0
;
}
lock
->
mode
=
mode
;
if
(
lock
->
flags
&
MU_LOCKER_RETRY
)
{
retries
=
lock
->
retries
;
}
if
(
locker_tab
[
type
].
lock
)
{
while
(
retries
--
)
{
if
(
lock
->
flags
&
MU_LOCKER_KERNEL
)
rc
=
_locker_lock_kernel
(
lock
);
else
rc
=
_locker_lock_dotlock
(
lock
);
rc
=
locker_tab
[
type
].
lock
(
lock
,
mode
);
if
(
rc
==
EAGAIN
&&
retries
)
{
sleep
(
lock
->
retry_sleep
);
else
if
(
rc
)
return
rc
;
else
/* rc == 0 */
break
;
continue
;
}
lock
->
refcnt
=
1
;
if
(
rc
==
0
)
lock
->
refcnt
++
;
return
0
;
break
;
}
}
else
rc
=
0
;
return
rc
;
}
int
mu_locker_
touch
lock
(
mu_locker_t
lock
)
mu_locker_lock
(
mu_locker_t
lock
)
{
if
(
!
lock
)
return
MU_ERR_LOCKER_NULL
;
if
(
lock
->
flags
==
MU_LOCKER_NULL
)
return
0
;
if
(
lock
->
refcnt
>
0
)
return
utime
(
lock
->
data
.
dot
.
dotlock
,
NULL
);
return
MU_ERR_LOCK_NOT_HELD
;
return
_mu_locker_lock
(
lock
,
mu_lck_exc
);
}
int
mu_locker_unlock
(
mu_locker_t
lock
)
{
int
rc
=
0
;
unsigned
type
;
if
(
!
lock
)
return
MU_ERR_LOCKER_NULL
;
if
(
lock
->
flags
==
MU_LOCKER_NULL
)
return
0
;
if
(
lock
->
refcnt
==
0
)
return
MU_ERR_LOCK_NOT_HELD
;
/* Do the lock with an external program, if requested. */
if
(
lock
->
flags
&
MU_LOCKER_EXTERNAL
)
return
lock_external
(
lock
,
0
);
if
(
lock
->
refcnt
>
1
)
{
lock
->
refcnt
--
;
return
0
;
}
if
((
rc
=
check_file_permissions
(
lock
->
file
)))
return
rc
;
if
(
lock
->
flags
&
MU_LOCKER_KERNEL
)
rc
=
_locker_unlock_kernel
(
lock
);
else
rc
=
_locker_unlock_dotlock
(
lock
);
if
(
--
lock
->
refcnt
>
0
)
return
0
;
if
(
rc
==
0
)
lock
->
refcnt
=
0
;
type
=
MU_LOCKER_TYPE
(
lock
);
if
(
locker_tab
[
type
].
unlock
)
rc
=
locker_tab
[
type
].
unlock
(
lock
);
else
rc
=
0
;
return
rc
;
}
...
...
@@ -525,21 +567,17 @@ mu_locker_unlock (mu_locker_t lock)
int
mu_locker_remove_lock
(
mu_locker_t
lock
)
{
int
err
;
if
(
!
lock
)
return
MU_ERR_LOCKER_NULL
;
if
(
lock
->
flags
==
MU_LOCKER_NULL
)
return
0
;
/* Force the reference count to 1 to unlock the file. */
lock
->
refcnt
=
1
;
err
=
mu_locker_unlock
(
lock
);
return
err
;
return
mu_locker_unlock
(
lock
);
}
#define DOTLOCK_SUFFIX ".lock"
/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
static
void
expire_stale_lock
(
mu_locker_t
lock
)
...
...
@@ -587,80 +625,60 @@ expire_stale_lock (mu_locker_t lock)
}
static
int
stat_check
(
const
char
*
file
,
int
fd
,
int
links
)
init_dotlock
(
mu_locker_t
locker
)
{
struct
stat
fn_stat
;
struct
stat
fd_stat
;
int
err
=
0
;
int
localfd
=
-
1
;
char
*
tmp
,
*
p
;
if
(
fd
==
-
1
)
{
localfd
=
open
(
file
,
O_RDONLY
);
/* Make sure the spool directory is writable */
tmp
=
strdup
(
locker
->
file
);
if
(
!
tmp
)
return
ENOMEM
;
if
(
localfd
==
-
1
)
return
errno
;
fd
=
localfd
;
strcpy
(
tmp
,
locker
->
file
);
p
=
strrchr
(
tmp
,
'/'
);
if
(
!
p
)
{
free
(
tmp
);
tmp
=
strdup
(
"."
);
if
(
!
tmp
)
return
ENOMEM
;
}
/* We should always be able to stat a valid fd, so this
is an error condition. */
if
(
lstat
(
file
,
&
fn_stat
)
||
fstat
(
fd
,
&
fd_stat
))
err
=
errno
;
else
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
#define CHK(X) if(X) err = EINVAL
CHK
(
!
S_ISREG
(
fn_stat
.
st_mode
));
CHK
(
!
S_ISREG
(
fd_stat
.
st_mode
));
CHK
(
fn_stat
.
st_nlink
!=
links
);
CHK
(
fn_stat
.
st_dev
!=
fd_stat
.
st_dev
);
CHK
(
fn_stat
.
st_ino
!=
fd_stat
.
st_ino
);
CHK
(
fn_stat
.
st_mode
!=
fd_stat
.
st_mode
);
CHK
(
fn_stat
.
st_nlink
!=
fd_stat
.
st_nlink
);
CHK
(
fn_stat
.
st_uid
!=
fd_stat
.
st_uid
);
CHK
(
fn_stat
.
st_gid
!=
fd_stat
.
st_gid
);
CHK
(
fn_stat
.
st_rdev
!=
fd_stat
.
st_rdev
);
*
p
=
0
;
#undef CHK
if
(
access
(
tmp
,
W_OK
))
{
/* Fallback to kernel locking */
free
(
tmp
);
return
mu_locker_set_flags
(
locker
,
MU_LOCKER_KERNEL
|
MU_LOCKER_OPTIONS
(
locker
->
flags
));
}
if
(
localfd
!=
-
1
)
close
(
localfd
);
return
err
;
}
static
int
check_file_permissions
(
const
char
*
file
)
{
int
fd
=
-
1
;
int
err
=
0
;
free
(
tmp
);
if
((
fd
=
open
(
file
,
O_RDONLY
))
==
-
1
)
return
errno
==
ENOENT
?
0
:
errno
;
locker
->
data
.
dot
.
dotlock
=
malloc
(
strlen
(
locker
->
file
)
+
sizeof
(
DOTLOCK_SUFFIX
))
;
err
=
stat_check
(
file
,
fd
,
1
);
close
(
fd
);
fd
=
-
1
;
if
(
err
)
{
if
(
err
==
EINVAL
)
err
=
MU_ERR_LOCK_BAD_FILE
;
return
err
;
}
if
(
!
locker
->
data
.
dot
.
dotlock
)
return
ENOMEM
;
strcpy
(
locker
->
data
.
dot
.
dotlock
,
locker
->
file
);
strcat
(
locker
->
data
.
dot
.
dotlock
,
DOTLOCK_SUFFIX
);
return
0
;
}
static
void
destroy_dotlock
(
mu_locker_t
locker
)
{
free
(
locker
->
data
.
dot
.
dotlock
);
}
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 256
#endif
/* Locker-specific lock/unlock functions */
int
_locker_lock_dotlock
(
mu_locker_t
lock
)
static
int
lock_dotlock
(
mu_locker_t
locker
,
enum
mu_locker_mode
mode
)
{
char
host
[
MAXHOSTNAMELEN
+
1
]
=
"localhost"
;
char
pid
[
11
];
/* 10 is strlen(2^32 = 4294967296) */
...
...
@@ -669,14 +687,14 @@ _locker_lock_dotlock (mu_locker_t lock)
int
err
=
0
;
int
fd
;
if
(
lock
->
data
.
dot
.
nfslock
)
if
(
lock
er
->
data
.
dot
.
nfslock
)
{
unlink
(
lock
->
data
.
dot
.
nfslock
);
free
(
lock
->
data
.
dot
.
nfslock
);
lock
->
data
.
dot
.
nfslock
=
0
;
unlink
(
lock
er
->
data
.
dot
.
nfslock
);
free
(
lock
er
->
data
.
dot
.
nfslock
);
lock
er
->
data
.
dot
.
nfslock
=
0
;
}
expire_stale_lock
(
lock
);
expire_stale_lock
(
lock
er
);
/* build the NFS hitching-post to the lock file */
...
...
@@ -689,20 +707,20 @@ _locker_lock_dotlock (mu_locker_t lock)
snprintf
(
pid
,
sizeof
(
pid
),
"%lu"
,
(
unsigned
long
)
getpid
());
pid
[
sizeof
(
pid
)
-
1
]
=
0
;
sz
=
strlen
(
lock
->
file
)
+
1
/* "." */
sz
=
strlen
(
lock
er
->
file
)
+
1
/* "." */
+
strlen
(
pid
)
+
1
/* "." */
+
strlen
(
now
)
+
1
/* "." */
+
strlen
(
host
)
+
1
;
lock
->
data
.
dot
.
nfslock
=
malloc
(
sz
);
lock
er
->
data
.
dot
.
nfslock
=
malloc
(
sz
);
if
(
!
lock
->
data
.
dot
.
nfslock
)
if
(
!
lock
er
->
data
.
dot
.
nfslock
)
return
ENOMEM
;
snprintf
(
lock
->
data
.
dot
.
nfslock
,
sz
,
"%s.%s.%s.%s"
,
lock
->
file
,
pid
,
now
,
host
);
snprintf
(
lock
er
->
data
.
dot
.
nfslock
,
sz
,
"%s.%s.%s.%s"
,
locker
->
file
,
pid
,
now
,
host
);
fd
=
open
(
lock
->
data
.
dot
.
nfslock
,
fd
=
open
(
lock
er
->
data
.
dot
.
nfslock
,
O_WRONLY
|
O_CREAT
|
O_EXCL
,
LOCKFILE_ATTR
);
if
(
fd
==
-
1
)
{
...
...
@@ -714,53 +732,53 @@ _locker_lock_dotlock (mu_locker_t lock)
close
(
fd
);
/* Try to link to the lockfile. */
if
(
link
(
lock
->
data
.
dot
.
nfslock
,
lock
->
data
.
dot
.
dotlock
)
==
-
1
)
if
(
link
(
lock
er
->
data
.
dot
.
nfslock
,
locker
->
data
.
dot
.
dotlock
)
==
-
1
)
{
unlink
(
lock
->
data
.
dot
.
nfslock
);
unlink
(
lock
er
->
data
.
dot
.
nfslock
);
if
(
errno
==
EEXIST
)
return
MU_ERR_LOCK_CONFLICT
;
return
errno
;
}
if
((
fd
=
open
(
lock
->
data
.
dot
.
dotlock
,
O_RDWR
))
==
-
1
)
if
((
fd
=
open
(
lock
er
->
data
.
dot
.
dotlock
,
O_RDWR
))
==
-
1
)
{
unlink
(
lock
->
data
.
dot
.
nfslock
);
unlink
(
lock
er
->
data
.
dot
.
nfslock
);
return
errno
;
}
err
=
stat_check
(
lock
->
data
.
dot
.
nfslock
,
fd
,
2
);
err
=
stat_check
(
lock
er
->
data
.
dot
.
nfslock
,
fd
,
2
);
if
(
err
)
{
unlink
(
lock
->
data
.
dot
.
nfslock
);
unlink
(
lock
er
->
data
.
dot
.
nfslock
);
if
(
err
==
EINVAL
)
return
MU_ERR_LOCK_BAD_LOCK
;
return
errno
;
}
unlink
(
lock
->
data
.
dot
.
nfslock
);
unlink
(
lock
er
->
data
.
dot
.
nfslock
);
/* If no errors, we have the lock. */
assert
(
lock
->
refcnt
==
0
);
/*
FIXME:
If no errors, we have the lock. */
assert
(
lock
er
->
refcnt
==
0
);
if
(
lock
->
flags
&
MU_LOCKER_PID
)
if
(
lock
er
->
flags
&
MU_LOCKER_PID
)
{
char
buf
[
16
];
sprintf
(
buf
,
"%ld"
,
(
long
)
getpid
());
write
(
fd
,
buf
,
strlen
(
buf
));
}
close
(
fd
);
close
(
fd
);
return
0
;
}
int
_locker_unlock_dotlock
(
mu_locker_t
lock
)
static
int
unlock_dotlock
(
mu_locker_t
locker
)
{
if
(
unlink
(
lock
->
data
.
dot
.
dotlock
)
==
-
1
)
if
(
unlink
(
lock
er
->
data
.
dot
.
dotlock
)
==
-
1
)
{
int
err
=
errno
;
if
(
err
==
ENOENT
)
{
lock
->
refcnt
=
0
;
lock
er
->
refcnt
=
0
;
/*FIXME?*/
err
=
MU_ERR_LOCK_NOT_HELD
;
return
err
;
}
...
...
@@ -770,16 +788,56 @@ _locker_unlock_dotlock (mu_locker_t lock)
}
int
_locker_lock_kernel
(
mu_locker_t
lock
)
mu_locker_touchlock
(
mu_locker_t
lock
)
{
if
(
!
lock
)
return
MU_ERR_LOCKER_NULL
;
if
(
MU_LOCKER_TYPE
(
lock
)
!=
MU_LOCKER_TYPE_DOTLOCK
)
return
0
;
if
(
lock
->
refcnt
>
0
)
return
utime
(
lock
->
data
.
dot
.
dotlock
,
NULL
);
return
MU_ERR_LOCK_NOT_HELD
;
}
/* Kernel locking */
static
int
init_kernel
(
mu_locker_t
locker
)
{
return
0
;
}
static
int
lock_kernel
(
mu_locker_t
locker
,
enum
mu_locker_mode
mode
)
{
int
fd
;
struct
flock
fl
;
fd
=
open
(
lock
->
file
,
O_RDWR
);
switch
(
mode
)
{
case
mu_lck_shr
:
case
mu_lck_opt
:
mode
=
O_RDONLY
;
fl
.
l_type
=
F_RDLCK
;
break
;
case
mu_lck_exc
:
mode
=
O_RDWR
;
fl
.
l_type
=
F_WRLCK
;
break
;
default
:
return
EINVAL
;
}
fd
=
open
(
locker
->
file
,
O_RDWR
);
if
(
fd
==
-
1
)
return
errno
;
lock
->
data
.
kernel
.
fd
=
fd
;
fl
.
l_type
=
F_WRLCK
;
lock
er
->
data
.
kernel
.
fd
=
fd
;
fl
.
l_whence
=
SEEK_SET
;
fl
.
l_start
=
0
;
fl
.
l_len
=
0
;
/* Lock entire file */
...
...
@@ -796,8 +854,8 @@ _locker_lock_kernel (mu_locker_t lock)
return
0
;
}
int
_locker_unlock_kernel
(
mu_locker_t
lock
)
static
int
unlock_kernel
(
mu_locker_t
locker
)
{
struct
flock
fl
;
...
...
@@ -805,7 +863,7 @@ _locker_unlock_kernel (mu_locker_t lock)
fl
.
l_whence
=
SEEK_SET
;
fl
.
l_start
=
0
;
fl
.
l_len
=
0
;
/* Unlock entire file */
if
(
fcntl
(
lock
->
data
.
kernel
.
fd
,
F_SETLK
,
&
fl
))
if
(
fcntl
(
lock
er
->
data
.
kernel
.
fd
,
F_SETLK
,
&
fl
))
{
#ifdef EACCESS
if
(
errno
==
EACCESS
)
...
...
@@ -815,17 +873,33 @@ _locker_unlock_kernel (mu_locker_t lock)
return
EAGAIN
;
return
errno
;
}
close
(
lock
->
data
.
kernel
.
fd
);
close
(
lock
er
->
data
.
kernel
.
fd
);
return
0
;
}
static
int
init_external
(
mu_locker_t
locker
)
{
if
(
!
(
locker
->
data
.
external
.
name
=
strdup
(
mu_locker_external_program
?
mu_locker_external_program
:
MU_LOCKER_EXTERNAL_PROGRAM
)))
return
ENOMEM
;
return
0
;
}
static
void
destroy_external
(
mu_locker_t
locker
)
{
free
(
locker
->
data
.
external
.
name
);
}
/*
Estimate 1 decimal digit per 3 bits, + 1 for round off.
*/
#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
static
int
lock_external
(
mu_locker_t
l
,
int
lock
)
external_locker
(
mu_locker_t
l
,
int
lock
)
{
int
err
=
0
;
char
*
av
[
6
];
...
...
@@ -836,6 +910,7 @@ lock_external (mu_locker_t l, int lock)
assert
(
l
);
assert
(
l
->
flags
&
MU_LOCKER_EXTERNAL
);
/* FIXME */
assert
(
lock
==
!
l
->
refcnt
);
/* lock is true, refcnt is 0 or lock is false and refcnt is 1 */
...
...
@@ -856,10 +931,8 @@ lock_external (mu_locker_t l, int lock)
av
[
ac
++
]
=
aretry
;
}
if
(
lock
==
0
)
{
if
(
!
lock
)
av
[
ac
++
]
=
"-u"
;
}
av
[
ac
++
]
=
l
->
file
;
...
...
@@ -879,19 +952,24 @@ lock_external (mu_locker_t l, int lock)
case
127
:
err
=
MU_ERR_LOCK_EXT_FAIL
;
break
;
case
MU_DL_EX_OK
:
err
=
0
;
l
->
refcnt
=
lock
;
break
;
case
MU_DL_EX_NEXIST
:
err
=
MU_ERR_LOCK_NOT_HELD
;
break
;
case
MU_DL_EX_EXIST
:
err
=
MU_ERR_LOCK_CONFLICT
;
break
;
case
MU_DL_EX_PERM
:
err
=
EPERM
;
break
;
default
:
case
MU_DL_EX_ERROR
:
err
=
MU_ERR_LOCK_EXT_ERR
;
...
...
@@ -902,3 +980,15 @@ lock_external (mu_locker_t l, int lock)
return
err
;
}
static
int
lock_external
(
mu_locker_t
locker
,
enum
mu_locker_mode
mode
)
{
return
external_locker
(
locker
,
1
);
}
static
int
unlock_external
(
mu_locker_t
locker
)
{
return
external_locker
(
locker
,
0
);
}
...
...
Please
register
or
sign in
to post a comment