mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
ITS#5408 part 2 - filesystem I/O, file error handling:
- Start moving file handling near the top - move_file(), ldif_tempname(). - Rename get_entry_for_fd() -> ldif_read_entry() and move open() into it. Rewrite slurp_file() as ldif_read_file(). Just stat() if output param==NULL. - Rewrite ldif_write_entry(). Add LDAP_DEBUG_TRACE output.
This commit is contained in:
parent
69a6d8ca2f
commit
b00aa30d37
@ -48,6 +48,9 @@ struct ldif_info {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define mkdir(a,b) mkdir(a)
|
#define mkdir(a,b) mkdir(a)
|
||||||
|
#define move_file(from, to) (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
|
||||||
|
#else
|
||||||
|
#define move_file(from, to) rename(from, to)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -215,48 +218,99 @@ dn2path( BackendDB *be, struct berval *dn, struct berval *res )
|
|||||||
assert( res->bv_len <= len );
|
assert( res->bv_len <= len );
|
||||||
}
|
}
|
||||||
|
|
||||||
static char * slurp_file(int fd) {
|
/* Make temporary filename pattern for mkstemp() based on dnpath. */
|
||||||
int read_chars_total = 0;
|
static char *
|
||||||
int read_chars = 0;
|
ldif_tempname( const struct berval *dnpath )
|
||||||
int entry_size;
|
{
|
||||||
char * entry;
|
static const char suffix[] = "XXXXXX";
|
||||||
char * entry_pos;
|
ber_len_t len = dnpath->bv_len;
|
||||||
struct stat st;
|
char *name = SLAP_MALLOC( len + sizeof( suffix ) );
|
||||||
|
|
||||||
fstat(fd, &st);
|
if ( name != NULL ) {
|
||||||
entry_size = st.st_size;
|
AC_MEMCPY( name, dnpath->bv_val, len );
|
||||||
entry = ch_malloc( entry_size+1 );
|
strcpy( name + len, suffix );
|
||||||
entry_pos = entry;
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
read_chars = read(fd, (void *) entry_pos, entry_size - read_chars_total);
|
|
||||||
if(read_chars == -1) {
|
|
||||||
SLAP_FREE(entry);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(read_chars == 0) {
|
|
||||||
entry[read_chars_total] = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
read_chars_total += read_chars;
|
|
||||||
entry_pos += read_chars;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return entry;
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a file, or stat() it if datap == NULL. Allocate and fill *datap.
|
||||||
|
* Return LDAP_SUCCESS, LDAP_NO_SUCH_OBJECT (no such file), or another error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ldif_read_file( const char *path, char **datap )
|
||||||
|
{
|
||||||
|
int rc, fd, len;
|
||||||
|
int res = -1; /* 0:success, <0:error, >0:file too big/growing. */
|
||||||
|
struct stat st;
|
||||||
|
char *data = NULL, *ptr;
|
||||||
|
|
||||||
|
if ( datap == NULL ) {
|
||||||
|
res = stat( path, &st );
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
fd = open( path, O_RDONLY );
|
||||||
|
if ( fd >= 0 ) {
|
||||||
|
if ( fstat( fd, &st ) == 0 ) {
|
||||||
|
if ( st.st_size > INT_MAX - 2 ) {
|
||||||
|
res = 1;
|
||||||
|
} else {
|
||||||
|
len = st.st_size + 1; /* +1 detects file size > st.st_size */
|
||||||
|
*datap = data = ptr = SLAP_MALLOC( len + 1 );
|
||||||
|
if ( ptr != NULL ) {
|
||||||
|
while ( len && (res = read( fd, ptr, len )) ) {
|
||||||
|
if ( res > 0 ) {
|
||||||
|
len -= res;
|
||||||
|
ptr += res;
|
||||||
|
} else if ( errno != EINTR ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( close( fd ) < 0 )
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if ( res == 0 ) {
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "ldif_read_file: %s: \"%s\"\n",
|
||||||
|
datap ? "read entry file" : "entry file exists", path, 0 );
|
||||||
|
rc = LDAP_SUCCESS;
|
||||||
|
} else {
|
||||||
|
if ( res < 0 && errno == ENOENT ) {
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "ldif_read_file: "
|
||||||
|
"no entry file \"%s\"\n", path, 0, 0 );
|
||||||
|
rc = LDAP_NO_SUCH_OBJECT;
|
||||||
|
} else {
|
||||||
|
const char *msg = res < 0 ? STRERROR( errno ) : "bad stat() size";
|
||||||
|
Debug( LDAP_DEBUG_ANY, "ldif_read_file: %s for \"%s\"\n",
|
||||||
|
msg, path, 0 );
|
||||||
|
rc = LDAP_OTHER;
|
||||||
|
}
|
||||||
|
if ( data != NULL )
|
||||||
|
SLAP_FREE( data );
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* return nonnegative for success or -1 for error
|
* return nonnegative for success or -1 for error
|
||||||
* do not return numbers less than -1
|
* do not return numbers less than -1
|
||||||
*/
|
*/
|
||||||
static int spew_file(int fd, char * spew, int len) {
|
static int
|
||||||
|
spew_file( int fd, const char *spew, int len, int *save_errno )
|
||||||
|
{
|
||||||
int writeres = 0;
|
int writeres = 0;
|
||||||
|
|
||||||
while(len > 0) {
|
while(len > 0) {
|
||||||
writeres = write(fd, spew, len);
|
writeres = write(fd, spew, len);
|
||||||
if(writeres == -1) {
|
if(writeres == -1) {
|
||||||
return -1;
|
*save_errno = errno;
|
||||||
|
if (*save_errno != EINTR)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spew += writeres;
|
spew += writeres;
|
||||||
@ -273,135 +327,136 @@ ldif_write_entry(
|
|||||||
const struct berval *path,
|
const struct berval *path,
|
||||||
const char **text )
|
const char **text )
|
||||||
{
|
{
|
||||||
int rs, save_errno = 0;
|
int rc = LDAP_OTHER, res, save_errno = 0;
|
||||||
int openres;
|
int fd, entry_length;
|
||||||
int res, spew_res;
|
char *entry_as_string, *tmpfname;
|
||||||
int entry_length;
|
|
||||||
char * entry_as_string;
|
|
||||||
char *tmpfname = NULL;
|
|
||||||
|
|
||||||
tmpfname = ch_malloc( path->bv_len + STRLENOF( "XXXXXX" ) + 1 );
|
tmpfname = ldif_tempname( path );
|
||||||
AC_MEMCPY( tmpfname, path->bv_val, path->bv_len );
|
fd = tmpfname == NULL ? -1 : mkstemp( tmpfname );
|
||||||
AC_MEMCPY( &tmpfname[ path->bv_len ], "XXXXXX", STRLENOF( "XXXXXX" ) + 1 );
|
if ( fd < 0 ) {
|
||||||
|
|
||||||
openres = mkstemp( tmpfname );
|
|
||||||
if ( openres == -1 ) {
|
|
||||||
save_errno = errno;
|
save_errno = errno;
|
||||||
rs = LDAP_UNWILLING_TO_PERFORM;
|
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s for \"%s\": %s\n",
|
||||||
Debug( LDAP_DEBUG_ANY, "could not create tmpfile \"%s\": %s\n",
|
"cannot create file", e->e_dn, STRERROR( save_errno ) );
|
||||||
tmpfname, STRERROR( save_errno ), 0 );
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
ber_len_t dn_len = e->e_name.bv_len;
|
||||||
struct berval rdn;
|
struct berval rdn;
|
||||||
int tmp;
|
|
||||||
|
|
||||||
/* Only save the RDN onto disk */
|
/* Only save the RDN onto disk */
|
||||||
dnRdn( &e->e_name, &rdn );
|
dnRdn( &e->e_name, &rdn );
|
||||||
if ( rdn.bv_len != e->e_name.bv_len ) {
|
if ( rdn.bv_len != dn_len ) {
|
||||||
e->e_name.bv_val[rdn.bv_len] = '\0';
|
e->e_name.bv_val[rdn.bv_len] = '\0';
|
||||||
tmp = e->e_name.bv_len;
|
|
||||||
e->e_name.bv_len = rdn.bv_len;
|
e->e_name.bv_len = rdn.bv_len;
|
||||||
rdn.bv_len = tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spew_res = -2;
|
res = -2;
|
||||||
ldap_pvt_thread_mutex_lock( &entry2str_mutex );
|
ldap_pvt_thread_mutex_lock( &entry2str_mutex );
|
||||||
entry_as_string = entry2str(e, &entry_length);
|
entry_as_string = entry2str( e, &entry_length );
|
||||||
if ( entry_as_string != NULL ) {
|
if ( entry_as_string != NULL )
|
||||||
spew_res = spew_file( openres,
|
res = spew_file( fd, entry_as_string, entry_length, &save_errno );
|
||||||
entry_as_string, entry_length );
|
|
||||||
if ( spew_res == -1 ) {
|
|
||||||
save_errno = errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
|
ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
|
||||||
|
|
||||||
/* Restore full DN */
|
/* Restore full DN */
|
||||||
if ( rdn.bv_len != e->e_name.bv_len ) {
|
if ( rdn.bv_len != dn_len ) {
|
||||||
e->e_name.bv_val[e->e_name.bv_len] = ',';
|
e->e_name.bv_val[rdn.bv_len] = ',';
|
||||||
e->e_name.bv_len = rdn.bv_len;
|
e->e_name.bv_len = dn_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = close( openres );
|
if ( close( fd ) < 0 && res >= 0 ) {
|
||||||
rs = LDAP_UNWILLING_TO_PERFORM;
|
res = -1;
|
||||||
|
save_errno = errno;
|
||||||
if ( spew_res > -2 ) {
|
}
|
||||||
if ( res == -1 || spew_res == -1 ) {
|
|
||||||
if ( save_errno == 0 ) {
|
|
||||||
save_errno = errno;
|
|
||||||
}
|
|
||||||
Debug( LDAP_DEBUG_ANY, "write error to tmpfile \"%s\": %s\n",
|
|
||||||
tmpfname, STRERROR( save_errno ), 0 );
|
|
||||||
|
|
||||||
|
if ( res >= 0 ) {
|
||||||
|
if ( move_file( tmpfname, path->bv_val ) == 0 ) {
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "ldif_write_entry: "
|
||||||
|
"wrote entry \"%s\"\n", e->e_name.bv_val, 0, 0 );
|
||||||
|
rc = LDAP_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
#ifdef _WIN32
|
save_errno = errno;
|
||||||
/* returns 0 on failure, nonzero on success */
|
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: "
|
||||||
res = MoveFileEx( tmpfname, path->bv_val,
|
"could not put entry file for \"%s\" in place: %s\n",
|
||||||
MOVEFILE_REPLACE_EXISTING ) == 0;
|
e->e_name.bv_val, STRERROR( save_errno ), 0 );
|
||||||
#else
|
|
||||||
res = rename( tmpfname, path->bv_val );
|
|
||||||
#endif
|
|
||||||
if ( res == 0 ) {
|
|
||||||
rs = LDAP_SUCCESS;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
save_errno = errno;
|
|
||||||
switch ( save_errno ) {
|
|
||||||
case ENOENT:
|
|
||||||
rs = LDAP_NO_SUCH_OBJECT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if ( res == -1 ) {
|
||||||
|
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s \"%s\": %s\n",
|
||||||
|
"write error to", tmpfname, STRERROR( save_errno ) );
|
||||||
|
*text = "internal error (write error to entry file)";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( rs != LDAP_SUCCESS ) {
|
if ( rc != LDAP_SUCCESS ) {
|
||||||
unlink( tmpfname );
|
unlink( tmpfname );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ch_free( tmpfname );
|
if ( rc == LDAP_OTHER && save_errno == ENOENT )
|
||||||
|
rc = LDAP_NO_SUCH_OBJECT;
|
||||||
|
|
||||||
return rs;
|
if ( tmpfname )
|
||||||
|
SLAP_FREE( tmpfname );
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Entry * get_entry_for_fd(int fd,
|
/*
|
||||||
|
* Read the entry at path, or if entryp==NULL just see if it exists.
|
||||||
|
* pdn and pndn are the parent's DN and normalized DN, or both NULL.
|
||||||
|
* Return an LDAP result code.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ldif_read_entry(
|
||||||
|
Operation *op,
|
||||||
|
const char *path,
|
||||||
struct berval *pdn,
|
struct berval *pdn,
|
||||||
struct berval *pndn)
|
struct berval *pndn,
|
||||||
|
Entry **entryp,
|
||||||
|
const char **text )
|
||||||
{
|
{
|
||||||
char * entry = (char *) slurp_file(fd);
|
int rc;
|
||||||
Entry * ldentry = NULL;
|
Entry *entry;
|
||||||
|
char *entry_as_string;
|
||||||
/* error reading file */
|
struct berval rdn;
|
||||||
if(entry == NULL) {
|
|
||||||
goto return_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ldentry = str2entry(entry);
|
rc = ldif_read_file( path, entryp ? &entry_as_string : NULL );
|
||||||
if ( ldentry ) {
|
|
||||||
struct berval rdn;
|
|
||||||
rdn = ldentry->e_name;
|
|
||||||
build_new_dn( &ldentry->e_name, pdn, &rdn, NULL );
|
|
||||||
ch_free( rdn.bv_val );
|
|
||||||
rdn = ldentry->e_nname;
|
|
||||||
build_new_dn( &ldentry->e_nname, pndn, &rdn, NULL );
|
|
||||||
ch_free( rdn.bv_val );
|
|
||||||
}
|
|
||||||
|
|
||||||
return_value:
|
switch ( rc ) {
|
||||||
if(fd != -1) {
|
case LDAP_SUCCESS:
|
||||||
if(close(fd) != 0) {
|
if ( entryp == NULL )
|
||||||
/* log error */
|
break;
|
||||||
|
*entryp = entry = str2entry( entry_as_string );
|
||||||
|
SLAP_FREE( entry_as_string );
|
||||||
|
if ( entry == NULL ) {
|
||||||
|
rc = LDAP_OTHER;
|
||||||
|
if ( text != NULL )
|
||||||
|
*text = "internal error (cannot parse some entry file)";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if ( pdn == NULL || BER_BVISEMPTY( pdn ) )
|
||||||
|
break;
|
||||||
|
/* Append parent DN to DN from LDIF file */
|
||||||
|
rdn = entry->e_name;
|
||||||
|
build_new_dn( &entry->e_name, pdn, &rdn, NULL );
|
||||||
|
SLAP_FREE( rdn.bv_val );
|
||||||
|
rdn = entry->e_nname;
|
||||||
|
build_new_dn( &entry->e_nname, pndn, &rdn, NULL );
|
||||||
|
SLAP_FREE( rdn.bv_val );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LDAP_OTHER:
|
||||||
|
if ( text != NULL )
|
||||||
|
*text = entryp
|
||||||
|
? "internal error (cannot read some entry file)"
|
||||||
|
: "internal error (cannot stat some entry file)";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(entry != NULL)
|
|
||||||
SLAP_FREE(entry);
|
return rc;
|
||||||
return ldentry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the operation's entry, or if entryp==NULL just see if it exists.
|
||||||
|
* Return an LDAP result code. May set *text to a message on failure.
|
||||||
|
* If pathp is non-NULL, set it to the entry filename on success.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
get_entry(
|
get_entry(
|
||||||
Operation *op,
|
Operation *op,
|
||||||
@ -411,19 +466,11 @@ get_entry(
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct berval path, pdn, pndn;
|
struct berval path, pdn, pndn;
|
||||||
int fd;
|
|
||||||
|
|
||||||
dnParent(&op->o_req_dn, &pdn);
|
dnParent(&op->o_req_dn, &pdn);
|
||||||
dnParent(&op->o_req_ndn, &pndn);
|
dnParent(&op->o_req_ndn, &pndn);
|
||||||
dn2path( op->o_bd, &op->o_req_ndn, &path );
|
dn2path( op->o_bd, &op->o_req_ndn, &path );
|
||||||
fd = open(path.bv_val, O_RDONLY);
|
rc = ldif_read_entry( op, path.bv_val, &pdn, &pndn, entryp, text );
|
||||||
/* error opening file (mebbe should log error) */
|
|
||||||
if ( fd == -1 && ( errno != ENOENT || op->o_tag != LDAP_REQ_ADD ) ) {
|
|
||||||
Debug( LDAP_DEBUG_ANY, "failed to open file \"%s\": %s\n",
|
|
||||||
path.bv_val, STRERROR(errno), 0 );
|
|
||||||
}
|
|
||||||
*entryp = fd < 0 ? NULL : get_entry_for_fd( fd, &pdn, &pndn );
|
|
||||||
rc = *entryp ? LDAP_SUCCESS : LDAP_NO_SUCH_OBJECT;
|
|
||||||
|
|
||||||
if ( rc == LDAP_SUCCESS && pathp != NULL ) {
|
if ( rc == LDAP_SUCCESS && pathp != NULL ) {
|
||||||
*pathp = path;
|
*pathp = path;
|
||||||
@ -459,22 +506,12 @@ static int r_enum_tree(enumCookie *ck, struct berval *path, int base,
|
|||||||
int fd = 0, rc = LDAP_SUCCESS;
|
int fd = 0, rc = LDAP_SUCCESS;
|
||||||
|
|
||||||
if ( !base ) {
|
if ( !base ) {
|
||||||
fd = open( path->bv_val, O_RDONLY );
|
rc = ldif_read_entry( ck->op, path->bv_val, pdn, pndn, &e,
|
||||||
if ( fd < 0 ) {
|
ck->rs == NULL ? NULL : &ck->rs->sr_text );
|
||||||
Debug( LDAP_DEBUG_TRACE,
|
if ( rc != LDAP_SUCCESS ) {
|
||||||
"=> ldif_enum_tree: failed to open %s: %s\n",
|
|
||||||
path->bv_val, STRERROR(errno), 0 );
|
|
||||||
return LDAP_NO_SUCH_OBJECT;
|
return LDAP_NO_SUCH_OBJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = get_entry_for_fd(fd, pdn, pndn);
|
|
||||||
if ( !e ) {
|
|
||||||
Debug( LDAP_DEBUG_ANY,
|
|
||||||
"=> ldif_enum_tree: failed to read entry for %s\n",
|
|
||||||
path->bv_val, 0, 0 );
|
|
||||||
return LDAP_BUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ck->op->ors_scope == LDAP_SCOPE_BASE ||
|
if ( ck->op->ors_scope == LDAP_SCOPE_BASE ||
|
||||||
ck->op->ors_scope == LDAP_SCOPE_SUBTREE ) {
|
ck->op->ors_scope == LDAP_SCOPE_SUBTREE ) {
|
||||||
/* Send right away? */
|
/* Send right away? */
|
||||||
@ -1140,13 +1177,6 @@ ldif_move_entry(
|
|||||||
res = rename( oldpath->bv_val, newpath.bv_val );
|
res = rename( oldpath->bv_val, newpath.bv_val );
|
||||||
res = LDAP_SUCCESS;
|
res = LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if(errno == ENOENT)
|
|
||||||
res = LDAP_NO_SUCH_OBJECT;
|
|
||||||
else
|
|
||||||
res = LDAP_UNWILLING_TO_PERFORM;
|
|
||||||
unlink(newpath.bv_val); /* in case file was created */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(exists_res) {
|
else if(exists_res) {
|
||||||
int close_res = close(exists_res);
|
int close_res = close(exists_res);
|
||||||
|
Loading…
Reference in New Issue
Block a user