nfs-ganesha 1.4

posixdb_replace.c

Go to the documentation of this file.
00001 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; -*-
00002  * vim:expandtab:shiftwidth=4:tabstop=4:
00003  */
00004 #ifdef HAVE_CONFIG_H
00005 #include "config.h"
00006 #endif
00007 #include "posixdb_internal.h"
00008 #include "posixdb_consistency.h"
00009 #include <string.h>
00010 
00011 fsal_posixdb_status_t fsal_posixdb_replace(fsal_posixdb_conn * p_conn,  /* IN */
00012                                            fsal_posixdb_fileinfo_t * p_object_info,     /* IN */
00013                                            posixfsal_handle_t * p_parent_directory_handle_old,  /* IN */
00014                                            fsal_name_t * p_filename_old,        /* IN */
00015                                            posixfsal_handle_t * p_parent_directory_handle_new,  /* IN */
00016                                            fsal_name_t * p_filename_new /* IN */ )
00017 {
00018   result_handle_t res;
00019   fsal_posixdb_status_t st;
00020   char query[4096];
00021   MYSQL_ROW row;
00022   int re_update = FALSE;
00023 
00024     /*******************
00025      * 1/ sanity check *
00026      *******************/
00027 
00028   if(!p_conn || !p_object_info || !p_parent_directory_handle_old || !p_filename_old
00029      || !p_parent_directory_handle_new || !p_filename_new)
00030     ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0);
00031 
00032   BeginTransaction(p_conn);
00033 
00034     /**************************************************************************
00035      * 2/ check that 'p_filename_old' exists in p_parent_directory_handle_old *
00036      **************************************************************************/
00037 
00038   /* 
00039      There are three cases :
00040      * the entry do not exists -> return an error (NOENT)
00041      * the entry exists.
00042      * the entry exists but its information are not consistent with p_object_info -> return an error (CONSISTENCY)
00043    */
00044 
00045   /* check if info is in cache or if this info is inconsistent */
00046   if(!fsal_posixdb_GetInodeCache(p_parent_directory_handle_old)
00047      || fsal_posixdb_consistency_check(&(p_parent_directory_handle_old->data.info),
00048                                        p_object_info))
00049     {
00050       snprintf(query, 4096,
00051                "SELECT Parent.handleid, Parent.handlets, Handle.deviceid, Handle.inode, "
00052                "Handle.nlink, Handle.ctime, Handle.ftype "
00053                "FROM Parent INNER JOIN Handle ON Parent.handleid = Handle.handleid "
00054                "AND Parent.handlets=Handle.handlets "
00055                "WHERE handleidparent=%llu AND handletsparent=%u AND name='%s'",
00056                p_parent_directory_handle_old->data.id, p_parent_directory_handle_old->data.ts,
00057                p_filename_old->name);
00058 
00059       st = db_exec_sql(p_conn, query, &res);
00060       if(FSAL_POSIXDB_IS_ERROR(st))
00061         goto rollback;
00062 
00063       if(mysql_num_rows(res) != 1)
00064         {
00065           /* parent entry not found */
00066           mysql_free_result(res);
00067           RollbackTransaction(p_conn);
00068           ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0);
00069         }
00070 
00071       row = mysql_fetch_row(res);
00072       if(!row)
00073         {
00074           /* Error */
00075           mysql_free_result(res);
00076           RollbackTransaction(p_conn);
00077           ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0);
00078         }
00079 
00080       /* fill 'infodb' with information about the handle in the database */
00081       posixdb_internal_fillFileinfoFromStrValues(&(p_parent_directory_handle_old->data.info),
00082                                                  row[2], row[3], row[4], row[5], row[6]);
00083 
00084       /* check consistency */
00085 
00086       if(fsal_posixdb_consistency_check
00087          (&(p_parent_directory_handle_old->data.info), p_object_info))
00088         {
00089           LogCrit(COMPONENT_FSAL, "Consistency check failed while renaming a file : Handle deleted");
00090           st = fsal_posixdb_recursiveDelete(p_conn, atoll(row[0]), atoi(row[1]),
00091                                             FSAL_TYPE_DIR);
00092           mysql_free_result(res);
00093           return EndTransaction(p_conn);
00094         }
00095 
00096       mysql_free_result(res);
00097     }
00098 
00099     /**********************************************************************************
00100      * 3/ update the parent entry (in order to change its name and its parent handle) *
00101      **********************************************************************************/
00102 
00103   /*
00104      Different cases :
00105      * a line has been updated -> everything is OK
00106      * no line has been updated -> the entry does not exists in the database. -> return NOENT
00107      *        (should never happen because of the previous check)
00108      * foreign key constraint violation -> new parentdir handle does not exists -> return NOENT
00109      * unique constraint violation -> there is already a file with this name in the directory -> replace it !
00110    */
00111 
00112   /* Remove target entry if it exists */
00113 
00114   snprintf(query, 4096,
00115            "SELECT Parent.handleid, Parent.handlets, Handle.deviceid, Handle.inode, "
00116            "Handle.nlink, Handle.ctime, Handle.ftype "
00117            "FROM Parent INNER JOIN Handle ON Parent.handleid = Handle.handleid AND Parent.handlets=Handle.handlets "
00118            "WHERE handleidparent=%llu AND handletsparent=%u AND name='%s' FOR UPDATE",
00119            p_parent_directory_handle_new->data.id, p_parent_directory_handle_new->data.ts,
00120            p_filename_new->name);
00121 
00122   st = db_exec_sql(p_conn, query, &res);
00123   if(FSAL_POSIXDB_IS_ERROR(st))
00124     goto rollback;
00125 
00126   if(mysql_num_rows(res) > 0)
00127     {
00128       row = mysql_fetch_row(res);
00129       if(!row)
00130         {
00131           /* Error */
00132           mysql_free_result(res);
00133           RollbackTransaction(p_conn);
00134           ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0);
00135         }
00136 
00137       st = fsal_posixdb_deleteParent(p_conn, atoll(row[0]), atoi(row[1]),
00138                                      p_parent_directory_handle_new->data.id,
00139                                      p_parent_directory_handle_new->data.ts,
00140                                      p_filename_new->name, atoi(row[4]) /* nlink */ );
00141 
00142       if(FSAL_POSIXDB_IS_ERROR(st) && !FSAL_POSIXDB_IS_NOENT(st))
00143         {
00144           mysql_free_result(res);
00145           goto rollback;
00146         }
00147 
00148     }
00149 
00150   mysql_free_result(res);
00151 
00152   do
00153     {
00154 
00155       re_update = FALSE;
00156 
00157       /* invalidate name cache */
00158       fsal_posixdb_InvalidateCache();
00159 
00160       snprintf(query, 4096, "UPDATE Parent "
00161                "SET handleidparent=%llu, handletsparent=%u, name='%s' "
00162                "WHERE handleidparent=%llu AND handletsparent=%u AND name='%s' ",
00163                p_parent_directory_handle_new->data.id,
00164                p_parent_directory_handle_new->data.ts,
00165                p_filename_new->name,
00166                p_parent_directory_handle_old->data.id,
00167                p_parent_directory_handle_old->data.ts, p_filename_old->name);
00168 
00169       st = db_exec_sql(p_conn, query, NULL);
00170 
00171       if(!FSAL_POSIXDB_IS_ERROR(st))
00172         {
00173           /* how many rows updated ? */
00174 
00175           if(mysql_affected_rows(&p_conn->db_conn) == 1)
00176             {
00177               /* there was 1 update */
00178               st.major = ERR_FSAL_POSIXDB_NOERR;
00179               st.minor = 0;
00180             }
00181           else
00182             {
00183               /* no row updated */
00184               st.major = ERR_FSAL_POSIXDB_NOENT;
00185               st.minor = 0;
00186             }
00187         }
00188       else
00189         {
00190           /* error (switch on MySQL status) */
00191 
00192           switch (st.minor)
00193             {
00194             case ER_NO_REFERENCED_ROW:
00195               /* Foreign key violation : new parentdir does not exist, do nothing */
00196               st.major = ERR_FSAL_POSIXDB_NOENT;
00197               st.minor = st.minor;
00198               break;
00199 
00200             case ER_DUP_UNIQUE:
00201               /* Unique violation : there is already a file with the same name in parentdir_new */
00202               /* Delete the existing entry, and then do the update again */
00203 
00204               snprintf(query, 4096,
00205                        "SELECT Parent.handleid, Parent.handlets, Handle.deviceid, Handle.inode, "
00206                        "Handle.nlink, Handle.ctime, Handle.ftype "
00207                        "FROM Parent INNER JOIN Handle ON Parent.handleid = Handle.handleid AND Parent.handlets=Handle.handlets "
00208                        "WHERE handleidparent=%llu AND handletsparent=%u AND name='%s' FOR UPDATE",
00209                        p_parent_directory_handle_new->data.id,
00210                        p_parent_directory_handle_new->data.ts, p_filename_new->name);
00211 
00212               st = db_exec_sql(p_conn, query, &res);
00213               if(FSAL_POSIXDB_IS_ERROR(st))
00214                 goto rollback;
00215 
00216               if(mysql_num_rows(res) > 0)
00217                 {
00218                   row = mysql_fetch_row(res);
00219                   if(!row)
00220                     {
00221                       /* Error */
00222                       mysql_free_result(res);
00223                       RollbackTransaction(p_conn);
00224                       ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0);
00225                     }
00226 
00227                   st = fsal_posixdb_deleteParent(p_conn, atoll(row[0]), atoi(row[1]), p_parent_directory_handle_new->data.id, p_parent_directory_handle_new->data.ts, p_filename_new->name, atoi(row[4]));        /* nlink */
00228 
00229                   if(FSAL_POSIXDB_IS_ERROR(st) && !FSAL_POSIXDB_IS_NOENT(st))
00230                     {
00231                       mysql_free_result(res);
00232                       break;
00233                     }
00234                 }
00235 
00236               mysql_free_result(res);
00237 
00238               /* the entry has been deleted, the update can now be done */
00239               re_update = TRUE;
00240 
00241               break;
00242 
00243             default:           /* keep error code as it is => do nothing */
00244               ;
00245             }
00246         }
00247 
00248     }
00249   while(re_update);
00250 
00251   if(FSAL_POSIXDB_IS_ERROR(st))
00252     goto rollback;
00253   else
00254     return EndTransaction(p_conn);
00255 
00256  rollback:
00257   RollbackTransaction(p_conn);
00258   return st;
00259 }