[RFC 05/15] fs: cifs: Add support for cifs to use 64 bit time

Deepa Dinamani
2016-01-07T05:37:06Z

Change all struct timespec references to struct inode_timespec.
Use inode timestamp accessors to access inode time fields.
This will help the switch to struct timespec64 when
CONFIG_FS_USES_64BIT_TIME is enabled.

Use current_fs_time() instead of CURRENT_TIME macros to help
range and precision checks.

Truncate and perform range checks before saving the times in
struct inode.

Switch over connection times to use SYSTEM_TIME macro instead
of CURRENT_TIME. Since SYSTEM_TIME is also under the
CONFIG_FS_USES_64BIT_TIME this will help the switch to use
timespec64.

Use long long for seconds field in cnvrtDosUnixTm(). This will
help represent 64 bit time.
Since DOS uses 1980 epoch, all the timestamps are positive when
represented in UNIX format. Change all arithmetic to unsigned.
Note that even though the theoretical max on DOS times is 2107,
its api's only support until the year 2099. This means we can
get away with 32 bit unsigned sec field. But, the sec field uses
long long to maintain uniformity in the kernel, where everyone
uses the theoretical max.

Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
---
fs/cifs/cache.c | 16 ++++++++-----
fs/cifs/cifsencrypt.c | 2 +-
fs/cifs/cifsglob.h | 6 ++---
fs/cifs/cifsproto.h | 9 +++----
fs/cifs/cifssmb.c | 17 +++++++++-----
fs/cifs/file.c | 9 ++++---
fs/cifs/inode.c | 65 +++++++++++++++++++++++++++++++++------------------
fs/cifs/netmisc.c | 26 +++++++++++----------
8 files changed, 92 insertions(+), 58 deletions(-)

diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c
index 6c665bf..8d27e69b 100644
--- a/fs/cifs/cache.c
+++ b/fs/cifs/cache.c
@@ -221,8 +221,8 @@ const struct fscache_cookie_def cifs_fscache_super_index_def = {
* Auxiliary data attached to CIFS inode within the cache
*/
struct cifs_fscache_inode_auxdata {
- struct timespec last_write_time;
- struct timespec last_change_time;
+ struct inode_timespec last_write_time;
+ struct inode_timespec last_change_time;
u64 eof;
};

@@ -259,8 +259,10 @@ cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer,

memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
- auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
- auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
+ auxdata.last_write_time =
+ VFS_INODE_GET_XTIME(i_mtime, &cifsi->vfs_inode);
+ auxdata.last_change_time =
+ VFS_INODE_GET_XTIME(i_ctime, &cifsi->vfs_inode);

if (maxbuf > sizeof(auxdata))
maxbuf = sizeof(auxdata);
@@ -283,8 +285,10 @@ fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,

memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof;
- auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
- auxdata.last_change_time = cifsi->vfs_inode.i_ctime;
+ auxdata.last_write_time =
+ VFS_INODE_GET_XTIME(i_mtime, &cifsi->vfs_inode);
+ auxdata.last_change_time =
+ VFS_INODE_GET_XTIME(i_ctime, &cifsi->vfs_inode);

if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE;
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index afa09fc..b0ef587 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -483,7 +483,7 @@ find_timestamp(struct cifs_ses *ses)
blobptr += attrsize; /* advance attr value */
}

- return cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ return cpu_to_le64(cifs_UnixTimeToNT(FS_TIME));
}

static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index a25b251..c95dce7 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1393,9 +1393,9 @@ struct cifs_fattr {
dev_t cf_rdev;
unsigned int cf_nlink;
unsigned int cf_dtype;
- struct timespec cf_atime;
- struct timespec cf_mtime;
- struct timespec cf_ctime;
+ struct inode_timespec cf_atime;
+ struct inode_timespec cf_mtime;
+ struct inode_timespec cf_ctime;
};

static inline void free_dfs_info_param(struct dfs_info3_param *param)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index eed7ff5..9979c74 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -126,10 +126,11 @@ extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested);
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp);
-extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
-extern u64 cifs_UnixTimeToNT(struct timespec);
-extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
- int offset);
+extern struct inode_timespec
+ cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
+extern u64 cifs_UnixTimeToNT(struct inode_timespec);
+extern struct inode_timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
+ int offset);
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int cifs_get_writer(struct cifsInodeInfo *cinode);
extern void cifs_put_writer(struct cifsInodeInfo *cinode);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 90b4f9f..a813bcd 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -478,13 +478,17 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
* this requirement.
*/
int val, seconds, remain, result;
- struct timespec ts, utc;
- utc = CURRENT_TIME;
+ struct inode_timespec ts, utc;
+
+ utc = FS_TIME;
ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
rsp->SrvTime.Time, 0);
- cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n",
- (int)ts.tv_sec, (int)utc.tv_sec,
- (int)(utc.tv_sec - ts.tv_sec));
+ cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n",
+ (long long)ts.tv_sec, (long long)utc.tv_sec,
+ (long long)(utc.tv_sec - ts.tv_sec));
+ /* Assume difference cannot be more than
+ * INT_MAX or INT_MIN
+ */
val = (int)(utc.tv_sec - ts.tv_sec);
seconds = abs(val);
result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
@@ -4000,7 +4004,8 @@ QInfRetry:
if (rc) {
cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc);
} else if (data) {
- struct timespec ts;
+ struct inode_timespec ts;
+
__u32 time = le32_to_cpu(pSMBr->last_write_time);

/* decode response */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 0a2752b..2d226cf 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -1839,6 +1839,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
int bytes_written = 0;
struct inode *inode;
struct cifsFileInfo *open_file;
+ struct inode_timespec now;

if (!mapping || !mapping->host)
return -EFAULT;
@@ -1870,7 +1871,9 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
write_data, to - from, &offset);
cifsFileInfo_put(open_file);
/* Does mm or vfs already set times? */
- inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
+ now = current_fs_time(inode->i_sb);
+ VFS_INODE_SET_XTIME(i_atime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);
if ((bytes_written > 0) && (offset))
rc = 0;
else if (bytes_written < 0)
@@ -3567,6 +3570,7 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
loff_t *poffset)
{
char *read_data;
+ struct inode *inode = file_inode(file);
int rc;

/* Is the page cached? */
@@ -3584,8 +3588,7 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
else
cifs_dbg(FYI, "Bytes read %d\n", rc);

- file_inode(file)->i_atime =
- current_fs_time(file_inode(file)->i_sb);
+ VFS_INODE_SET_XTIME(i_atime, inode, current_fs_time(inode->i_sb));

if (PAGE_CACHE_SIZE > rc)
memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index aeb26db..bb91bf7 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -92,6 +92,7 @@ static void
cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+ struct inode_timespec mtime;

cifs_dbg(FYI, "%s: revalidating inode %llu\n",
__func__, cifs_i->uniqueid);
@@ -110,12 +111,13 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
}

/* revalidate if mtime or size have changed */
- if (timespec_equal(&inode->i_mtime, &fattr->cf_mtime) &&
- cifs_i->server_eof == fattr->cf_eof) {
+ mtime = VFS_INODE_GET_XTIME(i_mtime, inode);
+ if (inode_timespec_equal(&mtime, &fattr->cf_mtime)
+ && cifs_i->server_eof == fattr->cf_eof) {
cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
- __func__, cifs_i->uniqueid);
- return;
- }
+ __func__, cifs_i->uniqueid);
+ return;
+ }

cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n",
__func__, cifs_i->uniqueid);
@@ -155,13 +157,17 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct super_block *sb = inode->i_sb;

cifs_revalidate_cache(inode, fattr);

spin_lock(&inode->i_lock);
- inode->i_atime = fattr->cf_atime;
- inode->i_mtime = fattr->cf_mtime;
- inode->i_ctime = fattr->cf_ctime;
+ VFS_INODE_SET_XTIME(i_atime, inode,
+ fs_time_trunc(fattr->cf_atime, sb));
+ VFS_INODE_SET_XTIME(i_mtime, inode,
+ fs_time_trunc(fattr->cf_mtime, sb));
+ VFS_INODE_SET_XTIME(i_ctime, inode,
+ fs_time_trunc(fattr->cf_ctime, sb));
inode->i_rdev = fattr->cf_rdev;
cifs_nlink_fattr_to_inode(inode, fattr);
inode->i_uid = fattr->cf_uid;
@@ -231,6 +237,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime);
fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
+
fattr->cf_mode = le64_to_cpu(info->Permissions);

/*
@@ -288,7 +295,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
fattr->cf_uid = uid;
}
}
-
+
fattr->cf_gid = cifs_sb->mnt_gid;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) {
u64 id = le64_to_cpu(info->Gid);
@@ -313,6 +320,7 @@ static void
cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct inode_timespec now;

cifs_dbg(FYI, "creating fake fattr for DFS referral\n");

@@ -320,9 +328,10 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
fattr->cf_uid = cifs_sb->mnt_uid;
fattr->cf_gid = cifs_sb->mnt_gid;
- fattr->cf_atime = CURRENT_TIME;
- fattr->cf_ctime = CURRENT_TIME;
- fattr->cf_mtime = CURRENT_TIME;
+ now = current_fs_time(sb);
+ fattr->cf_atime = now;
+ fattr->cf_ctime = now;
+ fattr->cf_mtime = now;
fattr->cf_nlink = 2;
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
}
@@ -584,9 +593,10 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
static void
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
- struct cifs_sb_info *cifs_sb, bool adjust_tz,
+ struct super_block *sb, bool adjust_tz,
bool symlink)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);

memset(fattr, 0, sizeof(*fattr));
@@ -597,7 +607,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
if (info->LastAccessTime)
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
else
- fattr->cf_atime = CURRENT_TIME;
+ fattr->cf_atime = current_fs_time(sb);

fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
@@ -657,7 +667,6 @@ cifs_get_file_info(struct file *filp)
FILE_ALL_INFO find_data;
struct cifs_fattr fattr;
struct inode *inode = file_inode(filp);
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
@@ -669,7 +678,7 @@ cifs_get_file_info(struct file *filp)
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
switch (rc) {
case 0:
- cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false,
+ cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false,
false);
break;
case -EREMOTE:
@@ -751,7 +760,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
}

if (!rc) {
- cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz,
+ cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
symlink);
} else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, sb);
@@ -1252,6 +1261,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
unsigned int xid;
char *full_path = NULL;
struct inode *inode = d_inode(dentry);
+ struct inode_timespec now;
struct cifsInodeInfo *cifs_inode;
struct super_block *sb = dir->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -1343,9 +1353,11 @@ out_reval:
cifs_inode = CIFS_I(inode);
cifs_inode->time = 0; /* will force revalidate to get info
when needed */
- inode->i_ctime = current_fs_time(sb);
+ VFS_INODE_SET_XTIME(i_ctime, inode, current_fs_time(sb));
}
- dir->i_ctime = dir->i_mtime = current_fs_time(sb);
+ now = current_fs_time(sb);
+ VFS_INODE_SET_XTIME(i_ctime, dir, now);
+ VFS_INODE_SET_XTIME(i_mtime, dir, now);
cifs_inode = CIFS_I(dir);
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
unlink_out:
@@ -1565,6 +1577,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
struct TCP_Server_Info *server;
char *full_path = NULL;
struct cifsInodeInfo *cifsInode;
+ struct inode_timespec now;

cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode);

@@ -1612,8 +1625,10 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
*/
cifsInode->time = 0;

