]> git.sur5r.net Git - openldap/commitdiff
ITS#5408 part 2 - filesystem I/O, file error handling:
authorHallvard Furuseth <hallvard@openldap.org>
Tue, 11 Nov 2008 23:00:18 +0000 (23:00 +0000)
committerHallvard Furuseth <hallvard@openldap.org>
Tue, 11 Nov 2008 23:00:18 +0000 (23:00 +0000)
- 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.

servers/slapd/back-ldif/ldif.c

index e5c133855e7c7b4a8adfe9b4c74b99940f4ee430..ded80ee3ef1a4429c8af35d0b497e9d2029caebe 100644 (file)
@@ -48,6 +48,9 @@ struct ldif_info {
 
 #ifdef _WIN32
 #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
 
 
@@ -215,48 +218,99 @@ dn2path( BackendDB *be, struct berval *dn, struct berval *res )
        assert( res->bv_len <= len );
 }
 
-static char * slurp_file(int fd) {
-       int read_chars_total = 0;
-       int read_chars = 0;
-       int entry_size;
-       char * entry;
-       char * entry_pos;
+/* Make temporary filename pattern for mkstemp() based on dnpath. */
+static char *
+ldif_tempname( const struct berval *dnpath )
+{
+       static const char suffix[] = "XXXXXX";
+       ber_len_t len = dnpath->bv_len;
+       char *name = SLAP_MALLOC( len + sizeof( suffix ) );
+
+       if ( name != NULL ) {
+               AC_MEMCPY( name, dnpath->bv_val, len );
+               strcpy( name + len, suffix );
+       }
+       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;
 
-       fstat(fd, &st);
-       entry_size = st.st_size;
-       entry = ch_malloc( entry_size+1 );
-       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;
+       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';
+                               }
+                       }
                }
-               else {
-                       read_chars_total += read_chars;
-                       entry_pos += read_chars;
+               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 entry;
+       return rc;
 }
 
 /*
  * return nonnegative for success or -1 for error
  * 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;
-       
+
        while(len > 0) {
                writeres = write(fd, spew, len);
                if(writeres == -1) {
-                       return -1;
+                       *save_errno = errno;
+                       if (*save_errno != EINTR)
+                               break;
                }
                else {
                        spew += writeres;
@@ -273,135 +327,136 @@ ldif_write_entry(
        const struct berval *path,
        const char **text )
 {
-       int rs, save_errno = 0;
-       int openres;
-       int res, spew_res;
-       int entry_length;
-       char * entry_as_string;
-       char *tmpfname = NULL;
-
-       tmpfname = ch_malloc( path->bv_len + STRLENOF( "XXXXXX" ) + 1 );
-       AC_MEMCPY( tmpfname, path->bv_val, path->bv_len );
-       AC_MEMCPY( &tmpfname[ path->bv_len ], "XXXXXX", STRLENOF( "XXXXXX" ) + 1 );
-
-       openres = mkstemp( tmpfname );
-       if ( openres == -1 ) {
+       int rc = LDAP_OTHER, res, save_errno = 0;
+       int fd, entry_length;
+       char *entry_as_string, *tmpfname;
+
+       tmpfname = ldif_tempname( path );
+       fd = tmpfname == NULL ? -1 : mkstemp( tmpfname );
+       if ( fd < 0 ) {
                save_errno = errno;
-               rs = LDAP_UNWILLING_TO_PERFORM;
-               Debug( LDAP_DEBUG_ANY, "could not create tmpfile \"%s\": %s\n",
-                       tmpfname, STRERROR( save_errno ), 0 );
+               Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s for \"%s\": %s\n",
+                       "cannot create file", e->e_dn, STRERROR( save_errno ) );
 
        } else {
+               ber_len_t dn_len = e->e_name.bv_len;
                struct berval rdn;
-               int tmp;
 
                /* Only save the RDN onto disk */
                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';
