Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117878427
lcb.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
24 KB
Referenced Files
None
Subscribers
None
lcb.c
View Options
/* lcb.c -- replication-based backup api
*
* Copyright (c) 1994-2015 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include
<config.h>
#include
<assert.h>
#include
<errno.h>
#include
<syslog.h>
#include
<zlib.h>
#include
"lib/cyrusdb.h"
#include
"lib/cyr_lock.h"
#include
"lib/exitcodes.h"
#include
"lib/gzuncat.h"
#include
"lib/map.h"
#include
"lib/sqldb.h"
#include
"lib/util.h"
#include
"lib/xmalloc.h"
#include
"lib/xsha1.h"
#include
"lib/xstrlcat.h"
#include
"lib/xstrlcpy.h"
#include
"imap/dlist.h"
#include
"imap/global.h"
#include
"imap/imap_err.h"
#include
"backup/backup.h"
#define LIBCYRUS_BACKUP_SOURCE
/* this file is part of libcyrus_backup */
#include
"backup/lcb_internal.h"
#include
"backup/lcb_sqlconsts.h"
static
const
char
*
staging_path
=
NULL
;
EXPORTED
const
char
*
backup_get_staging_path
(
void
)
{
if
(
staging_path
)
return
staging_path
;
staging_path
=
config_getstring
(
IMAPOPT_BACKUP_STAGING_PATH
);
if
(
!
staging_path
)
staging_path
=
strconcat
(
config_getstring
(
IMAPOPT_TEMP_PATH
),
"/backup"
,
NULL
);
return
staging_path
;
}
/* remove this process's staging directory.
* will warn in syslog if the path couldn't be removed
* (e.g. if it still has files in it)
*/
EXPORTED
void
backup_cleanup_staging_path
(
void
)
{
char
buf
[
MAX_MAILBOX_PATH
];
const
char
*
base
=
backup_get_staging_path
();
int
r
;
r
=
snprintf
(
buf
,
MAX_MAILBOX_PATH
,
"%s/sync./%lu"
,
base
,
(
unsigned
long
)
getpid
());
if
(
r
>=
MAX_MAILBOX_PATH
)
{
/* path was truncated, don't try to delete it */
return
;
}
r
=
rmdir
(
buf
);
if
(
r
&&
errno
!=
ENOENT
)
syslog
(
LOG_WARNING
,
"%s rmdir %s: %m"
,
__func__
,
buf
);
}
/*
* use cases:
* - backupd needs to be able to append to data stream and update index (exclusive)
* - backupd maybe needs to create a new backup from scratch (exclusive)
* - reindex needs to gzuc data stream and rewrite index (exclusive)
* - compact needs to rewrite data stream and index (exclusive)
* - restore needs to read data stream and index (shared)
*
* with only one shared case, might as well always lock exclusively...
*/
HIDDEN
int
backup_real_open
(
struct
backup
**
backupp
,
const
char
*
data_fname
,
const
char
*
index_fname
,
enum
backup_open_reindex
reindex
,
enum
backup_open_nonblock
nonblock
,
enum
backup_open_create
create
)
{
struct
backup
*
backup
=
xzmalloc
(
sizeof
*
backup
);
int
r
;
backup
->
fd
=
-1
;
backup
->
data_fname
=
xstrdup
(
data_fname
);
backup
->
index_fname
=
xstrdup
(
index_fname
);
int
open_flags
=
O_RDWR
|
O_APPEND
;
switch
(
create
)
{
case
BACKUP_OPEN_CREATE_EXCL
:
open_flags
|=
O_EXCL
;
/* fall thru */
case
BACKUP_OPEN_CREATE
:
open_flags
|=
O_CREAT
;
/* */
case
BACKUP_OPEN_NOCREATE
:
break
;
}
while
(
backup
->
fd
==
-1
)
{
struct
stat
sbuf1
,
sbuf2
;
int
fd
=
open
(
backup
->
data_fname
,
open_flags
,
S_IRUSR
|
S_IWUSR
);
if
(
fd
<
0
)
{
switch
(
errno
)
{
case
EEXIST
:
r
=
IMAP_MAILBOX_EXISTS
;
break
;
case
ENOENT
:
r
=
IMAP_MAILBOX_NONEXISTENT
;
break
;
default
:
syslog
(
LOG_ERR
,
"IOERROR: open %s: %m"
,
backup
->
data_fname
);
r
=
IMAP_IOERROR
;
break
;
}
goto
error
;
}
r
=
lock_setlock
(
fd
,
/*excl*/
1
,
nonblock
,
backup
->
data_fname
);
if
(
r
)
{
if
(
errno
==
EWOULDBLOCK
||
errno
==
EAGAIN
)
{
r
=
IMAP_MAILBOX_LOCKED
;
}
else
{
syslog
(
LOG_ERR
,
"IOERROR: lock_setlock: %s: %m"
,
backup
->
data_fname
);
r
=
IMAP_IOERROR
;
}
goto
error
;
}
r
=
fstat
(
fd
,
&
sbuf1
);
if
(
!
r
)
r
=
stat
(
backup
->
data_fname
,
&
sbuf2
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: (f)stat %s: %m"
,
backup
->
data_fname
);
r
=
IMAP_IOERROR
;
goto
error
;
}
if
(
sbuf1
.
st_ino
==
sbuf2
.
st_ino
)
{
backup
->
fd
=
fd
;
break
;
}
close
(
fd
);
}
if
(
reindex
)
{
// when reindexing, we want to move the old index out of the way
// and create a new, empty one -- while holding the lock
char
oldindex_fname
[
PATH_MAX
];
snprintf
(
oldindex_fname
,
sizeof
(
oldindex_fname
),
"%s.%ld"
,
backup
->
index_fname
,
(
int64_t
)
time
(
NULL
));
r
=
rename
(
backup
->
index_fname
,
oldindex_fname
);
if
(
r
&&
errno
!=
ENOENT
)
{
syslog
(
LOG_ERR
,
"IOERROR: rename %s %s: %m"
,
backup
->
index_fname
,
oldindex_fname
);
r
=
IMAP_IOERROR
;
goto
error
;
}
backup
->
oldindex_fname
=
xstrdup
(
oldindex_fname
);
}
else
{
// if there's data in the data file but the index file is empty
// or doesn't exist, insist on a reindex before opening
struct
stat
data_statbuf
;
r
=
fstat
(
backup
->
fd
,
&
data_statbuf
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstat %s: %m"
,
backup
->
data_fname
);
r
=
IMAP_IOERROR
;
goto
error
;
}
if
(
data_statbuf
.
st_size
>
0
)
{
struct
stat
index_statbuf
;
r
=
stat
(
backup
->
index_fname
,
&
index_statbuf
);
if
(
r
&&
errno
!=
ENOENT
)
{
syslog
(
LOG_ERR
,
"IOERROR: stat %s: %m"
,
backup
->
index_fname
);
r
=
IMAP_IOERROR
;
goto
error
;
}
if
((
r
&&
errno
==
ENOENT
)
||
index_statbuf
.
st_size
==
0
)
{
syslog
(
LOG_ERR
,
"reindex needed: %s"
,
backup
->
index_fname
);
r
=
IMAP_MAILBOX_BADFORMAT
;
goto
error
;
}
}
}
backup
->
db
=
sqldb_open
(
backup
->
index_fname
,
backup_index_initsql
,
backup_index_version
,
backup_index_upgrade
);
if
(
!
backup
->
db
)
{
r
=
IMAP_INTERNAL
;
// FIXME what does it mean to error here?
goto
error
;
}
*
backupp
=
backup
;
return
0
;
error
:
backup_close
(
&
backup
);
if
(
!
r
)
r
=
IMAP_INTERNAL
;
return
r
;
}
EXPORTED
int
backup_open
(
struct
backup
**
backupp
,
const
mbname_t
*
mbname
,
enum
backup_open_nonblock
nonblock
,
enum
backup_open_create
create
)
{
struct
buf
data_fname
=
BUF_INITIALIZER
;
struct
buf
index_fname
=
BUF_INITIALIZER
;
int
r
=
backup_get_paths
(
mbname
,
&
data_fname
,
&
index_fname
,
create
);
/* XXX convert CYRUSDB return code to IMAP */
if
(
r
)
goto
done
;
r
=
backup_real_open
(
backupp
,
buf_cstring
(
&
data_fname
),
buf_cstring
(
&
index_fname
),
BACKUP_OPEN_NOREINDEX
,
nonblock
,
create
);
if
(
r
)
goto
done
;
done
:
buf_free
(
&
data_fname
);
buf_free
(
&
index_fname
);
return
r
;
}
/* Uses mkstemp() to create a new, unique, backup path for the given user.
*
* On success, the file is not unlinked, presuming that it will shortly be
* used for storing backup data. This also ensures its uniqueness remains:
* this function won't generate the same value again as long as the previous
* file is intact, so there's no user-rename race.
*
* If out_fd is non-NULL, on successful return it will contain an open, locked
* file descriptor for the new file. In this case the caller must unlock
* and close the fd.
*
* On error, returns NULL and logs to syslog, without touching out_fd.
*/
static
const
char
*
_make_path
(
const
mbname_t
*
mbname
,
int
*
out_fd
)
{
static
char
pathresult
[
PATH_MAX
];
const
char
*
userid
=
mbname_userid
(
mbname
);
const
char
*
partition
=
partlist_backup_select
();
const
char
*
ret
=
NULL
;
if
(
!
partition
)
{
syslog
(
LOG_ERR
,
"unable to make backup path for %s: "
"couldn't select partition"
,
userid
);
return
NULL
;
}
char
hash_buf
[
2
];
char
*
template
=
strconcat
(
partition
,
"/"
,
dir_hash_b
(
userid
,
config_fulldirhash
,
hash_buf
),
"/"
,
userid
,
"_XXXXXX"
,
NULL
);
/* make sure the destination directory exists */
cyrus_mkdir
(
template
,
0755
);
int
fd
=
mkstemp
(
template
);
if
(
fd
<
0
)
{
syslog
(
LOG_ERR
,
"unable to make backup path for %s: %m"
,
userid
);
goto
error
;
}
/* lock it -- even if we're just going to immediately unlock it */
int
r
=
lock_setlock
(
fd
,
/*excl*/
1
,
/*nb*/
0
,
template
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"unable to obtain exclusive lock on just-created file %s: %m"
,
template
);
/* don't unlink it, we don't know what's in it */
goto
error
;
}
/* save the path */
if
(
strlcpy
(
pathresult
,
template
,
sizeof
(
pathresult
))
>=
sizeof
(
pathresult
))
{
syslog
(
LOG_ERR
,
"unable to make backup path for %s: path too long"
,
userid
);
unlink
(
template
);
goto
error
;
}
ret
=
pathresult
;
/* save or close the fd */
if
(
out_fd
)
*
out_fd
=
fd
;
else
close
(
fd
);
free
(
template
);
return
ret
;
error
:
if
(
fd
>=
0
)
close
(
fd
);
free
(
template
);
return
NULL
;
}
EXPORTED
int
backup_get_paths
(
const
mbname_t
*
mbname
,
struct
buf
*
data_fname
,
struct
buf
*
index_fname
,
enum
backup_open_create
create
)
{
struct
db
*
backups_db
=
NULL
;
struct
txn
*
tid
=
NULL
;
int
r
=
backupdb_open
(
&
backups_db
,
&
tid
);
if
(
r
)
return
r
;
const
char
*
userid
=
mbname_userid
(
mbname
);
const
char
*
backup_path
=
NULL
;
size_t
path_len
=
0
;
r
=
cyrusdb_fetch
(
backups_db
,
userid
,
strlen
(
userid
),
&
backup_path
,
&
path_len
,
&
tid
);
if
(
r
==
CYRUSDB_NOTFOUND
&&
create
)
{
syslog
(
LOG_DEBUG
,
"%s not found in backups.db, creating new record"
,
userid
);
backup_path
=
_make_path
(
mbname
,
NULL
);
if
(
!
backup_path
)
{
r
=
CYRUSDB_INTERNAL
;
goto
done
;
}
path_len
=
strlen
(
backup_path
);
r
=
cyrusdb_create
(
backups_db
,
userid
,
strlen
(
userid
),
backup_path
,
path_len
,
&
tid
);
if
(
r
)
cyrusdb_abort
(
backups_db
,
tid
);
else
r
=
cyrusdb_commit
(
backups_db
,
tid
);
tid
=
NULL
;
/* if we didn't store it in the database successfully, trash the file,
* it won't be used */
if
(
r
)
unlink
(
backup_path
);
}
if
(
r
)
goto
done
;
if
(
path_len
==
0
)
{
syslog
(
LOG_DEBUG
,
"unexpectedly got zero length backup path for user %s"
,
userid
);
r
=
CYRUSDB_INTERNAL
;
goto
done
;
}
if
(
data_fname
)
buf_setmap
(
data_fname
,
backup_path
,
path_len
);
if
(
index_fname
)
{
buf_setmap
(
index_fname
,
backup_path
,
path_len
);
buf_appendcstr
(
index_fname
,
".index"
);
}
done
:
if
(
backups_db
)
{
if
(
tid
)
cyrusdb_abort
(
backups_db
,
tid
);
cyrusdb_close
(
backups_db
);
}
return
r
;
}
/*
* If index_fname is NULL, it will be automatically derived from data_fname
*/
EXPORTED
int
backup_open_paths
(
struct
backup
**
backupp
,
const
char
*
data_fname
,
const
char
*
index_fname
,
enum
backup_open_nonblock
nonblock
,
enum
backup_open_create
create
)
{
if
(
index_fname
)
return
backup_real_open
(
backupp
,
data_fname
,
index_fname
,
BACKUP_OPEN_NOREINDEX
,
nonblock
,
create
);
char
*
tmp
=
strconcat
(
data_fname
,
".index"
,
NULL
);
int
r
=
backup_real_open
(
backupp
,
data_fname
,
tmp
,
BACKUP_OPEN_NOREINDEX
,
nonblock
,
create
);
free
(
tmp
);
return
r
;
}
EXPORTED
int
backup_close
(
struct
backup
**
backupp
)
{
struct
backup
*
backup
=
*
backupp
;
*
backupp
=
NULL
;
gzFile
gzfile
=
NULL
;
int
r1
=
0
,
r2
=
0
;
if
(
backup
->
append_state
)
{
if
(
backup
->
append_state
->
mode
!=
BACKUP_APPEND_INACTIVE
)
r1
=
backup_append_end
(
backup
,
NULL
);
gzfile
=
backup
->
append_state
->
gzfile
;
free
(
backup
->
append_state
);
backup
->
append_state
=
NULL
;
}
if
(
backup
->
db
)
r2
=
sqldb_close
(
&
backup
->
db
);
if
(
backup
->
oldindex_fname
)
{
if
(
r2
)
{
/* something went wrong closing the new index, put the old one back */
rename
(
backup
->
oldindex_fname
,
backup
->
index_fname
);
}
else
{
if
(
!
config_getswitch
(
IMAPOPT_BACKUP_KEEP_PREVIOUS
))
{
unlink
(
backup
->
oldindex_fname
);
}
}
}
if
(
backup
->
fd
>=
0
)
{
/* closing the file will also release the lock on the fd */
if
(
gzfile
)
gzclose_w
(
gzfile
);
else
close
(
backup
->
fd
);
}
if
(
backup
->
index_fname
)
free
(
backup
->
index_fname
);
if
(
backup
->
data_fname
)
free
(
backup
->
data_fname
);
if
(
backup
->
oldindex_fname
)
free
(
backup
->
oldindex_fname
);
free
(
backup
);
return
r1
?
r1
:
r2
;
}
EXPORTED
int
backup_unlink
(
struct
backup
**
backupp
)
{
struct
backup
*
backup
=
*
backupp
;
unlink
(
backup
->
index_fname
);
unlink
(
backup
->
data_fname
);
return
backup_close
(
backupp
);
}
EXPORTED
const
char
*
backup_get_data_fname
(
const
struct
backup
*
backup
)
{
return
backup
->
data_fname
;
}
EXPORTED
const
char
*
backup_get_index_fname
(
const
struct
backup
*
backup
)
{
return
backup
->
index_fname
;
}
EXPORTED
int
backup_stat
(
const
struct
backup
*
backup
,
struct
stat
*
data_statp
,
struct
stat
*
index_statp
)
{
struct
stat
data_statbuf
,
index_statbuf
;
int
r
;
r
=
fstat
(
backup
->
fd
,
&
data_statbuf
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstat %s: %m"
,
backup
->
data_fname
);
return
IMAP_IOERROR
;
}
r
=
stat
(
backup
->
index_fname
,
&
index_statbuf
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: stat %s: %m"
,
backup
->
index_fname
);
return
IMAP_IOERROR
;
}
if
(
data_statp
)
memcpy
(
data_statp
,
&
data_statbuf
,
sizeof
data_statbuf
);
if
(
index_statp
)
memcpy
(
index_statp
,
&
index_statbuf
,
sizeof
index_statbuf
);
return
0
;
}
static
ssize_t
_prot_fill_cb
(
unsigned
char
*
buf
,
size_t
len
,
void
*
rock
)
{
struct
gzuncat
*
gzuc
=
(
struct
gzuncat
*
)
rock
;
int
r
=
gzuc_read
(
gzuc
,
buf
,
len
);
if
(
r
<
0
)
syslog
(
LOG_ERR
,
"IOERROR: gzuc_read returned %i"
,
r
);
if
(
r
<
-1
)
errno
=
EIO
;
return
r
;
}
EXPORTED
int
backup_reindex
(
const
char
*
name
,
enum
backup_open_nonblock
nonblock
,
int
verbose
,
FILE
*
out
)
{
struct
buf
data_fname
=
BUF_INITIALIZER
;
struct
buf
index_fname
=
BUF_INITIALIZER
;
struct
backup
*
backup
=
NULL
;
int
r
;
buf_printf
(
&
data_fname
,
"%s"
,
name
);
buf_printf
(
&
index_fname
,
"%s.index"
,
name
);
r
=
backup_real_open
(
&
backup
,
buf_cstring
(
&
data_fname
),
buf_cstring
(
&
index_fname
),
BACKUP_OPEN_REINDEX
,
nonblock
,
BACKUP_OPEN_NOCREATE
);
buf_free
(
&
index_fname
);
buf_free
(
&
data_fname
);
if
(
r
)
return
r
;
struct
gzuncat
*
gzuc
=
gzuc_new
(
backup
->
fd
);
time_t
prev_member_ts
=
-1
;
struct
buf
cmd
=
BUF_INITIALIZER
;
while
(
gzuc
&&
!
gzuc_eof
(
gzuc
))
{
gzuc_member_start
(
gzuc
);
off_t
member_offset
=
gzuc_member_offset
(
gzuc
);
if
(
verbose
)
fprintf
(
out
,
"
\n
found chunk at offset %jd
\n\n
"
,
member_offset
);
struct
protstream
*
member
=
prot_readcb
(
_prot_fill_cb
,
gzuc
);
prot_setisclient
(
member
,
1
);
/* don't sync literals */
// FIXME stricter timestamp sequence checks
time_t
member_start_ts
=
-1
;
time_t
member_end_ts
=
-1
;
time_t
ts
=
-1
;
while
(
1
)
{
struct
dlist
*
dl
=
NULL
;
int
c
=
parse_backup_line
(
member
,
&
ts
,
&
cmd
,
&
dl
);
if
(
c
==
EOF
)
{
const
char
*
error
=
prot_error
(
member
);
if
(
error
&&
0
!=
strcmp
(
error
,
PROT_EOF_STRING
))
{
syslog
(
LOG_ERR
,
"IOERROR: %s: error reading chunk at offset %jd, byte %i: %s
\n
"
,
name
,
member_offset
,
prot_bytes_in
(
member
),
error
);
if
(
out
)
fprintf
(
out
,
"error reading chunk at offset %jd, byte %i: %s
\n
"
,
member_offset
,
prot_bytes_in
(
member
),
error
);
r
=
IMAP_IOERROR
;
}
member_end_ts
=
ts
;
break
;
}
if
(
member_start_ts
==
-1
)
{
if
(
prev_member_ts
!=
-1
&&
prev_member_ts
>
ts
)
{
fatal
(
"member timestamp older than previous"
,
EC_DATAERR
);
}
member_start_ts
=
ts
;
char
file_sha1
[
2
*
SHA1_DIGEST_LENGTH
+
1
];
sha1_file
(
backup
->
fd
,
backup
->
data_fname
,
member_offset
,
file_sha1
);
backup_real_append_start
(
backup
,
member_start_ts
,
member_offset
,
file_sha1
,
1
,
0
);
}
else
if
(
member_start_ts
>
ts
)
fatal
(
"line timestamp older than previous"
,
EC_DATAERR
);
if
(
strcmp
(
buf_cstring
(
&
cmd
),
"APPLY"
)
!=
0
)
{
dlist_unlink_files
(
dl
);
dlist_free
(
&
dl
);
continue
;
}
ucase
(
dl
->
name
);
r
=
backup_append
(
backup
,
dl
,
&
ts
,
BACKUP_APPEND_NOFLUSH
);
if
(
r
)
{
// FIXME do something
syslog
(
LOG_ERR
,
"backup_append returned %d
\n
"
,
r
);
fprintf
(
out
,
"backup_append returned %d
\n
"
,
r
);
}
dlist_unlink_files
(
dl
);
dlist_free
(
&
dl
);
}
if
(
backup
->
append_state
&&
backup
->
append_state
->
mode
)
backup_real_append_end
(
backup
,
member_end_ts
);
prot_free
(
member
);
gzuc_member_end
(
gzuc
,
NULL
);
prev_member_ts
=
member_start_ts
;
}
buf_free
(
&
cmd
);
if
(
verbose
)
fprintf
(
out
,
"reached end of file
\n
"
);
gzuc_free
(
&
gzuc
);
backup_close
(
&
backup
);
return
r
;
}
struct
_rename_meta
{
const
char
*
userid
;
char
*
fname
;
char
*
ext_ptr
;
int
fd
;
};
#define RENAME_META_INITIALIZER { NULL, NULL, NULL, -1 }
static
void
_rename_meta_set_fname
(
struct
_rename_meta
*
meta
,
const
char
*
data_fname
)
{
size_t
len
=
strlen
(
data_fname
)
+
strlen
(
".index"
)
+
1
;
meta
->
fname
=
xmalloc
(
len
);
snprintf
(
meta
->
fname
,
len
,
"%s.index"
,
data_fname
);
meta
->
ext_ptr
=
strrchr
(
meta
->
fname
,
'.'
);
*
meta
->
ext_ptr
=
'\0'
;
}
static
void
_rename_meta_fini
(
struct
_rename_meta
*
meta
)
{
if
(
meta
->
fname
)
free
(
meta
->
fname
);
memset
(
meta
,
0
,
sizeof
(
*
meta
));
meta
->
fd
=
-1
;
}
EXPORTED
int
backup_rename
(
const
mbname_t
*
old_mbname
,
const
mbname_t
*
new_mbname
)
{
struct
db
*
backups_db
=
NULL
;
struct
txn
*
tid
=
NULL
;
struct
_rename_meta
old
=
RENAME_META_INITIALIZER
;
struct
_rename_meta
new
=
RENAME_META_INITIALIZER
;
old
.
userid
=
mbname_userid
(
old_mbname
);
new
.
userid
=
mbname_userid
(
new_mbname
);
const
char
*
path
;
size_t
path_len
;
int
r
;
/* bail out if the names are the same */
if
(
strcmp
(
old
.
userid
,
new
.
userid
)
==
0
)
return
0
;
/* exclusively open backups database */
r
=
backupdb_open
(
&
backups_db
,
&
tid
);
if
(
r
)
goto
error
;
// FIXME log
/* make sure new_mbname isn't already in use */
r
=
cyrusdb_fetch
(
backups_db
,
new
.
userid
,
strlen
(
new
.
userid
),
&
path
,
&
path_len
,
&
tid
);
if
(
!
r
)
r
=
CYRUSDB_EXISTS
;
if
(
r
)
goto
error
;
// FIXME log
/* locate (but not create) backup for old_mbname, open and lock it */
r
=
cyrusdb_fetch
(
backups_db
,
old
.
userid
,
strlen
(
old
.
userid
),
&
path
,
&
path_len
,
&
tid
);
if
(
r
)
goto
error
;
// FIXME log
_rename_meta_set_fname
(
&
old
,
path
);
old
.
fd
=
open
(
old
.
fname
,
O_RDWR
|
O_APPEND
,
/* no O_CREAT */
S_IRUSR
|
S_IWUSR
);
if
(
old
.
fd
<
0
)
{
syslog
(
LOG_ERR
,
"IOERROR: open %s: %m"
,
old
.
fname
);
r
=
-1
;
goto
error
;
}
/* non-blocking, to avoid deadlock */
r
=
lock_setlock
(
old
.
fd
,
/*excl*/
1
,
/*nb*/
1
,
old
.
fname
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: lock_setlock: %s: %m"
,
old
.
fname
);
goto
error
;
}
/* make a path for new_mbname, open and lock it */
path
=
_make_path
(
new_mbname
,
&
new
.
fd
);
if
(
!
path
)
goto
error
;
// FIXME log
_rename_meta_set_fname
(
&
new
,
path
);
/* copy old data and index files to new paths */
r
=
cyrus_copyfile
(
old
.
fname
,
new
.
fname
,
0
);
if
(
r
)
goto
error
;
// FIXME log
*
old
.
ext_ptr
=
*
new
.
ext_ptr
=
'.'
;
r
=
cyrus_copyfile
(
old
.
fname
,
new
.
fname
,
0
);
*
old
.
ext_ptr
=
*
new
.
ext_ptr
=
'\0'
;
if
(
r
)
goto
error
;
// FIXME log
/* files exist under both names now. try to update the database */
r
=
cyrusdb_create
(
backups_db
,
new
.
userid
,
strlen
(
new
.
userid
),
new
.
fname
,
strlen
(
new
.
fname
),
&
tid
);
if
(
r
)
goto
error
;
// FIXME log
r
=
cyrusdb_delete
(
backups_db
,
old
.
userid
,
strlen
(
old
.
userid
),
&
tid
,
0
);
if
(
r
)
goto
error
;
// FIXME log
r
=
cyrusdb_commit
(
backups_db
,
tid
);
tid
=
NULL
;
if
(
r
)
goto
error
;
// FIXME log
/* database update succeeded. unlink old names */
unlink
(
old
.
fname
);
*
old
.
ext_ptr
=
'.'
;
unlink
(
old
.
fname
);
*
old
.
ext_ptr
=
'\0'
;
/* unlock and close backup files */
lock_unlock
(
new
.
fd
,
new
.
fname
);
close
(
new
.
fd
);
lock_unlock
(
old
.
fd
,
old
.
fname
);
close
(
old
.
fd
);
/* close backups database */
cyrusdb_close
(
backups_db
);
/* clean up and exit */
_rename_meta_fini
(
&
old
);
_rename_meta_fini
(
&
new
);
return
0
;
error
:
/* we didn't finish, so unlink the new filenames if we got that far */
if
(
new
.
fname
)
{
unlink
(
new
.
fname
);
*
new
.
ext_ptr
=
'.'
;
unlink
(
new
.
fname
);
*
new
.
ext_ptr
=
'\0'
;
}
/* close the files if we got that far (also unlocks) */
if
(
new
.
fd
!=
-1
)
close
(
new
.
fd
);
if
(
old
.
fd
!=
-1
)
close
(
old
.
fd
);
/* abort any transaction and close the database */
if
(
backups_db
)
{
if
(
tid
)
cyrusdb_abort
(
backups_db
,
tid
);
cyrusdb_close
(
backups_db
);
}
/* clean up and exit */
_rename_meta_fini
(
&
old
);
_rename_meta_fini
(
&
new
);
return
r
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Apr 5, 10:10 PM (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831149
Default Alt Text
lcb.c (24 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline