query.c

Go to the documentation of this file.
00001 /*
00002   mysqlfs - MySQL Filesystem
00003   Copyright (C) 2006 Tsukasa Hamano <code@cuspy.org>
00004   Copyright (C) 2006,2007 Michal Ludvig <michal@logix.cz>
00005   $Id: query.c 55 2009-07-12 22:23:33Z chickenandporn $
00006 
00007   This program can be distributed under the terms of the GNU GPL.
00008   See the file COPYING.
00009 */
00010 
00011 #ifdef HAVE_CONFIG_H
00012 #include "config.h"
00013 #endif
00014 
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <errno.h>
00019 #include <fcntl.h>
00020 #include <time.h>
00021 #include <libgen.h>
00022 #include <fuse/fuse.h>
00023 #ifdef HAVE_MYSQL_MYSQL_H
00024 #include <mysql/mysql.h>
00025 #endif
00026 #ifdef HAVE_MYSQL_H
00027 #include <mysql.h>
00028 #endif
00029 
00030 #include "mysqlfs.h"
00031 #include "query.h"
00032 #include "log.h"
00033 
00034 #define SQL_MAX 10240
00035 #define INODE_CACHE_MAX 4096
00036 
00037 static inline int lock_inode(MYSQL *mysql, long inode)
00038 {
00039     // TODO
00040     return 0;
00041 }
00042 
00043 static inline int unlock_inode(MYSQL *mysql, long inode)
00044 {
00045     // TODO
00046     return 0;
00047 }
00048 
00049 static struct data_blocks_info *
00050 fill_data_blocks_info(struct data_blocks_info *info, size_t size, off_t offset)
00051 {
00052     info->seq_first = offset / DATA_BLOCK_SIZE;
00053     info->offset_first = offset % DATA_BLOCK_SIZE;
00054 
00055     unsigned long  nr_following_blocks = ((info->offset_first + size) / DATA_BLOCK_SIZE);       
00056     info->length_first = nr_following_blocks > 0 ? DATA_BLOCK_SIZE - info->offset_first : size;
00057 
00058     info->seq_last = info->seq_first + nr_following_blocks;
00059     info->length_last = (info->offset_first + size) % DATA_BLOCK_SIZE;
00060     /* offset in last block (if it's a different one from the first block) 
00061      * is always 0 */
00062 
00063     return info;
00064 }
00065 
00079 int query_getattr(MYSQL *mysql, const char *path, struct stat *stbuf)
00080 {
00081     int ret;
00082     long inode, nlinks;
00083     char sql[SQL_MAX];
00084     MYSQL_RES* result;
00085     MYSQL_ROW row;
00086     ret = query_inode_full(mysql, path, NULL, 0, &inode, NULL, &nlinks);
00087     if (ret < 0)
00088       return ret;
00089 
00090     snprintf(sql, SQL_MAX,
00091              "SELECT inode, mode, uid, gid, atime, mtime "
00092              "FROM inodes WHERE inode=%ld",
00093              inode);
00094 
00095     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00096 
00097     ret = mysql_query(mysql, sql);
00098     if(ret){
00099         log_printf(LOG_ERROR, "ERROR: mysql_query()\n");
00100         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00101         return -EIO;
00102     }
00103 
00104     result = mysql_store_result(mysql);
00105     if(!result){
00106         log_printf(LOG_ERROR, "ERROR: mysql_store_result()\n");
00107         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00108         return -EIO;
00109     }
00110 
00111     if(mysql_num_rows(result) != 1){
00112         mysql_free_result(result);
00113         return -ENOENT;
00114     }
00115     row = mysql_fetch_row(result);
00116     if(!row){
00117         return -EIO;
00118     }
00119 
00120     stbuf->st_ino = inode;
00121     stbuf->st_mode = atoi(row[1]);
00122     stbuf->st_uid = atol(row[2]);
00123     stbuf->st_gid = atol(row[3]);
00124     stbuf->st_atime = atol(row[4]);
00125     stbuf->st_mtime = atol(row[5]);
00126     stbuf->st_nlink = nlinks;
00127 
00128     mysql_free_result(result);
00129 
00130     return 0;
00131 }
00132 
00154 int query_inode_full(MYSQL *mysql, const char *path, char *name, size_t name_len,
00155                       long *inode, long *parent, long *nlinks)
00156 {
00157     long ret;
00158     char sql[SQL_MAX];
00159     MYSQL_RES* result;
00160     MYSQL_ROW row;
00161 
00162     int depth = 0;
00163     char *pathptr = strdup(path), *pathptr_saved = pathptr;
00164     char *nameptr, *saveptr = NULL;
00165     char sql_from[SQL_MAX], sql_where[SQL_MAX];
00166     char *sql_from_end = sql_from, *sql_where_end = sql_where;
00167     char esc_name[PATH_MAX * 2];
00168 
00169     // TODO: Handle too long or too nested paths that don't fit in SQL_MAX!!!
00170     sql_from_end += snprintf(sql_from_end, SQL_MAX, "tree AS t0");
00171     sql_where_end += snprintf(sql_where_end, SQL_MAX, "t0.parent IS NULL");
00172     while ((nameptr = strtok_r(pathptr, "/", &saveptr)) != NULL) {
00173         if (depth++ == 0) {
00174           pathptr = NULL;
00175         }
00176 
00177         mysql_real_escape_string(mysql, esc_name, nameptr, strlen(nameptr));
00178         sql_from_end += snprintf(sql_from_end, SQL_MAX, " LEFT JOIN tree AS t%d ON t%d.inode = t%d.parent",
00179                  depth, depth-1, depth);
00180         sql_where_end += snprintf(sql_where_end, SQL_MAX, " AND t%d.name = '%s'",
00181                  depth, esc_name);
00182     }
00183     free(pathptr_saved);
00184 
00185     // TODO: Only run subquery when pointer to nlinks != NULL, otherwise we don't need it.
00186     snprintf(sql, SQL_MAX, "SELECT t%d.inode, t%d.name, t%d.parent, "
00187                            "       (SELECT COUNT(inode) FROM tree AS t%d WHERE t%d.inode=t%d.inode) "
00188                            "               AS nlinks "
00189                            "FROM %s WHERE %s",
00190              depth, depth, depth, 
00191              depth+1, depth+1, depth,
00192              sql_from, sql_where);
00193     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00194     ret = mysql_query(mysql, sql);
00195     if(ret){
00196         log_printf(LOG_ERROR, "ERROR: mysql_query()\n");
00197         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00198         return -EIO;
00199     }
00200 
00201     result = mysql_store_result(mysql);
00202     if(!result){
00203         log_printf(LOG_ERROR, "ERROR: mysql_store_result()\n");
00204         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00205         return -EIO;
00206     }
00207 
00208     if(mysql_num_rows(result) != 1){
00209         mysql_free_result(result);
00210         return -ENOENT;
00211     }
00212 
00213     row = mysql_fetch_row(result);
00214     if(!row){
00215         log_printf(LOG_ERROR, "ERROR: mysql_fetch_row()\n");
00216         return -EIO;
00217     }
00218     log_printf(LOG_D_OTHER, "query_inode(path='%s') => %s, %s, %s, %s\n",
00219                path, row[0], row[1], row[2], row[3]);
00220     
00221     if (inode)
00222         *inode = atol(row[0]);
00223     if (name)
00224         snprintf(name, name_len, "%s", row[1]);
00225     if (parent)
00226         *parent = row[2] ? atol(row[2]) : -1;   /* parent may be NULL */
00227     if (nlinks)
00228         *nlinks = atol(row[3]);
00229 
00230     mysql_free_result(result);
00231 
00232     return 0;
00233 }
00234 
00245 long query_inode(MYSQL *mysql, const char *path)
00246 {
00247     long inode, ret;
00248     
00249     ret = query_inode_full(mysql, path, NULL, 0, &inode, NULL, NULL);
00250     if (ret < 0)
00251       return ret;
00252     return inode;
00253 }
00254 
00269 int query_truncate(MYSQL *mysql, const char *path, off_t length)
00270 {
00271     int ret;
00272     char sql[SQL_MAX];
00273     struct data_blocks_info info;
00274 
00275     fill_data_blocks_info(&info, length, 0);
00276 
00277     long inode = query_inode(mysql, path);
00278     if (inode < 0)
00279       return inode;
00280 
00281     lock_inode(mysql, inode);
00282 
00283     snprintf(sql, SQL_MAX,
00284              "DELETE FROM data_blocks WHERE inode=%ld AND seq > %ld",
00285              inode, info.seq_last);
00286     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00287     if ((ret = mysql_query(mysql, sql))) goto err_out;
00288 
00289     snprintf(sql, SQL_MAX,
00290              "UPDATE data_blocks SET data=RPAD(data, %zu, '\\0') "
00291              "WHERE inode=%ld AND seq=%ld",
00292              info.length_last, inode, info.seq_last);
00293     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00294     if ((ret = mysql_query(mysql, sql))) goto err_out;
00295 
00296     snprintf(sql, SQL_MAX,
00297              "UPDATE inodes SET size=%lld WHERE inode=%ld",
00298              length, inode);
00299     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00300     if ((ret = mysql_query(mysql, sql))) goto err_out;
00301 
00302     unlock_inode(mysql, inode);
00303 
00304     return 0;
00305 
00306 err_out:
00307     unlock_inode(mysql, inode);
00308     log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00309     return ret;
00310 }
00311 
00323 int query_mkdirentry(MYSQL *mysql, long inode, const char *name, long parent)
00324 {
00325     int ret;
00326     char sql[SQL_MAX];
00327     char esc_name[PATH_MAX * 2];
00328 
00329     mysql_real_escape_string(mysql, esc_name, name, strlen(name));
00330     snprintf(sql, SQL_MAX,
00331              "INSERT INTO tree (name, parent, inode) VALUES ('%s', %ld, %ld)",
00332              esc_name, parent, inode);
00333 
00334     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00335     ret = mysql_query(mysql, sql);
00336     if(ret) {
00337       log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00338       return -EIO;
00339     }
00340 
00341     return 0;
00342 }
00343 
00353 int query_rmdirentry(MYSQL *mysql, const char *name, long parent)
00354 {
00355     int ret;
00356     char sql[SQL_MAX];
00357     char esc_name[PATH_MAX * 2];
00358 
00359     mysql_real_escape_string(mysql, esc_name, name, strlen(name));
00360     snprintf(sql, SQL_MAX,
00361              "DELETE FROM tree WHERE name='%s' AND parent=%ld",
00362              esc_name, parent);
00363 
00364     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00365     ret = mysql_query(mysql, sql);
00366     if(ret) {
00367       log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00368       return -EIO;
00369     }
00370 
00371     return 0;
00372 }
00373 
00392 long query_mknod(MYSQL *mysql, const char *path, mode_t mode, dev_t rdev,
00393                 long parent, int alloc_data)
00394 {
00395     int ret;
00396     char sql[SQL_MAX];
00397     long new_inode_number = 0;
00398     char *name, esc_name[PATH_MAX * 2];
00399 
00400     if (path[0] == '/' && path[1] == '\0')  {
00401         snprintf(sql, SQL_MAX,
00402                  "INSERT INTO tree (name, parent) VALUES ('/', NULL)");
00403 
00404         log_printf(LOG_D_SQL, "sql=%s\n", sql);
00405         ret = mysql_query(mysql, sql);
00406         if(ret)
00407           goto err_out;
00408     } else {
00409         name = strrchr(path, '/');
00410         if (!name || *++name == '\0') 
00411             return -ENOENT;
00412             
00413         mysql_real_escape_string(mysql, esc_name, name, strlen(name));
00414         snprintf(sql, SQL_MAX,
00415                  "INSERT INTO tree (name, parent) VALUES ('%s', %ld)",
00416                  esc_name, parent);
00417 
00418         log_printf(LOG_D_SQL, "sql=%s\n", sql);
00419         ret = mysql_query(mysql, sql);
00420         if(ret)
00421           goto err_out;
00422     }
00423 
00424     new_inode_number = mysql_insert_id(mysql);
00425 
00426     snprintf(sql, SQL_MAX,
00427              "INSERT INTO inodes(inode, mode, uid, gid, atime, ctime, mtime)"
00428              "VALUES(%ld, %d, %d, %d, UNIX_TIMESTAMP(NOW()), "
00429                     "UNIX_TIMESTAMP(NOW()), UNIX_TIMESTAMP(NOW()))",
00430              new_inode_number, mode,
00431              fuse_get_context()->uid, fuse_get_context()->gid);
00432 
00433     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00434     ret = mysql_query(mysql, sql);
00435     if(ret)
00436       goto err_out;
00437 
00438     return new_inode_number;
00439 
00440 err_out:
00441     log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00442     return ret;
00443 }
00444 
00456 long query_mkdir(MYSQL *mysql, const char *path, mode_t mode, long parent)
00457 {
00458     return query_mknod(mysql, path, S_IFDIR | mode, 0, parent, 0);
00459 }
00460 
00478 int query_readdir(MYSQL *mysql, long inode, void *buf, fuse_fill_dir_t filler)
00479 {
00480     int ret;
00481     char sql[SQL_MAX];
00482     MYSQL_RES* result;
00483     MYSQL_ROW row;
00484 
00485     snprintf(sql, sizeof(sql), "SELECT name FROM tree WHERE parent = '%ld'",
00486              inode);
00487 
00488     ret = mysql_query(mysql, sql);
00489     if(ret){
00490         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00491         return -EIO;
00492     }
00493 
00494     result = mysql_store_result(mysql);
00495     if(!result){
00496         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00497         return -EIO;
00498     }
00499 
00500     while((row = mysql_fetch_row(result)) != NULL){
00501         filler(buf, (char*)basename(row[0]), NULL, 0);
00502     }
00503 
00504     mysql_free_result(result);
00505 
00506     return ret;
00507 }
00508 
00521 int query_chmod(MYSQL *mysql, long inode, mode_t mode)
00522 {
00523     int ret;
00524     char sql[SQL_MAX];
00525 
00526     snprintf(sql, SQL_MAX,
00527              "UPDATE inodes SET mode=%d WHERE inode=%ld",
00528              mode, inode);
00529 
00530     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00531 
00532     ret = mysql_query(mysql, sql);
00533     if(ret){
00534         log_printf(LOG_ERROR, "Error: mysql_query()\n");
00535         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00536         return -EIO;
00537     }
00538 
00539     return 0;
00540 }
00541 
00555 int query_chown(MYSQL *mysql, long inode, uid_t uid, gid_t gid)
00556 {
00557     int ret;
00558     char sql[SQL_MAX];
00559     size_t index;
00560 
00561     index = snprintf(sql, SQL_MAX, "UPDATE inodes SET ");
00562     if (uid != (uid_t)-1)
00563         index += snprintf(sql + index, SQL_MAX - index, 
00564                           "uid=%d ", uid);
00565     if (gid != (gid_t)-1)
00566         index += snprintf(sql + index, SQL_MAX - index,
00567                           "%s gid=%d ", 
00568                           /* Insert comma if this is a second argument */
00569                           (uid != (uid_t)-1) ? "," : "",
00570                           gid);
00571     snprintf(sql + index, SQL_MAX - index, "WHERE inode=%ld", inode);
00572 
00573     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00574 
00575     ret = mysql_query(mysql, sql);
00576     if(ret){
00577         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00578         return -EIO;
00579     }
00580 
00581     return 0;
00582 }
00583 
00597 int query_utime(MYSQL *mysql, long inode, struct utimbuf *time)
00598 {
00599     int ret;
00600     char sql[SQL_MAX];
00601 
00602     snprintf(sql, SQL_MAX,
00603              "UPDATE inodes "
00604              "SET atime=%ld, mtime=%ld "
00605              "WHERE inode=%lu",
00606              time->actime, time->modtime, inode);
00607 
00608     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00609 
00610     ret = mysql_query(mysql, sql);
00611     if(ret){
00612         log_printf(LOG_ERROR, "Error: mysql_query()\n");
00613         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00614         return -EIO;
00615     }
00616 
00617     return 0;
00618 }
00619 
00634 int query_read(MYSQL *mysql, long inode, const char *buf, size_t size,
00635                off_t offset)
00636 {
00637     int ret;
00638     char sql[SQL_MAX];
00639     MYSQL_RES* result;
00640     MYSQL_ROW row;
00641     unsigned long length = 0L, copy_len, seq;
00642     struct data_blocks_info info;
00643     char *dst = (char *)buf;
00644     char *src, *zeroes = alloca(DATA_BLOCK_SIZE);
00645 
00646     fill_data_blocks_info(&info, size, offset);
00647 
00648     /* Read all required blocks */
00649     snprintf(sql, SQL_MAX,
00650              "SELECT seq, data, LENGTH(data) FROM data_blocks WHERE inode=%ld AND seq>=%lu AND seq <=%lu ORDER BY seq ASC",
00651              inode, info.seq_first, info.seq_last);
00652 
00653     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00654 
00655     ret = mysql_query(mysql, sql);
00656     if(ret){
00657         log_printf(LOG_ERROR, "ERROR: mysql_query()\n");
00658         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00659         return -EIO;
00660     }
00661 
00662     result = mysql_store_result(mysql);
00663     if(!result){
00664         log_printf(LOG_ERROR, "ERROR: mysql_store_result()\n");
00665         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00666         return -EIO;
00667     }
00668 
00669     /* This is a bit tricky as we support 'sparse' files now.
00670      * It means not all requested blocks must exist in the
00671      * database. For those that don't exist we'll return
00672      * a block of \0 instead.  */
00673     row = mysql_fetch_row(result);
00674     memset(zeroes, 0L, DATA_BLOCK_SIZE);
00675     for (seq = info.seq_first; seq<=info.seq_last; seq++) {
00676         off_t row_seq = -1;
00677         size_t row_len = DATA_BLOCK_SIZE;
00678         char *data = zeroes;
00679 
00680         if (row && (row_seq = atoll(row[0])) == seq) {
00681             data = row[1];
00682             row_len = atoll(row[2]);
00683         }
00684             
00685         if (seq == info.seq_first) {
00686             if (row_len < info.offset_first)
00687                 goto go_away;
00688 
00689             copy_len = MIN(row_len - info.offset_first, info.length_first);
00690             src = data + info.offset_first;
00691         } else if (seq == info.seq_last) {
00692             copy_len = MIN(info.length_last, row_len);
00693             src = data;
00694         } else {
00695             copy_len = MIN(DATA_BLOCK_SIZE, row_len);
00696             src = data;
00697         }
00698 
00699         memcpy(dst, src, copy_len);
00700         dst += copy_len;
00701         length += copy_len;
00702 
00703         if (row && row_seq == seq)
00704             row = mysql_fetch_row(result);
00705     }
00706 
00707 go_away:
00708     /* Read all remaining rows */
00709     while (mysql_fetch_row(result));
00710     mysql_free_result(result);
00711 
00712     return length;
00713 }
00714 
00736 static int write_one_block(MYSQL *mysql, long inode,
00737                                  unsigned long seq,
00738                                  const char *data, size_t size,
00739                                  off_t offset)
00740 {
00741     MYSQL_STMT *stmt;
00742     MYSQL_BIND bind[1];
00743     char sql[SQL_MAX];
00744     size_t current_block_size = query_size_block(mysql, inode, seq);
00745 
00746     /* Shortcut */
00747     if (size == 0) return 0;
00748 
00749     if (offset + size > DATA_BLOCK_SIZE) {
00750         log_printf(LOG_ERROR, "%s(): offset(%zu)+size(%zu)>max_block(%d)\n", 
00751                    __func__, offset, size, DATA_BLOCK_SIZE);
00752         return -EIO;
00753     }
00754 
00755     /* We expect the inode is already locked for this thread by caller! */
00756 
00757     if (current_block_size == -ENXIO) {
00758         /* This data block has not yet been allocated */
00759         snprintf(sql, SQL_MAX,
00760                  "INSERT INTO data_blocks SET inode=%ld, seq=%lu, data=''", inode, seq);
00761         log_printf(LOG_D_SQL, "sql=%s\n", sql);
00762         if(mysql_query(mysql, sql)){
00763             log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00764             return -EIO;
00765         }
00766 
00767         current_block_size = query_size_block(mysql, inode, seq);
00768     }
00769 
00770     stmt = mysql_stmt_init(mysql);
00771     if (!stmt)
00772     {
00773         log_printf(LOG_ERROR, "mysql_stmt_init(), out of memory\n");
00774         return -EIO;
00775     }
00776 
00777     memset(bind, 0, sizeof(bind));
00778     if (offset == 0 && current_block_size == 0) {
00779         snprintf(sql, SQL_MAX,
00780                  "UPDATE data_blocks "
00781                  "SET data=? "
00782                  "WHERE inode=%ld AND seq=%lu",
00783                  inode, seq);
00784     } else if (offset == current_block_size) {
00785         snprintf(sql, sizeof(sql),
00786                  "UPDATE data_blocks "
00787                  "SET data=CONCAT(data, ?) "
00788                  "WHERE inode=%ld AND seq=%lu",
00789                  inode, seq);
00790     } else {
00791         size_t pos, new_size;
00792         pos = snprintf(sql, sizeof(sql),
00793                  "UPDATE data_blocks SET data=CONCAT(");
00794         if (offset > 0)
00795             pos += snprintf(sql + pos, sizeof(sql) - pos, "RPAD(IF(ISNULL(data),'', data), %llu, '\\0'),", offset);
00796         pos += snprintf(sql + pos, sizeof(sql) - pos, "?,");
00797         new_size = offset + size;
00798         if (offset + size < current_block_size) {
00799             pos += snprintf(sql + pos, sizeof(sql) - pos, "SUBSTRING(data FROM %llu),", offset + size + 1);
00800             new_size = current_block_size;
00801         }
00802         sql[--pos] = '\0';      /* Remove the trailing comma. */
00803         pos += snprintf(sql + pos, sizeof(sql) - pos, ") WHERE inode=%ld AND seq=%lu",
00804                         inode, seq);
00805     }
00806     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00807 
00808     if (mysql_stmt_prepare(stmt, sql, strlen(sql))) {
00809         log_printf(LOG_ERROR, "mysql_stmt_prepare() failed: %s\n", mysql_stmt_error(stmt));
00810         goto err_out;
00811     }
00812 
00813     if (mysql_stmt_param_count(stmt) != 1) {
00814       log_printf(LOG_ERROR, "%s(): stmt_param_count=%d, expected 1\n", __func__, mysql_stmt_param_count(stmt));
00815       return -EIO;
00816     }
00817     bind[0].buffer_type= MYSQL_TYPE_LONG_BLOB;
00818     bind[0].buffer= (char *)data;
00819     bind[0].is_null= 0;
00820     bind[0].length= (unsigned long *)(void *)&size;
00821 
00822     if (mysql_stmt_bind_param(stmt, bind)) {
00823         log_printf(LOG_ERROR, "mysql_stmt_bind_param() failed: %s\n", mysql_stmt_error(stmt));
00824         goto err_out;
00825     }
00826 
00827     /*
00828     if (!mysql_stmt_send_long_data(stmt, 0, data, size))
00829     {
00830         log_printf(" send_long_data failed");
00831         goto err_out;
00832     }
00833     */
00834     if (mysql_stmt_execute(stmt)) {
00835         log_printf(LOG_ERROR, "mysql_stmt_execute() failed: %s\n", mysql_stmt_error(stmt));
00836         goto err_out;
00837     }
00838 
00839     if (mysql_stmt_close(stmt))
00840         log_printf(LOG_ERROR, "failed closing the statement: %s\n", mysql_stmt_error(stmt));
00841 
00842     /* Update file size */
00843     snprintf(sql, SQL_MAX,
00844              "UPDATE inodes SET size=("
00845                 "SELECT seq*%d + LENGTH(data) FROM data_blocks WHERE inode=%ld AND seq=("
00846                         "SELECT MAX(seq) FROM data_blocks WHERE inode=%ld"
00847                 ")"
00848              ") "
00849              "WHERE inode=%ld",
00850              DATA_BLOCK_SIZE, inode, inode, inode);
00851     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00852     if(mysql_query(mysql, sql)) {
00853         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00854         return -EIO;
00855     }
00856 
00857     return size;
00858 
00859 err_out:
00860         log_printf(LOG_ERROR, " %s\n", mysql_stmt_error(stmt));
00861         if (mysql_stmt_close(stmt))
00862             log_printf(LOG_ERROR, "failed closing the statement: %s\n", mysql_stmt_error(stmt));
00863         return -EIO;
00864 }
00865 
00879 int query_write(MYSQL *mysql, long inode, const char *data, size_t size,
00880                 off_t offset)
00881 {
00882     struct data_blocks_info info;
00883     unsigned long seq;
00884     const char *ptr;
00885     int ret, ret_size = 0;
00886 
00887     fill_data_blocks_info(&info, size, offset);
00888 
00889     /* Handle first block */
00890     lock_inode(mysql, inode);
00891     ret = write_one_block(mysql, inode, info.seq_first, data,
00892                           info.length_first, info.offset_first);
00893     unlock_inode(mysql, inode);
00894     if (ret < 0)
00895         return ret;
00896     ret_size = ret;
00897 
00898     /* Shortcut - if last block seq is the same as first block
00899      * seq simply go away as it's the same block */
00900     if (info.seq_first == info.seq_last)
00901         return ret_size;
00902 
00903     ptr = data + info.length_first;
00904 
00905     /* Handle all full-sized intermediate blocks */
00906     for (seq = info.seq_first + 1; seq < info.seq_last; seq++) {
00907         lock_inode(mysql, inode);
00908         ret = write_one_block(mysql, inode, seq, ptr, DATA_BLOCK_SIZE, 0);
00909         unlock_inode(mysql, inode);
00910         if (ret < 0)
00911             return ret;
00912         ptr += DATA_BLOCK_SIZE;
00913         ret_size += ret;
00914     }
00915 
00916     /* Handle last block */
00917     lock_inode(mysql, inode);
00918     ret = write_one_block(mysql, inode, info.seq_last, ptr,
00919                           info.length_last, 0);
00920     unlock_inode(mysql, inode);
00921     if (ret < 0)
00922         return ret;
00923     ret_size += ret;
00924 
00925     return ret_size;
00926 }
00927 
00940 ssize_t query_size(MYSQL *mysql, long inode)
00941 {
00942     size_t ret;
00943     char sql[SQL_MAX];
00944     MYSQL_RES *result;
00945     MYSQL_ROW row;
00946 
00947     snprintf(sql, SQL_MAX, "SELECT size FROM inodes WHERE inode=%ld",
00948              inode);
00949 
00950     ret = mysql_query(mysql, sql);
00951     if(ret){
00952         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00953         return -EIO;
00954     }
00955     log_printf(LOG_D_SQL, "sql=%s\n", sql);
00956 
00957     result = mysql_store_result(mysql);
00958     if(!result){
00959         log_printf(LOG_ERROR, "ERROR: mysql_store_result()\n");
00960         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
00961         return -EIO;
00962     }
00963 
00964     if(mysql_num_rows(result) != 1 || mysql_num_fields(result) != 1){
00965         mysql_free_result(result);
00966         return -EIO;
00967     }
00968 
00969     row = mysql_fetch_row(result);
00970     if(!row){
00971         return -EIO;
00972     }
00973 
00974     if(row[0]){
00975         ret = atoll(row[0]);
00976     }else{
00977         ret = 0;
00978     }
00979     mysql_free_result(result);
00980 
00981     return ret;
00982 }
00983 
00995 ssize_t query_size_block(MYSQL *mysql, long inode, unsigned long seq)
00996 {
00997     size_t ret;
00998     char sql[SQL_MAX];
00999     MYSQL_RES *result;
01000     MYSQL_ROW row;
01001 
01002     snprintf(sql, SQL_MAX, "SELECT LENGTH(data) FROM data_blocks WHERE inode=%ld AND seq=%lu",
01003              inode, seq);
01004 
01005     ret = mysql_query(mysql, sql);
01006     if(ret){
01007         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01008         return -EIO;
01009     }
01010     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01011 
01012     result = mysql_store_result(mysql);
01013     if(!result){
01014         log_printf(LOG_ERROR, "ERROR: mysql_store_result()\n");
01015         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01016         return -EIO;
01017     }
01018 
01019     if(mysql_num_rows(result) == 0) {
01020         mysql_free_result(result);
01021         return -ENXIO;
01022     }
01023 
01024     row = mysql_fetch_row(result);
01025     if(!row){
01026         return -EIO;
01027     }
01028 
01029     if(row[0]){
01030         ret = atoll(row[0]);
01031     }else{
01032         ret = 0;
01033     }
01034     mysql_free_result(result);
01035 
01036     return ret;
01037 }
01038 
01050 int query_rename(MYSQL *mysql, const char *from, const char *to)
01051 {
01052     int ret;
01053     long inode, parent_to, parent_from;
01054     char *tmp, *new_name, *old_name;
01055     char esc_new_name[PATH_MAX * 2], esc_old_name[PATH_MAX * 2];
01056     char sql[SQL_MAX];
01057 
01058     inode = query_inode(mysql, from);
01059 
01060     /* Lots of strdup()s follow because dirname() & basename()
01061      * may modify the original string. */
01062     tmp = strdup(from);
01063     parent_from = query_inode(mysql, dirname(tmp));
01064     free(tmp);
01065 
01066     tmp = strdup(from);
01067     old_name = basename(tmp);
01068     mysql_real_escape_string(mysql, esc_old_name, old_name, strlen(old_name));
01069     free(tmp);
01070 
01071     tmp = strdup(to);
01072     parent_to = query_inode(mysql, dirname(tmp));
01073     free(tmp);
01074 
01075     tmp = strdup(to);
01076     new_name = basename(tmp);
01077     mysql_real_escape_string(mysql, esc_new_name, new_name, strlen(new_name));
01078     free(tmp);
01079 
01080     snprintf(sql, SQL_MAX,
01081              "UPDATE tree "
01082              "SET name='%s', parent=%ld "
01083              "WHERE inode=%ld AND name='%s' AND parent=%ld ",
01084              esc_new_name, parent_to,
01085              inode, esc_old_name, parent_from);
01086 
01087     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01088 
01089     ret = mysql_query(mysql, sql);
01090     if(ret){
01091         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01092         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01093         return -EIO;
01094     }
01095 
01096     /*
01097     if (mysql_affected_rows(mysql) < 1)
01098       return -ETHIS_IS_STRANGE; / * Someone deleted the direntry? Do we care? * /
01099     */
01100 
01101     return 0;
01102 }
01103 
01114 int query_inuse_inc(MYSQL *mysql, long inode, int increment)
01115 {
01116     int ret;
01117     char sql[SQL_MAX];
01118 
01119     snprintf(sql, SQL_MAX,
01120              "UPDATE inodes SET inuse = inuse + %d "
01121              "WHERE inode=%lu",
01122              increment, inode);
01123 
01124     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01125 
01126     ret = mysql_query(mysql, sql);
01127     if(ret){
01128         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01129         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01130         return -EIO;
01131     }
01132 
01133     return 0;
01134 }
01135 
01144 int query_purge_deleted(MYSQL *mysql, long inode)
01145 {
01146     int ret;
01147     char sql[SQL_MAX];
01148 
01149     snprintf(sql, SQL_MAX,
01150              "DELETE FROM inodes WHERE inode=%ld AND inuse=0 AND deleted=1",
01151              inode);
01152 
01153     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01154 
01155     ret = mysql_query(mysql, sql);
01156     if(ret){
01157         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01158         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01159         return -EIO;
01160     }
01161 
01162     return 0;
01163 }
01164 
01174 int query_set_deleted(MYSQL *mysql, long inode)
01175 {
01176     int ret;
01177     char sql[SQL_MAX];
01178 
01179     snprintf(sql, SQL_MAX,
01180              "UPDATE inodes LEFT JOIN tree ON inodes.inode = tree.inode SET inodes.deleted=1 "
01181              "WHERE inodes.inode = %ld AND tree.name IS NULL",
01182              inode);
01183 
01184     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01185 
01186     ret = mysql_query(mysql, sql);
01187     if(ret){
01188         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01189         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01190         return -EIO;
01191     }
01192 
01193     return 0;
01194 }
01195 
01209 int query_fsck(MYSQL *mysql)
01210 {
01211 
01212     /*
01213      query_fsck by florian wiessner (f.wiessner@smart-weblications.de)
01214     */
01215     printf("Starting fsck\n");
01216 
01217     // 1. delete inodes with deleted==1
01218     int ret;
01219 //    int ret2;
01220     int result;
01221     char sql[SQL_MAX];
01222     printf("Stage 1...\n");
01223     snprintf(sql, SQL_MAX,
01224              "DELETE from inodes WHERE inodes.deleted = 1");
01225 
01226     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01227 
01228     ret = mysql_query(mysql, sql);
01229     if(ret){
01230         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01231         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01232         return -EIO;
01233     }
01234     // 2. - delete direntries without corresponding inode
01235     printf("Stage 2...\n");
01236     snprintf(sql, SQL_MAX, "delete from tree where tree.inode not in (select inode from inodes);");
01237 
01238     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01239     ret = mysql_query(mysql, sql);
01240 
01241     if(ret){
01242         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01243         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01244         return -EIO;
01245     }
01246 
01247 
01248 
01249     // 3. set inuse=0 for all inodes
01250     printf("Stage 3...\n");
01251     snprintf(sql, SQL_MAX, "UPDATE inodes SET inuse=0;");
01252 
01253     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01254 
01255     ret = mysql_query(mysql, sql);
01256     if(ret){
01257         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01258         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01259         return -EIO;
01260     }
01261 
01262 
01263     // 4. delete data without existing inode
01264     printf("Stage 4...\n");
01265     snprintf(sql, SQL_MAX, "delete from data_blocks where inode not in (select inode from inodes);");
01266 
01267     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01268 
01269     ret = mysql_query(mysql, sql);
01270     if(ret){
01271         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01272         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01273         return -EIO;
01274     }
01275 
01276 
01277     // 5. synchronize inodes.size=data.LENGTH(data)
01278     printf("Stage 5...\n");
01279     long int inode;
01280     long int size;
01281     
01282     snprintf(sql, SQL_MAX, "select inode, sum(OCTET_LENGTH(data)) as size from data_blocks group by inode");
01283 
01284     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01285 
01286     ret = mysql_query(mysql, sql);
01287 
01288     MYSQL_RES* myresult;
01289     MYSQL_ROW row;
01290 
01291     myresult = mysql_store_result(mysql);
01292     while ((row = mysql_fetch_row(myresult)) != NULL) {
01293      inode = atol(row[0]);
01294      size = atol(row[1]);
01295                      
01296       snprintf(sql, SQL_MAX, "update inodes set size=%ld where inode=%ld;", size, inode);
01297       log_printf(LOG_D_SQL, "sql=%s\n", sql);
01298       result = mysql_query(mysql, sql);
01299 
01300 /*      if (myresult) { // something has gone wrong.. delete datablocks...
01301 
01302         snprintf(sql, SQL_MAX, "delete from inodes where inode=%ld;", inode);
01303         log_printf(LOG_D_SQL, "sql=%s\n", sql);
01304         ret2 = mysql_query(mysql, sql);
01305 
01306       }
01307 */ // skip this for now!
01308 
01309     }
01310 
01311     if(ret){
01312         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01313         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01314        return -EIO;
01315     }
01316     mysql_free_result(myresult);
01317 
01318 /*    printf("optimizing tables\n");
01319     snprintf(sql, SQL_MAX,
01320              "OPTIMIZE TABLE inodes;");
01321 
01322     log_printf(LOG_D_SQL, "sql=%s\n", sql);
01323 
01324     ret = mysql_query(mysql, sql);
01325     if(ret){
01326         log_printf(LOG_ERROR, "Error: mysql_query()\n");
01327         log_printf(LOG_ERROR, "mysql_error: %s\n", mysql_error(mysql));
01328         return -EIO;
01329     }
01330 */
01331     printf("fsck done!\n");
01332     return ret;
01333 
01334 }

Generated on Sun Jul 12 20:25:26 2009 for mysqlfs by  doxygen 1.4.7