Commit c10104ad authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.1.0 - First steps towards write support: implement file overwrite.

- Add configuration option for developmental write support with an
  appropriately scary configuration help text.
- Initial implementation of fs/ntfs/aops.c::ntfs_writepage() and its
  helper fs/ntfs/aops.c::ntfs_write_block(). This enables mmap(2) based
  overwriting of existing files on ntfs. Note: Resident files are
  only written into memory, and not written out to disk at present, so
  avoid writing to files smaller than about 1kiB.
- Initial implementation of fs/ntfs/aops.c::ntfs_prepare_write(), its
  helper fs/ntfs/aops.c::ntfs_prepare_nonresident_write() and their
  counterparts, fs/ntfs/aops.c::ntfs_commit_write(), and
  fs/ntfs/aops.c::ntfs_commit_nonresident_write(), respectively. Also,
  add generic_file_write() to the ntfs file operations (fs/ntfs/file.c).
  This enables write(2) based overwriting of existing files on ntfs.
  Note: As with mmap(2) based overwriting, resident files are only
  written into memory, and not written out to disk at present, so avoid
  writing to files smaller than about 1kiB.
- Implement ->truncate (fs/ntfs/inode.c::ntfs_truncate()) and
  ->setattr() (fs/ntfs/inode.c::ntfs_setattr()) inode operations for
  files with the purpose of intercepting and aborting all i_size
  changes which we do not support yet. ntfs_truncate() actually only
  emits a warning message but AFAICS our interception of i_size changes
  elsewhere means ntfs_truncate() never gets called for i_size changes.
  It is only called from generic_file_write() when we fail in
  ntfs_prepare_{,nonresident_}write() in order to discard any
  instantiated buffers beyond i_size. Thus i_size is not actually
  changed so our warning message is enough. Unfortunately it is not
  possible to easily determine if i_size is being changed or not hence
  we just emit an appropriately worded error message.
