]> git.proxmox.com Git - qemu.git/blobdiff - hw/9pfs/virtio-9p.c
hw/9pfs: Fix memleaks in some 9p operation
[qemu.git] / hw / 9pfs / virtio-9p.c
index 4ba2590566aa966baa802d5a6b0e546dfbcb1ba3..1f186a25fb8c8e8241be6299cc46bbde27a05ef3 100644 (file)
@@ -235,6 +235,27 @@ static size_t v9fs_string_size(V9fsString *str)
     return str->size;
 }
 
+/*
+ * returns 0 if fid got re-opened, 1 if not, < 0 on error */
+static int v9fs_reopen_fid(V9fsState *s, V9fsFidState *f)
+{
+    int err = 1;
+    if (f->fid_type == P9_FID_FILE) {
+        if (f->fs.fd == -1) {
+            do {
+                err = v9fs_co_open(s, f, f->open_flags);
+            } while (err == -EINTR);
+        }
+    } else if (f->fid_type == P9_FID_DIR) {
+        if (f->fs.dir == NULL) {
+            do {
+                err = v9fs_co_opendir(s, f);
+            } while (err == -EINTR);
+        }
+    }
+    return err;
+}
+
 static V9fsFidState *get_fid(V9fsState *s, int32_t fid)
 {
     int err;
@@ -249,23 +270,16 @@ static V9fsFidState *get_fid(V9fsState *s, int32_t fid)
              * in open later.
              */
             f->ref++;
-
             /*
              * check whether we need to reopen the
              * file. We might have closed the fd
              * while trying to free up some file
              * descriptors.
              */
-            if (f->fid_type == P9_FID_FILE) {
-                if (f->fs.fd == -1) {
-                    do {
-                        err = v9fs_co_open(s, f, f->open_flags);
-                    } while (err == -EINTR);
-                    if (err < 0) {
-                        f->ref--;
-                        return NULL;
-                    }
-                }
+            err = v9fs_reopen_fid(s, f);
+            if (err < 0) {
+                f->ref--;
+                return NULL;
             }
             /*
              * Mark the fid as referenced so that the LRU
@@ -348,7 +362,9 @@ static int free_fid(V9fsState *s, V9fsFidState *fidp)
             retval = v9fs_co_close(s, fidp->fs.fd);
         }
     } else if (fidp->fid_type == P9_FID_DIR) {
-        retval = v9fs_co_closedir(s, fidp->fs.dir);
+        if (fidp->fs.dir != NULL) {
+            retval = v9fs_co_closedir(s, fidp->fs.dir);
+        }
     } else if (fidp->fid_type == P9_FID_XATTR) {
         retval = v9fs_xattr_fid_clunk(s, fidp);
     }
@@ -429,6 +445,19 @@ void v9fs_reclaim_fd(V9fsState *s)
                 f->fs.fd = -1;
                 reclaim_count++;
             }
+        } else if (f->fid_type == P9_FID_DIR) {
+            if (f->fs.dir != NULL) {
+                /*
+                 * Up the reference count so that
+                 * a clunk request won't free this fid
+                 */
+                f->ref++;
+                f->rclm_lst = reclaim_list;
+                reclaim_list = f;
+                f->fs_reclaim.dir = f->fs.dir;
+                f->fs.dir = NULL;
+                reclaim_count++;
+            }
         }
         if (reclaim_count >= open_fd_rc) {
             break;
@@ -443,6 +472,8 @@ void v9fs_reclaim_fd(V9fsState *s)
         reclaim_list = f->rclm_lst;
         if (f->fid_type == P9_FID_FILE) {
             v9fs_co_close(s, f->fs_reclaim.fd);
+        } else if (f->fid_type == P9_FID_DIR) {
+            v9fs_co_closedir(s, f->fs_reclaim.dir);
         }
         f->rclm_lst = NULL;
         /*
@@ -463,19 +494,18 @@ static int v9fs_mark_fids_unreclaim(V9fsState *s, V9fsString *str)
         if (!strcmp(fidp->path.data, str->data)) {
             /* Mark the fid non reclaimable. */
             fidp->flags |= FID_NON_RECLAIMABLE;
-            /* reopen the file if already closed */
-            if (fidp->fs.fd == -1) {
-                do {
-                    err = v9fs_co_open(s, fidp, fidp->open_flags);
-                } while (err == -EINTR);
-                if (err < 0) {
-                    return -1;
-                }
-                /*
-                 * Go back to head of fid list because
-                 * the list could have got updated when
-                 * switched to the worker thread
-                 */
+
+            /* reopen the file/dir if already closed */
+            err = v9fs_reopen_fid(s, fidp);
+            if (err < 0) {
+                return -1;
+            }
+            /*
+             * Go back to head of fid list because
+             * the list could have got updated when
+             * switched to the worker thread
+             */
+            if (err == 0) {
                 fidp = &head_fid;
             }
         }
@@ -513,6 +543,7 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
 {
     size_t size;
 
+    memset(&qidp->path, 0, sizeof(qidp->path));
     size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
     memcpy(&qidp->path, &stbuf->st_ino, size);
     qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
@@ -1592,7 +1623,7 @@ static void v9fs_lcreate(void *opaque)
     if (err < 0) {
         fidp->fid_type = P9_FID_NONE;
         if (fidp->fs.fd > 0) {
-            close(fidp->fs.fd);
+            v9fs_co_close(pdu->s, fidp->fs.fd);
         }
         goto out;
     }
@@ -2076,6 +2107,7 @@ static void v9fs_create(void *opaque)
         if (err < 0) {
             goto out;
         }
+        v9fs_string_copy(&fidp->path, &fullname);
         err = v9fs_co_opendir(pdu->s, fidp);
         if (err < 0) {
             goto out;
@@ -2160,7 +2192,7 @@ static void v9fs_create(void *opaque)
     if (err < 0) {
         fidp->fid_type = P9_FID_NONE;
         if (fidp->fs.fd) {
-            close(fidp->fs.fd);
+            v9fs_co_close(pdu->s, fidp->fs.fd);
         }
         goto out;
     }
@@ -2664,6 +2696,7 @@ out_nofid:
     err = offset;
     err += pdu_marshal(pdu, offset, "b", status);
     complete_pdu(s, pdu, err);
+    v9fs_string_free(&flock->client_id);
     g_free(flock);
 }
 
@@ -2704,6 +2737,7 @@ out:
     put_fid(s, fidp);
 out_nofid:
     complete_pdu(s, pdu, err);
+    v9fs_string_free(&glock->client_id);
     g_free(glock);
 }