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:
Hallvard Furuseth 2008-11-11 23:00:18 +00:00
parent 69a6d8ca2f
commit b00aa30d37

View File

@ -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);