Merge branch 'device-sys'
[lunaix-os.git] / lunaix-os / kernel / fs / vfs.c
index 476b00bbc7e28c4426f9711464cbfeee2052cf31..df374cdf41ad41fcf3cd15592a8419c1f22c4c3d 100644 (file)
@@ -163,6 +163,7 @@ vfs_dcache_remove(struct v_dnode* dnode)
     assert(dnode->ref_count == 1);
 
     llist_delete(&dnode->siblings);
+    llist_delete(&dnode->aka_list);
     hlist_delete(&dnode->hash_list);
 
     dnode->parent = NULL;
@@ -225,8 +226,10 @@ void
 vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode)
 {
     if (assign_to->inode) {
+        llist_delete(&assign_to->aka_list);
         assign_to->inode->link_count--;
     }
+    llist_append(&inode->aka_dnodes, &assign_to->aka_list);
     assign_to->inode = inode;
     inode->link_count++;
 }
@@ -254,7 +257,7 @@ vfs_link(struct v_dnode* to_link, struct v_dnode* name)
 }
 
 int
-vfs_close(struct v_file* file)
+vfs_pclose(struct v_file* file, pid_t pid)
 {
     int errno = 0;
     if (file->ref_count > 1) {
@@ -263,26 +266,25 @@ vfs_close(struct v_file* file)
         atomic_fetch_sub(&file->dnode->ref_count, 1);
         file->inode->open_count--;
 
-        // Remove dead lock.
+        // Prevent dead lock.
         // This happened when process is terminated while blocking on read.
         // In that case, the process is still holding the inode lock and it will
         // never get released.
-        // FIXME is this a good solution?
         /*
-         * Consider two process both open the same file both with fd=x.
+         * The unlocking should also include ownership check.
+         *
+         * To see why, consider two process both open the same file both with
+         * fd=x.
          *      Process A: busy on reading x
          *      Process B: do nothing with x
-         * Assume that, after a very short time, process B get terminated while
-         * process A is still busy in it's reading business. By this design, the
-         * inode lock of this file x is get released by B rather than A. And
-         * this will cause a probable race condition on A if other process is
-         * writing to this file later after B exit.
-         *
-         * A possible solution is to add a owner identification in the lock
-         * context, so only the lock holder can do the release.
+         * Assuming that, after a very short time, process B get terminated
+         * while process A is still busy in it's reading business. By this
+         * design, the inode lock of this file x is get released by B rather
+         * than A. And this will cause a probable race condition on A if other
+         * process is writing to this file later after B exit.
          */
         if (mutex_on_hold(&file->inode->lock)) {
-            unlock_inode(file->inode);
+            mutex_unlock_for(&file->inode->lock, pid);
         }
         mnt_chillax(file->dnode->mnt);
 
@@ -292,6 +294,12 @@ vfs_close(struct v_file* file)
     return errno;
 }
 
+int
+vfs_close(struct v_file* file)
+{
+    return vfs_pclose(file, __current->pid);
+}
+
 int
 vfs_fsync(struct v_file* file)
 {
@@ -382,6 +390,7 @@ vfs_d_alloc(struct v_dnode* parent, struct hstr* name)
     memset(dnode, 0, sizeof(*dnode));
     llist_init_head(&dnode->children);
     llist_init_head(&dnode->siblings);
+    llist_init_head(&dnode->aka_list);
     mutex_init(&dnode->lock);
 
     dnode->ref_count = ATOMIC_VAR_INIT(0);
@@ -464,6 +473,7 @@ vfs_i_alloc(struct v_superblock* sb)
     memset(inode, 0, sizeof(*inode));
     mutex_init(&inode->lock);
     llist_init_head(&inode->xattrs);
+    llist_init_head(&inode->aka_dnodes);
 
     sb->ops.init_inode(sb, inode);
 
@@ -773,7 +783,7 @@ done:
 int
 vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
 {
-    if (!dnode || dnode->parent == dnode) {
+    if (!dnode) {
         return 0;
     }
 
@@ -781,13 +791,19 @@ vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth)
         return ENAMETOOLONG;
     }
 
-    size_t len = vfs_get_path(dnode->parent, buf, size, depth + 1);
+    size_t len = 0;
+
+    if (dnode->parent != dnode) {
+        len = vfs_get_path(dnode->parent, buf, size, depth + 1);
+    }
 
     if (len >= size) {
         return len;
     }
 
-    buf[len++] = VFS_PATH_DELIM;
+    if (!len || buf[len - 1] != VFS_PATH_DELIM) {
+        buf[len++] = VFS_PATH_DELIM;
+    }
 
     size_t cpy_size = MIN(dnode->name.len, size - len);
     strncpy(buf + len, dnode->name.value, cpy_size);