nfs-ganesha 1.4
|
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 }