X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fcifs%2Finode.c;h=62b324f26a565b112b59d6cdd7f688dbf735e715;hb=a8bef8ff6ea15fa4c67433cab0f5f3484574ef7c;hp=ff8c68de4a9205a9d0cd592d8a0c58872b0e8e65;hpb=ae6884a9da56f8921e432e663b4ccb4a1851b2ea;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ff8c68d..62b324f 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1,7 +1,7 @@ /* * fs/cifs/inode.c * - * Copyright (C) International Business Machines Corp., 2002,2007 + * Copyright (C) International Business Machines Corp., 2002,2010 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include "cifsfs.h" @@ -77,205 +78,276 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) } } -static void cifs_unix_info_to_inode(struct inode *inode, - FILE_UNIX_BASIC_INFO *info, int force_uid_gid) +/* check inode attributes against fattr. If they don't match, tag the + * inode for cache invalidation + */ +static void +cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cFYI(1, "%s: revalidating inode %llu", __func__, cifs_i->uniqueid); + + if (inode->i_state & I_NEW) { + cFYI(1, "%s: inode %llu is new", __func__, cifs_i->uniqueid); + return; + } + + /* don't bother with revalidation if we have an oplock */ + if (cifs_i->clientCanCacheRead) { + cFYI(1, "%s: inode %llu is oplocked", __func__, + cifs_i->uniqueid); + return; + } + + /* revalidate if mtime or size have changed */ + if (timespec_equal(&inode->i_mtime, &fattr->cf_mtime) && + cifs_i->server_eof == fattr->cf_eof) { + cFYI(1, "%s: inode %llu is unchanged", __func__, + cifs_i->uniqueid); + return; + } + + cFYI(1, "%s: invalidating inode %llu mapping", __func__, + cifs_i->uniqueid); + cifs_i->invalid_mapping = true; +} + +/* populate an inode with info from a cifs_fattr struct */ +void +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 cifsInodeInfo *cifsInfo = CIFS_I(inode); - __u64 num_of_bytes = le64_to_cpu(info->NumOfBytes); - __u64 end_of_file = le64_to_cpu(info->EndOfFile); + unsigned long oldtime = cifs_i->time; + + cifs_revalidate_cache(inode, fattr); + + inode->i_atime = fattr->cf_atime; + inode->i_mtime = fattr->cf_mtime; + inode->i_ctime = fattr->cf_ctime; + inode->i_rdev = fattr->cf_rdev; + inode->i_nlink = fattr->cf_nlink; + inode->i_uid = fattr->cf_uid; + inode->i_gid = fattr->cf_gid; + + /* if dynperm is set, don't clobber existing mode */ + if (inode->i_state & I_NEW || + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) + inode->i_mode = fattr->cf_mode; + + cifs_i->cifsAttrs = fattr->cf_cifsattrs; + + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + cifs_i->time = 0; + else + cifs_i->time = jiffies; + + cFYI(1, "inode 0x%p old_time=%ld new_time=%ld", inode, + oldtime, cifs_i->time); + + cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING; + + cifs_i->server_eof = fattr->cf_eof; + /* + * Can't safely change the file size here if the client is writing to + * it due to potential races. + */ + spin_lock(&inode->i_lock); + if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) { + i_size_write(inode, fattr->cf_eof); + + /* + * i_blocks is not related to (i_size / i_blksize), + * but instead 512 byte (2**9) size is required for + * calculating num blocks. + */ + inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; + } + spin_unlock(&inode->i_lock); + + cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL); +} + +void +cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + return; - inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(info->LastAccessTime)); - inode->i_mtime = - cifs_NTtimeToUnix(le64_to_cpu(info->LastModificationTime)); - inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(info->LastStatusChange)); - inode->i_mode = le64_to_cpu(info->Permissions); + fattr->cf_uniqueid = iunique(sb, ROOT_I); +} + +/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ +void +cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); + fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + 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); /* * Since we set the inode type below we need to mask off * to avoid strange results if bits set above. */ - inode->i_mode &= ~S_IFMT; + fattr->cf_mode &= ~S_IFMT; switch (le32_to_cpu(info->Type)) { case UNIX_FILE: - inode->i_mode |= S_IFREG; + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; break; case UNIX_SYMLINK: - inode->i_mode |= S_IFLNK; + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; break; case UNIX_DIR: - inode->i_mode |= S_IFDIR; + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; break; case UNIX_CHARDEV: - inode->i_mode |= S_IFCHR; - inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); break; case UNIX_BLOCKDEV: - inode->i_mode |= S_IFBLK; - inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); break; case UNIX_FIFO: - inode->i_mode |= S_IFIFO; + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; break; case UNIX_SOCKET: - inode->i_mode |= S_IFSOCK; + fattr->cf_mode |= S_IFSOCK; + fattr->cf_dtype = DT_SOCK; break; default: /* safest to call it a file if we do not know */ - inode->i_mode |= S_IFREG; - cFYI(1, ("unknown type %d", le32_to_cpu(info->Type))); + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + cFYI(1, "unknown type %d", le32_to_cpu(info->Type)); break; } - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) && - !force_uid_gid) - inode->i_uid = cifs_sb->mnt_uid; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) + fattr->cf_uid = cifs_sb->mnt_uid; else - inode->i_uid = le64_to_cpu(info->Uid); + fattr->cf_uid = le64_to_cpu(info->Uid); - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) && - !force_uid_gid) - inode->i_gid = cifs_sb->mnt_gid; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) + fattr->cf_gid = cifs_sb->mnt_gid; else - inode->i_gid = le64_to_cpu(info->Gid); - - inode->i_nlink = le64_to_cpu(info->Nlinks); - - spin_lock(&inode->i_lock); - if (is_size_safe_to_change(cifsInfo, end_of_file)) { - /* - * We can not safely change the file size here if the client - * is writing to it due to potential races. - */ - i_size_write(inode, end_of_file); + fattr->cf_gid = le64_to_cpu(info->Gid); - /* - * i_blocks is not related to (i_size / i_blksize), - * but instead 512 byte (2**9) size is required for - * calculating num blocks. - */ - inode->i_blocks = (512 - 1 + num_of_bytes) >> 9; - } - spin_unlock(&inode->i_lock); + fattr->cf_nlink = le64_to_cpu(info->Nlinks); } - /* - * Needed to setup inode data for the directory which is the - * junction to the new submount (ie to setup the fake directory - * which represents a DFS referral) + * Fill a cifs_fattr struct with fake inode info. + * + * Needed to setup cifs_fattr data for the directory which is the + * junction to the new submount (ie to setup the fake directory + * which represents a DFS referral). */ -static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat, - struct super_block *sb) +static void +cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) { - struct inode *pinode = NULL; - - memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO)); - -/* __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); - __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0); - __u64 UniqueId = 0; */ - pfnd_dat->LastStatusChange = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->LastAccessTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->LastModificationTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->Type = cpu_to_le32(UNIX_DIR); - pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU); - pfnd_dat->Nlinks = cpu_to_le64(2); - if (sb->s_root) - pinode = sb->s_root->d_inode; - if (pinode == NULL) - return; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + cFYI(1, "creating fake fattr for DFS referral"); + + memset(fattr, 0, sizeof(*fattr)); + 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; + fattr->cf_nlink = 2; + fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL; +} + +int cifs_get_file_info_unix(struct file *filp) +{ + int rc; + int xid; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr; + struct inode *inode = filp->f_path.dentry->d_inode; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + + xid = GetXid(); + rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } - /* fill in default values for the remaining based on root - inode since we can not query the server for this inode info */ - pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev)); - pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev)); - pfnd_dat->Uid = cpu_to_le64(pinode->i_uid); - pfnd_dat->Gid = cpu_to_le64(pinode->i_gid); + cifs_fattr_to_inode(inode, &fattr); + FreeXid(xid); + return rc; } int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *full_path, struct super_block *sb, int xid) + const unsigned char *full_path, + struct super_block *sb, int xid) { - int rc = 0; + int rc; FILE_UNIX_BASIC_INFO find_data; - struct cifsTconInfo *pTcon; - struct inode *inode; + struct cifs_fattr fattr; + struct cifsTconInfo *tcon; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - bool is_dfs_referral = false; - struct cifsInodeInfo *cifsInfo; - __u64 num_of_bytes; - __u64 end_of_file; - pTcon = cifs_sb->tcon; - cFYI(1, ("Getting info on %s", full_path)); + tcon = cifs_sb->tcon; + cFYI(1, "Getting info on %s", full_path); /* could have done a find first instead but this returns more info */ - rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data, + rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc == -EREMOTE && !is_dfs_referral) { - is_dfs_referral = true; - cFYI(DBG2, ("DFS ref")); - /* for DFS, server does not give us real inode data */ - fill_fake_finddataunix(&find_data, sb); - rc = 0; - } else if (rc) - goto cgiiu_exit; - num_of_bytes = le64_to_cpu(find_data.NumOfBytes); - end_of_file = le64_to_cpu(find_data.EndOfFile); + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + } else { + return rc; + } - /* get new inode */ if (*pinode == NULL) { - *pinode = new_inode(sb); - if (*pinode == NULL) { + /* get new inode */ + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) rc = -ENOMEM; - goto cgiiu_exit; - } - /* Is an i_ino of zero legal? */ - /* note ino incremented to unique num in new_inode */ - /* Are there sanity checks we can use to ensure that - the server is really filling in that field? */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - (*pinode)->i_ino = (unsigned long)find_data.UniqueId; - - if (sb->s_flags & MS_NOATIME) - (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; - - insert_inode_hash(*pinode); + } else { + /* we already have inode, update it */ + cifs_fattr_to_inode(*pinode, &fattr); } - inode = *pinode; - cifsInfo = CIFS_I(inode); - - cFYI(1, ("Old time %ld", cifsInfo->time)); - cifsInfo->time = jiffies; - cFYI(1, ("New time %ld", cifsInfo->time)); - /* this is ok to set on every inode revalidate */ - atomic_set(&cifsInfo->inUse, 1); - - cifs_unix_info_to_inode(inode, &find_data, 0); - - if (num_of_bytes < end_of_file) - cFYI(1, ("allocation size less than end of file")); - cFYI(1, ("Size %ld and blocks %llu", - (unsigned long) inode->i_size, - (unsigned long long)inode->i_blocks)); - - cifs_set_ops(inode, is_dfs_referral); -cgiiu_exit: return rc; } -static int decode_sfu_inode(struct inode *inode, __u64 size, - const unsigned char *path, - struct cifs_sb_info *cifs_sb, int xid) +static int +cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, + struct cifs_sb_info *cifs_sb, int xid) { int rc; int oplock = 0; @@ -287,10 +359,15 @@ static int decode_sfu_inode(struct inode *inode, __u64 size, pbuf = buf; - if (size == 0) { - inode->i_mode |= S_IFIFO; + fattr->cf_mode &= ~S_IFMT; + + if (fattr->cf_eof == 0) { + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; return 0; - } else if (size < 8) { + } else if (fattr->cf_eof < 8) { + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; return -EINVAL; /* EOPNOTSUPP? */ } @@ -302,42 +379,46 @@ static int decode_sfu_inode(struct inode *inode, __u64 size, if (rc == 0) { int buf_type = CIFS_NO_BUFFER; /* Read header */ - rc = CIFSSMBRead(xid, pTcon, - netfid, + rc = CIFSSMBRead(xid, pTcon, netfid, 24 /* length */, 0 /* offset */, &bytes_read, &pbuf, &buf_type); if ((rc == 0) && (bytes_read >= 8)) { if (memcmp("IntxBLK", pbuf, 8) == 0) { - cFYI(1, ("Block device")); - inode->i_mode |= S_IFBLK; + cFYI(1, "Block device"); + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; if (bytes_read == 24) { /* we have enough to decode dev num */ __u64 mjr; /* major */ __u64 mnr; /* minor */ mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - inode->i_rdev = MKDEV(mjr, mnr); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxCHR", pbuf, 8) == 0) { - cFYI(1, ("Char device")); - inode->i_mode |= S_IFCHR; + cFYI(1, "Char device"); + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; if (bytes_read == 24) { /* we have enough to decode dev num */ __u64 mjr; /* major */ __u64 mnr; /* minor */ mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - inode->i_rdev = MKDEV(mjr, mnr); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxLNK", pbuf, 7) == 0) { - cFYI(1, ("Symlink")); - inode->i_mode |= S_IFLNK; + cFYI(1, "Symlink"); + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; } else { - inode->i_mode |= S_IFREG; /* file? */ + fattr->cf_mode |= S_IFREG; /* file? */ + fattr->cf_dtype = DT_REG; rc = -EOPNOTSUPP; } } else { - inode->i_mode |= S_IFREG; /* then it is a file */ + fattr->cf_mode |= S_IFREG; /* then it is a file */ + fattr->cf_dtype = DT_REG; rc = -EOPNOTSUPP; /* or some unknown SFU type */ } CIFSSMBClose(xid, pTcon, netfid); @@ -347,85 +428,142 @@ static int decode_sfu_inode(struct inode *inode, __u64 size, #define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ -static int get_sfu_mode(struct inode *inode, - const unsigned char *path, - struct cifs_sb_info *cifs_sb, int xid) +/* + * Fetch mode bits as provided by SFU. + * + * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? + */ +static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, + struct cifs_sb_info *cifs_sb, int xid) { #ifdef CONFIG_CIFS_XATTR ssize_t rc; char ea_value[4]; __u32 mode; - rc = CIFSSMBQueryEA(xid, cifs_sb->tcon, path, "SETFILEBITS", - ea_value, 4 /* size of buf */, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + rc = CIFSSMBQAllEAs(xid, cifs_sb->tcon, path, "SETFILEBITS", + ea_value, 4 /* size of buf */, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc < 0) return (int)rc; else if (rc > 3) { mode = le32_to_cpu(*((__le32 *)ea_value)); - inode->i_mode &= ~SFBITS_MASK; - cFYI(1, ("special bits 0%o org mode 0%o", mode, inode->i_mode)); - inode->i_mode = (mode & SFBITS_MASK) | inode->i_mode; - cFYI(1, ("special mode bits 0%o", mode)); - return 0; - } else { - return 0; + fattr->cf_mode &= ~SFBITS_MASK; + cFYI(1, "special bits 0%o org mode 0%o", mode, + fattr->cf_mode); + fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; + cFYI(1, "special mode bits 0%o", mode); } + + return 0; #else return -EOPNOTSUPP; #endif } -/* - * Needed to setup inode data for the directory which is the - * junction to the new submount (ie to setup the fake directory - * which represents a DFS referral) - */ -static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat, - struct super_block *sb) +/* 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) { - memset(pfnd_dat, 0, sizeof(FILE_ALL_INFO)); - -/* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0); - __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); - __u8 pfnd_dat->DeletePending = 0; - __u8 pfnd_data->Directory = 0; - __le32 pfnd_dat->EASize = 0; - __u64 pfnd_dat->IndexNumber = 0; - __u64 pfnd_dat->IndexNumber1 = 0; */ - pfnd_dat->CreationTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->LastAccessTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->LastWriteTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->ChangeTime = - cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); - pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY); - pfnd_dat->NumberOfLinks = cpu_to_le32(2); + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); + if (info->DeletePending) + fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + fattr->cf_atime = CURRENT_TIME; + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; + fattr->cf_dtype = DT_DIR; + } else { + fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; + fattr->cf_dtype = DT_REG; + + /* clear write bits if ATTR_READONLY is set */ + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~(S_IWUGO); + } + + fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + + fattr->cf_uid = cifs_sb->mnt_uid; + fattr->cf_gid = cifs_sb->mnt_gid; +} + +int cifs_get_file_info(struct file *filp) +{ + int rc; + int xid; + FILE_ALL_INFO find_data; + struct cifs_fattr fattr; + struct inode *inode = filp->f_path.dentry->d_inode; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + + xid = GetXid(); + rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); + if (rc == -EOPNOTSUPP || rc == -EINVAL) { + /* + * FIXME: legacy server -- fall back to path-based call? + * for now, just skip revalidating and mark inode for + * immediate reval. + */ + rc = 0; + CIFS_I(inode)->time = 0; + goto cgfi_exit; + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } else if (rc) + goto cgfi_exit; + + /* + * don't bother with SFU junk here -- just mark inode as needing + * revalidation. + */ + cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false); + fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + cifs_fattr_to_inode(inode, &fattr); +cgfi_exit: + FreeXid(xid); + return rc; } int cifs_get_inode_info(struct inode **pinode, const unsigned char *full_path, FILE_ALL_INFO *pfindData, struct super_block *sb, int xid, const __u16 *pfid) { - int rc = 0; - __u32 attr; - struct cifsInodeInfo *cifsInfo; + int rc = 0, tmprc; struct cifsTconInfo *pTcon; - struct inode *inode; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *buf = NULL; bool adjustTZ = false; - bool is_dfs_referral = false; - umode_t default_mode; + struct cifs_fattr fattr; pTcon = cifs_sb->tcon; - cFYI(1, ("Getting info on %s", full_path)); + cFYI(1, "Getting info on %s", full_path); if ((pfindData == NULL) && (*pinode != NULL)) { if (CIFS_I(*pinode)->clientCanCacheRead) { - cFYI(1, ("No need to revalidate cached inode sizes")); + cFYI(1, "No need to revalidate cached inode sizes"); return rc; } } @@ -453,164 +591,82 @@ int cifs_get_inode_info(struct inode **pinode, adjustTZ = true; } } - /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ - if (rc == -EREMOTE) { - is_dfs_referral = true; - fill_fake_finddata(pfindData, sb); + + if (!rc) { + cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *) pfindData, + cifs_sb, adjustTZ); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, sb); rc = 0; - } else if (rc) + } else { goto cgii_exit; + } - attr = le32_to_cpu(pfindData->Attributes); - - /* get new inode */ + /* + * If an inode wasn't passed in, then get the inode number + * + * Is an i_ino of zero legal? Can we use that to check if the server + * supports returning inode numbers? Are there other sanity checks we + * can use to ensure that the server is really filling in that field? + * + * We can not use the IndexNumber field by default from Windows or + * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA + * CIFS spec claims that this value is unique within the scope of a + * share, and the windows docs hint that it's actually unique + * per-machine. + * + * There may be higher info levels that work but are there Windows + * server or network appliances for which IndexNumber field is not + * guaranteed unique? + */ if (*pinode == NULL) { - *pinode = new_inode(sb); - if (*pinode == NULL) { - rc = -ENOMEM; - goto cgii_exit; - } - /* Is an i_ino of zero legal? Can we use that to check - if the server supports returning inode numbers? Are - there other sanity checks we can use to ensure that - the server is really filling in that field? */ - - /* We can not use the IndexNumber field by default from - Windows or Samba (in ALL_INFO buf) but we can request - it explicitly. It may not be unique presumably if - the server has multiple devices mounted under one share */ - - /* There may be higher info levels that work but are - there Windows server or network appliances for which - IndexNumber field is not guaranteed unique? */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { int rc1 = 0; - __u64 inode_num; rc1 = CIFSGetSrvInodeNumber(xid, pTcon, - full_path, &inode_num, + full_path, &fattr.cf_uniqueid, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc1) { - cFYI(1, ("GetSrvInodeNum rc %d", rc1)); - /* BB EOPNOSUPP disable SERVER_INUM? */ - } else /* do we need cast or hash to ino? */ - (*pinode)->i_ino = inode_num; - } /* else ino incremented to unique num in new_inode*/ - if (sb->s_flags & MS_NOATIME) - (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; - insert_inode_hash(*pinode); - } - inode = *pinode; - cifsInfo = CIFS_I(inode); - cifsInfo->cifsAttrs = attr; - cifsInfo->delete_pending = pfindData->DeletePending ? true : false; - cFYI(1, ("Old time %ld", cifsInfo->time)); - cifsInfo->time = jiffies; - cFYI(1, ("New time %ld", cifsInfo->time)); - - /* blksize needs to be multiple of two. So safer to default to - blksize and blkbits set in superblock so 2**blkbits and blksize - will match rather than setting to: - (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ - - /* Linux can not store file creation time so ignore it */ - if (pfindData->LastAccessTime) - inode->i_atime = cifs_NTtimeToUnix - (le64_to_cpu(pfindData->LastAccessTime)); - else /* do not need to use current_fs_time - time not stored */ - inode->i_atime = CURRENT_TIME; - inode->i_mtime = - cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); - inode->i_ctime = - cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); - cFYI(DBG2, ("Attributes came in as 0x%x", attr)); - if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { - inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; - inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; - } - - /* get default inode mode */ - if (attr & ATTR_DIRECTORY) - default_mode = cifs_sb->mnt_dir_mode; - else - default_mode = cifs_sb->mnt_file_mode; - - /* set permission bits */ - if (atomic_read(&cifsInfo->inUse) == 0 || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) - inode->i_mode = default_mode; - else { - /* just reenable write bits if !ATTR_READONLY */ - if ((inode->i_mode & S_IWUGO) == 0 && - (attr & ATTR_READONLY) == 0) - inode->i_mode |= (S_IWUGO & default_mode); - - inode->i_mode &= ~S_IFMT; - } - /* clear write bits if ATTR_READONLY is set */ - if (attr & ATTR_READONLY) - inode->i_mode &= ~S_IWUGO; - - /* set inode type */ - if ((attr & ATTR_SYSTEM) && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { - /* no need to fix endianness on 0 */ - if (pfindData->EndOfFile == 0) - inode->i_mode |= S_IFIFO; - else if (decode_sfu_inode(inode, - le64_to_cpu(pfindData->EndOfFile), - full_path, cifs_sb, xid)) - cFYI(1, ("unknown SFU file type\n")); + if (rc1 || !fattr.cf_uniqueid) { + cFYI(1, "GetSrvInodeNum rc %d", rc1); + fattr.cf_uniqueid = iunique(sb, ROOT_I); + cifs_autodisable_serverino(cifs_sb); + } + } else { + fattr.cf_uniqueid = iunique(sb, ROOT_I); + } } else { - if (attr & ATTR_DIRECTORY) - inode->i_mode |= S_IFDIR; - else - inode->i_mode |= S_IFREG; + fattr.cf_uniqueid = CIFS_I(*pinode)->uniqueid; } - spin_lock(&inode->i_lock); - if (is_size_safe_to_change(cifsInfo, - le64_to_cpu(pfindData->EndOfFile))) { - /* can not safely shrink the file size here if the - client is writing to it due to potential races */ - i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); - - /* 512 bytes (2**9) is the fake blocksize that must be - used for this calculation */ - inode->i_blocks = (512 - 1 + le64_to_cpu( - pfindData->AllocationSize)) >> 9; + /* query for SFU type info if supported and needed */ + if (fattr.cf_cifsattrs & ATTR_SYSTEM && + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { + tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cFYI(1, "cifs_sfu_type failed: %d", tmprc); } - spin_unlock(&inode->i_lock); - - inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); - /* BB fill in uid and gid here? with help from winbind? - or retrieve from NTFS stream extended attribute */ #ifdef CONFIG_CIFS_EXPERIMENTAL /* fill in 0777 bits from ACL */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - cFYI(1, ("Getting mode bits from ACL")); - acl_to_uid_mode(inode, full_path, pfid); + cFYI(1, "Getting mode bits from ACL"); + cifs_acl_to_fattr(cifs_sb, &fattr, *pinode, full_path, pfid); } #endif - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - /* fill in remaining high mode bits e.g. SUID, VTX */ - get_sfu_mode(inode, full_path, cifs_sb, xid); - } else if (atomic_read(&cifsInfo->inUse) == 0) { - inode->i_uid = cifs_sb->mnt_uid; - inode->i_gid = cifs_sb->mnt_gid; - /* set so we do not keep refreshing these fields with - bad data after user has changed them in memory */ - atomic_set(&cifsInfo->inUse, 1); - } - - cifs_set_ops(inode, is_dfs_referral); - + /* fill in remaining high mode bits e.g. SUID, VTX */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); + if (!*pinode) { + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) + rc = -ENOMEM; + } else { + cifs_fattr_to_inode(*pinode, &fattr); + } cgii_exit: kfree(buf); @@ -621,30 +677,138 @@ static const struct inode_operations cifs_ipc_inode_ops = { .lookup = cifs_lookup, }; +char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) +{ + int pplen = cifs_sb->prepathlen; + int dfsplen; + char *full_path = NULL; + + /* if no prefix path, simply set path to the root of share to "" */ + if (pplen == 0) { + full_path = kmalloc(1, GFP_KERNEL); + if (full_path) + full_path[0] = 0; + return full_path; + } + + if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) + dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return full_path; + + if (dfsplen) { + strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); + /* switch slash direction in prepath depending on whether + * windows or posix style path names + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { + int i; + for (i = 0; i < dfsplen; i++) { + if (full_path[i] == '\\') + full_path[i] = '/'; + } + } + } + strncpy(full_path + dfsplen, cifs_sb->prepath, pplen); + full_path[dfsplen + pplen] = 0; /* add trailing null */ + return full_path; +} + +static int +cifs_find_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = (struct cifs_fattr *) opaque; + + if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) + return 0; + + /* + * uh oh -- it's a directory. We can't use it since hardlinked dirs are + * verboten. Disable serverino and return it as if it were found, the + * caller can discard it, generate a uniqueid and retry the find + */ + if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) { + fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; + cifs_autodisable_serverino(CIFS_SB(inode->i_sb)); + } + + return 1; +} + +static int +cifs_init_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = (struct cifs_fattr *) opaque; + + CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; + return 0; +} + +/* Given fattrs, get a corresponding inode */ +struct inode * +cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) +{ + unsigned long hash; + struct inode *inode; + +retry_iget5_locked: + cFYI(1, "looking for uniqueid=%llu", fattr->cf_uniqueid); + + /* hash down to 32-bits on 32-bit arch */ + hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); + + inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); + if (inode) { + /* was there a problematic inode number collision? */ + if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { + iput(inode); + fattr->cf_uniqueid = iunique(sb, ROOT_I); + fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; + goto retry_iget5_locked; + } + + cifs_fattr_to_inode(inode, fattr); + if (sb->s_flags & MS_NOATIME) + inode->i_flags |= S_NOATIME | S_NOCMTIME; + if (inode->i_state & I_NEW) { + inode->i_ino = hash; + unlock_new_inode(inode); + } + } + + return inode; +} + /* gets root inode */ -struct inode *cifs_iget(struct super_block *sb, unsigned long ino) +struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) { int xid; struct cifs_sb_info *cifs_sb; - struct inode *inode; + struct inode *inode = NULL; long rc; + char *full_path; - inode = iget_locked(sb, ino); - if (!inode) + cifs_sb = CIFS_SB(sb); + full_path = cifs_build_path_to_root(cifs_sb); + if (full_path == NULL) return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - cifs_sb = CIFS_SB(inode->i_sb); xid = GetXid(); - if (cifs_sb->tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid); + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else - rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid, - NULL); + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); + + if (!inode) + return ERR_PTR(-ENOMEM); + if (rc && cifs_sb->tcon->ipc) { - cFYI(1, ("ipc connection - fake read inode")); + cFYI(1, "ipc connection - fake read inode"); inode->i_mode |= S_IFDIR; inode->i_nlink = 2; inode->i_op = &cifs_ipc_inode_ops; @@ -652,13 +816,14 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; } else if (rc) { + kfree(full_path); _FreeXid(xid); iget_failed(inode); return ERR_PTR(rc); } - unlock_new_inode(inode); + kfree(full_path); /* can not call macro FreeXid here since in a void func * TODO: This is no longer true */ @@ -681,6 +846,9 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, struct cifsTconInfo *pTcon = cifs_sb->tcon; FILE_BASIC_INFO info_buf; + if (attrs == NULL) + return -EINVAL; + if (attrs->ia_valid & ATTR_ATIME) { set_time = true; info_buf.LastAccessTime = @@ -702,7 +870,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, * server times. */ if (set_time && (attrs->ia_valid & ATTR_CTIME)) { - cFYI(1, ("CIFS - CTIME changed")); + cFYI(1, "CIFS - CTIME changed"); info_buf.ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); } else @@ -737,8 +905,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, goto out; } - cFYI(1, ("calling SetFileInfo since SetPathInfo for " - "times not supported by this server")); + cFYI(1, "calling SetFileInfo since SetPathInfo for " + "times not supported by this server"); rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, &netfid, &oplock, @@ -762,7 +930,7 @@ set_via_filehandle: if (open_file == NULL) CIFSSMBClose(xid, pTcon, netfid); else - atomic_dec(&open_file->wrtPending); + cifsFileInfo_put(open_file); out: return rc; } @@ -875,20 +1043,28 @@ undo_setattr: goto out_close; } + +/* + * If dentry->d_inode is null (usually meaning the cached dentry + * is a negative dentry) then we would attempt a standard SMB delete, but + * if that fails we can not attempt the fall back mechanisms on EACCESS + * but will return the EACCESS to the caller. Note that the VFS does not call + * unlink on negative dentries currently. + */ int cifs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; int xid; char *full_path = NULL; struct inode *inode = dentry->d_inode; - struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *tcon = cifs_sb->tcon; struct iattr *attrs = NULL; __u32 dosattr = 0, origattr = 0; - cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry)); + cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry); xid = GetXid(); @@ -896,8 +1072,9 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) * sb->s_vfs_rename_mutex here */ full_path = build_path_from_dentry(dentry); if (full_path == NULL) { + rc = -ENOMEM; FreeXid(xid); - return -ENOMEM; + return rc; } if ((tcon->ses->capabilities & CAP_UNIX) && @@ -906,7 +1083,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) rc = CIFSPOSIXDelFile(xid, tcon, full_path, SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1, ("posix del rc %d", rc)); + cFYI(1, "posix del rc %d", rc); if ((rc == 0) || (rc == -ENOENT)) goto psx_del_no_retry; } @@ -925,7 +1102,7 @@ psx_del_no_retry: rc = cifs_rename_pending_delete(full_path, dentry, xid); if (rc == 0) drop_nlink(inode); - } else if (rc == -EACCES && dosattr == 0) { + } else if ((rc == -EACCES) && (dosattr == 0) && inode) { attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) { rc = -ENOMEM; @@ -933,7 +1110,8 @@ psx_del_no_retry: } /* try to reset dos attributes */ - origattr = cifsInode->cifsAttrs; + cifs_inode = CIFS_I(inode); + origattr = cifs_inode->cifsAttrs; if (origattr == 0) origattr |= ATTR_NORMAL; dosattr = origattr & ~ATTR_READONLY; @@ -954,13 +1132,13 @@ psx_del_no_retry: out_reval: if (inode) { - cifsInode = CIFS_I(inode); - cifsInode->time = 0; /* will force revalidate to get info + cifs_inode = CIFS_I(inode); + cifs_inode->time = 0; /* will force revalidate to get info when needed */ inode->i_ctime = current_fs_time(sb); } dir->i_ctime = dir->i_mtime = current_fs_time(sb); - cifsInode = CIFS_I(dir); + cifs_inode = CIFS_I(dir); CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ kfree(full_path); @@ -969,44 +1147,6 @@ out_reval: return rc; } -static void posix_fill_in_inode(struct inode *tmp_inode, - FILE_UNIX_BASIC_INFO *pData, int isNewInode) -{ - struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode); - loff_t local_size; - struct timespec local_mtime; - - cifsInfo->time = jiffies; - atomic_inc(&cifsInfo->inUse); - - /* save mtime and size */ - local_mtime = tmp_inode->i_mtime; - local_size = tmp_inode->i_size; - - cifs_unix_info_to_inode(tmp_inode, pData, 1); - cifs_set_ops(tmp_inode, false); - - if (!S_ISREG(tmp_inode->i_mode)) - return; - - /* - * No sense invalidating pages for new inode - * since we we have not started caching - * readahead file data yet. - */ - if (isNewInode) - return; - - if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && - (local_size == tmp_inode->i_size)) { - cFYI(1, ("inode exists but unchanged")); - } else { - /* file may have changed on server */ - cFYI(1, ("invalidate inode, readdir detected change")); - invalidate_remote_inode(tmp_inode); - } -} - int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) { int rc = 0, tmprc; @@ -1015,8 +1155,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; + struct cifs_fattr fattr; - cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode)); + cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode); xid = GetXid(); @@ -1025,8 +1166,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) full_path = build_path_from_dentry(direntry); if (full_path == NULL) { + rc = -ENOMEM; FreeXid(xid); - return -ENOMEM; + return rc; } if ((pTcon->ses->capabilities & CAP_UNIX) && @@ -1040,7 +1182,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) goto mkdir_out; } - mode &= ~current->fs->umask; + mode &= ~current_umask(); rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, NULL /* netfid */, pInfo, &oplock, full_path, cifs_sb->local_nls, @@ -1050,7 +1192,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) kfree(pInfo); goto mkdir_retry_old; } else if (rc) { - cFYI(1, ("posix mkdir returned 0x%x", rc)); + cFYI(1, "posix mkdir returned 0x%x", rc); d_drop(direntry); } else { if (pInfo->Type == cpu_to_le32(-1)) { @@ -1066,37 +1208,23 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) else direntry->d_op = &cifs_dentry_ops; - newinode = new_inode(inode->i_sb); - if (newinode == NULL) { + cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb); + cifs_fill_uniqueid(inode->i_sb, &fattr); + newinode = cifs_iget(inode->i_sb, &fattr); + if (!newinode) { kfree(pInfo); goto mkdir_get_info; } - /* Is an i_ino of zero legal? */ - /* Are there sanity checks we can use to ensure that - the server is really filling in that field? */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - newinode->i_ino = - (unsigned long)pInfo->UniqueId; - } /* note ino incremented to unique num in new_inode */ - if (inode->i_sb->s_flags & MS_NOATIME) - newinode->i_flags |= S_NOATIME | S_NOCMTIME; - newinode->i_nlink = 2; - - insert_inode_hash(newinode); d_instantiate(direntry, newinode); - /* we already checked in POSIXCreate whether - frame was long enough */ - posix_fill_in_inode(direntry->d_inode, - pInfo, 1 /* NewInode */); #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1, ("instantiated dentry %p %s to inode %p", - direntry, direntry->d_name.name, newinode)); + cFYI(1, "instantiated dentry %p %s to inode %p", + direntry, direntry->d_name.name, newinode); if (newinode->i_nlink != 2) - cFYI(1, ("unexpected number of links %d", - newinode->i_nlink)); + cFYI(1, "unexpected number of links %d", + newinode->i_nlink); #endif } kfree(pInfo); @@ -1107,7 +1235,7 @@ mkdir_retry_old: rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { - cFYI(1, ("cifs_mkdir returned 0x%x", rc)); + cFYI(1, "cifs_mkdir returned 0x%x", rc); d_drop(direntry); } else { mkdir_get_info: @@ -1129,7 +1257,7 @@ mkdir_get_info: if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2)) direntry->d_inode->i_nlink = 2; - mode &= ~current->fs->umask; + mode &= ~current_umask(); /* must turn on setgid bit if parent dir has it */ if (inode->i_mode & S_ISGID) mode |= S_ISGID; @@ -1143,19 +1271,19 @@ mkdir_get_info: .device = 0, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64)current->fsuid; + args.uid = (__u64)current_fsuid(); if (inode->i_mode & S_ISGID) args.gid = (__u64)inode->i_gid; else - args.gid = (__u64)current->fsgid; + args.gid = (__u64)current_fsgid(); } else { args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; } - CIFSSMBUnixSetInfo(xid, pTcon, full_path, &args, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); } else { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && (mode & S_IWUGO) == 0) { @@ -1184,13 +1312,13 @@ mkdir_get_info: if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { direntry->d_inode->i_uid = - current->fsuid; + current_fsuid(); if (inode->i_mode & S_ISGID) direntry->d_inode->i_gid = inode->i_gid; else direntry->d_inode->i_gid = - current->fsgid; + current_fsgid(); } } } @@ -1210,7 +1338,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) char *full_path = NULL; struct cifsInodeInfo *cifsInode; - cFYI(1, ("cifs_rmdir, inode = 0x%p", inode)); + cFYI(1, "cifs_rmdir, inode = 0x%p", inode); xid = GetXid(); @@ -1219,8 +1347,9 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) full_path = build_path_from_dentry(direntry); if (full_path == NULL) { + rc = -ENOMEM; FreeXid(xid); - return -ENOMEM; + return rc; } rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, @@ -1237,6 +1366,11 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) cifsInode = CIFS_I(direntry->d_inode); cifsInode->time = 0; /* force revalidate to go get info when needed */ + + cifsInode = CIFS_I(inode); + cifsInode->time = 0; /* force revalidate to get parent dir info + since cached search results now invalid */ + direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); @@ -1370,7 +1504,8 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, checking the UniqueId via FILE_INTERNAL_INFO */ unlink_target: - if ((rc == -EACCES) || (rc == -EEXIST)) { + /* Try unlinking the target dentry if it's not negative */ + if (target_dentry->d_inode && (rc == -EACCES || rc == -EEXIST)) { tmprc = cifs_unlink(target_dir, target_dentry); if (tmprc) goto cifs_rename_exit; @@ -1387,134 +1522,108 @@ cifs_rename_exit: return rc; } -int cifs_revalidate(struct dentry *direntry) +static bool +cifs_inode_needs_reval(struct inode *inode) { - int xid; - int rc = 0, wbrc = 0; - char *full_path; - struct cifs_sb_info *cifs_sb; - struct cifsInodeInfo *cifsInode; - loff_t local_size; - struct timespec local_mtime; - bool invalidate_inode = false; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); - if (direntry->d_inode == NULL) - return -ENOENT; + if (cifs_i->clientCanCacheRead) + return false; - cifsInode = CIFS_I(direntry->d_inode); + if (!lookupCacheEnabled) + return true; - if (cifsInode == NULL) - return -ENOENT; + if (cifs_i->time == 0) + return true; - /* no sense revalidating inode info on file that no one can write */ - if (CIFS_I(direntry->d_inode)->clientCanCacheRead) - return rc; + /* FIXME: the actimeo should be tunable */ + if (time_after_eq(jiffies, cifs_i->time + HZ)) + return true; + + /* hardlinked files w/ noserverino get "special" treatment */ + if (!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && + S_ISREG(inode->i_mode) && inode->i_nlink != 1) + return true; + + return false; +} + +/* check invalid_mapping flag and zap the cache if it's set */ +static void +cifs_invalidate_mapping(struct inode *inode) +{ + int rc; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cifs_i->invalid_mapping = false; + + /* write back any cached data */ + if (inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = filemap_write_and_wait(inode->i_mapping); + if (rc) + cifs_i->write_behind_rc = rc; + } + invalidate_remote_inode(inode); +} + +int cifs_revalidate_file(struct file *filp) +{ + int rc = 0; + struct inode *inode = filp->f_path.dentry->d_inode; + + if (!cifs_inode_needs_reval(inode)) + goto check_inval; + + if (CIFS_SB(inode->i_sb)->tcon->unix_ext) + rc = cifs_get_file_info_unix(filp); + else + rc = cifs_get_file_info(filp); + +check_inval: + if (CIFS_I(inode)->invalid_mapping) + cifs_invalidate_mapping(inode); + + return rc; +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ + int xid; + int rc = 0; + char *full_path = NULL; + struct inode *inode = dentry->d_inode; + struct super_block *sb = dentry->d_sb; + + if (inode == NULL) + return -ENOENT; xid = GetXid(); - cifs_sb = CIFS_SB(direntry->d_sb); + if (!cifs_inode_needs_reval(inode)) + goto check_inval; /* can not safely grab the rename sem here if rename calls revalidate since that would deadlock */ - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(dentry); if (full_path == NULL) { - FreeXid(xid); - return -ENOMEM; - } - cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " - "jiffies %ld", full_path, direntry->d_inode, - direntry->d_inode->i_count.counter, direntry, - direntry->d_time, jiffies)); - - if (cifsInode->time == 0) { - /* was set to zero previously to force revalidate */ - } else if (time_before(jiffies, cifsInode->time + HZ) && - lookupCacheEnabled) { - if ((S_ISREG(direntry->d_inode->i_mode) == 0) || - (direntry->d_inode->i_nlink == 1)) { - kfree(full_path); - FreeXid(xid); - return rc; - } else { - cFYI(1, ("Have to revalidate file due to hardlinks")); - } - } - - /* save mtime and size */ - local_mtime = direntry->d_inode->i_mtime; - local_size = direntry->d_inode->i_size; - - if (cifs_sb->tcon->unix_ext) { - rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path, - direntry->d_sb, xid); - if (rc) { - cFYI(1, ("error on getting revalidate info %d", rc)); -/* if (rc != -ENOENT) - rc = 0; */ /* BB should we cache info on - certain errors? */ - } - } else { - rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, - direntry->d_sb, xid, NULL); - if (rc) { - cFYI(1, ("error on getting revalidate info %d", rc)); -/* if (rc != -ENOENT) - rc = 0; */ /* BB should we cache info on - certain errors? */ - } + rc = -ENOMEM; + goto check_inval; } - /* should we remap certain errors, access denied?, to zero */ - /* if not oplocked, we invalidate inode pages if mtime or file size - had changed on server */ + cFYI(1, "Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld " + "jiffies %ld", full_path, inode, inode->i_count.counter, + dentry, dentry->d_time, jiffies); - if (timespec_equal(&local_mtime, &direntry->d_inode->i_mtime) && - (local_size == direntry->d_inode->i_size)) { - cFYI(1, ("cifs_revalidate - inode unchanged")); - } else { - /* file may have changed on server */ - if (cifsInode->clientCanCacheRead) { - /* no need to invalidate inode pages since we were the - only ones who could have modified the file and the - server copy is staler than ours */ - } else { - invalidate_inode = true; - } - } + if (CIFS_SB(sb)->tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); - /* can not grab this sem since kernel filesys locking documentation - indicates i_mutex may be taken by the kernel on lookup and rename - which could deadlock if we grab the i_mutex here as well */ -/* mutex_lock(&direntry->d_inode->i_mutex);*/ - /* need to write out dirty pages here */ - if (direntry->d_inode->i_mapping) { - /* do we need to lock inode until after invalidate completes - below? */ - wbrc = filemap_fdatawrite(direntry->d_inode->i_mapping); - if (wbrc) - CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; - } - if (invalidate_inode) { - /* shrink_dcache not necessary now that cifs dentry ops - are exported for negative dentries */ -/* if (S_ISDIR(direntry->d_inode->i_mode)) - shrink_dcache_parent(direntry); */ - if (S_ISREG(direntry->d_inode->i_mode)) { - if (direntry->d_inode->i_mapping) { - wbrc = filemap_fdatawait(direntry->d_inode->i_mapping); - if (wbrc) - CIFS_I(direntry->d_inode)->write_behind_rc = wbrc; - } - /* may eventually have to do this for open files too */ - if (list_empty(&(cifsInode->openFileList))) { - /* changed on server - flush read ahead pages */ - cFYI(1, ("Invalidating read ahead data on " - "closed file")); - invalidate_remote_inode(direntry->d_inode); - } - } - } -/* mutex_unlock(&direntry->d_inode->i_mutex); */ +check_inval: + if (CIFS_I(inode)->invalid_mapping) + cifs_invalidate_mapping(inode); kfree(full_path); FreeXid(xid); @@ -1524,10 +1633,11 @@ int cifs_revalidate(struct dentry *direntry) int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - int err = cifs_revalidate(dentry); + int err = cifs_revalidate_dentry(dentry); if (!err) { generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; + stat->ino = CIFS_I(dentry->d_inode)->uniqueid; } return err; } @@ -1551,57 +1661,24 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from) static int cifs_vmtruncate(struct inode *inode, loff_t offset) { - struct address_space *mapping = inode->i_mapping; - unsigned long limit; + loff_t oldsize; + int err; spin_lock(&inode->i_lock); - if (inode->i_size < offset) - goto do_expand; - /* - * truncation of in-use swapfiles is disallowed - it would cause - * subsequent swapout to scribble on the now-freed blocks. - */ - if (IS_SWAPFILE(inode)) { + err = inode_newsize_ok(inode, offset); + if (err) { spin_unlock(&inode->i_lock); - goto out_busy; - } - i_size_write(inode, offset); - spin_unlock(&inode->i_lock); - /* - * unmap_mapping_range is called twice, first simply for efficiency - * so that truncate_inode_pages does fewer single-page unmaps. However - * after this first call, and before truncate_inode_pages finishes, - * it is possible for private pages to be COWed, which remain after - * truncate_inode_pages finishes, hence the second unmap_mapping_range - * call must be made for correctness. - */ - unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); - truncate_inode_pages(mapping, offset); - unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); - goto out_truncate; - -do_expand: - limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; - if (limit != RLIM_INFINITY && offset > limit) { - spin_unlock(&inode->i_lock); - goto out_sig; - } - if (offset > inode->i_sb->s_maxbytes) { - spin_unlock(&inode->i_lock); - goto out_big; + goto out; } + + oldsize = inode->i_size; i_size_write(inode, offset); spin_unlock(&inode->i_lock); -out_truncate: - if (inode->i_op && inode->i_op->truncate) + truncate_pagecache(inode, oldsize, offset); + if (inode->i_op->truncate) inode->i_op->truncate(inode); - return 0; -out_sig: - send_sig(SIGXFSZ, current, 0); -out_big: - return -EFBIG; -out_busy: - return -ETXTBSY; +out: + return err; } static int @@ -1629,13 +1706,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, __u32 npid = open_file->pid; rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, false); - atomic_dec(&open_file->wrtPending); - cFYI(1, ("SetFSize for attrs rc = %d", rc)); + cifsFileInfo_put(open_file); + cFYI(1, "SetFSize for attrs rc = %d", rc); if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { unsigned int bytes_written; rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size, &bytes_written, NULL, NULL, 1); - cFYI(1, ("Wrt seteof rc %d", rc)); + cFYI(1, "Wrt seteof rc %d", rc); } } else rc = -EINVAL; @@ -1649,7 +1726,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, false, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); + cFYI(1, "SetEOF by path (setattrs) rc = %d", rc); if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { __u16 netfid; int oplock = 0; @@ -1666,13 +1743,14 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, attrs->ia_size, &bytes_written, NULL, NULL, 1); - cFYI(1, ("wrt seteof rc %d", rc)); + cFYI(1, "wrt seteof rc %d", rc); CIFSSMBClose(xid, pTcon, netfid); } } } if (rc == 0) { + cifsInode->server_eof = attrs->ia_size; rc = cifs_vmtruncate(inode, attrs->ia_size); cifs_truncate_page(inode->i_mapping, inode->i_size); } @@ -1691,9 +1769,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsTconInfo *pTcon = cifs_sb->tcon; struct cifs_unix_set_info_args *args = NULL; + struct cifsFileInfo *open_file; - cFYI(1, ("setattr_unix on file %s attrs->ia_valid=0x%x", - direntry->d_name.name, attrs->ia_valid)); + cFYI(1, "setattr_unix on file %s attrs->ia_valid=0x%x", + direntry->d_name.name, attrs->ia_valid); xid = GetXid(); @@ -1712,20 +1791,21 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) goto out; } - if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { - /* - Flush data before changing file size or changing the last - write time of the file on the server. If the - flush returns error, store it to report later and continue. - BB: This should be smarter. Why bother flushing pages that - will be truncated anyway? Also, should we error out here if - the flush returns error? - */ - rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the + * ownership or mode then we may also need to do this. Here, we take + * the safe way out and just do the flush on all setattr requests. If + * the flush returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? + */ + rc = filemap_write_and_wait(inode->i_mapping); + if (rc != 0) { + cifsInode->write_behind_rc = rc; + rc = 0; } if (attrs->ia_valid & ATTR_SIZE) { @@ -1776,13 +1856,31 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) args->ctime = NO_CHANGE_64; args->device = 0; - rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path, args, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + open_file = find_writable_file(cifsInode); + if (open_file) { + u16 nfid = open_file->netfid; + u32 npid = open_file->pid; + rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); + cifsFileInfo_put(open_file); + } else { + rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + } - if (!rc) + if (!rc) { rc = inode_setattr(inode, attrs); + + /* force revalidate when any of these times are set since some + of the fs types (eg ext3, fat) do not have fine enough + time granularity to match protocol, and we do not have a + a way (yet) to query the server fs's time granularity (and + whether it rounds times down). + */ + if (!rc && (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME))) + cifsInode->time = 0; + } out: kfree(args); kfree(full_path); @@ -1804,8 +1902,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) xid = GetXid(); - cFYI(1, ("setattr on file %s attrs->iavalid 0x%x", - direntry->d_name.name, attrs->ia_valid)); + cFYI(1, "setattr on file %s attrs->iavalid 0x%x", + direntry->d_name.name, attrs->ia_valid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { /* check if we have permission to change attrs */ @@ -1819,24 +1917,26 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) full_path = build_path_from_dentry(direntry); if (full_path == NULL) { + rc = -ENOMEM; FreeXid(xid); - return -ENOMEM; + return rc; } - if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { - /* - Flush data before changing file size or changing the last - write time of the file on the server. If the - flush returns error, store it to report later and continue. - BB: This should be smarter. Why bother flushing pages that - will be truncated anyway? Also, should we error out here if - the flush returns error? - */ - rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) { - cifsInode->write_behind_rc = rc; - rc = 0; - } + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the + * ownership or mode then we may also need to do this. Here, we take + * the safe way out and just do the flush on all setattr requests. If + * the flush returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? + */ + rc = filemap_write_and_wait(inode->i_mapping); + if (rc != 0) { + cifsInode->write_behind_rc = rc; + rc = 0; } if (attrs->ia_valid & ATTR_SIZE) { @@ -1860,7 +1960,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) attrs->ia_valid &= ~ATTR_MODE; if (attrs->ia_valid & ATTR_MODE) { - cFYI(1, ("Mode changed to 0%o", attrs->ia_mode)); + cFYI(1, "Mode changed to 0%o", attrs->ia_mode); mode = attrs->ia_mode; } @@ -1946,7 +2046,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) #if 0 void cifs_delete_inode(struct inode *inode) { - cFYI(1, ("In cifs_delete_inode, inode = 0x%p", inode)); + cFYI(1, "In cifs_delete_inode, inode = 0x%p", inode); /* may have to add back in if and when safe distributed caching of directories added e.g. via FindNotify */ }