ITS#5408 part 4 - preparing for entry creation. More error handling.

- Move mkdir() into ldif_write_entry().  Modrdn lacked this.
- Factor tests for whether to create entry & dir out to ldif_prepare_create().
- Ensure an entry always exists if the corresponding subtree directory does.
- Move get_parent_path() up, accept NULL output param and malloc errors.
This commit is contained in:
Hallvard Furuseth 2008-11-11 23:16:22 +00:00
parent ddc0613920
commit a9481e437c

View file

@ -240,6 +240,27 @@ dn2path( BackendDB *be, struct berval *dn, struct berval *res )
#define ldif2dir_name(bv) ((bv).bv_val[(bv).bv_len] = '\0')
#define dir2ldif_name(bv) ((bv).bv_val[(bv).bv_len] = LDIF_FILETYPE_SEP)
/* Get the parent directory path, plus the LDIF suffix overwritten by a \0. */
static int
get_parent_path( struct berval *dnpath, struct berval *res )
{
ber_len_t i = dnpath->bv_len;
while ( i > 0 && dnpath->bv_val[ --i ] != LDAP_DIRSEP[0] ) ;
if ( res == NULL ) {
res = dnpath;
} else {
res->bv_val = SLAP_MALLOC( i + 1 + STRLENOF(LDIF) );
if ( res->bv_val == NULL )
return LDAP_OTHER;
AC_MEMCPY( res->bv_val, dnpath->bv_val, i );
}
res->bv_len = i;
strcpy( res->bv_val + i, LDIF );
res->bv_val[i] = '\0';
return LDAP_SUCCESS;
}
/* Make temporary filename pattern for mkstemp() based on dnpath. */
static char *
ldif_tempname( const struct berval *dnpath )
@ -342,23 +363,35 @@ spew_file( int fd, const char *spew, int len, int *save_errno )
return writeres;
}
/* Write an entry LDIF file. Create parentdir first if non-NULL. */
static int
ldif_write_entry(
Operation *op,
Entry *e,
const struct berval *path,
const char *parentdir,
const char **text )
{
int rc = LDAP_OTHER, res, save_errno = 0;
int fd, entry_length;
char *entry_as_string, *tmpfname;
if ( parentdir != NULL && mkdir( parentdir, 0750 ) < 0 ) {
save_errno = errno;
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s \"%s\": %s\n",
"cannot create parent directory",
parentdir, STRERROR( save_errno ) );
*text = "internal error (cannot create parent directory)";
return rc;
}
tmpfname = ldif_tempname( path );
fd = tmpfname == NULL ? -1 : mkstemp( tmpfname );
if ( fd < 0 ) {
save_errno = errno;
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s for \"%s\": %s\n",
"cannot create file", e->e_dn, STRERROR( save_errno ) );
*text = "internal error (cannot create file)";
} else {
ber_len_t dn_len = e->e_name.bv_len;
@ -399,6 +432,7 @@ ldif_write_entry(
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: "
"could not put entry file for \"%s\" in place: %s\n",
e->e_name.bv_val, STRERROR( save_errno ), 0 );
*text = "internal error (could not put entry file in place)";
}
} else if ( res == -1 ) {
Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s \"%s\": %s\n",
@ -411,9 +445,6 @@ ldif_write_entry(
}
}
if ( rc == LDAP_OTHER && save_errno == ENOENT )
rc = LDAP_NO_SUCH_OBJECT;
if ( tmpfname )
SLAP_FREE( tmpfname );
return rc;
@ -705,21 +736,89 @@ enum_tree(
}
/* Get the parent directory path, plus the LDIF suffix overwritten by a \0 */
static void
get_parent_path( struct berval *dnpath, struct berval *res )
/*
* Prepare to create or rename an entry:
* Check that the entry does not already exist.
* Check that the parent entry exists and can have subordinates,
* unless need_dir is NULL or adding the suffix entry.
*
* Return an LDAP result code. May set *text to a message on failure.
* If success, set *dnpath to LDIF entry path and *need_dir to
* (directory must be created ? dirname : NULL).
*/
static int
ldif_prepare_create(
Operation *op,
Entry *e,
struct berval *dnpath,
char **need_dir,
const char **text )
{
int dnpathlen = dnpath->bv_len;
int i;
for(i = dnpathlen;i>0;i--) /* find the first path seperator */
if(dnpath->bv_val[i] == LDAP_DIRSEP[0])
BackendDB *be = op->o_bd;
struct ldif_info *li = (struct ldif_info *) be->be_private;
struct berval *ndn = &e->e_nname;
struct berval ppath = BER_BVNULL;
struct stat st;
int rc = LDAP_SUCCESS;
dn2path( be, ndn, dnpath );
if ( stat( dnpath->bv_val, &st ) == 0 ) { /* entry .ldif file */
rc = LDAP_ALREADY_EXISTS;
} else if ( errno != ENOENT ) {
Debug( LDAP_DEBUG_ANY,
"ldif_prepare_create: cannot stat \"%s\": %s\n",
dnpath->bv_val, STRERROR( errno ), 0 );
rc = LDAP_OTHER;
*text = "internal error (cannot check entry file)";
} else if ( need_dir != NULL ) {
*need_dir = NULL;
rc = get_parent_path( dnpath, &ppath );
/* If parent dir exists, so does parent .ldif:
* The directory gets created after and removed before the .ldif.
* Except with the database directory, which has no matching entry.
*/
if ( rc == LDAP_SUCCESS && stat( ppath.bv_val, &st ) < 0 ) {
rc = errno == ENOENT && ppath.bv_len > li->li_base_path.bv_len
? LDAP_NO_SUCH_OBJECT : LDAP_OTHER;
}
switch ( rc ) {
case LDAP_NO_SUCH_OBJECT:
/* No parent dir, check parent .ldif */
dir2ldif_name( ppath );
rc = ldif_read_entry( op, ppath.bv_val, NULL, NULL,
NULL, text );
switch ( rc ) {
case LDAP_SUCCESS:
/* Must create parent directory. */
ldif2dir_name( ppath );
*need_dir = ppath.bv_val;
break;
case LDAP_NO_SUCH_OBJECT:
*text = op->o_tag == LDAP_REQ_MODDN
? "newSuperior object does not exist"
: "parent does not exist";
break;
}
break;
res->bv_len = i;
res->bv_val = ch_malloc( res->bv_len + 1 + STRLENOF(LDIF) );
strncpy(res->bv_val, dnpath->bv_val, i);
strcpy(res->bv_val+i, LDIF);
res->bv_val[i] = '\0';
case LDAP_OTHER:
Debug( LDAP_DEBUG_ANY,
"ldif_prepare_create: cannot stat \"%s\" parent dir: %s\n",
ndn->bv_val, STRERROR( errno ), 0 );
*text = "internal error (cannot stat parent dir)";
break;
}
if ( *need_dir == NULL && ppath.bv_val != NULL )
SLAP_FREE( ppath.bv_val );
}
if ( rc != LDAP_SUCCESS ) {
SLAP_FREE( dnpath->bv_val );
BER_BVZERO( dnpath );
}
return rc;
}
static int apply_modify_to_entry(Entry * entry,
@ -1008,75 +1107,38 @@ static int ldif_back_search(Operation *op, SlapReply *rs)
static int ldif_back_add(Operation *op, SlapReply *rs) {
struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
Entry * e = op->ora_e;
struct berval dn = e->e_nname;
struct berval leaf_path = BER_BVNULL;
struct stat stats;
int statres;
struct berval path;
char *parentdir;
char textbuf[SLAP_TEXT_BUFLEN];
int rc;
Debug( LDAP_DEBUG_TRACE, "ldif_back_add: \"%s\"\n", dn.bv_val, 0, 0);
Debug( LDAP_DEBUG_TRACE, "ldif_back_add: \"%s\"\n", e->e_dn, 0, 0 );
rs->sr_err = entry_schema_check(op, e, NULL, 0, 1,
rc = entry_schema_check( op, e, NULL, 0, 1,
&rs->sr_text, textbuf, sizeof( textbuf ) );
if ( rs->sr_err != LDAP_SUCCESS ) goto send_res;
if ( rc != LDAP_SUCCESS )
goto send_res;
rs->sr_err = slap_add_opattrs( op,
&rs->sr_text, textbuf, sizeof( textbuf ), 1 );
if ( rs->sr_err != LDAP_SUCCESS ) goto send_res;
rc = slap_add_opattrs( op, &rs->sr_text, textbuf, sizeof( textbuf ), 1 );
if ( rc != LDAP_SUCCESS )
goto send_res;
ldap_pvt_thread_rdwr_wlock(&li->li_rdwr);
dn2path( op->o_bd, &dn, &leaf_path );
if(leaf_path.bv_val != NULL) {
struct berval base = BER_BVNULL;
/* build path to container and ldif of container */
get_parent_path(&leaf_path, &base);
statres = stat(base.bv_val, &stats); /* check if container exists */
if(statres == -1 && errno == ENOENT) { /* container missing */
base.bv_val[base.bv_len] = LDIF_FILETYPE_SEP;
statres = stat(base.bv_val, &stats); /* check for leaf node */
base.bv_val[base.bv_len] = '\0';
if(statres == -1 && errno == ENOENT) {
rs->sr_err = LDAP_NO_SUCH_OBJECT; /* parent doesn't exist */
rs->sr_text = "Parent does not exist";
}
else if(statres != -1) { /* create parent */
int mkdirres = mkdir(base.bv_val, 0750);
if(mkdirres == -1) {
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
rs->sr_text = "Could not create parent folder";
Debug( LDAP_DEBUG_ANY, "could not create folder \"%s\": %s\n",
base.bv_val, STRERROR( errno ), 0 );
}
}
else
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
}/* container was possibly created, move on to add the entry */
if(rs->sr_err == LDAP_SUCCESS) {
statres = stat(leaf_path.bv_val, &stats);
if(statres == -1 && errno == ENOENT) {
rs->sr_err = ldif_write_entry( op, e, &leaf_path, &rs->sr_text );
}
else if ( statres == -1 ) {
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
Debug( LDAP_DEBUG_ANY, "could not stat file \"%s\": %s\n",
leaf_path.bv_val, STRERROR( errno ), 0 );
}
else /* it already exists */
rs->sr_err = LDAP_ALREADY_EXISTS;
}
SLAP_FREE(base.bv_val);
SLAP_FREE(leaf_path.bv_val);
rc = ldif_prepare_create( op, e, &path, &parentdir, &rs->sr_text );
if ( rc == LDAP_SUCCESS ) {
rc = ldif_write_entry( op, e, &path, parentdir, &rs->sr_text );
SLAP_FREE( path.bv_val );
if ( parentdir != NULL )
SLAP_FREE( parentdir );
}
ldap_pvt_thread_rdwr_wunlock(&li->li_rdwr);
send_res:
Debug( LDAP_DEBUG_TRACE,
"ldif_back_add: err: %d text: %s\n", rs->sr_err, rs->sr_text ?
rs->sr_text : "", 0);
send_res:
rs->sr_err = rc;
Debug( LDAP_DEBUG_TRACE, "ldif_back_add: err: %d text: %s\n",
rc, rs->sr_text ? rs->sr_text : "", 0 );
send_ldap_result(op, rs);
slap_graduate_commit_csn( op );
return rs->sr_err;
@ -1097,7 +1159,7 @@ static int ldif_back_modify(Operation *op, SlapReply *rs) {
if ( rc == LDAP_SUCCESS ) {
rc = apply_modify_to_entry( entry, modlst, op, rs );
if ( rc == LDAP_SUCCESS ) {
rc = ldif_write_entry( op, entry, &path, &rs->sr_text );
rc = ldif_write_entry( op, entry, &path, NULL, &rs->sr_text );
}
entry_free( entry );
@ -1181,43 +1243,29 @@ ldif_move_entry(
const char **text )
{
int res;
int exists_res;
struct berval newpath;
char *parentdir = NULL;
int rc;
dn2path( op->o_bd, &entry->e_nname, &newpath );
rc = ldif_prepare_create( op, entry, &newpath,
op->orr_newSup ? &parentdir : NULL, text );
if((entry == NULL || oldpath->bv_val == NULL) || newpath.bv_val == NULL) {
/* some object doesn't exist */
res = LDAP_NO_SUCH_OBJECT;
}
else { /* do the modrdn */
exists_res = open(newpath.bv_val, O_RDONLY);
if(exists_res == -1 && errno == ENOENT) {
res = ldif_write_entry( op, entry, &newpath, text );
if ( res == LDAP_SUCCESS ) {
if ( rc == LDAP_SUCCESS ) {
rc = ldif_write_entry( op, entry, &newpath, parentdir, text );
if ( rc == LDAP_SUCCESS ) {
/* if this fails we should log something bad */
res = unlink( oldpath->bv_val );
oldpath->bv_val[oldpath->bv_len - STRLENOF(".ldif")] = '\0';
newpath.bv_val[newpath.bv_len - STRLENOF(".ldif")] = '\0';
res = rename( oldpath->bv_val, newpath.bv_val );
res = LDAP_SUCCESS;
}
}
else if(exists_res) {
int close_res = close(exists_res);
res = LDAP_ALREADY_EXISTS;
if(close_res == -1) {
/* log heinous error */
}
}
else {
res = LDAP_UNWILLING_TO_PERFORM;
}
SLAP_FREE( newpath.bv_val );
if ( parentdir != NULL )
SLAP_FREE( parentdir );
}
if(newpath.bv_val != NULL)
SLAP_FREE(newpath.bv_val);
return res;
return rc;
}
static int
@ -1237,21 +1285,7 @@ ldif_back_modrdn(Operation *op, SlapReply *rs)
if ( rc == LDAP_SUCCESS ) {
/* build new dn, and new ndn for the entry */
if ( op->oq_modrdn.rs_newSup != NULL ) {
struct berval op_dn = op->o_req_dn,
op_ndn = op->o_req_ndn;
Entry *np;
/* new superior */
p_dn = *op->oq_modrdn.rs_newSup;
op->o_req_dn = *op->oq_modrdn.rs_newSup;
op->o_req_ndn = *op->oq_modrdn.rs_nnewSup;
rc = get_entry( op, &np, NULL, &rs->sr_text );
op->o_req_dn = op_dn;
op->o_req_ndn = op_ndn;
if ( rc != LDAP_SUCCESS ) {
goto no_such_object;
}
entry_free( np );
} else {
dnParent( &entry->e_name, &p_dn );
}
@ -1267,7 +1301,6 @@ ldif_back_modrdn(Operation *op, SlapReply *rs)
if ( rc == LDAP_SUCCESS )
rc = ldif_move_entry( op, entry, &old_path, &rs->sr_text );
no_such_object:;
entry_free( entry );
SLAP_FREE( old_path.bv_val );
}
@ -1376,52 +1409,22 @@ ldif_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
{
int rc;
const char *errmsg = NULL;
struct berval leaf_path = BER_BVNULL;
struct stat stats;
int statres;
int res = LDAP_SUCCESS;
struct berval path;
char *parentdir;
Operation op = {0};
dn2path( be, &e->e_nname, &leaf_path );
op.o_bd = be;
rc = ldif_prepare_create( &op, e, &path, &parentdir, &errmsg );
if ( rc == LDAP_SUCCESS ) {
rc = ldif_write_entry( &op, e, &path, parentdir, &errmsg );
if(leaf_path.bv_val != NULL) {
struct berval base = BER_BVNULL;
/* build path to container, and path to ldif of container */
get_parent_path(&leaf_path, &base);
statres = stat(base.bv_val, &stats); /* check if container exists */
if(statres == -1 && errno == ENOENT) { /* container missing */
base.bv_val[base.bv_len] = LDIF_FILETYPE_SEP;
statres = stat(base.bv_val, &stats); /* check for leaf node */
base.bv_val[base.bv_len] = '\0';
if(statres == -1 && errno == ENOENT) {
res = LDAP_NO_SUCH_OBJECT; /* parent doesn't exist */
}
else if(statres != -1) { /* create parent */
int mkdirres = mkdir(base.bv_val, 0750);
if(mkdirres == -1) {
res = LDAP_UNWILLING_TO_PERFORM;
}
}
else
res = LDAP_UNWILLING_TO_PERFORM;
}/* container was possibly created, move on to add the entry */
if(res == LDAP_SUCCESS) {
statres = stat(leaf_path.bv_val, &stats);
if(statres == -1 && errno == ENOENT) {
res = ldif_write_entry( &op, e, &leaf_path, &errmsg );
}
else /* it already exists */
res = LDAP_ALREADY_EXISTS;
}
SLAP_FREE(base.bv_val);
SLAP_FREE(leaf_path.bv_val);
SLAP_FREE( path.bv_val );
if ( parentdir != NULL )
SLAP_FREE( parentdir );
if ( rc == LDAP_SUCCESS )
return 1;
}
if(res == LDAP_SUCCESS) {
return 1;
}
rc = res;
if ( errmsg == NULL && rc != LDAP_OTHER )
errmsg = ldap_err2string( rc );
if ( errmsg != NULL )