- d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime =
- current_fs_time(inode->i_sb);
+ now = current_fs_time(inode->i_sb);
+ VFS_INODE_SET_XTIME(i_ctime, d_inode(direntry), now);
+ VFS_INODE_SET_XTIME(i_ctime, inode, now);
+ VFS_INODE_SET_XTIME(i_mtime, inode, now);

rmdir_exit:
kfree(full_path);
@@ -1692,6 +1707,7 @@ cifs_rename2(struct inode *source_dir, struct dentry *source_dentry,
struct cifs_tcon *tcon;
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
FILE_UNIX_BASIC_INFO *info_buf_target;
+ struct inode_timespec now;
unsigned int xid;
int rc, tmprc;

@@ -1785,8 +1801,11 @@ unlink_target:
/* force revalidate to go get info when needed */
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;

- source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime =
- target_dir->i_mtime = current_fs_time(source_dir->i_sb);
+ now = current_fs_time(source_dir->i_sb);
+ VFS_INODE_SET_XTIME(i_ctime, source_dir, now);
+ VFS_INODE_SET_XTIME(i_mtime, source_dir, now);
+ VFS_INODE_SET_XTIME(i_ctime, target_dir, now);
+ VFS_INODE_SET_XTIME(i_mtime, target_dir, now);

cifs_rename_exit:
kfree(info_buf_source);
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index 301d3d4..4a26260 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -918,10 +918,10 @@ smbCalcSize(void *buf)
* Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
* into Unix UTC (based 1970-01-01, in seconds).
*/
-struct timespec
+struct inode_timespec
cifs_NTtimeToUnix(__le64 ntutc)
{
- struct timespec ts;
+ struct inode_timespec ts;
/* BB what about the timezone? BB */

/* Subtract the NTFS time offset, then convert to 1s intervals. */
@@ -949,7 +949,7 @@ cifs_NTtimeToUnix(__le64 ntutc)

/* Convert the Unix UTC into NT UTC. */
u64
-cifs_UnixTimeToNT(struct timespec t)
+cifs_UnixTimeToNT(struct inode_timespec t)
{
/* Convert to 100ns intervals and then add the NTFS time offset. */
return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
@@ -959,10 +959,11 @@ static const int total_days_of_prev_months[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};

-struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
+struct inode_timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
{
- struct timespec ts;
- int sec, min, days, month, year;
+ struct inode_timespec ts;
+ unsigned long long sec;
+ unsigned int min, days, month, year;
u16 date = le16_to_cpu(le_date);
u16 time = le16_to_cpu(le_time);
SMB_TIME *st = (SMB_TIME *)&time;
@@ -973,7 +974,7 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
sec = 2 * st->TwoSeconds;
min = st->Minutes;
if ((sec > 59) || (min > 59))
- cifs_dbg(VFS, "illegal time min %d sec %d\n", min, sec);
+ cifs_dbg(VFS, "illegal time min %d sec %llu\n", min, sec);
sec += (min * 60);
sec += 60 * 60 * st->Hours;
if (st->Hours > 24)
@@ -992,11 +993,12 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
days += year * 365;
days += (year/4); /* leap year */
/* generalized leap year calculation is more complex, ie no leap year
- for years/100 except for years/400, but since the maximum number for DOS
- year is 2**7, the last year is 1980+127, which means we need only
- consider 2 special case years, ie the years 2000 and 2100, and only
- adjust for the lack of leap year for the year 2100, as 2000 was a
- leap year (divisable by 400) */
+ * for years/100 except for years/400, but since the maximum number for
+ * DOS year is 2**7, the last year is 1980+127, which means we need only
+ * consider 2 special case years, ie the years 2000 and 2100, and only
+ * adjust for the lack of leap year for the year 2100, as 2000 was a
+ * leap year (divisable by 400)
+ */
if (year >= 120) /* the year 2100 */
days = days - 1; /* do not count leap year for the year 2100 */

--
1.9.1