)]}'
{
  "commit": "fe17c202b59ee36cff6ac87ca7ed31a9e9ea5f73",
  "tree": "e6056ee9e8aad09522420df0d69a1d8247fdf5b0",
  "parents": [
    "f4a69e06dc99cd683fa001c49eb92da36fd76d1d"
  ],
  "author": {
    "name": "Maxim Patlasov",
    "email": "MPatlasov@parallels.com",
    "time": "Fri Aug 30 17:06:04 2013 +0400"
  },
  "committer": {
    "name": "Greg Kroah-Hartman",
    "email": "gregkh@linuxfoundation.org",
    "time": "Tue Mar 11 16:10:04 2014 -0700"
  },
  "message": "fuse: hotfix truncate_pagecache() issue\n\ncommit 06a7c3c2781409af95000c60a5df743fd4e2f8b4 upstream.\n\nThe way how fuse calls truncate_pagecache() from fuse_change_attributes()\nis completely wrong. Because, w/o i_mutex held, we never sure whether\n\u0027oldsize\u0027 and \u0027attr-\u003esize\u0027 are valid by the time of execution of\ntruncate_pagecache(inode, oldsize, attr-\u003esize). In fact, as soon as we\nreleased fc-\u003elock in the middle of fuse_change_attributes(), we completely\nloose control of actions which may happen with given inode until we reach\ntruncate_pagecache. The list of potentially dangerous actions includes\nmmap-ed reads and writes, ftruncate(2) and write(2) extending file size.\n\nThe typical outcome of doing truncate_pagecache() with outdated arguments\nis data corruption from user point of view. This is (in some sense)\nacceptable in cases when the issue is triggered by a change of the file on\nthe server (i.e. externally wrt fuse operation), but it is absolutely\nintolerable in scenarios when a single fuse client modifies a file without\nany external intervention. A real life case I discovered by fsx-linux\nlooked like this:\n\n1. Shrinking ftruncate(2) comes to fuse_do_setattr(). The latter sends\nFUSE_SETATTR to the server synchronously, but before getting fc-\u003elock ...\n2. fuse_dentry_revalidate() is asynchronously called. It sends FUSE_LOOKUP\nto the server synchronously, then calls fuse_change_attributes(). The\nlatter updates i_size, releases fc-\u003elock, but before comparing oldsize vs\nattr-\u003esize..\n3. fuse_do_setattr() from the first step proceeds by acquiring fc-\u003elock and\nupdating attributes and i_size, but now oldsize is equal to\noutarg.attr.size because i_size has just been updated (step 2). Hence,\nfuse_do_setattr() returns w/o calling truncate_pagecache().\n4. As soon as ftruncate(2) completes, the user extends file size by\nwrite(2) making a hole in the middle of file, then reads data from the hole\neither by read(2) or mmap-ed read. The user expects to get zero data from\nthe hole, but gets stale data because truncate_pagecache() is not executed\nyet.\n\nThe scenario above illustrates one side of the problem: not truncating the\npage cache even though we should. Another side corresponds to truncating\npage cache too late, when the state of inode changed significantly.\nTheoretically, the following is possible:\n\n1. As in the previous scenario fuse_dentry_revalidate() discovered that\ni_size changed (due to our own fuse_do_setattr()) and is going to call\ntruncate_pagecache() for some \u0027new_size\u0027 it believes valid right now. But\nby the time that particular truncate_pagecache() is called ...\n2. fuse_do_setattr() returns (either having called truncate_pagecache() or\nnot -- it doesn\u0027t matter).\n3. The file is extended either by write(2) or ftruncate(2) or fallocate(2).\n4. mmap-ed write makes a page in the extended region dirty.\n\nThe result will be the lost of data user wrote on the fourth step.\n\nThe patch is a hotfix resolving the issue in a simplistic way: let\u0027s skip\ndangerous i_size update and truncate_pagecache if an operation changing\nfile size is in progress. This simplistic approach looks correct for the\ncases w/o external changes. And to handle them properly, more sophisticated\nand intrusive techniques (e.g. NFS-like one) would be required. I\u0027d like to\npostpone it until the issue is well discussed on the mailing list(s).\n\nChanged in v2:\n - improved patch description to cover both sides of the issue.\n\nSigned-off-by: Maxim Patlasov \u003cmpatlasov@parallels.com\u003e\nSigned-off-by: Miklos Szeredi \u003cmszeredi@suse.cz\u003e\n[bwh: Backported to 3.2: add the fuse_inode::state field which we didn\u0027t have]\nSigned-off-by: Ben Hutchings \u003cben@decadent.org.uk\u003e\nCc: Rui Xiang \u003crui.xiang@huawei.com\u003e\nSigned-off-by: Greg Kroah-Hartman \u003cgregkh@linuxfoundation.org\u003e\n\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "9966cae5a9e5bc596ad1f0f958f95515181c057a",
      "old_mode": 33188,
      "old_path": "fs/fuse/dir.c",
      "new_id": "f31c1365a0131c190130cf43c943e605b3328c8a",
      "new_mode": 33188,
      "new_path": "fs/fuse/dir.c"
    },
    {
      "type": "modify",
      "old_id": "df25454eadf17655e8eabc89ca84cb107bf4c8dd",
      "old_mode": 33188,
      "old_path": "fs/fuse/file.c",
      "new_id": "e4f1f1ace347ecf482c62537847edd6a0feeb144",
      "new_mode": 33188,
      "new_path": "fs/fuse/file.c"
    },
    {
      "type": "modify",
      "old_id": "d1819269aab0007bccec96f0d66be907e2769651",
      "old_mode": 33188,
      "old_path": "fs/fuse/fuse_i.h",
      "new_id": "81b96c58569c809244a95c4db4d9e625896274be",
      "new_mode": 33188,
      "new_path": "fs/fuse/fuse_i.h"
    },
    {
      "type": "modify",
      "old_id": "a59cf5e673d7406b0cca21eef5549b8d2acb3de9",
      "old_mode": 33188,
      "old_path": "fs/fuse/inode.c",
      "new_id": "a5c8b343a156238cab963bc2d462a981a9795407",
      "new_mode": 33188,
      "new_path": "fs/fuse/inode.c"
    }
  ]
}
