Below is our modification to the Linux inotify and fsnotify kernel APIs (based on the 3.0.0-12 kernel), in order to return the size and location of file updates.

This information is readily available inside the Linux kernel; our modified API simply exposes this information to applications, and thus it is a backwards compatible kernel modification.

We believe that our extension to inotify (and fsnotify) is a worthwhile addition to the operating system. Using the strace command, we tracked the system calls made by many commercial cloud storage applications (e.g. Dropbox, UbuntuOne, TeamDrive, SpiderOak, etc.) and confirmed that they all use the inotify API. Thus, there is a large class of applications that would benefit from merging our modified API into the Linux kernel.

For more details, please refer to our paper ---

Zhenhua Li, Christo Wilson, Zhefu Jiang, Yao Liu, Ben Y. Zhao, Cheng Jin, Zhi-Li Zhang, and Yafei Dai. Efficient Batched Synchronization in Dropbox-like Cloud Storage Services. The 14th ACM/IFIP/USENIX International Middleware Conference (Middleware), Dec. 9-13, 2013, Beijing, China. (Accept ratio: 24/128 = 18.8%)

_____________________________________________________________________________________

diff --git fs/compat.c fs/compat.c
index f2944ac..6fbadea 100644
--- fs/compat.c
+++ fs/compat.c
@@ -1126,7 +1126,7 @@ out:
if (type == READ)
fsnotify_access(file);
else
- fsnotify_modify(file);
+ fsnotify_modify(file, *pos, tot_len);
}
return ret;
}
diff --git fs/nfsd/vfs.c fs/nfsd/vfs.c
index 296d671..c1b5aac 100644
--- fs/nfsd/vfs.c
+++ fs/nfsd/vfs.c
@@ -1043,7 +1043,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
goto out_nfserr;
*cnt = host_err;
nfsdstats.io_write += host_err;
- fsnotify_modify(file);
+ fsnotify_modify(file, offset, vlen);

/* clear setuid/setgid flag after write */
if (inode->i_mode & (S_ISUID | S_ISGID))
diff --git fs/notify/inotify/inotify_user.c fs/notify/inotify/inotify_user.c
index 8445fbc..b9961c9 100644
--- fs/notify/inotify/inotify_user.c
+++ fs/notify/inotify/inotify_user.c
@@ -171,6 +171,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
struct inotify_event_private_data *priv;
size_t event_size = sizeof(struct inotify_event);
size_t name_len = 0;
+ size_t m_params = 0;

pr_debug("%s: group=%p event=%p\n", __func__, group, event);

@@ -196,6 +197,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
name_len = roundup(event->name_len + 1, event_size);
inotify_event.len = name_len;

+ if (event->mask == FS_MODIFY) {
+ m_params = roundup(sizeof(uint64_t) * 2, event_size);
+ }
+
inotify_event.mask = inotify_mask_to_arg(event->mask);
inotify_event.cookie = event->sync_cookie;

@@ -224,6 +229,18 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
event_size += name_len;
}

+ if (m_params) {
+ int len_to_zero = m_params - sizeof(uint64_t) * 2;
+ if (copy_to_user(buf, &(event->m_pos), sizeof(uint64_t)))
+ return -EFAULT;
+ if (copy_to_user(buf + sizeof(uint64_t), &(event->m_len), sizeof(uint64_t)))
+ return -EFAULT;
+ if(clear_user(buf + sizeof(uint64_t) * 2, len_to_zero))
+ return -EFAULT;
+ buf += m_params;
+ event_size += m_params;
+ }
+
return event_size;
}

diff --git fs/notify/notification.c fs/notify/notification.c
index c887b13..9d88c62 100644
--- fs/notify/notification.c
+++ fs/notify/notification.c
@@ -398,6 +398,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
u32 cookie, gfp_t gfp)
{
struct fsnotify_event *event;
+ uint64_t *m_pos_len;

event = kmem_cache_zalloc(fsnotify_event_cachep, gfp);
if (!event)
@@ -444,6 +445,12 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,

event->mask = mask;

+ if(event->mask == FS_MODIFY) {
+ m_pos_len = (uint64_t *)(data + sizeof(struct path));
+ event->m_pos = *(m_pos_len);
+ event->m_len = *(m_pos_len + 1);
+ }
+
return event;
}

diff --git fs/read_write.c fs/read_write.c
index ffc99d2..ce6b8f7 100644
--- fs/read_write.c
+++ fs/read_write.c
@@ -436,7 +436,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
else
ret = do_sync_write(file, buf, count, pos);
if (ret > 0) {
- fsnotify_modify(file);
+ fsnotify_modify(file, *pos, count);
add_wchar(current, ret);
}
inc_syscw(current);
@@ -754,7 +754,7 @@ out:
if (type == READ)
fsnotify_access(file);
else
- fsnotify_modify(file);
+ fsnotify_modify(file, *pos, tot_len);
}
return ret;
}
diff --git include/linux/fsnotify.h include/linux/fsnotify.h
index a6dfe69..394d20f 100644
--- include/linux/fsnotify.h
+++ include/linux/fsnotify.h
@@ -207,18 +207,26 @@ static inline void fsnotify_access(struct file *file)
/*
* fsnotify_modify - file was modified
*/
-static inline void fsnotify_modify(struct file *file)
+static inline void fsnotify_modify(struct file *file, uint64_t pos, uint64_t len)
{
struct path *path = &file->f_path;
struct inode *inode = path->dentry->d_inode;
__u32 mask = FS_MODIFY;

+ char modify_info[sizeof(struct path) + sizeof(uint64_t) * 2];
+ uint64_t *pos_len = (uint64_t *)(modify_info + sizeof(struct path));
+
+ /* inotify++ compatible with inotify */
+ memcpy(modify_info, path, sizeof(struct path));
+ *(pos_len) = pos;
+ *(pos_len + 1) = len;
+
if (S_ISDIR(inode->i_mode))
mask |= FS_ISDIR;

if (!(file->f_mode & FMODE_NONOTIFY)) {
fsnotify_parent(path, NULL, mask);
- fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+ fsnotify(inode, mask, modify_info, FSNOTIFY_EVENT_PATH, NULL, 0);
}
}

diff --git include/linux/fsnotify_backend.h include/linux/fsnotify_backend.h
index 91d0e0a3..8e72727 100644
--- include/linux/fsnotify_backend.h
+++ include/linux/fsnotify_backend.h
@@ -227,6 +227,10 @@ struct fsnotify_event {
struct path path;
struct inode *inode;
};
+
+ /* purly for inotify++ */
+ uint64_t m_pos, m_len;
+
/* when calling fsnotify tell it if the data is a path or inode */
#define FSNOTIFY_EVENT_NONE 0
#define FSNOTIFY_EVENT_PATH 1

กก