-                       tmp = e->e_name.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 );
-               entry_as_string = entry2str(e, &entry_length);
-               if ( entry_as_string != NULL ) {
-                       spew_res = spew_file( openres,
-                               entry_as_string, entry_length );
-                       if ( spew_res == -1 ) {
-                               save_errno = errno;
-                       }
-               }
+               entry_as_string = entry2str( e, &entry_length );
+               if ( entry_as_string != NULL )
+                       res = spew_file( fd, entry_as_string, entry_length, &save_errno );
                ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
 
                /* Restore full DN */
-               if ( rdn.bv_len != e->e_name.bv_len ) {
-                       e->e_name.bv_val[e->e_name.bv_len] = ',';
-                       e->e_name.bv_len = rdn.bv_len;
+               if ( rdn.bv_len != dn_len ) {
+                       e->e_name.bv_val[rdn.bv_len] = ',';
+                       e->e_name.bv_len = dn_len;
                }
 
-               res = close( openres );
-               rs = LDAP_UNWILLING_TO_PERFORM;
-
-               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 ( close( fd ) < 0 && res >= 0 ) {
+                       res = -1;
+                       save_errno = errno;
+               }
 
+               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 {
-#ifdef _WIN32
-                               /* returns 0 on failure, nonzero on success */
-                               res = MoveFileEx( tmpfname, path->bv_val,
-                                       MOVEFILE_REPLACE_EXISTING ) == 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;
-                                       }
-                               }
+                               save_errno = errno;
+                               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 );
                        }
+               } 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 );
                }
        }
 
-       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 *pndn)
+       struct berval *pndn,
+       Entry **entryp,
+       const char **text )
 {
-       char * entry = (char *) slurp_file(fd);
-       Entry * ldentry = NULL;
-       
-       /* error reading file */
-       if(entry == NULL) {
-               goto return_value;
-       }
+       int rc;
+       Entry *entry;
+       char *entry_as_string;
+       struct berval rdn;
 
-       ldentry = str2entry(entry);
-       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 );
-       }
+       rc = ldif_read_file( path, entryp ? &entry_as_string : NULL );
 
- return_value:
-       if(fd != -1) {
-               if(close(fd) != 0) {
-                       /* log error */
+       switch ( rc ) {
+       case LDAP_SUCCESS:
+               if ( entryp == NULL )
+                       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 ldentry;
+
+       return rc;
 }
 
+/*
+ * 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
 get_entry(
        Operation *op,
@@ -411,19 +466,11 @@ get_entry(
 {
        int rc;
        struct berval path, pdn, pndn;
-       int fd;
 
        dnParent(&op->o_req_dn, &pdn);
        dnParent(&op->o_req_ndn, &pndn);
        dn2path( op->o_bd, &op->o_req_ndn, &path );
-       fd = open(path.bv_val, O_RDONLY);
-       /* 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;
+       rc = ldif_read_entry( op, path.bv_val, &pdn, &pndn, entryp, text );
 
        if ( rc == LDAP_SUCCESS && pathp != NULL ) {
                *pathp = path;
@@ -459,22 +506,12 @@ static int r_enum_tree(enumCookie *ck, struct berval *path, int base,
        int fd = 0, rc = LDAP_SUCCESS;
 
        if ( !base ) {
-               fd = open( path->bv_val, O_RDONLY );
-               if ( fd < 0 ) {
-                       Debug( LDAP_DEBUG_TRACE,
-                               "=> ldif_enum_tree: failed to open %s: %s\n",
-                               path->bv_val, STRERROR(errno), 0 );
+               rc = ldif_read_entry( ck->op, path->bv_val, pdn, pndn, &e,
+                       ck->rs == NULL ? NULL : &ck->rs->sr_text );
+               if ( rc != LDAP_SUCCESS ) {
                        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 ||
                        ck->op->ors_scope == LDAP_SCOPE_SUBTREE ) {
                        /* Send right away? */
@@ -1140,13 +1177,6 @@ ldif_move_entry(
                                res = rename( oldpath->bv_val, newpath.bv_val );
                                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) {
                        int close_res = close(exists_res);