parent e9042fae
......@@ -252,6 +252,7 @@ Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
- Initial implementation of file overwriting. (Writes to resident files
are not written out to disk yet, so avoid writing to files smaller
than about 1kiB.)
- Intercept/abort changes in file size as they are not implemented yet.
2.0.25:
- Minor bugfixes in error code paths and small cleanups.
2.0.24:
......
......@@ -38,6 +38,18 @@ ToDo:
Note: As with mmap(2) based overwriting, resident files are only
written into memory, and not written out to disk at present, so avoid
writing to files smaller than about 1kiB.
- Implement ->truncate (fs/ntfs/inode.c::ntfs_truncate()) and
->setattr() (fs/ntfs/inode.c::ntfs_setattr()) inode operations for
files with the purpose of intercepting and aborting all i_size
changes which we do not support yet. ntfs_truncate() actually only
emits a warning message but AFAICS our interception of i_size changes
elsewhere means ntfs_truncate() never gets called for i_size changes.
It is only called from generic_file_write() when we fail in
ntfs_prepare_{,nonresident_}write() in order to discard any
instantiated buffers beyond i_size. Thus i_size is not actually
changed so our warning message is enough. Unfortunately it is not
possible to easily determine if i_size is being changed or not hence
we just emit an appropriately worded error message.
2.0.25 - Small bug fixes and cleanups.
......
......@@ -1523,8 +1523,10 @@ static int ntfs_commit_nonresident_write(struct page *page,
* exceeds i_size case, so this will never trigger which is fine.
*/
if (pos > vi->i_size) {
vi->i_size = pos;
mark_inode_dirty(vi);
ntfs_error(vi->i_sb, "Writing beyond the existing file size is "
"not supported yet. Sorry.");
// vi->i_size = pos;
// mark_inode_dirty(vi);
}
ntfs_debug("Done.");
return 0;
......
......@@ -63,7 +63,10 @@ struct file_operations ntfs_file_ops = {
.open = ntfs_file_open, /* Open file. */
};
struct inode_operations ntfs_file_inode_ops = {};
struct inode_operations ntfs_file_inode_ops = {
.truncate = ntfs_truncate,
.setattr = ntfs_setattr,
};
struct file_operations ntfs_empty_file_ops = {};
......
......@@ -21,6 +21,8 @@
#include <linux/pagemap.h>
#include <linux/buffer_head.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
#include "ntfs.h"
#include "dir.h"
......@@ -1926,3 +1928,92 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
return 0;
}
/**
* ntfs_truncate - called when the i_size of an ntfs inode is changed
* @vi: inode for which the i_size was changed
*
* We don't support i_size changes yet.
*
* Called with ->i_sem held.
*/
void ntfs_truncate(struct inode *vi)
{
// TODO: Implement...
ntfs_warning(vi->i_sb, "Eeek: i_size may have changed! If you see "
"this right after a message from "
"ntfs_{prepare,commit}_{,nonresident_}write() then "
"just ignore it. Otherwise it is bad news.");
// TODO: reset i_size now!
return;
}
/**
* ntfs_setattr - called from notify_change() when an attribute is being changed
* @dentry: dentry whose attributes to change
* @attr: structure describing the attributes and the changes
*
* We have to trap VFS attempts to truncate the file described by @dentry as
* soon as possible, because we do not implement changes in i_size yet. So we
* abort all i_size changes here.
*
* Called with ->i_sem held.
*
* Basically this is a copy of generic notify_change() and inode_setattr()
* functionality, except we intercept and abort changes in i_size.
*/
int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *vi;
int err;
unsigned int ia_valid = attr->ia_valid;
vi = dentry->d_inode;
err = inode_change_ok(vi, attr);
if (err)
return err;
if ((ia_valid & ATTR_UID && attr->ia_uid != vi->i_uid) ||
(ia_valid & ATTR_GID && attr->ia_gid != vi->i_gid)) {
err = DQUOT_TRANSFER(vi, attr) ? -EDQUOT : 0;
if (err)
return err;
}
lock_kernel();
if (ia_valid & ATTR_SIZE) {
ntfs_error(vi->i_sb, "Changes in i_size are not supported "
"yet. Sorry.");
// TODO: Implement...
// err = vmtruncate(vi, attr->ia_size);
err = -EOPNOTSUPP;
if (err)
goto trunc_err;
}
if (ia_valid & ATTR_UID)
vi->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
vi->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
vi->i_atime = attr->ia_atime;
if (ia_valid & ATTR_MTIME)
vi->i_mtime = attr->ia_mtime;
if (ia_valid & ATTR_CTIME)
vi->i_ctime = attr->ia_ctime;
if (ia_valid & ATTR_MODE) {
vi->i_mode = attr->ia_mode;
if (!in_group_p(vi->i_gid) &&
!capable(CAP_FSETID))
vi->i_mode &= ~S_ISGID;
}
mark_inode_dirty(vi);
trunc_err:
unlock_kernel();
return err;
}
......@@ -36,8 +36,8 @@ typedef struct _ntfs_inode ntfs_inode;
* fields already provided in the VFS inode.
*/
struct _ntfs_inode {
s64 initialized_size; /* Copy from $DATA/$INDEX_ALLOCATION. */
s64 allocated_size; /* Copy from $DATA/$INDEX_ALLOCATION. */
s64 initialized_size; /* Copy from the attribute record. */
s64 allocated_size; /* Copy from the attribute record. */
unsigned long state; /* NTFS specific flags describing this inode.
See ntfs_inode_state_bits below. */
unsigned long mft_no; /* Number of the mft record / inode. */
......@@ -245,5 +245,9 @@ extern void ntfs_put_inode(struct inode *vi);
extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt);
extern void ntfs_truncate(struct inode *vi);
extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr);
#endif /* _LINUX_NTFS_FS_INODE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment