OSDN Git Service

Handle I/O errors in exfat_put_node().
authorresver@gmail.com <resver@gmail.com@60bc1c72-a15a-11de-b98f-4500b42dc123>
Tue, 8 Apr 2014 04:21:21 +0000 (04:21 +0000)
committerresver@gmail.com <resver@gmail.com@60bc1c72-a15a-11de-b98f-4500b42dc123>
Tue, 8 Apr 2014 04:21:21 +0000 (04:21 +0000)
git-svn-id: http://exfat.googlecode.com/svn/trunk@396 60bc1c72-a15a-11de-b98f-4500b42dc123

fuse/main.c
libexfat/cluster.c
libexfat/exfat.h
libexfat/mount.c
libexfat/node.c

index 9f900ae..825fa19 100644 (file)
@@ -83,6 +83,13 @@ static int fuse_exfat_truncate(const char* path, off_t size)
                return rc;
 
        rc = exfat_truncate(&ef, node, size, true);
+       if (rc != 0)
+       {
+               exfat_flush_node(&ef, node);    /* ignore return code */
+               exfat_put_node(&ef, node);
+               return rc;
+       }
+       rc = exfat_flush_node(&ef, node);
        exfat_put_node(&ef, node);
        return rc;
 }
@@ -151,7 +158,18 @@ static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
 {
        exfat_debug("[%s] %s", __func__, path);
        exfat_put_node(&ef, get_node(fi));
-       return 0;
+       return 0; /* FUSE ignores this return value */
+}
+
+static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
+{
+       /*
+          This handler is called by FUSE on close(). FUSE also deals with removals
+          of open files, so we don't free clusters here but only on rmdir and
+          unlink.
+       */
+       exfat_debug("[%s] %s", __func__, path);
+       return exfat_flush_node(&ef, get_node(fi));
 }
 
 static int fuse_exfat_fsync(const char* path, int datasync,
@@ -160,12 +178,6 @@ static int fuse_exfat_fsync(const char* path, int datasync,
        int rc;
 
        exfat_debug("[%s] %s", __func__, path);
-       if (get_node(fi) != NULL)
-       {
-               rc = exfat_flush_node(&ef, get_node(fi));
-               if (rc != 0)
-                       return rc;
-       }
        rc = exfat_flush(&ef);
        if (rc != 0)
                return rc;
@@ -209,7 +221,9 @@ static int fuse_exfat_unlink(const char* path)
 
        rc = exfat_unlink(&ef, node);
        exfat_put_node(&ef, node);
-       return rc;
+       if (rc != 0)
+               return rc;
+       return exfat_cleanup_node(&ef, node);
 }
 
 static int fuse_exfat_rmdir(const char* path)
@@ -225,7 +239,9 @@ static int fuse_exfat_rmdir(const char* path)
 
        rc = exfat_rmdir(&ef, node);
        exfat_put_node(&ef, node);
-       return rc;
+       if (rc != 0)
+               return rc;
+       return exfat_cleanup_node(&ef, node);
 }
 
 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
@@ -333,6 +349,7 @@ static struct fuse_operations fuse_exfat_ops =
        .readdir        = fuse_exfat_readdir,
        .open           = fuse_exfat_open,
        .release        = fuse_exfat_release,
+       .flush          = fuse_exfat_flush,
        .fsync          = fuse_exfat_fsync,
        .fsyncdir       = fuse_exfat_fsync,
        .read           = fuse_exfat_read,
index e3cb251..8c61e45 100644 (file)
@@ -136,8 +136,23 @@ static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
        return EXFAT_CLUSTER_END;
 }
 
+static int flush_nodes(struct exfat* ef, struct exfat_node* node)
+{
+       struct exfat_node* p;
+
+       for (p = node->child; p != NULL; p = p->next)
+       {
+               int rc = flush_nodes(ef, p);
+               if (rc != 0)
+                       return rc;
+       }
+       return exfat_flush_node(ef, node);
+}
+
 int exfat_flush(struct exfat* ef)
 {
+       int rc = flush_nodes(ef, ef->root);
+
        if (ef->cmap.dirty)
        {
                if (exfat_pwrite(ef->dev, ef->cmap.chunk,
@@ -149,7 +164,8 @@ int exfat_flush(struct exfat* ef)
                }
                ef->cmap.dirty = false;
        }
-       return 0;
+
+       return rc;
 }
 
 static bool set_next_cluster(const struct exfat* ef, bool contiguous,
@@ -316,6 +332,7 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node,
        {
                previous = node->start_cluster;
                node->start_cluster = EXFAT_CLUSTER_FREE;
+               node->flags |= EXFAT_ATTRIB_DIRTY;
        }
        node->fptr_index = 0;
        node->fptr_cluster = node->start_cluster;
index f8924cb..3c5788d 100644 (file)
@@ -193,6 +193,7 @@ size_t utf16_length(const le16_t* str);
 
 struct exfat_node* exfat_get_node(struct exfat_node* node);
 void exfat_put_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node);
 int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir);
 void exfat_reset_cache(struct exfat* ef);
 int exfat_flush_node(struct exfat* ef, struct exfat_node* node);
index 70c8af5..e9bac09 100644 (file)
@@ -343,11 +343,12 @@ static void finalize_super_block(struct exfat* ef)
                ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total;
        }
 
-       commit_super_block(ef);
+       commit_super_block(ef); /* ignore return code */
 }
 
 void exfat_unmount(struct exfat* ef)
 {
+       exfat_flush(ef);        /* ignore return code */
        exfat_put_node(ef, ef->root);
        exfat_reset_cache(ef);
        free(ef->root);
index af05533..4020874 100644 (file)
@@ -44,31 +44,47 @@ struct exfat_node* exfat_get_node(struct exfat_node* node)
 
 void exfat_put_node(struct exfat* ef, struct exfat_node* node)
 {
-       if (--node->references < 0)
+       char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+       --node->references;
+       if (node->references < 0)
        {
-               char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_bug("reference counter of `%s' is below zero", buffer);
        }
-
-       if (node->references == 0)
+       else if (node->references == 0 && node != ef->root)
        {
-               /* FIXME handle I/O error */
-               if (exfat_flush_node(ef, node) != 0)
-                       exfat_bug("node flush failed");
-               if (node->flags & EXFAT_ATTRIB_UNLINKED)
+               if (node->flags & EXFAT_ATTRIB_DIRTY)
                {
-                       /* free all clusters and node structure itself */
-                       exfat_truncate(ef, node, 0, true);
-                       free(node);
+                       exfat_get_name(node, buffer, sizeof(buffer) - 1);
+                       exfat_warn("dirty node `%s' with zero references", buffer);
                }
-               /* FIXME handle I/O error */
-               if (exfat_flush(ef) != 0)
-                       exfat_bug("flush failed");
        }
 }
 
 /**
+ * This function must be called on rmdir and unlink (after the last
+ * exfat_put_node()) to free clusters.
+ */
+int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node)
+{
+       int rc = 0;
+
+       if (node->references != 0)
+               exfat_bug("unable to cleanup a node with %d references",
+                               node->references);
+
+       if (node->flags & EXFAT_ATTRIB_UNLINKED)
+       {
+               /* free all clusters and node structure itself */
+               rc = exfat_truncate(ef, node, 0, true);
+               /* free the node even in case of error or its memory will be lost */
+               free(node);
+       }
+       return rc;
+}
+
+/**
  * Cluster + offset from the beginning of the directory to absolute offset.
  */
 static off_t co2o(struct exfat* ef, cluster_t cluster, off_t offset)
@@ -525,6 +541,8 @@ static void tree_detach(struct exfat_node* node)
 
 static void reset_cache(struct exfat* ef, struct exfat_node* node)
 {
+       char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
        while (node->child)
        {
                struct exfat_node* p = node->child;
@@ -535,11 +553,15 @@ static void reset_cache(struct exfat* ef, struct exfat_node* node)
        node->flags &= ~EXFAT_ATTRIB_CACHED;
        if (node->references != 0)
        {
-               char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_warn("non-zero reference counter (%d) for `%s'",
                                node->references, buffer);
        }
+       if (node != ef->root && (node->flags & EXFAT_ATTRIB_DIRTY))
+       {
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
+               exfat_bug("node `%s' is dirty", buffer);
+       }
        while (node->references)
                exfat_put_node(ef, node);
 }
@@ -731,9 +753,15 @@ static int delete(struct exfat* ef, struct exfat_node* node)
        exfat_update_mtime(parent);
        tree_detach(node);
        rc = shrink_directory(ef, parent, deleted_offset);
-       exfat_put_node(ef, parent);
-       /* file clusters will be freed when node reference counter becomes 0 */
        node->flags |= EXFAT_ATTRIB_UNLINKED;
+       if (rc != 0)
+       {
+               exfat_flush_node(ef, parent);
+               exfat_put_node(ef, parent);
+               return rc;
+       }
+       rc = exfat_flush_node(ef, parent);
+       exfat_put_node(ef, parent);
        return rc;
 }
 
@@ -746,10 +774,14 @@ int exfat_unlink(struct exfat* ef, struct exfat_node* node)
 
 int exfat_rmdir(struct exfat* ef, struct exfat_node* node)
 {
+       int rc;
+
        if (!(node->flags & EXFAT_ATTRIB_DIR))
                return -ENOTDIR;
        /* check that directory is empty */
-       exfat_cache_directory(ef, node);
+       rc = exfat_cache_directory(ef, node);
+       if (rc != 0)
+               return rc;
        if (node->child)
                return -ENOTEMPTY;
        return delete(ef, node);
@@ -909,6 +941,12 @@ static int create(struct exfat* ef, const char* path, uint16_t attrib)
                return rc;
        }
        rc = write_entry(ef, dir, name, cluster, offset, attrib);
+       if (rc != 0)
+       {
+               exfat_put_node(ef, dir);
+               return rc;
+       }
+       rc = exfat_flush_node(ef, dir);
        exfat_put_node(ef, dir);
        return rc;
 }
@@ -937,6 +975,13 @@ int exfat_mkdir(struct exfat* ef, const char* path)
                exfat_put_node(ef, node);
                return rc;
        }
+       rc = exfat_flush_node(ef, node);
+       if (rc != 0)
+       {
+               delete(ef, node);
+               exfat_put_node(ef, node);
+               return rc;
+       }
        exfat_put_node(ef, node);
        return 0;
 }