@@ -, +, @@ Update mtime when mmap'ed pages become writable GNU Tar is sensitive to situations where mtimes on open files are updated to times in the past. When doing write operations on mmap'ed files, the modification time update presently occurs during a flush. Observant programs that open such files shortly after they are closed will observe that modification times appear to occur in the past. This caused a failure to be observed during binary package generation on Gentoo where PaX-marking is done on certain files via mmap almost immediately prior to package generation, which relies on GNU tar. This can be replicated with a Hello World program by doing: gcc hello.c && scanelf -Xxz -r a.out && tar -cf hello.tgz a.out Brian Behlendorf pointed out that we could avoid this problem by doing mtime updates in the kernel's page_mkwrite() callback function, which is invoked whenever a page becomes writeable. We implement that though a C++-style overload of the page_mkwrite function to add a call to zfs_inode_update(). It would be better to do this on each close() operation done from userland, but unfortunately, the Linux kernel provides no way for us to be informed of such events. Signed-off-by: Richard Yao --- a/include/sys/zpl.h +++ a/include/sys/zpl.h @@ -52,6 +52,7 @@ extern long zpl_fallocate_common(struct inode *ip, int mode, extern const struct address_space_operations zpl_address_space_operations; extern const struct file_operations zpl_file_operations; extern const struct file_operations zpl_dir_file_operations; +extern const struct vm_operations_struct zpl_file_vm_ops; /* zpl_super.c */ extern void zpl_prune_sbs(int64_t bytes_to_scan, void *private); --- a/module/zfs/zpl_file.c +++ a/module/zfs/zpl_file.c @@ -294,6 +294,8 @@ zpl_mmap(struct file *filp, struct vm_area_struct *vma) if (error) return (error); + vma->vm_ops = &zpl_file_vm_ops; + mutex_enter(&zp->z_lock); zp->z_is_mapped = 1; mutex_exit(&zp->z_lock); @@ -433,6 +435,16 @@ zpl_fallocate(struct file *filp, int mode, loff_t offset, loff_t len) } #endif /* HAVE_FILE_FALLOCATE */ +int zpl_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + int error = filemap_page_mkwrite(vma, vmf); + + if (VM_FAULT_NOPAGE != error) + zfs_inode_update(ITOZ(vma->vm_file->f_path.dentry->d_inode)); + + return error; +} + const struct address_space_operations zpl_address_space_operations = { .readpages = zpl_readpages, .readpage = zpl_readpage, @@ -460,3 +472,8 @@ const struct file_operations zpl_dir_file_operations = { .readdir = zpl_readdir, .fsync = zpl_fsync, }; + +const struct vm_operations_struct zpl_file_vm_ops = { + .page_mkwrite = zpl_page_mkwrite, + .fault = filemap_fault, +};