{"action":"closed","issue":{"url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/issues/57","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/issues/57/labels{/name}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/issues/57/comments","events_url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/issues/57/events","html_url":"https://github.com/GoogleCloudPlatform/gcsfuse/issues/57","id":79233832,"number":57,"title":"Inode ID reuse causes EBUSY errors on Linux","user":{"login":"jacobsa","id":202895,"avatar_url":"https://avatars.githubusercontent.com/u/202895?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobsa","html_url":"https://github.com/jacobsa","followers_url":"https://api.github.com/users/jacobsa/followers","following_url":"https://api.github.com/users/jacobsa/following{/other_user}","gists_url":"https://api.github.com/users/jacobsa/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobsa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobsa/subscriptions","organizations_url":"https://api.github.com/users/jacobsa/orgs","repos_url":"https://api.github.com/users/jacobsa/repos","events_url":"https://api.github.com/users/jacobsa/events{/privacy}","received_events_url":"https://api.github.com/users/jacobsa/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":{"login":"jacobsa","id":202895,"avatar_url":"https://avatars.githubusercontent.com/u/202895?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobsa","html_url":"https://github.com/jacobsa","followers_url":"https://api.github.com/users/jacobsa/followers","following_url":"https://api.github.com/users/jacobsa/following{/other_user}","gists_url":"https://api.github.com/users/jacobsa/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobsa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobsa/subscriptions","organizations_url":"https://api.github.com/users/jacobsa/orgs","repos_url":"https://api.github.com/users/jacobsa/repos","events_url":"https://api.github.com/users/jacobsa/events{/privacy}","received_events_url":"https://api.github.com/users/jacobsa/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/milestones/3","html_url":"https://github.com/GoogleCloudPlatform/gcsfuse/milestones/ga","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/gcsfuse/milestones/3/labels","id":1035127,"number":3,"title":"ga","description":null,"creator":{"login":"jacobsa","id":202895,"avatar_url":"https://avatars.githubusercontent.com/u/202895?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobsa","html_url":"https://github.com/jacobsa","followers_url":"https://api.github.com/users/jacobsa/followers","following_url":"https://api.github.com/users/jacobsa/following{/other_user}","gists_url":"https://api.github.com/users/jacobsa/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobsa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobsa/subscriptions","organizations_url":"https://api.github.com/users/jacobsa/orgs","repos_url":"https://api.github.com/users/jacobsa/repos","events_url":"https://api.github.com/users/jacobsa/events{/privacy}","received_events_url":"https://api.github.com/users/jacobsa/received_events","type":"User","site_admin":false},"open_issues":4,"closed_issues":4,"state":"open","created_at":"2015-03-23T20:56:49Z","updated_at":"2015-05-22T03:42:12Z","due_on":null,"closed_at":null},"comments":0,"created_at":"2015-05-22T01:29:28Z","updated_at":"2015-05-22T03:42:12Z","closed_at":"2015-05-22T03:42:12Z","body":"On Linux, we currently have a test failure that looks like this:\r\n\r\n    [ RUN      ] DirectoryTest.Rmdir_OpenedForReading\r\n    local_modifications_test.go:1083:\r\n    Expected: is nil\r\n    Actual:   mkdir /tmp/fs_test771555359/dir/foo: no such file or directory\r\n    \r\n    [  FAILED  ] DirectoryTest.Rmdir_OpenedForReading (60.081933ms)\r\n\r\nI believe this is what happens:\r\n\r\n*   When asked to open the directory, the kernel sees inode 2 and opens that inode.\r\n*   Later the directory is deleted.\r\n*   Then it is recreated, but the file system returns 2 as the new inode ID from mkdir.\r\n*   The kernel is surprised because it observed this inode before sending mkdir, without an intervening forget. It probably happens to notice because the inode is still open.\r\n\r\nIn any method that creates a new inode (mkdir, symlink, create), we should ensure that we are not returning an ID we've already returned without being told to forget it in the meantime.\r\n\r\nThis doesn't come up with files and symlinks, because their inodes are backed by particular generations of particular objects. Creating the new backing object ensures that a new inode will be created, which ensures we will return a  new ID (since our IDs are just sequential). In contrast, directory inodes are identified only by their name, not a generation number, so we reuse the stale inode.\r\n\r\nThis behavior for directory inodes was (re-)introduced in ff95390569570173d96946ad54916692175abc14, with the justification that implicit directories made directory inode identity difficult. That is true, but we can fix it a better way. Plan:\r\n\r\n*   Make `DirInode` an interface, embedding `Inode` and adding extra methods.\r\n*   Add another interfadce `ExplicitDirInode` that embeds `DirInode` plus adds a `SourceGeneration` method.\r\n*   `LookUpChild` doesn't [create dummy objects](https://github.com/GoogleCloudPlatform/gcsfuse/blob/21e944764aaa1feae807145a3dc29d90921bf192/fs/inode/dir.go#L222-L226) to represent implicit dirs. Instead the fact that they are implicit is returned as first-class info.\r\n*   `NewExplicitDirInode` requires an object record. `NewDirInode` only requires a name.\r\n*   We index `ExplicitDirInode`s in the same way we do files and symlinks, and we use object records to look them up in the same way, too.\r\n*   `DirInode`s that aren't `ExplicitDirInode`s work like directories do today.\r\n*   Because mkdir, symlink, and create actually create a backing object and then use its record to create an inode, we won't wind up reusing anything.\r\n"}}