diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-07-23 09:20:10 (GMT) |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-07-23 09:20:10 (GMT) |
commit | 39fe5434cb9de5da40510028b17b96bc4eb312b3 (patch) | |
tree | 7a02a317b9ad57da51ca99887c119e779ccf3f13 /fs | |
parent | 0fc72b81d3111d114ab378935b1cf07680ca1289 (diff) | |
parent | f695baf2df9e0413d3521661070103711545207a (diff) | |
download | linux-fsl-qoriq-39fe5434cb9de5da40510028b17b96bc4eb312b3.tar.xz |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'fs')
416 files changed, 24469 insertions, 18776 deletions
diff --git a/fs/9p/9p.h b/fs/9p/9p.h deleted file mode 100644 index 94e2f92..0000000 --- a/fs/9p/9p.h +++ /dev/null @@ -1,375 +0,0 @@ -/* - * linux/fs/9p/9p.h - * - * 9P protocol definitions. - * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -/* Message Types */ -enum { - TVERSION = 100, - RVERSION, - TAUTH = 102, - RAUTH, - TATTACH = 104, - RATTACH, - TERROR = 106, - RERROR, - TFLUSH = 108, - RFLUSH, - TWALK = 110, - RWALK, - TOPEN = 112, - ROPEN, - TCREATE = 114, - RCREATE, - TREAD = 116, - RREAD, - TWRITE = 118, - RWRITE, - TCLUNK = 120, - RCLUNK, - TREMOVE = 122, - RREMOVE, - TSTAT = 124, - RSTAT, - TWSTAT = 126, - RWSTAT, -}; - -/* modes */ -enum { - V9FS_OREAD = 0x00, - V9FS_OWRITE = 0x01, - V9FS_ORDWR = 0x02, - V9FS_OEXEC = 0x03, - V9FS_OEXCL = 0x04, - V9FS_OTRUNC = 0x10, - V9FS_OREXEC = 0x20, - V9FS_ORCLOSE = 0x40, - V9FS_OAPPEND = 0x80, -}; - -/* permissions */ -enum { - V9FS_DMDIR = 0x80000000, - V9FS_DMAPPEND = 0x40000000, - V9FS_DMEXCL = 0x20000000, - V9FS_DMMOUNT = 0x10000000, - V9FS_DMAUTH = 0x08000000, - V9FS_DMTMP = 0x04000000, - V9FS_DMSYMLINK = 0x02000000, - V9FS_DMLINK = 0x01000000, - /* 9P2000.u extensions */ - V9FS_DMDEVICE = 0x00800000, - V9FS_DMNAMEDPIPE = 0x00200000, - V9FS_DMSOCKET = 0x00100000, - V9FS_DMSETUID = 0x00080000, - V9FS_DMSETGID = 0x00040000, -}; - -/* qid.types */ -enum { - V9FS_QTDIR = 0x80, - V9FS_QTAPPEND = 0x40, - V9FS_QTEXCL = 0x20, - V9FS_QTMOUNT = 0x10, - V9FS_QTAUTH = 0x08, - V9FS_QTTMP = 0x04, - V9FS_QTSYMLINK = 0x02, - V9FS_QTLINK = 0x01, - V9FS_QTFILE = 0x00, -}; - -#define V9FS_NOTAG (u16)(~0) -#define V9FS_NOFID (u32)(~0) -#define V9FS_MAXWELEM 16 - -/* ample room for Twrite/Rread header (iounit) */ -#define V9FS_IOHDRSZ 24 - -struct v9fs_str { - u16 len; - char *str; -}; - -/* qids are the unique ID for a file (like an inode */ -struct v9fs_qid { - u8 type; - u32 version; - u64 path; -}; - -/* Plan 9 file metadata (stat) structure */ -struct v9fs_stat { - u16 size; - u16 type; - u32 dev; - struct v9fs_qid qid; - u32 mode; - u32 atime; - u32 mtime; - u64 length; - struct v9fs_str name; - struct v9fs_str uid; - struct v9fs_str gid; - struct v9fs_str muid; - struct v9fs_str extension; /* 9p2000.u extensions */ - u32 n_uid; /* 9p2000.u extensions */ - u32 n_gid; /* 9p2000.u extensions */ - u32 n_muid; /* 9p2000.u extensions */ -}; - -/* file metadata (stat) structure used to create Twstat message - The is similar to v9fs_stat, but the strings don't point to - the same memory block and should be freed separately -*/ -struct v9fs_wstat { - u16 size; - u16 type; - u32 dev; - struct v9fs_qid qid; - u32 mode; - u32 atime; - u32 mtime; - u64 length; - char *name; - char *uid; - char *gid; - char *muid; - char *extension; /* 9p2000.u extensions */ - u32 n_uid; /* 9p2000.u extensions */ - u32 n_gid; /* 9p2000.u extensions */ - u32 n_muid; /* 9p2000.u extensions */ -}; - -/* Structures for Protocol Operations */ - -struct Tversion { - u32 msize; - struct v9fs_str version; -}; - -struct Rversion { - u32 msize; - struct v9fs_str version; -}; - -struct Tauth { - u32 afid; - struct v9fs_str uname; - struct v9fs_str aname; -}; - -struct Rauth { - struct v9fs_qid qid; -}; - -struct Rerror { - struct v9fs_str error; - u32 errno; /* 9p2000.u extension */ -}; - -struct Tflush { - u16 oldtag; -}; - -struct Rflush { -}; - -struct Tattach { - u32 fid; - u32 afid; - struct v9fs_str uname; - struct v9fs_str aname; -}; - -struct Rattach { - struct v9fs_qid qid; -}; - -struct Twalk { - u32 fid; - u32 newfid; - u16 nwname; - struct v9fs_str wnames[16]; -}; - -struct Rwalk { - u16 nwqid; - struct v9fs_qid wqids[16]; -}; - -struct Topen { - u32 fid; - u8 mode; -}; - -struct Ropen { - struct v9fs_qid qid; - u32 iounit; -}; - -struct Tcreate { - u32 fid; - struct v9fs_str name; - u32 perm; - u8 mode; - struct v9fs_str extension; -}; - -struct Rcreate { - struct v9fs_qid qid; - u32 iounit; -}; - -struct Tread { - u32 fid; - u64 offset; - u32 count; -}; - -struct Rread { - u32 count; - u8 *data; -}; - -struct Twrite { - u32 fid; - u64 offset; - u32 count; - u8 *data; -}; - -struct Rwrite { - u32 count; -}; - -struct Tclunk { - u32 fid; -}; - -struct Rclunk { -}; - -struct Tremove { - u32 fid; -}; - -struct Rremove { -}; - -struct Tstat { - u32 fid; -}; - -struct Rstat { - struct v9fs_stat stat; -}; - -struct Twstat { - u32 fid; - struct v9fs_stat stat; -}; - -struct Rwstat { -}; - -/* - * fcall is the primary packet structure - * - */ - -struct v9fs_fcall { - u32 size; - u8 id; - u16 tag; - void *sdata; - - union { - struct Tversion tversion; - struct Rversion rversion; - struct Tauth tauth; - struct Rauth rauth; - struct Rerror rerror; - struct Tflush tflush; - struct Rflush rflush; - struct Tattach tattach; - struct Rattach rattach; - struct Twalk twalk; - struct Rwalk rwalk; - struct Topen topen; - struct Ropen ropen; - struct Tcreate tcreate; - struct Rcreate rcreate; - struct Tread tread; - struct Rread rread; - struct Twrite twrite; - struct Rwrite rwrite; - struct Tclunk tclunk; - struct Rclunk rclunk; - struct Tremove tremove; - struct Rremove rremove; - struct Tstat tstat; - struct Rstat rstat; - struct Twstat twstat; - struct Rwstat rwstat; - } params; -}; - -#define PRINT_FCALL_ERROR(s, fcall) dprintk(DEBUG_ERROR, "%s: %.*s\n", s, \ - fcall?fcall->params.rerror.error.len:0, \ - fcall?fcall->params.rerror.error.str:""); - -int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, - char *version, struct v9fs_fcall **rcall); - -int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, - u32 fid, u32 afid, struct v9fs_fcall **rcall); - -int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid); - -int v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **rcall); - -int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_wstat *wstat, struct v9fs_fcall **rcall); - -int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, - char *name, struct v9fs_fcall **rcall); - -int v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, - struct v9fs_fcall **rcall); - -int v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **rcall); - -int v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, - u32 perm, u8 mode, char *extension, struct v9fs_fcall **rcall); - -int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, - u64 offset, u32 count, struct v9fs_fcall **rcall); - -int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, const char __user * data, - struct v9fs_fcall **rcall); -int v9fs_printfcall(char *, int, struct v9fs_fcall *, int); diff --git a/fs/9p/Makefile b/fs/9p/Makefile index 87897f8..bc7f0d1 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -1,18 +1,12 @@ obj-$(CONFIG_9P_FS) := 9p.o 9p-objs := \ - trans_fd.o \ - mux.o \ - fcall.o \ - conv.o \ vfs_super.o \ vfs_inode.o \ vfs_addr.o \ vfs_file.o \ vfs_dir.o \ vfs_dentry.o \ - error.o \ v9fs.o \ fid.o \ - fcprint.o diff --git a/fs/9p/conv.c b/fs/9p/conv.c deleted file mode 100644 index a3ed571..0000000 --- a/fs/9p/conv.c +++ /dev/null @@ -1,845 +0,0 @@ -/* - * linux/fs/9p/conv.c - * - * 9P protocol conversion functions - * - * Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/idr.h> -#include <asm/uaccess.h> -#include "debug.h" -#include "v9fs.h" -#include "9p.h" -#include "conv.h" - -/* - * Buffer to help with string parsing - */ -struct cbuf { - unsigned char *sp; - unsigned char *p; - unsigned char *ep; -}; - -static inline void buf_init(struct cbuf *buf, void *data, int datalen) -{ - buf->sp = buf->p = data; - buf->ep = data + datalen; -} - -static inline int buf_check_overflow(struct cbuf *buf) -{ - return buf->p > buf->ep; -} - -static int buf_check_size(struct cbuf *buf, int len) -{ - if (buf->p + len > buf->ep) { - if (buf->p < buf->ep) { - eprintk(KERN_ERR, "buffer overflow: want %d has %d\n", - len, (int)(buf->ep - buf->p)); - dump_stack(); - buf->p = buf->ep + 1; - } - - return 0; - } - - return 1; -} - -static void *buf_alloc(struct cbuf *buf, int len) -{ - void *ret = NULL; - - if (buf_check_size(buf, len)) { - ret = buf->p; - buf->p += len; - } - - return ret; -} - -static void buf_put_int8(struct cbuf *buf, u8 val) -{ - if (buf_check_size(buf, 1)) { - buf->p[0] = val; - buf->p++; - } -} - -static void buf_put_int16(struct cbuf *buf, u16 val) -{ - if (buf_check_size(buf, 2)) { - *(__le16 *) buf->p = cpu_to_le16(val); - buf->p += 2; - } -} - -static void buf_put_int32(struct cbuf *buf, u32 val) -{ - if (buf_check_size(buf, 4)) { - *(__le32 *)buf->p = cpu_to_le32(val); - buf->p += 4; - } -} - -static void buf_put_int64(struct cbuf *buf, u64 val) -{ - if (buf_check_size(buf, 8)) { - *(__le64 *)buf->p = cpu_to_le64(val); - buf->p += 8; - } -} - -static char *buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) -{ - char *ret; - - ret = NULL; - if (buf_check_size(buf, slen + 2)) { - buf_put_int16(buf, slen); - ret = buf->p; - memcpy(buf->p, s, slen); - buf->p += slen; - } - - return ret; -} - -static inline void buf_put_string(struct cbuf *buf, const char *s) -{ - buf_put_stringn(buf, s, strlen(s)); -} - -static u8 buf_get_int8(struct cbuf *buf) -{ - u8 ret = 0; - - if (buf_check_size(buf, 1)) { - ret = buf->p[0]; - buf->p++; - } - - return ret; -} - -static u16 buf_get_int16(struct cbuf *buf) -{ - u16 ret = 0; - - if (buf_check_size(buf, 2)) { - ret = le16_to_cpu(*(__le16 *)buf->p); - buf->p += 2; - } - - return ret; -} - -static u32 buf_get_int32(struct cbuf *buf) -{ - u32 ret = 0; - - if (buf_check_size(buf, 4)) { - ret = le32_to_cpu(*(__le32 *)buf->p); - buf->p += 4; - } - - return ret; -} - -static u64 buf_get_int64(struct cbuf *buf) -{ - u64 ret = 0; - - if (buf_check_size(buf, 8)) { - ret = le64_to_cpu(*(__le64 *)buf->p); - buf->p += 8; - } - - return ret; -} - -static void buf_get_str(struct cbuf *buf, struct v9fs_str *vstr) -{ - vstr->len = buf_get_int16(buf); - if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) { - vstr->str = buf->p; - buf->p += vstr->len; - } else { - vstr->len = 0; - vstr->str = NULL; - } -} - -static void buf_get_qid(struct cbuf *bufp, struct v9fs_qid *qid) -{ - qid->type = buf_get_int8(bufp); - qid->version = buf_get_int32(bufp); - qid->path = buf_get_int64(bufp); -} - -/** - * v9fs_size_wstat - calculate the size of a variable length stat struct - * @stat: metadata (stat) structure - * @extended: non-zero if 9P2000.u - * - */ - -static int v9fs_size_wstat(struct v9fs_wstat *wstat, int extended) -{ - int size = 0; - - if (wstat == NULL) { - eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n"); - return 0; - } - - size = /* 2 + *//* size[2] */ - 2 + /* type[2] */ - 4 + /* dev[4] */ - 1 + /* qid.type[1] */ - 4 + /* qid.vers[4] */ - 8 + /* qid.path[8] */ - 4 + /* mode[4] */ - 4 + /* atime[4] */ - 4 + /* mtime[4] */ - 8 + /* length[8] */ - 8; /* minimum sum of string lengths */ - - if (wstat->name) - size += strlen(wstat->name); - if (wstat->uid) - size += strlen(wstat->uid); - if (wstat->gid) - size += strlen(wstat->gid); - if (wstat->muid) - size += strlen(wstat->muid); - - if (extended) { - size += 4 + /* n_uid[4] */ - 4 + /* n_gid[4] */ - 4 + /* n_muid[4] */ - 2; /* string length of extension[4] */ - if (wstat->extension) - size += strlen(wstat->extension); - } - - return size; -} - -/** - * buf_get_stat - safely decode a recieved metadata (stat) structure - * @bufp: buffer to deserialize - * @stat: metadata (stat) structure - * @extended: non-zero if 9P2000.u - * - */ - -static void -buf_get_stat(struct cbuf *bufp, struct v9fs_stat *stat, int extended) -{ - stat->size = buf_get_int16(bufp); - stat->type = buf_get_int16(bufp); - stat->dev = buf_get_int32(bufp); - stat->qid.type = buf_get_int8(bufp); - stat->qid.version = buf_get_int32(bufp); - stat->qid.path = buf_get_int64(bufp); - stat->mode = buf_get_int32(bufp); - stat->atime = buf_get_int32(bufp); - stat->mtime = buf_get_int32(bufp); - stat->length = buf_get_int64(bufp); - buf_get_str(bufp, &stat->name); - buf_get_str(bufp, &stat->uid); - buf_get_str(bufp, &stat->gid); - buf_get_str(bufp, &stat->muid); - - if (extended) { - buf_get_str(bufp, &stat->extension); - stat->n_uid = buf_get_int32(bufp); - stat->n_gid = buf_get_int32(bufp); - stat->n_muid = buf_get_int32(bufp); - } -} - -/** - * v9fs_deserialize_stat - decode a received metadata structure - * @buf: buffer to deserialize - * @buflen: length of received buffer - * @stat: metadata structure to decode into - * @extended: non-zero if 9P2000.u - * - * Note: stat will point to the buf region. - */ - -int -v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, - int extended) -{ - struct cbuf buffer; - struct cbuf *bufp = &buffer; - unsigned char *p; - - buf_init(bufp, buf, buflen); - p = bufp->p; - buf_get_stat(bufp, stat, extended); - - if (buf_check_overflow(bufp)) - return 0; - else - return bufp->p - p; -} - -/** - * deserialize_fcall - unmarshal a response - * @buf: recieved buffer - * @buflen: length of received buffer - * @rcall: fcall structure to populate - * @rcalllen: length of fcall structure to populate - * @extended: non-zero if 9P2000.u - * - */ - -int -v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, - int extended) -{ - - struct cbuf buffer; - struct cbuf *bufp = &buffer; - int i = 0; - - buf_init(bufp, buf, buflen); - - rcall->size = buf_get_int32(bufp); - rcall->id = buf_get_int8(bufp); - rcall->tag = buf_get_int16(bufp); - - dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id, - rcall->tag); - - switch (rcall->id) { - default: - eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id); - return -EPROTO; - case RVERSION: - rcall->params.rversion.msize = buf_get_int32(bufp); - buf_get_str(bufp, &rcall->params.rversion.version); - break; - case RFLUSH: - break; - case RATTACH: - rcall->params.rattach.qid.type = buf_get_int8(bufp); - rcall->params.rattach.qid.version = buf_get_int32(bufp); - rcall->params.rattach.qid.path = buf_get_int64(bufp); - break; - case RWALK: - rcall->params.rwalk.nwqid = buf_get_int16(bufp); - if (rcall->params.rwalk.nwqid > V9FS_MAXWELEM) { - eprintk(KERN_ERR, "Rwalk with more than %d qids: %d\n", - V9FS_MAXWELEM, rcall->params.rwalk.nwqid); - return -EPROTO; - } - - for (i = 0; i < rcall->params.rwalk.nwqid; i++) - buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]); - break; - case ROPEN: - buf_get_qid(bufp, &rcall->params.ropen.qid); - rcall->params.ropen.iounit = buf_get_int32(bufp); - break; - case RCREATE: - buf_get_qid(bufp, &rcall->params.rcreate.qid); - rcall->params.rcreate.iounit = buf_get_int32(bufp); - break; - case RREAD: - rcall->params.rread.count = buf_get_int32(bufp); - rcall->params.rread.data = bufp->p; - buf_check_size(bufp, rcall->params.rread.count); - break; - case RWRITE: - rcall->params.rwrite.count = buf_get_int32(bufp); - break; - case RCLUNK: - break; - case RREMOVE: - break; - case RSTAT: - buf_get_int16(bufp); - buf_get_stat(bufp, &rcall->params.rstat.stat, extended); - break; - case RWSTAT: - break; - case RERROR: - buf_get_str(bufp, &rcall->params.rerror.error); - if (extended) - rcall->params.rerror.errno = buf_get_int16(bufp); - break; - } - - if (buf_check_overflow(bufp)) { - dprintk(DEBUG_ERROR, "buffer overflow\n"); - return -EIO; - } - - return bufp->p - bufp->sp; -} - -static inline void v9fs_put_int8(struct cbuf *bufp, u8 val, u8 * p) -{ - *p = val; - buf_put_int8(bufp, val); -} - -static inline void v9fs_put_int16(struct cbuf *bufp, u16 val, u16 * p) -{ - *p = val; - buf_put_int16(bufp, val); -} - -static inline void v9fs_put_int32(struct cbuf *bufp, u32 val, u32 * p) -{ - *p = val; - buf_put_int32(bufp, val); -} - -static inline void v9fs_put_int64(struct cbuf *bufp, u64 val, u64 * p) -{ - *p = val; - buf_put_int64(bufp, val); -} - -static void -v9fs_put_str(struct cbuf *bufp, char *data, struct v9fs_str *str) -{ - int len; - char *s; - - if (data) - len = strlen(data); - else - len = 0; - - s = buf_put_stringn(bufp, data, len); - if (str) { - str->len = len; - str->str = s; - } -} - -static int -v9fs_put_user_data(struct cbuf *bufp, const char __user * data, int count, - unsigned char **pdata) -{ - *pdata = buf_alloc(bufp, count); - return copy_from_user(*pdata, data, count); -} - -static void -v9fs_put_wstat(struct cbuf *bufp, struct v9fs_wstat *wstat, - struct v9fs_stat *stat, int statsz, int extended) -{ - v9fs_put_int16(bufp, statsz, &stat->size); - v9fs_put_int16(bufp, wstat->type, &stat->type); - v9fs_put_int32(bufp, wstat->dev, &stat->dev); - v9fs_put_int8(bufp, wstat->qid.type, &stat->qid.type); - v9fs_put_int32(bufp, wstat->qid.version, &stat->qid.version); - v9fs_put_int64(bufp, wstat->qid.path, &stat->qid.path); - v9fs_put_int32(bufp, wstat->mode, &stat->mode); - v9fs_put_int32(bufp, wstat->atime, &stat->atime); - v9fs_put_int32(bufp, wstat->mtime, &stat->mtime); - v9fs_put_int64(bufp, wstat->length, &stat->length); - - v9fs_put_str(bufp, wstat->name, &stat->name); - v9fs_put_str(bufp, wstat->uid, &stat->uid); - v9fs_put_str(bufp, wstat->gid, &stat->gid); - v9fs_put_str(bufp, wstat->muid, &stat->muid); - - if (extended) { - v9fs_put_str(bufp, wstat->extension, &stat->extension); - v9fs_put_int32(bufp, wstat->n_uid, &stat->n_uid); - v9fs_put_int32(bufp, wstat->n_gid, &stat->n_gid); - v9fs_put_int32(bufp, wstat->n_muid, &stat->n_muid); - } -} - -static struct v9fs_fcall * -v9fs_create_common(struct cbuf *bufp, u32 size, u8 id) -{ - struct v9fs_fcall *fc; - - size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ - fc = kmalloc(sizeof(struct v9fs_fcall) + size, GFP_KERNEL); - if (!fc) - return ERR_PTR(-ENOMEM); - - fc->sdata = (char *)fc + sizeof(*fc); - - buf_init(bufp, (char *)fc->sdata, size); - v9fs_put_int32(bufp, size, &fc->size); - v9fs_put_int8(bufp, id, &fc->id); - v9fs_put_int16(bufp, V9FS_NOTAG, &fc->tag); - - return fc; -} - -void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag) -{ - fc->tag = tag; - *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); -} - -struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 2 + strlen(version); /* msize[4] version[s] */ - fc = v9fs_create_common(bufp, size, TVERSION); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, msize, &fc->params.tversion.msize); - v9fs_put_str(bufp, version, &fc->params.tversion.version); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -#if 0 -struct v9fs_fcall *v9fs_create_tauth(u32 afid, char *uname, char *aname) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 2 + strlen(uname) + 2 + strlen(aname); /* afid[4] uname[s] aname[s] */ - fc = v9fs_create_common(bufp, size, TAUTH); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, afid, &fc->params.tauth.afid); - v9fs_put_str(bufp, uname, &fc->params.tauth.uname); - v9fs_put_str(bufp, aname, &fc->params.tauth.aname); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} -#endif /* 0 */ - -struct v9fs_fcall * -v9fs_create_tattach(u32 fid, u32 afid, char *uname, char *aname) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 4 + 2 + strlen(uname) + 2 + strlen(aname); /* fid[4] afid[4] uname[s] aname[s] */ - fc = v9fs_create_common(bufp, size, TATTACH); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tattach.fid); - v9fs_put_int32(bufp, afid, &fc->params.tattach.afid); - v9fs_put_str(bufp, uname, &fc->params.tattach.uname); - v9fs_put_str(bufp, aname, &fc->params.tattach.aname); - - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tflush(u16 oldtag) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 2; /* oldtag[2] */ - fc = v9fs_create_common(bufp, size, TFLUSH); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, - char **wnames) -{ - int i, size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - if (nwname > V9FS_MAXWELEM) { - dprintk(DEBUG_ERROR, "nwname > %d\n", V9FS_MAXWELEM); - return NULL; - } - - size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ - for (i = 0; i < nwname; i++) { - size += 2 + strlen(wnames[i]); /* wname[s] */ - } - - fc = v9fs_create_common(bufp, size, TWALK); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.twalk.fid); - v9fs_put_int32(bufp, newfid, &fc->params.twalk.newfid); - v9fs_put_int16(bufp, nwname, &fc->params.twalk.nwname); - for (i = 0; i < nwname; i++) { - v9fs_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); - } - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 1; /* fid[4] mode[1] */ - fc = v9fs_create_common(bufp, size, TOPEN); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.topen.fid); - v9fs_put_int8(bufp, mode, &fc->params.topen.mode); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode, - char *extension, int extended) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 2 + strlen(name) + 4 + 1; /* fid[4] name[s] perm[4] mode[1] */ - if (extended) { - size += 2 + /* extension[s] */ - (extension == NULL ? 0 : strlen(extension)); - } - - fc = v9fs_create_common(bufp, size, TCREATE); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tcreate.fid); - v9fs_put_str(bufp, name, &fc->params.tcreate.name); - v9fs_put_int32(bufp, perm, &fc->params.tcreate.perm); - v9fs_put_int8(bufp, mode, &fc->params.tcreate.mode); - if (extended) - v9fs_put_str(bufp, extension, &fc->params.tcreate.extension); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ - fc = v9fs_create_common(bufp, size, TREAD); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tread.fid); - v9fs_put_int64(bufp, offset, &fc->params.tread.offset); - v9fs_put_int32(bufp, count, &fc->params.tread.count); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, - const char __user * data) -{ - int size, err; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4 + 8 + 4 + count; /* fid[4] offset[8] count[4] data[count] */ - fc = v9fs_create_common(bufp, size, TWRITE); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.twrite.fid); - v9fs_put_int64(bufp, offset, &fc->params.twrite.offset); - v9fs_put_int32(bufp, count, &fc->params.twrite.count); - err = v9fs_put_user_data(bufp, data, count, &fc->params.twrite.data); - if (err) { - kfree(fc); - fc = ERR_PTR(err); - } - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tclunk(u32 fid) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4; /* fid[4] */ - fc = v9fs_create_common(bufp, size, TCLUNK); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tclunk.fid); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tremove(u32 fid) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4; /* fid[4] */ - fc = v9fs_create_common(bufp, size, TREMOVE); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tremove.fid); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_tstat(u32 fid) -{ - int size; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - size = 4; /* fid[4] */ - fc = v9fs_create_common(bufp, size, TSTAT); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.tstat.fid); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} - -struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, - int extended) -{ - int size, statsz; - struct v9fs_fcall *fc; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - statsz = v9fs_size_wstat(wstat, extended); - size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ - fc = v9fs_create_common(bufp, size, TWSTAT); - if (IS_ERR(fc)) - goto error; - - v9fs_put_int32(bufp, fid, &fc->params.twstat.fid); - buf_put_int16(bufp, statsz + 2); - v9fs_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, extended); - - if (buf_check_overflow(bufp)) { - kfree(fc); - fc = ERR_PTR(-ENOMEM); - } - error: - return fc; -} diff --git a/fs/9p/conv.h b/fs/9p/conv.h deleted file mode 100644 index dd5b6b1..0000000 --- a/fs/9p/conv.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * linux/fs/9p/conv.h - * - * 9P protocol conversion definitions. - * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, - int extended); -int v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, - int extended); - -void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag); - -struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version); -struct v9fs_fcall *v9fs_create_tattach(u32 fid, u32 afid, char *uname, - char *aname); -struct v9fs_fcall *v9fs_create_tflush(u16 oldtag); -struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, - char **wnames); -struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode); -struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode, - char *extension, int extended); -struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count); -struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, - const char __user *data); -struct v9fs_fcall *v9fs_create_tclunk(u32 fid); -struct v9fs_fcall *v9fs_create_tremove(u32 fid); -struct v9fs_fcall *v9fs_create_tstat(u32 fid); -struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, - int extended); diff --git a/fs/9p/debug.h b/fs/9p/debug.h deleted file mode 100644 index 4228c0b..0000000 --- a/fs/9p/debug.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * linux/fs/9p/debug.h - V9FS Debug Definitions - * - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#define DEBUG_ERROR (1<<0) -#define DEBUG_CURRENT (1<<1) -#define DEBUG_9P (1<<2) -#define DEBUG_VFS (1<<3) -#define DEBUG_CONV (1<<4) -#define DEBUG_MUX (1<<5) -#define DEBUG_TRANS (1<<6) -#define DEBUG_SLABS (1<<7) -#define DEBUG_FCALL (1<<8) - -#define DEBUG_DUMP_PKT 0 - -extern int v9fs_debug_level; - -#define dprintk(level, format, arg...) \ -do { \ - if((v9fs_debug_level & level)==level) \ - printk(KERN_NOTICE "-- %s (%d): " \ - format , __FUNCTION__, current->pid , ## arg); \ -} while(0) - -#define eprintk(level, format, arg...) \ -do { \ - printk(level "v9fs: %s (%d): " \ - format , __FUNCTION__, current->pid , ## arg); \ -} while(0) - -#if DEBUG_DUMP_PKT -static inline void dump_data(const unsigned char *data, unsigned int datalen) -{ - int i, n; - char buf[5*8]; - - n = 0; - i = 0; - while (i < datalen) { - n += snprintf(buf+n, sizeof(buf)-n, "%02x", data[i++]); - if (i%4 == 0) - n += snprintf(buf+n, sizeof(buf)-n, " "); - - if (i%16 == 0) { - dprintk(DEBUG_ERROR, "%s\n", buf); - n = 0; - } - } - - dprintk(DEBUG_ERROR, "%s\n", buf); -} -#else /* DEBUG_DUMP_PKT */ -static inline void dump_data(const unsigned char *data, unsigned int datalen) -{ - -} -#endif /* DEBUG_DUMP_PKT */ diff --git a/fs/9p/error.c b/fs/9p/error.c deleted file mode 100644 index 0d7fa4e..0000000 --- a/fs/9p/error.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * linux/fs/9p/error.c - * - * Error string handling - * - * Plan 9 uses error strings, Unix uses error numbers. These functions - * try to help manage that and provide for dynamically adding error - * mappings. - * - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/module.h> - -#include <linux/list.h> -#include <linux/jhash.h> - -#include "debug.h" -#include "error.h" - -/** - * v9fs_error_init - preload - * @errstr: error string - * - */ - -int v9fs_error_init(void) -{ - struct errormap *c; - int bucket; - - /* initialize hash table */ - for (bucket = 0; bucket < ERRHASHSZ; bucket++) - INIT_HLIST_HEAD(&hash_errmap[bucket]); - - /* load initial error map into hash table */ - for (c = errmap; c->name != NULL; c++) { - c->namelen = strlen(c->name); - bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ; - INIT_HLIST_NODE(&c->list); - hlist_add_head(&c->list, &hash_errmap[bucket]); - } - - return 1; -} - -/** - * errstr2errno - convert error string to error number - * @errstr: error string - * - */ - -int v9fs_errstr2errno(char *errstr, int len) -{ - int errno = 0; - struct hlist_node *p = NULL; - struct errormap *c = NULL; - int bucket = jhash(errstr, len, 0) % ERRHASHSZ; - - hlist_for_each_entry(c, p, &hash_errmap[bucket], list) { - if (c->namelen==len && !memcmp(c->name, errstr, len)) { - errno = c->val; - break; - } - } - - if (errno == 0) { - /* TODO: if error isn't found, add it dynamically */ - errstr[len] = 0; - printk(KERN_ERR "%s: errstr :%s: not found\n", __FUNCTION__, - errstr); - errno = 1; - } - - return -errno; -} diff --git a/fs/9p/error.h b/fs/9p/error.h deleted file mode 100644 index 5f3ca52..0000000 --- a/fs/9p/error.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * linux/fs/9p/error.h - * - * Huge Nasty Error Table - * - * Plan 9 uses error strings, Unix uses error numbers. This table tries to - * match UNIX strings and Plan 9 strings to unix error numbers. It is used - * to preload the dynamic error table which can also track user-specific error - * strings. - * - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/errno.h> -#include <asm/errno.h> - -struct errormap { - char *name; - int val; - - int namelen; - struct hlist_node list; -}; - -#define ERRHASHSZ 32 -static struct hlist_head hash_errmap[ERRHASHSZ]; - -/* FixMe - reduce to a reasonable size */ -static struct errormap errmap[] = { - {"Operation not permitted", EPERM}, - {"wstat prohibited", EPERM}, - {"No such file or directory", ENOENT}, - {"directory entry not found", ENOENT}, - {"file not found", ENOENT}, - {"Interrupted system call", EINTR}, - {"Input/output error", EIO}, - {"No such device or address", ENXIO}, - {"Argument list too long", E2BIG}, - {"Bad file descriptor", EBADF}, - {"Resource temporarily unavailable", EAGAIN}, - {"Cannot allocate memory", ENOMEM}, - {"Permission denied", EACCES}, - {"Bad address", EFAULT}, - {"Block device required", ENOTBLK}, - {"Device or resource busy", EBUSY}, - {"File exists", EEXIST}, - {"Invalid cross-device link", EXDEV}, - {"No such device", ENODEV}, - {"Not a directory", ENOTDIR}, - {"Is a directory", EISDIR}, - {"Invalid argument", EINVAL}, - {"Too many open files in system", ENFILE}, - {"Too many open files", EMFILE}, - {"Text file busy", ETXTBSY}, - {"File too large", EFBIG}, - {"No space left on device", ENOSPC}, - {"Illegal seek", ESPIPE}, - {"Read-only file system", EROFS}, - {"Too many links", EMLINK}, - {"Broken pipe", EPIPE}, - {"Numerical argument out of domain", EDOM}, - {"Numerical result out of range", ERANGE}, - {"Resource deadlock avoided", EDEADLK}, - {"File name too long", ENAMETOOLONG}, - {"No locks available", ENOLCK}, - {"Function not implemented", ENOSYS}, - {"Directory not empty", ENOTEMPTY}, - {"Too many levels of symbolic links", ELOOP}, - {"No message of desired type", ENOMSG}, - {"Identifier removed", EIDRM}, - {"No data available", ENODATA}, - {"Machine is not on the network", ENONET}, - {"Package not installed", ENOPKG}, - {"Object is remote", EREMOTE}, - {"Link has been severed", ENOLINK}, - {"Communication error on send", ECOMM}, - {"Protocol error", EPROTO}, - {"Bad message", EBADMSG}, - {"File descriptor in bad state", EBADFD}, - {"Streams pipe error", ESTRPIPE}, - {"Too many users", EUSERS}, - {"Socket operation on non-socket", ENOTSOCK}, - {"Message too long", EMSGSIZE}, - {"Protocol not available", ENOPROTOOPT}, - {"Protocol not supported", EPROTONOSUPPORT}, - {"Socket type not supported", ESOCKTNOSUPPORT}, - {"Operation not supported", EOPNOTSUPP}, - {"Protocol family not supported", EPFNOSUPPORT}, - {"Network is down", ENETDOWN}, - {"Network is unreachable", ENETUNREACH}, - {"Network dropped connection on reset", ENETRESET}, - {"Software caused connection abort", ECONNABORTED}, - {"Connection reset by peer", ECONNRESET}, - {"No buffer space available", ENOBUFS}, - {"Transport endpoint is already connected", EISCONN}, - {"Transport endpoint is not connected", ENOTCONN}, - {"Cannot send after transport endpoint shutdown", ESHUTDOWN}, - {"Connection timed out", ETIMEDOUT}, - {"Connection refused", ECONNREFUSED}, - {"Host is down", EHOSTDOWN}, - {"No route to host", EHOSTUNREACH}, - {"Operation already in progress", EALREADY}, - {"Operation now in progress", EINPROGRESS}, - {"Is a named type file", EISNAM}, - {"Remote I/O error", EREMOTEIO}, - {"Disk quota exceeded", EDQUOT}, -/* errors from fossil, vacfs, and u9fs */ - {"fid unknown or out of range", EBADF}, - {"permission denied", EACCES}, - {"file does not exist", ENOENT}, - {"authentication failed", ECONNREFUSED}, - {"bad offset in directory read", ESPIPE}, - {"bad use of fid", EBADF}, - {"wstat can't convert between files and directories", EPERM}, - {"directory is not empty", ENOTEMPTY}, - {"file exists", EEXIST}, - {"file already exists", EEXIST}, - {"file or directory already exists", EEXIST}, - {"fid already in use", EBADF}, - {"file in use", ETXTBSY}, - {"i/o error", EIO}, - {"file already open for I/O", ETXTBSY}, - {"illegal mode", EINVAL}, - {"illegal name", ENAMETOOLONG}, - {"not a directory", ENOTDIR}, - {"not a member of proposed group", EPERM}, - {"not owner", EACCES}, - {"only owner can change group in wstat", EACCES}, - {"read only file system", EROFS}, - {"no access to special file", EPERM}, - {"i/o count too large", EIO}, - {"unknown group", EINVAL}, - {"unknown user", EINVAL}, - {"bogus wstat buffer", EPROTO}, - {"exclusive use file already open", EAGAIN}, - {"corrupted directory entry", EIO}, - {"corrupted file entry", EIO}, - {"corrupted block label", EIO}, - {"corrupted meta data", EIO}, - {"illegal offset", EINVAL}, - {"illegal path element", ENOENT}, - {"root of file system is corrupted", EIO}, - {"corrupted super block", EIO}, - {"protocol botch", EPROTO}, - {"file system is full", ENOSPC}, - {"file is in use", EAGAIN}, - {"directory entry is not allocated", ENOENT}, - {"file is read only", EROFS}, - {"file has been removed", EIDRM}, - {"only support truncation to zero length", EPERM}, - {"cannot remove root", EPERM}, - {"file too big", EFBIG}, - {"venti i/o error", EIO}, - /* these are not errors */ - {"u9fs rhostsauth: no authentication required", 0}, - {"u9fs authnone: no authentication required", 0}, - {NULL, -1} -}; - -extern int v9fs_error_init(void); diff --git a/fs/9p/fcall.c b/fs/9p/fcall.c deleted file mode 100644 index dc336a6..0000000 --- a/fs/9p/fcall.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * linux/fs/9p/fcall.c - * - * This file contains functions to perform synchronous 9P calls - * - * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/idr.h> - -#include "debug.h" -#include "v9fs.h" -#include "9p.h" -#include "conv.h" -#include "mux.h" - -/** - * v9fs_t_version - negotiate protocol parameters with sever - * @v9ses: 9P2000 session information - * @msize: requested max size packet - * @version: requested version.extension string - * @fcall: pointer to response fcall pointer - * - */ - -int -v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, - char *version, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version); - tc = v9fs_create_tversion(msize, version); - - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_attach - mount the server - * @v9ses: 9P2000 session information - * @uname: user name doing the attach - * @aname: remote name being attached to - * @fid: mount fid to attatch to root node - * @afid: authentication fid (in this case result key) - * @fcall: pointer to response fcall pointer - * - */ - -int -v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, - u32 fid, u32 afid, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall* tc; - - dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname, - aname, fid, afid); - - tc = v9fs_create_tattach(fid, afid, uname, aname); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc, - struct v9fs_fcall *rc, int err) -{ - int fid, id; - struct v9fs_session_info *v9ses; - - id = 0; - fid = tc->params.tclunk.fid; - if (rc) - id = rc->id; - - kfree(tc); - kfree(rc); - if (id == RCLUNK) { - v9ses = a; - v9fs_put_idpool(fid, &v9ses->fidpool); - } -} - -/** - * v9fs_t_clunk - release a fid (finish a transaction) - * @v9ses: 9P2000 session information - * @fid: fid to release - * @fcall: pointer to response fcall pointer - * - */ - -int -v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) -{ - int ret; - struct v9fs_fcall *tc, *rc; - - dprintk(DEBUG_9P, "fid %d\n", fid); - - rc = NULL; - tc = v9fs_create_tclunk(fid); - if (!IS_ERR(tc)) - ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); - else - ret = PTR_ERR(tc); - - if (ret) - dprintk(DEBUG_ERROR, "failed fid %d err %d\n", fid, ret); - - v9fs_t_clunk_cb(v9ses, tc, rc, ret); - return ret; -} - -#if 0 -/** - * v9fs_v9fs_t_flush - flush a pending transaction - * @v9ses: 9P2000 session information - * @tag: tag to release - * - */ -int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "oldtag %d\n", oldtag); - - tc = v9fs_create_tflush(oldtag); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, NULL); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} -#endif - -/** - * v9fs_t_stat - read a file's meta-data - * @v9ses: 9P2000 session information - * @fid: fid pointing to file or directory to get info about - * @fcall: pointer to response fcall - * - */ - -int -v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "fid %d\n", fid); - - ret = -ENOMEM; - tc = v9fs_create_tstat(fid); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_wstat - write a file's meta-data - * @v9ses: 9P2000 session information - * @fid: fid pointing to file or directory to write info about - * @stat: metadata - * @fcall: pointer to response fcall - * - */ - -int -v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_wstat *wstat, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "fid %d\n", fid); - - tc = v9fs_create_twstat(fid, wstat, v9ses->extended); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_walk - walk a fid to a new file or directory - * @v9ses: 9P2000 session information - * @fid: fid to walk - * @newfid: new fid (for clone operations) - * @name: path to walk fid to - * @fcall: pointer to response fcall - * - */ - -/* TODO: support multiple walk */ - -int -v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, - char *name, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - int nwname; - - dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name); - - if (name) - nwname = 1; - else - nwname = 0; - - tc = v9fs_create_twalk(fid, newfid, nwname, &name); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_open - open a file - * - * @v9ses - 9P2000 session information - * @fid - fid to open - * @mode - mode to open file (R, RW, etc) - * @fcall - pointer to response fcall - * - */ - -int -v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, - struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); - - tc = v9fs_create_topen(fid, mode); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_remove - remove a file or directory - * @v9ses: 9P2000 session information - * @fid: fid to remove - * @fcall: pointer to response fcall - * - */ - -int -v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "fid %d\n", fid); - - tc = v9fs_create_tremove(fid); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_create - create a file or directory - * @v9ses: 9P2000 session information - * @fid: fid to create - * @name: name of the file or directory to create - * @perm: permissions to create with - * @mode: mode to open file (R, RW, etc) - * @fcall: pointer to response fcall - * - */ - -int -v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, u32 perm, - u8 mode, char *extension, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc; - - dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n", - fid, name, perm, mode); - - tc = v9fs_create_tcreate(fid, name, perm, mode, extension, - v9ses->extended); - - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_read - read data - * @v9ses: 9P2000 session information - * @fid: fid to read from - * @offset: offset to start read at - * @count: how many bytes to read - * @fcall: pointer to response fcall (with data) - * - */ - -int -v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc, *rc; - - dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, - (long long unsigned) offset, count); - - tc = v9fs_create_tread(fid, offset, count); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); - if (!ret) - ret = rc->params.rread.count; - if (rcp) - *rcp = rc; - else - kfree(rc); - - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - -/** - * v9fs_t_write - write data - * @v9ses: 9P2000 session information - * @fid: fid to write to - * @offset: offset to start write at - * @count: how many bytes to write - * @fcall: pointer to response fcall - * - */ - -int -v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, - const char __user *data, struct v9fs_fcall **rcp) -{ - int ret; - struct v9fs_fcall *tc, *rc; - - dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, - (long long unsigned) offset, count); - - tc = v9fs_create_twrite(fid, offset, count, data); - if (!IS_ERR(tc)) { - ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); - - if (!ret) - ret = rc->params.rwrite.count; - if (rcp) - *rcp = rc; - else - kfree(rc); - - kfree(tc); - } else - ret = PTR_ERR(tc); - - return ret; -} - diff --git a/fs/9p/fcprint.c b/fs/9p/fcprint.c deleted file mode 100644 index 34b9611..0000000 --- a/fs/9p/fcprint.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * linux/fs/9p/fcprint.c - * - * Print 9P call. - * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/idr.h> - -#include "debug.h" -#include "v9fs.h" -#include "9p.h" -#include "mux.h" - -static int -v9fs_printqid(char *buf, int buflen, struct v9fs_qid *q) -{ - int n; - char b[10]; - - n = 0; - if (q->type & V9FS_QTDIR) - b[n++] = 'd'; - if (q->type & V9FS_QTAPPEND) - b[n++] = 'a'; - if (q->type & V9FS_QTAUTH) - b[n++] = 'A'; - if (q->type & V9FS_QTEXCL) - b[n++] = 'l'; - if (q->type & V9FS_QTTMP) - b[n++] = 't'; - if (q->type & V9FS_QTSYMLINK) - b[n++] = 'L'; - b[n] = '\0'; - - return scnprintf(buf, buflen, "(%.16llx %x %s)", (long long int) q->path, - q->version, b); -} - -static int -v9fs_printperm(char *buf, int buflen, int perm) -{ - int n; - char b[15]; - - n = 0; - if (perm & V9FS_DMDIR) - b[n++] = 'd'; - if (perm & V9FS_DMAPPEND) - b[n++] = 'a'; - if (perm & V9FS_DMAUTH) - b[n++] = 'A'; - if (perm & V9FS_DMEXCL) - b[n++] = 'l'; - if (perm & V9FS_DMTMP) - b[n++] = 't'; - if (perm & V9FS_DMDEVICE) - b[n++] = 'D'; - if (perm & V9FS_DMSOCKET) - b[n++] = 'S'; - if (perm & V9FS_DMNAMEDPIPE) - b[n++] = 'P'; - if (perm & V9FS_DMSYMLINK) - b[n++] = 'L'; - b[n] = '\0'; - - return scnprintf(buf, buflen, "%s%03o", b, perm&077); -} - -static int -v9fs_printstat(char *buf, int buflen, struct v9fs_stat *st, int extended) -{ - int n; - - n = scnprintf(buf, buflen, "'%.*s' '%.*s'", st->name.len, - st->name.str, st->uid.len, st->uid.str); - if (extended) - n += scnprintf(buf+n, buflen-n, "(%d)", st->n_uid); - - n += scnprintf(buf+n, buflen-n, " '%.*s'", st->gid.len, st->gid.str); - if (extended) - n += scnprintf(buf+n, buflen-n, "(%d)", st->n_gid); - - n += scnprintf(buf+n, buflen-n, " '%.*s'", st->muid.len, st->muid.str); - if (extended) - n += scnprintf(buf+n, buflen-n, "(%d)", st->n_muid); - - n += scnprintf(buf+n, buflen-n, " q "); - n += v9fs_printqid(buf+n, buflen-n, &st->qid); - n += scnprintf(buf+n, buflen-n, " m "); - n += v9fs_printperm(buf+n, buflen-n, st->mode); - n += scnprintf(buf+n, buflen-n, " at %d mt %d l %lld", - st->atime, st->mtime, (long long int) st->length); - - if (extended) - n += scnprintf(buf+n, buflen-n, " ext '%.*s'", - st->extension.len, st->extension.str); - - return n; -} - -static int -v9fs_dumpdata(char *buf, int buflen, u8 *data, int datalen) -{ - int i, n; - - i = n = 0; - while (i < datalen) { - n += scnprintf(buf + n, buflen - n, "%02x", data[i]); - if (i%4 == 3) - n += scnprintf(buf + n, buflen - n, " "); - if (i%32 == 31) - n += scnprintf(buf + n, buflen - n, "\n"); - - i++; - } - n += scnprintf(buf + n, buflen - n, "\n"); - - return n; -} - -static int -v9fs_printdata(char *buf, int buflen, u8 *data, int datalen) -{ - return v9fs_dumpdata(buf, buflen, data, datalen<16?datalen:16); -} - -int -v9fs_printfcall(char *buf, int buflen, struct v9fs_fcall *fc, int extended) -{ - int i, ret, type, tag; - - if (!fc) - return scnprintf(buf, buflen, "<NULL>"); - - type = fc->id; - tag = fc->tag; - - ret = 0; - switch (type) { - case TVERSION: - ret += scnprintf(buf+ret, buflen-ret, - "Tversion tag %u msize %u version '%.*s'", tag, - fc->params.tversion.msize, fc->params.tversion.version.len, - fc->params.tversion.version.str); - break; - - case RVERSION: - ret += scnprintf(buf+ret, buflen-ret, - "Rversion tag %u msize %u version '%.*s'", tag, - fc->params.rversion.msize, fc->params.rversion.version.len, - fc->params.rversion.version.str); - break; - - case TAUTH: - ret += scnprintf(buf+ret, buflen-ret, - "Tauth tag %u afid %d uname '%.*s' aname '%.*s'", tag, - fc->params.tauth.afid, fc->params.tauth.uname.len, - fc->params.tauth.uname.str, fc->params.tauth.aname.len, - fc->params.tauth.aname.str); - break; - - case RAUTH: - ret += scnprintf(buf+ret, buflen-ret, "Rauth tag %u qid ", tag); - v9fs_printqid(buf+ret, buflen-ret, &fc->params.rauth.qid); - break; - - case TATTACH: - ret += scnprintf(buf+ret, buflen-ret, - "Tattach tag %u fid %d afid %d uname '%.*s' aname '%.*s'", - tag, fc->params.tattach.fid, fc->params.tattach.afid, - fc->params.tattach.uname.len, fc->params.tattach.uname.str, - fc->params.tattach.aname.len, fc->params.tattach.aname.str); - break; - - case RATTACH: - ret += scnprintf(buf+ret, buflen-ret, "Rattach tag %u qid ", tag); - v9fs_printqid(buf+ret, buflen-ret, &fc->params.rattach.qid); - break; - - case RERROR: - ret += scnprintf(buf+ret, buflen-ret, "Rerror tag %u ename '%.*s'", - tag, fc->params.rerror.error.len, - fc->params.rerror.error.str); - if (extended) - ret += scnprintf(buf+ret, buflen-ret, " ecode %d\n", - fc->params.rerror.errno); - break; - - case TFLUSH: - ret += scnprintf(buf+ret, buflen-ret, "Tflush tag %u oldtag %u", - tag, fc->params.tflush.oldtag); - break; - - case RFLUSH: - ret += scnprintf(buf+ret, buflen-ret, "Rflush tag %u", tag); - break; - - case TWALK: - ret += scnprintf(buf+ret, buflen-ret, - "Twalk tag %u fid %d newfid %d nwname %d", tag, - fc->params.twalk.fid, fc->params.twalk.newfid, - fc->params.twalk.nwname); - for(i = 0; i < fc->params.twalk.nwname; i++) - ret += scnprintf(buf+ret, buflen-ret," '%.*s'", - fc->params.twalk.wnames[i].len, - fc->params.twalk.wnames[i].str); - break; - - case RWALK: - ret += scnprintf(buf+ret, buflen-ret, "Rwalk tag %u nwqid %d", - tag, fc->params.rwalk.nwqid); - for(i = 0; i < fc->params.rwalk.nwqid; i++) - ret += v9fs_printqid(buf+ret, buflen-ret, - &fc->params.rwalk.wqids[i]); - break; - - case TOPEN: - ret += scnprintf(buf+ret, buflen-ret, - "Topen tag %u fid %d mode %d", tag, - fc->params.topen.fid, fc->params.topen.mode); - break; - - case ROPEN: - ret += scnprintf(buf+ret, buflen-ret, "Ropen tag %u", tag); - ret += v9fs_printqid(buf+ret, buflen-ret, &fc->params.ropen.qid); - ret += scnprintf(buf+ret, buflen-ret," iounit %d", - fc->params.ropen.iounit); - break; - - case TCREATE: - ret += scnprintf(buf+ret, buflen-ret, - "Tcreate tag %u fid %d name '%.*s' perm ", tag, - fc->params.tcreate.fid, fc->params.tcreate.name.len, - fc->params.tcreate.name.str); - - ret += v9fs_printperm(buf+ret, buflen-ret, fc->params.tcreate.perm); - ret += scnprintf(buf+ret, buflen-ret, " mode %d", - fc->params.tcreate.mode); - break; - - case RCREATE: - ret += scnprintf(buf+ret, buflen-ret, "Rcreate tag %u", tag); - ret += v9fs_printqid(buf+ret, buflen-ret, &fc->params.rcreate.qid); - ret += scnprintf(buf+ret, buflen-ret, " iounit %d", - fc->params.rcreate.iounit); - break; - - case TREAD: - ret += scnprintf(buf+ret, buflen-ret, - "Tread tag %u fid %d offset %lld count %u", tag, - fc->params.tread.fid, - (long long int) fc->params.tread.offset, - fc->params.tread.count); - break; - - case RREAD: - ret += scnprintf(buf+ret, buflen-ret, - "Rread tag %u count %u data ", tag, - fc->params.rread.count); - ret += v9fs_printdata(buf+ret, buflen-ret, fc->params.rread.data, - fc->params.rread.count); - break; - - case TWRITE: - ret += scnprintf(buf+ret, buflen-ret, - "Twrite tag %u fid %d offset %lld count %u data ", - tag, fc->params.twrite.fid, - (long long int) fc->params.twrite.offset, - fc->params.twrite.count); - ret += v9fs_printdata(buf+ret, buflen-ret, fc->params.twrite.data, - fc->params.twrite.count); - break; - - case RWRITE: - ret += scnprintf(buf+ret, buflen-ret, "Rwrite tag %u count %u", - tag, fc->params.rwrite.count); - break; - - case TCLUNK: - ret += scnprintf(buf+ret, buflen-ret, "Tclunk tag %u fid %d", - tag, fc->params.tclunk.fid); - break; - - case RCLUNK: - ret += scnprintf(buf+ret, buflen-ret, "Rclunk tag %u", tag); - break; - - case TREMOVE: - ret += scnprintf(buf+ret, buflen-ret, "Tremove tag %u fid %d", - tag, fc->params.tremove.fid); - break; - - case RREMOVE: - ret += scnprintf(buf+ret, buflen-ret, "Rremove tag %u", tag); - break; - - case TSTAT: - ret += scnprintf(buf+ret, buflen-ret, "Tstat tag %u fid %d", - tag, fc->params.tstat.fid); - break; - - case RSTAT: - ret += scnprintf(buf+ret, buflen-ret, "Rstat tag %u ", tag); - ret += v9fs_printstat(buf+ret, buflen-ret, &fc->params.rstat.stat, - extended); - break; - - case TWSTAT: - ret += scnprintf(buf+ret, buflen-ret, "Twstat tag %u fid %d ", - tag, fc->params.twstat.fid); - ret += v9fs_printstat(buf+ret, buflen-ret, &fc->params.twstat.stat, - extended); - break; - - case RWSTAT: - ret += scnprintf(buf+ret, buflen-ret, "Rwstat tag %u", tag); - break; - - default: - ret += scnprintf(buf+ret, buflen-ret, "unknown type %d", type); - break; - } - - return ret; -} diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 9041971..08fa320 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -26,10 +26,10 @@ #include <linux/sched.h> #include <linux/idr.h> #include <asm/semaphore.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -40,67 +40,29 @@ * */ -int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry) +int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) { - struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; - dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid, - dentry->d_iname, dentry); - if (dentry->d_fsdata == NULL) { - dentry->d_fsdata = - kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (dentry->d_fsdata == NULL) { - dprintk(DEBUG_ERROR, "Out of memory\n"); - return -ENOMEM; - } - fid_list = (struct list_head *)dentry->d_fsdata; - INIT_LIST_HEAD(fid_list); /* Initialize list head */ - } + struct v9fs_dentry *dent; - fid->uid = current->uid; - list_add(&fid->list, fid_list); - return 0; -} + P9_DPRINTK(P9_DEBUG_VFS, "fid %d dentry %s\n", + fid->fid, dentry->d_iname); -/** - * v9fs_fid_create - allocate a FID structure - * @dentry - dentry to link newly created fid to - * - */ - -struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) -{ - struct v9fs_fid *new; + dent = dentry->d_fsdata; + if (!dent) { + dent = kmalloc(sizeof(struct v9fs_dentry), GFP_KERNEL); + if (!dent) + return -ENOMEM; - dprintk(DEBUG_9P, "fid create fid %d\n", fid); - new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); - if (new == NULL) { - dprintk(DEBUG_ERROR, "Out of Memory\n"); - return ERR_PTR(-ENOMEM); + spin_lock_init(&dent->lock); + INIT_LIST_HEAD(&dent->fidlist); + dentry->d_fsdata = dent; } - new->fid = fid; - new->v9ses = v9ses; - new->fidopen = 0; - new->fidclunked = 0; - new->iounit = 0; - new->rdir_pos = 0; - new->rdir_fcall = NULL; - init_MUTEX(&new->lock); - INIT_LIST_HEAD(&new->list); - - return new; -} - -/** - * v9fs_fid_destroy - deallocate a FID structure - * @fid: fid to destroy - * - */ + spin_lock(&dent->lock); + list_add(&fid->dlist, &dent->fidlist); + spin_unlock(&dent->lock); -void v9fs_fid_destroy(struct v9fs_fid *fid) -{ - list_del(&fid->list); - kfree(fid); + return 0; } /** @@ -114,30 +76,42 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) * */ -struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) +struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) { - struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; - struct v9fs_fid *return_fid = NULL; - - dprintk(DEBUG_9P, " dentry: %s (%p)\n", dentry->d_iname, dentry); - - if (fid_list) - return_fid = list_entry(fid_list->next, struct v9fs_fid, list); + struct v9fs_dentry *dent; + struct p9_fid *fid; + + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + dent = dentry->d_fsdata; + if (dent) + fid = list_entry(dent->fidlist.next, struct p9_fid, dlist); + else + fid = ERR_PTR(-EBADF); + + P9_DPRINTK(P9_DEBUG_VFS, " fid: %p\n", fid); + return fid; +} - if (!return_fid) { - dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); - return_fid = ERR_PTR(-EBADF); +struct p9_fid *v9fs_fid_lookup_remove(struct dentry *dentry) +{ + struct p9_fid *fid; + struct v9fs_dentry *dent; + + dent = dentry->d_fsdata; + fid = v9fs_fid_lookup(dentry); + if (!IS_ERR(fid)) { + spin_lock(&dent->lock); + list_del(&fid->dlist); + spin_unlock(&dent->lock); } - if(down_interruptible(&return_fid->lock)) - return ERR_PTR(-EINTR); - - return return_fid; + return fid; } + /** * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and - * release it + * release it * @dentry: dentry to look for fid in * * find a fid in the dentry and then clone to a new private fid @@ -146,49 +120,15 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) * */ -struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) +struct p9_fid *v9fs_fid_clone(struct dentry *dentry) { - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); - struct v9fs_fcall *fcall = NULL; - int fid, err; - - base_fid = v9fs_fid_lookup(dentry); - - if(IS_ERR(base_fid)) - return base_fid; - - if(base_fid) { /* clone fid */ - fid = v9fs_get_idpool(&v9ses->fidpool); - if (fid < 0) { - eprintk(KERN_WARNING, "newfid fails!\n"); - new_fid = ERR_PTR(-ENOSPC); - goto Release_Fid; - } - - err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); - if (err < 0) { - dprintk(DEBUG_ERROR, "clone walk didn't work\n"); - v9fs_put_idpool(fid, &v9ses->fidpool); - new_fid = ERR_PTR(err); - goto Free_Fcall; - } - new_fid = v9fs_fid_create(v9ses, fid); - if (new_fid == NULL) { - dprintk(DEBUG_ERROR, "out of memory\n"); - new_fid = ERR_PTR(-ENOMEM); - } -Free_Fcall: - kfree(fcall); - } + struct p9_fid *ofid, *fid; -Release_Fid: - up(&base_fid->lock); - return new_fid; -} + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + ofid = v9fs_fid_lookup(dentry); + if (IS_ERR(ofid)) + return ofid; -void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) -{ - v9fs_t_clunk(v9ses, fid->fid); - v9fs_fid_destroy(fid); + fid = p9_client_walk(ofid, 0, NULL, 1); + return fid; } diff --git a/fs/9p/fid.h b/fs/9p/fid.h index 48fc170..47a0ba7 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h @@ -22,41 +22,12 @@ #include <linux/list.h> -#define FID_OP 0 -#define FID_WALK 1 -#define FID_CREATE 2 - -struct v9fs_fid { - struct list_head list; /* list of fids associated with a dentry */ - struct list_head active; /* XXX - debug */ - - struct semaphore lock; - - u32 fid; - unsigned char fidopen; /* set when fid is opened */ - unsigned char fidclunked; /* set when fid has already been clunked */ - - struct v9fs_qid qid; - u32 iounit; - - /* readdir stuff */ - int rdir_fpos; - loff_t rdir_pos; - struct v9fs_fcall *rdir_fcall; - - /* management stuff */ - uid_t uid; /* user associated with this fid */ - - /* private data */ - struct file *filp; /* backpointer to File struct for open files */ - struct v9fs_session_info *v9ses; /* session info for this FID */ +struct v9fs_dentry { + spinlock_t lock; /* protect fidlist */ + struct list_head fidlist; }; -struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry); -struct v9fs_fid *v9fs_fid_get_created(struct dentry *); -void v9fs_fid_destroy(struct v9fs_fid *fid); -struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); -int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); -struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry); -void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid); - +struct p9_fid *v9fs_fid_lookup(struct dentry *dentry); +struct p9_fid *v9fs_fid_lookup_remove(struct dentry *dentry); +struct p9_fid *v9fs_fid_clone(struct dentry *dentry); +int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); diff --git a/fs/9p/mux.c b/fs/9p/mux.c deleted file mode 100644 index c783874..0000000 --- a/fs/9p/mux.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* - * linux/fs/9p/mux.c - * - * Protocol Multiplexer - * - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/kthread.h> -#include <linux/idr.h> -#include <linux/mutex.h> - -#include "debug.h" -#include "v9fs.h" -#include "9p.h" -#include "conv.h" -#include "transport.h" -#include "mux.h" - -#define ERREQFLUSH 1 -#define SCHED_TIMEOUT 10 -#define MAXPOLLWADDR 2 - -enum { - Rworksched = 1, /* read work scheduled or running */ - Rpending = 2, /* can read */ - Wworksched = 4, /* write work scheduled or running */ - Wpending = 8, /* can write */ -}; - -enum { - None, - Flushing, - Flushed, -}; - -struct v9fs_mux_poll_task; - -struct v9fs_req { - spinlock_t lock; - int tag; - struct v9fs_fcall *tcall; - struct v9fs_fcall *rcall; - int err; - v9fs_mux_req_callback cb; - void *cba; - int flush; - struct list_head req_list; -}; - -struct v9fs_mux_data { - spinlock_t lock; - struct list_head mux_list; - struct v9fs_mux_poll_task *poll_task; - int msize; - unsigned char *extended; - struct v9fs_transport *trans; - struct v9fs_idpool tagpool; - int err; - wait_queue_head_t equeue; - struct list_head req_list; - struct list_head unsent_req_list; - struct v9fs_fcall *rcall; - int rpos; - char *rbuf; - int wpos; - int wsize; - char *wbuf; - wait_queue_t poll_wait[MAXPOLLWADDR]; - wait_queue_head_t *poll_waddr[MAXPOLLWADDR]; - poll_table pt; - struct work_struct rq; - struct work_struct wq; - unsigned long wsched; -}; - -struct v9fs_mux_poll_task { - struct task_struct *task; - struct list_head mux_list; - int muxnum; -}; - -struct v9fs_mux_rpc { - struct v9fs_mux_data *m; - int err; - struct v9fs_fcall *tcall; - struct v9fs_fcall *rcall; - wait_queue_head_t wqueue; -}; - -static int v9fs_poll_proc(void *); -static void v9fs_read_work(struct work_struct *work); -static void v9fs_write_work(struct work_struct *work); -static void v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, - poll_table * p); -static u16 v9fs_mux_get_tag(struct v9fs_mux_data *); -static void v9fs_mux_put_tag(struct v9fs_mux_data *, u16); - -static DEFINE_MUTEX(v9fs_mux_task_lock); -static struct workqueue_struct *v9fs_mux_wq; - -static int v9fs_mux_num; -static int v9fs_mux_poll_task_num; -static struct v9fs_mux_poll_task v9fs_mux_poll_tasks[100]; - -int v9fs_mux_global_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) - v9fs_mux_poll_tasks[i].task = NULL; - - v9fs_mux_wq = create_workqueue("v9fs"); - if (!v9fs_mux_wq) { - printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n"); - return -ENOMEM; - } - - return 0; -} - -void v9fs_mux_global_exit(void) -{ - destroy_workqueue(v9fs_mux_wq); -} - -/** - * v9fs_mux_calc_poll_procs - calculates the number of polling procs - * based on the number of mounted v9fs filesystems. - * - * The current implementation returns sqrt of the number of mounts. - */ -static int v9fs_mux_calc_poll_procs(int muxnum) -{ - int n; - - if (v9fs_mux_poll_task_num) - n = muxnum / v9fs_mux_poll_task_num + - (muxnum % v9fs_mux_poll_task_num ? 1 : 0); - else - n = 1; - - if (n > ARRAY_SIZE(v9fs_mux_poll_tasks)) - n = ARRAY_SIZE(v9fs_mux_poll_tasks); - - return n; -} - -static int v9fs_mux_poll_start(struct v9fs_mux_data *m) -{ - int i, n; - struct v9fs_mux_poll_task *vpt, *vptlast; - struct task_struct *pproc; - - dprintk(DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, v9fs_mux_num, - v9fs_mux_poll_task_num); - mutex_lock(&v9fs_mux_task_lock); - - n = v9fs_mux_calc_poll_procs(v9fs_mux_num + 1); - if (n > v9fs_mux_poll_task_num) { - for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { - if (v9fs_mux_poll_tasks[i].task == NULL) { - vpt = &v9fs_mux_poll_tasks[i]; - dprintk(DEBUG_MUX, "create proc %p\n", vpt); - pproc = kthread_create(v9fs_poll_proc, vpt, - "v9fs-poll"); - - if (!IS_ERR(pproc)) { - vpt->task = pproc; - INIT_LIST_HEAD(&vpt->mux_list); - vpt->muxnum = 0; - v9fs_mux_poll_task_num++; - wake_up_process(vpt->task); - } - break; - } - } - - if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) - dprintk(DEBUG_ERROR, "warning: no free poll slots\n"); - } - - n = (v9fs_mux_num + 1) / v9fs_mux_poll_task_num + - ((v9fs_mux_num + 1) % v9fs_mux_poll_task_num ? 1 : 0); - - vptlast = NULL; - for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { - vpt = &v9fs_mux_poll_tasks[i]; - if (vpt->task != NULL) { - vptlast = vpt; - if (vpt->muxnum < n) { - dprintk(DEBUG_MUX, "put in proc %d\n", i); - list_add(&m->mux_list, &vpt->mux_list); - vpt->muxnum++; - m->poll_task = vpt; - memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); - init_poll_funcptr(&m->pt, v9fs_pollwait); - break; - } - } - } - - if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) { - if (vptlast == NULL) - return -ENOMEM; - - dprintk(DEBUG_MUX, "put in proc %d\n", i); - list_add(&m->mux_list, &vptlast->mux_list); - vptlast->muxnum++; - m->poll_task = vptlast; - memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); - init_poll_funcptr(&m->pt, v9fs_pollwait); - } - - v9fs_mux_num++; - mutex_unlock(&v9fs_mux_task_lock); - - return 0; -} - -static void v9fs_mux_poll_stop(struct v9fs_mux_data *m) -{ - int i; - struct v9fs_mux_poll_task *vpt; - - mutex_lock(&v9fs_mux_task_lock); - vpt = m->poll_task; - list_del(&m->mux_list); - for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { - if (m->poll_waddr[i] != NULL) { - remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]); - m->poll_waddr[i] = NULL; - } - } - vpt->muxnum--; - if (!vpt->muxnum) { - dprintk(DEBUG_MUX, "destroy proc %p\n", vpt); - kthread_stop(vpt->task); - vpt->task = NULL; - v9fs_mux_poll_task_num--; - } - v9fs_mux_num--; - mutex_unlock(&v9fs_mux_task_lock); -} - -/** - * v9fs_mux_init - allocate and initialize the per-session mux data - * Creates the polling task if this is the first session. - * - * @trans - transport structure - * @msize - maximum message size - * @extended - pointer to the extended flag - */ -struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, - unsigned char *extended) -{ - int i, n; - struct v9fs_mux_data *m, *mtmp; - - dprintk(DEBUG_MUX, "transport %p msize %d\n", trans, msize); - m = kmalloc(sizeof(struct v9fs_mux_data), GFP_KERNEL); - if (!m) - return ERR_PTR(-ENOMEM); - - spin_lock_init(&m->lock); - INIT_LIST_HEAD(&m->mux_list); - m->msize = msize; - m->extended = extended; - m->trans = trans; - idr_init(&m->tagpool.pool); - init_MUTEX(&m->tagpool.lock); - m->err = 0; - init_waitqueue_head(&m->equeue); - INIT_LIST_HEAD(&m->req_list); - INIT_LIST_HEAD(&m->unsent_req_list); - m->rcall = NULL; - m->rpos = 0; - m->rbuf = NULL; - m->wpos = m->wsize = 0; - m->wbuf = NULL; - INIT_WORK(&m->rq, v9fs_read_work); - INIT_WORK(&m->wq, v9fs_write_work); - m->wsched = 0; - memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); - m->poll_task = NULL; - n = v9fs_mux_poll_start(m); - if (n) - return ERR_PTR(n); - - n = trans->poll(trans, &m->pt); - if (n & POLLIN) { - dprintk(DEBUG_MUX, "mux %p can read\n", m); - set_bit(Rpending, &m->wsched); - } - - if (n & POLLOUT) { - dprintk(DEBUG_MUX, "mux %p can write\n", m); - set_bit(Wpending, &m->wsched); - } - - for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { - if (IS_ERR(m->poll_waddr[i])) { - v9fs_mux_poll_stop(m); - mtmp = (void *)m->poll_waddr; /* the error code */ - kfree(m); - m = mtmp; - break; - } - } - - return m; -} - -/** - * v9fs_mux_destroy - cancels all pending requests and frees mux resources - */ -void v9fs_mux_destroy(struct v9fs_mux_data *m) -{ - dprintk(DEBUG_MUX, "mux %p prev %p next %p\n", m, - m->mux_list.prev, m->mux_list.next); - v9fs_mux_cancel(m, -ECONNRESET); - - if (!list_empty(&m->req_list)) { - /* wait until all processes waiting on this session exit */ - dprintk(DEBUG_MUX, "mux %p waiting for empty request queue\n", - m); - wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000); - dprintk(DEBUG_MUX, "mux %p request queue empty: %d\n", m, - list_empty(&m->req_list)); - } - - v9fs_mux_poll_stop(m); - m->trans = NULL; - - kfree(m); -} - -/** - * v9fs_pollwait - called by files poll operation to add v9fs-poll task - * to files wait queue - */ -static void -v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, - poll_table * p) -{ - int i; - struct v9fs_mux_data *m; - - m = container_of(p, struct v9fs_mux_data, pt); - for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) - if (m->poll_waddr[i] == NULL) - break; - - if (i >= ARRAY_SIZE(m->poll_waddr)) { - dprintk(DEBUG_ERROR, "not enough wait_address slots\n"); - return; - } - - m->poll_waddr[i] = wait_address; - - if (!wait_address) { - dprintk(DEBUG_ERROR, "no wait_address\n"); - m->poll_waddr[i] = ERR_PTR(-EIO); - return; - } - - init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task); - add_wait_queue(wait_address, &m->poll_wait[i]); -} - -/** - * v9fs_poll_mux - polls a mux and schedules read or write works if necessary - */ -static void v9fs_poll_mux(struct v9fs_mux_data *m) -{ - int n; - - if (m->err < 0) - return; - - n = m->trans->poll(m->trans, NULL); - if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) { - dprintk(DEBUG_MUX, "error mux %p err %d\n", m, n); - if (n >= 0) - n = -ECONNRESET; - v9fs_mux_cancel(m, n); - } - - if (n & POLLIN) { - set_bit(Rpending, &m->wsched); - dprintk(DEBUG_MUX, "mux %p can read\n", m); - if (!test_and_set_bit(Rworksched, &m->wsched)) { - dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); - queue_work(v9fs_mux_wq, &m->rq); - } - } - - if (n & POLLOUT) { - set_bit(Wpending, &m->wsched); - dprintk(DEBUG_MUX, "mux %p can write\n", m); - if ((m->wsize || !list_empty(&m->unsent_req_list)) - && !test_and_set_bit(Wworksched, &m->wsched)) { - dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); - queue_work(v9fs_mux_wq, &m->wq); - } - } -} - -/** - * v9fs_poll_proc - polls all v9fs transports for new events and queues - * the appropriate work to the work queue - */ -static int v9fs_poll_proc(void *a) -{ - struct v9fs_mux_data *m, *mtmp; - struct v9fs_mux_poll_task *vpt; - - vpt = a; - dprintk(DEBUG_MUX, "start %p %p\n", current, vpt); - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - - list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { - v9fs_poll_mux(m); - } - - dprintk(DEBUG_MUX, "sleeping...\n"); - schedule_timeout(SCHED_TIMEOUT * HZ); - } - - __set_current_state(TASK_RUNNING); - dprintk(DEBUG_MUX, "finish\n"); - return 0; -} - -/** - * v9fs_write_work - called when a transport can send some data - */ -static void v9fs_write_work(struct work_struct *work) -{ - int n, err; - struct v9fs_mux_data *m; - struct v9fs_req *req; - - m = container_of(work, struct v9fs_mux_data, wq); - - if (m->err < 0) { - clear_bit(Wworksched, &m->wsched); - return; - } - - if (!m->wsize) { - if (list_empty(&m->unsent_req_list)) { - clear_bit(Wworksched, &m->wsched); - return; - } - - spin_lock(&m->lock); -again: - req = list_entry(m->unsent_req_list.next, struct v9fs_req, - req_list); - list_move_tail(&req->req_list, &m->req_list); - if (req->err == ERREQFLUSH) - goto again; - - m->wbuf = req->tcall->sdata; - m->wsize = req->tcall->size; - m->wpos = 0; - dump_data(m->wbuf, m->wsize); - spin_unlock(&m->lock); - } - - dprintk(DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos, m->wsize); - clear_bit(Wpending, &m->wsched); - err = m->trans->write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos); - dprintk(DEBUG_MUX, "mux %p sent %d bytes\n", m, err); - if (err == -EAGAIN) { - clear_bit(Wworksched, &m->wsched); - return; - } - - if (err <= 0) - goto error; - - m->wpos += err; - if (m->wpos == m->wsize) - m->wpos = m->wsize = 0; - - if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) { - if (test_and_clear_bit(Wpending, &m->wsched)) - n = POLLOUT; - else - n = m->trans->poll(m->trans, NULL); - - if (n & POLLOUT) { - dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); - queue_work(v9fs_mux_wq, &m->wq); - } else - clear_bit(Wworksched, &m->wsched); - } else - clear_bit(Wworksched, &m->wsched); - - return; - - error: - v9fs_mux_cancel(m, err); - clear_bit(Wworksched, &m->wsched); -} - -static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) -{ - int ecode; - struct v9fs_str *ename; - - if (!req->err && req->rcall->id == RERROR) { - ecode = req->rcall->params.rerror.errno; - ename = &req->rcall->params.rerror.error; - - dprintk(DEBUG_MUX, "Rerror %.*s\n", ename->len, ename->str); - - if (*m->extended) - req->err = -ecode; - - if (!req->err) { - req->err = v9fs_errstr2errno(ename->str, ename->len); - - if (!req->err) { /* string match failed */ - PRINT_FCALL_ERROR("unknown error", req->rcall); - } - - if (!req->err) - req->err = -ESERVERFAULT; - } - } else if (req->tcall && req->rcall->id != req->tcall->id + 1) { - dprintk(DEBUG_ERROR, "fcall mismatch: expected %d, got %d\n", - req->tcall->id + 1, req->rcall->id); - if (!req->err) - req->err = -EIO; - } -} - -/** - * v9fs_read_work - called when there is some data to be read from a transport - */ -static void v9fs_read_work(struct work_struct *work) -{ - int n, err; - struct v9fs_mux_data *m; - struct v9fs_req *req, *rptr, *rreq; - struct v9fs_fcall *rcall; - char *rbuf; - - m = container_of(work, struct v9fs_mux_data, rq); - - if (m->err < 0) - return; - - rcall = NULL; - dprintk(DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos); - - if (!m->rcall) { - m->rcall = - kmalloc(sizeof(struct v9fs_fcall) + m->msize, GFP_KERNEL); - if (!m->rcall) { - err = -ENOMEM; - goto error; - } - - m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); - m->rpos = 0; - } - - clear_bit(Rpending, &m->wsched); - err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos); - dprintk(DEBUG_MUX, "mux %p got %d bytes\n", m, err); - if (err == -EAGAIN) { - clear_bit(Rworksched, &m->wsched); - return; - } - - if (err <= 0) - goto error; - - m->rpos += err; - while (m->rpos > 4) { - n = le32_to_cpu(*(__le32 *) m->rbuf); - if (n >= m->msize) { - dprintk(DEBUG_ERROR, - "requested packet size too big: %d\n", n); - err = -EIO; - goto error; - } - - if (m->rpos < n) - break; - - dump_data(m->rbuf, n); - err = - v9fs_deserialize_fcall(m->rbuf, n, m->rcall, *m->extended); - if (err < 0) { - goto error; - } - - if ((v9fs_debug_level&DEBUG_FCALL) == DEBUG_FCALL) { - char buf[150]; - - v9fs_printfcall(buf, sizeof(buf), m->rcall, - *m->extended); - printk(KERN_NOTICE ">>> %p %s\n", m, buf); - } - - rcall = m->rcall; - rbuf = m->rbuf; - if (m->rpos > n) { - m->rcall = kmalloc(sizeof(struct v9fs_fcall) + m->msize, - GFP_KERNEL); - if (!m->rcall) { - err = -ENOMEM; - goto error; - } - - m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); - memmove(m->rbuf, rbuf + n, m->rpos - n); - m->rpos -= n; - } else { - m->rcall = NULL; - m->rbuf = NULL; - m->rpos = 0; - } - - dprintk(DEBUG_MUX, "mux %p fcall id %d tag %d\n", m, rcall->id, - rcall->tag); - - req = NULL; - spin_lock(&m->lock); - list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { - if (rreq->tag == rcall->tag) { - req = rreq; - if (req->flush != Flushing) - list_del(&req->req_list); - break; - } - } - spin_unlock(&m->lock); - - if (req) { - req->rcall = rcall; - process_request(m, req); - - if (req->flush != Flushing) { - if (req->cb) - (*req->cb) (req, req->cba); - else - kfree(req->rcall); - - wake_up(&m->equeue); - } - } else { - if (err >= 0 && rcall->id != RFLUSH) - dprintk(DEBUG_ERROR, - "unexpected response mux %p id %d tag %d\n", - m, rcall->id, rcall->tag); - kfree(rcall); - } - } - - if (!list_empty(&m->req_list)) { - if (test_and_clear_bit(Rpending, &m->wsched)) - n = POLLIN; - else - n = m->trans->poll(m->trans, NULL); - - if (n & POLLIN) { - dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); - queue_work(v9fs_mux_wq, &m->rq); - } else - clear_bit(Rworksched, &m->wsched); - } else - clear_bit(Rworksched, &m->wsched); - - return; - - error: - v9fs_mux_cancel(m, err); - clear_bit(Rworksched, &m->wsched); -} - -/** - * v9fs_send_request - send 9P request - * The function can sleep until the request is scheduled for sending. - * The function can be interrupted. Return from the function is not - * a guarantee that the request is sent successfully. Can return errors - * that can be retrieved by PTR_ERR macros. - * - * @m: mux data - * @tc: request to be sent - * @cb: callback function to call when response is received - * @cba: parameter to pass to the callback function - */ -static struct v9fs_req *v9fs_send_request(struct v9fs_mux_data *m, - struct v9fs_fcall *tc, - v9fs_mux_req_callback cb, void *cba) -{ - int n; - struct v9fs_req *req; - - dprintk(DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current, - tc, tc->id); - if (m->err < 0) - return ERR_PTR(m->err); - - req = kmalloc(sizeof(struct v9fs_req), GFP_KERNEL); - if (!req) - return ERR_PTR(-ENOMEM); - - if (tc->id == TVERSION) - n = V9FS_NOTAG; - else - n = v9fs_mux_get_tag(m); - - if (n < 0) - return ERR_PTR(-ENOMEM); - - v9fs_set_tag(tc, n); - if ((v9fs_debug_level&DEBUG_FCALL) == DEBUG_FCALL) { - char buf[150]; - - v9fs_printfcall(buf, sizeof(buf), tc, *m->extended); - printk(KERN_NOTICE "<<< %p %s\n", m, buf); - } - - spin_lock_init(&req->lock); - req->tag = n; - req->tcall = tc; - req->rcall = NULL; - req->err = 0; - req->cb = cb; - req->cba = cba; - req->flush = None; - - spin_lock(&m->lock); - list_add_tail(&req->req_list, &m->unsent_req_list); - spin_unlock(&m->lock); - - if (test_and_clear_bit(Wpending, &m->wsched)) - n = POLLOUT; - else - n = m->trans->poll(m->trans, NULL); - - if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched)) - queue_work(v9fs_mux_wq, &m->wq); - - return req; -} - -static void v9fs_mux_free_request(struct v9fs_mux_data *m, struct v9fs_req *req) -{ - v9fs_mux_put_tag(m, req->tag); - kfree(req); -} - -static void v9fs_mux_flush_cb(struct v9fs_req *freq, void *a) -{ - v9fs_mux_req_callback cb; - int tag; - struct v9fs_mux_data *m; - struct v9fs_req *req, *rreq, *rptr; - - m = a; - dprintk(DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m, - freq->tcall, freq->rcall, freq->err, - freq->tcall->params.tflush.oldtag); - - spin_lock(&m->lock); - cb = NULL; - tag = freq->tcall->params.tflush.oldtag; - req = NULL; - list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { - if (rreq->tag == tag) { - req = rreq; - list_del(&req->req_list); - break; - } - } - spin_unlock(&m->lock); - - if (req) { - spin_lock(&req->lock); - req->flush = Flushed; - spin_unlock(&req->lock); - - if (req->cb) - (*req->cb) (req, req->cba); - else - kfree(req->rcall); - - wake_up(&m->equeue); - } - - kfree(freq->tcall); - kfree(freq->rcall); - v9fs_mux_free_request(m, freq); -} - -static int -v9fs_mux_flush_request(struct v9fs_mux_data *m, struct v9fs_req *req) -{ - struct v9fs_fcall *fc; - struct v9fs_req *rreq, *rptr; - - dprintk(DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag); - - /* if a response was received for a request, do nothing */ - spin_lock(&req->lock); - if (req->rcall || req->err) { - spin_unlock(&req->lock); - dprintk(DEBUG_MUX, "mux %p req %p response already received\n", m, req); - return 0; - } - - req->flush = Flushing; - spin_unlock(&req->lock); - - spin_lock(&m->lock); - /* if the request is not sent yet, just remove it from the list */ - list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) { - if (rreq->tag == req->tag) { - dprintk(DEBUG_MUX, "mux %p req %p request is not sent yet\n", m, req); - list_del(&rreq->req_list); - req->flush = Flushed; - spin_unlock(&m->lock); - if (req->cb) - (*req->cb) (req, req->cba); - return 0; - } - } - spin_unlock(&m->lock); - - clear_thread_flag(TIF_SIGPENDING); - fc = v9fs_create_tflush(req->tag); - v9fs_send_request(m, fc, v9fs_mux_flush_cb, m); - return 1; -} - -static void -v9fs_mux_rpc_cb(struct v9fs_req *req, void *a) -{ - struct v9fs_mux_rpc *r; - - dprintk(DEBUG_MUX, "req %p r %p\n", req, a); - r = a; - r->rcall = req->rcall; - r->err = req->err; - - if (req->flush!=None && !req->err) - r->err = -ERESTARTSYS; - - wake_up(&r->wqueue); -} - -/** - * v9fs_mux_rpc - sends 9P request and waits until a response is available. - * The function can be interrupted. - * @m: mux data - * @tc: request to be sent - * @rc: pointer where a pointer to the response is stored - */ -int -v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, - struct v9fs_fcall **rc) -{ - int err, sigpending; - unsigned long flags; - struct v9fs_req *req; - struct v9fs_mux_rpc r; - - r.err = 0; - r.tcall = tc; - r.rcall = NULL; - r.m = m; - init_waitqueue_head(&r.wqueue); - - if (rc) - *rc = NULL; - - sigpending = 0; - if (signal_pending(current)) { - sigpending = 1; - clear_thread_flag(TIF_SIGPENDING); - } - - req = v9fs_send_request(m, tc, v9fs_mux_rpc_cb, &r); - if (IS_ERR(req)) { - err = PTR_ERR(req); - dprintk(DEBUG_MUX, "error %d\n", err); - return err; - } - - err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0); - if (r.err < 0) - err = r.err; - - if (err == -ERESTARTSYS && m->trans->status == Connected && m->err == 0) { - if (v9fs_mux_flush_request(m, req)) { - /* wait until we get response of the flush message */ - do { - clear_thread_flag(TIF_SIGPENDING); - err = wait_event_interruptible(r.wqueue, - r.rcall || r.err); - } while (!r.rcall && !r.err && err==-ERESTARTSYS && - m->trans->status==Connected && !m->err); - - err = -ERESTARTSYS; - } - sigpending = 1; - } - - if (sigpending) { - spin_lock_irqsave(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - } - - if (rc) - *rc = r.rcall; - else - kfree(r.rcall); - - v9fs_mux_free_request(m, req); - if (err > 0) - err = -EIO; - - return err; -} - -#if 0 -/** - * v9fs_mux_rpcnb - sends 9P request without waiting for response. - * @m: mux data - * @tc: request to be sent - * @cb: callback function to be called when response arrives - * @cba: value to pass to the callback function - */ -int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, - v9fs_mux_req_callback cb, void *a) -{ - int err; - struct v9fs_req *req; - - req = v9fs_send_request(m, tc, cb, a); - if (IS_ERR(req)) { - err = PTR_ERR(req); - dprintk(DEBUG_MUX, "error %d\n", err); - return PTR_ERR(req); - } - - dprintk(DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag); - return 0; -} -#endif /* 0 */ - -/** - * v9fs_mux_cancel - cancel all pending requests with error - * @m: mux data - * @err: error code - */ -void v9fs_mux_cancel(struct v9fs_mux_data *m, int err) -{ - struct v9fs_req *req, *rtmp; - LIST_HEAD(cancel_list); - - dprintk(DEBUG_ERROR, "mux %p err %d\n", m, err); - m->err = err; - spin_lock(&m->lock); - list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { - list_move(&req->req_list, &cancel_list); - } - list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) { - list_move(&req->req_list, &cancel_list); - } - spin_unlock(&m->lock); - - list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { - list_del(&req->req_list); - if (!req->err) - req->err = err; - - if (req->cb) - (*req->cb) (req, req->cba); - else - kfree(req->rcall); - } - - wake_up(&m->equeue); -} - -static u16 v9fs_mux_get_tag(struct v9fs_mux_data *m) -{ - int tag; - - tag = v9fs_get_idpool(&m->tagpool); - if (tag < 0) - return V9FS_NOTAG; - else - return (u16) tag; -} - -static void v9fs_mux_put_tag(struct v9fs_mux_data *m, u16 tag) -{ - if (tag != V9FS_NOTAG && v9fs_check_idpool(tag, &m->tagpool)) - v9fs_put_idpool(tag, &m->tagpool); -} diff --git a/fs/9p/mux.h b/fs/9p/mux.h deleted file mode 100644 index fb10c50..0000000 --- a/fs/9p/mux.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * linux/fs/9p/mux.h - * - * Multiplexer Definitions - * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -struct v9fs_mux_data; -struct v9fs_req; - -/** - * v9fs_mux_req_callback - callback function that is called when the - * response of a request is received. The callback is called from - * a workqueue and shouldn't block. - * - * @a - the pointer that was specified when the request was send to be - * passed to the callback - * @tc - request call - * @rc - response call - * @err - error code (non-zero if error occured) - */ -typedef void (*v9fs_mux_req_callback)(struct v9fs_req *req, void *a); - -int v9fs_mux_global_init(void); -void v9fs_mux_global_exit(void); - -struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, - unsigned char *extended); -void v9fs_mux_destroy(struct v9fs_mux_data *); - -int v9fs_mux_send(struct v9fs_mux_data *m, struct v9fs_fcall *tc); -struct v9fs_fcall *v9fs_mux_recv(struct v9fs_mux_data *m); -int v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, struct v9fs_fcall **rc); - -void v9fs_mux_flush(struct v9fs_mux_data *m, int sendflush); -void v9fs_mux_cancel(struct v9fs_mux_data *m, int err); -int v9fs_errstr2errno(char *errstr, int len); diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c deleted file mode 100644 index 34d4335..0000000 --- a/fs/9p/trans_fd.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * linux/fs/9p/trans_fd.c - * - * Fd transport layer. Includes deprecated socket layer. - * - * Copyright (C) 2006 by Russ Cox <rsc@swtch.com> - * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004-2005 by Eric Van Hensbergen <ericvh@gmail.com> - * Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -#include <linux/in.h> -#include <linux/module.h> -#include <linux/net.h> -#include <linux/ipv6.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/un.h> -#include <asm/uaccess.h> -#include <linux/inet.h> -#include <linux/idr.h> -#include <linux/file.h> - -#include "debug.h" -#include "v9fs.h" -#include "transport.h" - -#define V9FS_PORT 564 - -struct v9fs_trans_fd { - struct file *rd; - struct file *wr; -}; - -/** - * v9fs_fd_read- read from a fd - * @v9ses: session information - * @v: buffer to receive data into - * @len: size of receive buffer - * - */ -static int v9fs_fd_read(struct v9fs_transport *trans, void *v, int len) -{ - int ret; - struct v9fs_trans_fd *ts; - - if (!trans || trans->status == Disconnected || !(ts = trans->priv)) - return -EREMOTEIO; - - if (!(ts->rd->f_flags & O_NONBLOCK)) - dprintk(DEBUG_ERROR, "blocking read ...\n"); - - ret = kernel_read(ts->rd, ts->rd->f_pos, v, len); - if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) - trans->status = Disconnected; - return ret; -} - -/** - * v9fs_fd_write - write to a socket - * @v9ses: session information - * @v: buffer to send data from - * @len: size of send buffer - * - */ -static int v9fs_fd_write(struct v9fs_transport *trans, void *v, int len) -{ - int ret; - mm_segment_t oldfs; - struct v9fs_trans_fd *ts; - - if (!trans || trans->status == Disconnected || !(ts = trans->priv)) - return -EREMOTEIO; - - if (!(ts->wr->f_flags & O_NONBLOCK)) - dprintk(DEBUG_ERROR, "blocking write ...\n"); - - oldfs = get_fs(); - set_fs(get_ds()); - /* The cast to a user pointer is valid due to the set_fs() */ - ret = vfs_write(ts->wr, (void __user *)v, len, &ts->wr->f_pos); - set_fs(oldfs); - - if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) - trans->status = Disconnected; - return ret; -} - -static unsigned int -v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) -{ - int ret, n; - struct v9fs_trans_fd *ts; - mm_segment_t oldfs; - - if (!trans || trans->status != Connected || !(ts = trans->priv)) - return -EREMOTEIO; - - if (!ts->rd->f_op || !ts->rd->f_op->poll) - return -EIO; - - if (!ts->wr->f_op || !ts->wr->f_op->poll) - return -EIO; - - oldfs = get_fs(); - set_fs(get_ds()); - - ret = ts->rd->f_op->poll(ts->rd, pt); - if (ret < 0) - goto end; - - if (ts->rd != ts->wr) { - n = ts->wr->f_op->poll(ts->wr, pt); - if (n < 0) { - ret = n; - goto end; - } - ret = (ret & ~POLLOUT) | (n & ~POLLIN); - } - - end: - set_fs(oldfs); - return ret; -} - -static int v9fs_fd_open(struct v9fs_session_info *v9ses, int rfd, int wfd) -{ - struct v9fs_transport *trans = v9ses->transport; - struct v9fs_trans_fd *ts = kmalloc(sizeof(struct v9fs_trans_fd), - GFP_KERNEL); - if (!ts) - return -ENOMEM; - - ts->rd = fget(rfd); - ts->wr = fget(wfd); - if (!ts->rd || !ts->wr) { - if (ts->rd) - fput(ts->rd); - if (ts->wr) - fput(ts->wr); - kfree(ts); - return -EIO; - } - - trans->priv = ts; - trans->status = Connected; - - return 0; -} - -static int v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, - char *data) -{ - if (v9ses->rfdno == ~0 || v9ses->wfdno == ~0) { - printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); - return -ENOPROTOOPT; - } - - return v9fs_fd_open(v9ses, v9ses->rfdno, v9ses->wfdno); -} - -static int v9fs_socket_open(struct v9fs_session_info *v9ses, - struct socket *csocket) -{ - int fd, ret; - - csocket->sk->sk_allocation = GFP_NOIO; - if ((fd = sock_map_fd(csocket)) < 0) { - eprintk(KERN_ERR, "v9fs_socket_open: failed to map fd\n"); - ret = fd; - release_csocket: - sock_release(csocket); - return ret; - } - - if ((ret = v9fs_fd_open(v9ses, fd, fd)) < 0) { - sockfd_put(csocket); - eprintk(KERN_ERR, "v9fs_socket_open: failed to open fd\n"); - goto release_csocket; - } - - ((struct v9fs_trans_fd *)v9ses->transport->priv)->rd->f_flags |= - O_NONBLOCK; - return 0; -} - -static int v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, - char *data) -{ - int ret; - struct socket *csocket = NULL; - struct sockaddr_in sin_server; - - sin_server.sin_family = AF_INET; - sin_server.sin_addr.s_addr = in_aton(addr); - sin_server.sin_port = htons(v9ses->port); - sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket); - - if (!csocket) { - eprintk(KERN_ERR, "v9fs_trans_tcp: problem creating socket\n"); - return -1; - } - - ret = csocket->ops->connect(csocket, - (struct sockaddr *)&sin_server, - sizeof(struct sockaddr_in), 0); - if (ret < 0) { - eprintk(KERN_ERR, - "v9fs_trans_tcp: problem connecting socket to %s\n", - addr); - return ret; - } - - return v9fs_socket_open(v9ses, csocket); -} - -static int -v9fs_unix_init(struct v9fs_session_info *v9ses, const char *addr, char *data) -{ - int ret; - struct socket *csocket; - struct sockaddr_un sun_server; - - if (strlen(addr) > UNIX_PATH_MAX) { - eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", - addr); - return -ENAMETOOLONG; - } - - sun_server.sun_family = PF_UNIX; - strcpy(sun_server.sun_path, addr); - sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket); - ret = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server, - sizeof(struct sockaddr_un) - 1, 0); - if (ret < 0) { - eprintk(KERN_ERR, - "v9fs_trans_unix: problem connecting socket: %s: %d\n", - addr, ret); - return ret; - } - - return v9fs_socket_open(v9ses, csocket); -} - -/** - * v9fs_sock_close - shutdown socket - * @trans: private socket structure - * - */ -static void v9fs_fd_close(struct v9fs_transport *trans) -{ - struct v9fs_trans_fd *ts; - - if (!trans) - return; - - ts = xchg(&trans->priv, NULL); - - if (!ts) - return; - - trans->status = Disconnected; - if (ts->rd) - fput(ts->rd); - if (ts->wr) - fput(ts->wr); - kfree(ts); -} - -struct v9fs_transport v9fs_trans_fd = { - .init = v9fs_fd_init, - .write = v9fs_fd_write, - .read = v9fs_fd_read, - .close = v9fs_fd_close, - .poll = v9fs_fd_poll, -}; - -struct v9fs_transport v9fs_trans_tcp = { - .init = v9fs_tcp_init, - .write = v9fs_fd_write, - .read = v9fs_fd_read, - .close = v9fs_fd_close, - .poll = v9fs_fd_poll, -}; - -struct v9fs_transport v9fs_trans_unix = { - .init = v9fs_unix_init, - .write = v9fs_fd_write, - .read = v9fs_fd_read, - .close = v9fs_fd_close, - .poll = v9fs_fd_poll, -}; diff --git a/fs/9p/transport.h b/fs/9p/transport.h deleted file mode 100644 index b38a4b8..0000000 --- a/fs/9p/transport.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * linux/fs/9p/transport.h - * - * Transport Definition - * - * Copyright (C) 2005 by Latchesar Ionkov <lucho@ionkov.net> - * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to: - * Free Software Foundation - * 51 Franklin Street, Fifth Floor - * Boston, MA 02111-1301 USA - * - */ - -enum v9fs_transport_status { - Connected, - Disconnected, - Hung, -}; - -struct v9fs_transport { - enum v9fs_transport_status status; - void *priv; - - int (*init) (struct v9fs_session_info *, const char *, char *); - int (*write) (struct v9fs_transport *, void *, int); - int (*read) (struct v9fs_transport *, void *, int); - void (*close) (struct v9fs_transport *); - unsigned int (*poll)(struct v9fs_transport *, struct poll_table_struct *); -}; - -extern struct v9fs_transport v9fs_trans_tcp; -extern struct v9fs_transport v9fs_trans_unix; -extern struct v9fs_transport v9fs_trans_fd; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 6ad6f19..0a7068e 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -29,16 +29,12 @@ #include <linux/sched.h> #include <linux/parser.h> #include <linux/idr.h> - -#include "debug.h" +#include <net/9p/9p.h> +#include <net/9p/transport.h> +#include <net/9p/conn.h> +#include <net/9p/client.h> #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" -#include "transport.h" -#include "mux.h" - -/* TODO: sysfs or debugfs interface */ -int v9fs_debug_level = 0; /* feature-rific global debug level */ /* * Option Parsing (code inspired by NFS code) @@ -47,12 +43,12 @@ int v9fs_debug_level = 0; /* feature-rific global debug level */ enum { /* Options that take integer arguments */ - Opt_port, Opt_msize, Opt_uid, Opt_gid, Opt_afid, Opt_debug, + Opt_debug, Opt_port, Opt_msize, Opt_uid, Opt_gid, Opt_afid, Opt_rfdno, Opt_wfdno, /* String options */ Opt_uname, Opt_remotename, /* Options that take no arguments */ - Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, + Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, Opt_pci, /* Cache options */ Opt_cache_loose, /* Error token */ @@ -60,6 +56,7 @@ enum { }; static match_table_t tokens = { + {Opt_debug, "debug=%x"}, {Opt_port, "port=%u"}, {Opt_msize, "msize=%u"}, {Opt_uid, "uid=%u"}, @@ -67,12 +64,14 @@ static match_table_t tokens = { {Opt_afid, "afid=%u"}, {Opt_rfdno, "rfdno=%u"}, {Opt_wfdno, "wfdno=%u"}, - {Opt_debug, "debug=%x"}, {Opt_uname, "uname=%s"}, {Opt_remotename, "aname=%s"}, {Opt_unix, "proto=unix"}, {Opt_tcp, "proto=tcp"}, {Opt_fd, "proto=fd"}, +#ifdef CONFIG_PCI_9P + {Opt_pci, "proto=pci"}, +#endif {Opt_tcp, "tcp"}, {Opt_unix, "unix"}, {Opt_fd, "fd"}, @@ -83,6 +82,8 @@ static match_table_t tokens = { {Opt_err, NULL} }; +extern struct p9_transport *p9pci_trans_create(void); + /* * Parse option string. */ @@ -122,12 +123,18 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) token = match_token(p, tokens, args); if (token < Opt_uname) { if ((ret = match_int(&args[0], &option)) < 0) { - dprintk(DEBUG_ERROR, + P9_DPRINTK(P9_DEBUG_ERROR, "integer field, but no integer?\n"); continue; } } switch (token) { + case Opt_debug: + v9ses->debug = option; +#ifdef CONFIG_NET_9P_DEBUG + p9_debug_level = option; +#endif + break; case Opt_port: v9ses->port = option; break; @@ -149,15 +156,15 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) case Opt_wfdno: v9ses->wfdno = option; break; - case Opt_debug: - v9ses->debug = option; - break; case Opt_tcp: v9ses->proto = PROTO_TCP; break; case Opt_unix: v9ses->proto = PROTO_UNIX; break; + case Opt_pci: + v9ses->proto = PROTO_PCI; + break; case Opt_fd: v9ses->proto = PROTO_FD; break; @@ -183,82 +190,6 @@ static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) } /** - * v9fs_inode2v9ses - safely extract v9fs session info from super block - * @inode: inode to extract information from - * - * Paranoid function to extract v9ses information from superblock, - * if anything is missing it will report an error. - * - */ - -struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode) -{ - return (inode->i_sb->s_fs_info); -} - -/** - * v9fs_get_idpool - allocate numeric id from pool - * @p - pool to allocate from - * - * XXX - This seems to be an awful generic function, should it be in idr.c with - * the lock included in struct idr? - */ - -int v9fs_get_idpool(struct v9fs_idpool *p) -{ - int i = 0; - int error; - -retry: - if (idr_pre_get(&p->pool, GFP_KERNEL) == 0) - return 0; - - if (down_interruptible(&p->lock) == -EINTR) { - eprintk(KERN_WARNING, "Interrupted while locking\n"); - return -1; - } - - /* no need to store exactly p, we just need something non-null */ - error = idr_get_new(&p->pool, p, &i); - up(&p->lock); - - if (error == -EAGAIN) - goto retry; - else if (error) - return -1; - - return i; -} - -/** - * v9fs_put_idpool - release numeric id from pool - * @p - pool to allocate from - * - * XXX - This seems to be an awful generic function, should it be in idr.c with - * the lock included in struct idr? - */ - -void v9fs_put_idpool(int id, struct v9fs_idpool *p) -{ - if (down_interruptible(&p->lock) == -EINTR) { - eprintk(KERN_WARNING, "Interrupted while locking\n"); - return; - } - idr_remove(&p->pool, id); - up(&p->lock); -} - -/** - * v9fs_check_idpool - check if the specified id is available - * @id - id to check - * @p - pool - */ -int v9fs_check_idpool(int id, struct v9fs_idpool *p) -{ - return idr_find(&p->pool, id) != NULL; -} - -/** * v9fs_session_init - initialize session * @v9ses: session information structure * @dev_name: device being mounted @@ -266,25 +197,21 @@ int v9fs_check_idpool(int id, struct v9fs_idpool *p) * */ -int -v9fs_session_init(struct v9fs_session_info *v9ses, +struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, const char *dev_name, char *data) { - struct v9fs_fcall *fcall = NULL; - struct v9fs_transport *trans_proto; - int n = 0; - int newfid = -1; int retval = -EINVAL; - struct v9fs_str *version; + struct p9_transport *trans; + struct p9_fid *fid; v9ses->name = __getname(); if (!v9ses->name) - return -ENOMEM; + return ERR_PTR(-ENOMEM); v9ses->remotename = __getname(); if (!v9ses->remotename) { __putname(v9ses->name); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } strcpy(v9ses->name, V9FS_DEFUSER); @@ -292,130 +219,60 @@ v9fs_session_init(struct v9fs_session_info *v9ses, v9fs_parse_options(data, v9ses); - /* set global debug level */ - v9fs_debug_level = v9ses->debug; - - /* id pools that are session-dependent: fids and tags */ - idr_init(&v9ses->fidpool.pool); - init_MUTEX(&v9ses->fidpool.lock); - switch (v9ses->proto) { case PROTO_TCP: - trans_proto = &v9fs_trans_tcp; + trans = p9_trans_create_tcp(dev_name, v9ses->port); break; case PROTO_UNIX: - trans_proto = &v9fs_trans_unix; + trans = p9_trans_create_unix(dev_name); *v9ses->remotename = 0; break; case PROTO_FD: - trans_proto = &v9fs_trans_fd; + trans = p9_trans_create_fd(v9ses->rfdno, v9ses->wfdno); *v9ses->remotename = 0; break; +#ifdef CONFIG_PCI_9P + case PROTO_PCI: + trans = p9pci_trans_create(); + *v9ses->remotename = 0; + break; +#endif default: printk(KERN_ERR "v9fs: Bad mount protocol %d\n", v9ses->proto); retval = -ENOPROTOOPT; - goto SessCleanUp; + goto error; }; - v9ses->transport = kmalloc(sizeof(*v9ses->transport), GFP_KERNEL); - if (!v9ses->transport) { - retval = -ENOMEM; - goto SessCleanUp; + if (IS_ERR(trans)) { + retval = PTR_ERR(trans); + trans = NULL; + goto error; } - memmove(v9ses->transport, trans_proto, sizeof(*v9ses->transport)); + v9ses->clnt = p9_client_create(trans, v9ses->maxdata + P9_IOHDRSZ, + v9ses->extended); - if ((retval = v9ses->transport->init(v9ses, dev_name, data)) < 0) { - eprintk(KERN_ERR, "problem initializing transport\n"); - goto SessCleanUp; + if (IS_ERR(v9ses->clnt)) { + retval = PTR_ERR(v9ses->clnt); + v9ses->clnt = NULL; + P9_DPRINTK(P9_DEBUG_ERROR, "problem initializing 9p client\n"); + goto error; } - v9ses->inprogress = 0; - v9ses->shutdown = 0; - v9ses->session_hung = 0; - - v9ses->mux = v9fs_mux_init(v9ses->transport, v9ses->maxdata + V9FS_IOHDRSZ, - &v9ses->extended); - - if (IS_ERR(v9ses->mux)) { - retval = PTR_ERR(v9ses->mux); - v9ses->mux = NULL; - dprintk(DEBUG_ERROR, "problem initializing mux\n"); - goto SessCleanUp; + fid = p9_client_attach(v9ses->clnt, NULL, v9ses->name, + v9ses->remotename); + if (IS_ERR(fid)) { + retval = PTR_ERR(fid); + fid = NULL; + P9_DPRINTK(P9_DEBUG_ERROR, "cannot attach\n"); + goto error; } - if (v9ses->afid == ~0) { - if (v9ses->extended) - retval = - v9fs_t_version(v9ses, v9ses->maxdata, "9P2000.u", - &fcall); - else - retval = v9fs_t_version(v9ses, v9ses->maxdata, "9P2000", - &fcall); - - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_version failed\n"); - goto FreeFcall; - } - - version = &fcall->params.rversion.version; - if (version->len==8 && !memcmp(version->str, "9P2000.u", 8)) { - dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n"); - v9ses->extended = 1; - } else if (version->len==6 && !memcmp(version->str, "9P2000", 6)) { - dprintk(DEBUG_9P, "9P2000 legacy mode enabled\n"); - v9ses->extended = 0; - } else { - retval = -EREMOTEIO; - goto FreeFcall; - } + return fid; - n = fcall->params.rversion.msize; - kfree(fcall); - - if (n < v9ses->maxdata) - v9ses->maxdata = n; - } - - newfid = v9fs_get_idpool(&v9ses->fidpool); - if (newfid < 0) { - eprintk(KERN_WARNING, "couldn't allocate FID\n"); - retval = -ENOMEM; - goto SessCleanUp; - } - /* it is a little bit ugly, but we have to prevent newfid */ - /* being the same as afid, so if it is, get a new fid */ - if (v9ses->afid != ~0 && newfid == v9ses->afid) { - newfid = v9fs_get_idpool(&v9ses->fidpool); - if (newfid < 0) { - eprintk(KERN_WARNING, "couldn't allocate FID\n"); - retval = -ENOMEM; - goto SessCleanUp; - } - } - - if ((retval = - v9fs_t_attach(v9ses, v9ses->name, v9ses->remotename, newfid, - v9ses->afid, NULL)) - < 0) { - dprintk(DEBUG_ERROR, "cannot attach\n"); - goto SessCleanUp; - } - - if (v9ses->afid != ~0) { - dprintk(DEBUG_ERROR, "afid not equal to ~0\n"); - if (v9fs_t_clunk(v9ses, v9ses->afid)) - dprintk(DEBUG_ERROR, "clunk failed\n"); - } - - return newfid; - - FreeFcall: - kfree(fcall); - - SessCleanUp: +error: v9fs_session_close(v9ses); - return retval; + return ERR_PTR(retval); } /** @@ -426,15 +283,9 @@ v9fs_session_init(struct v9fs_session_info *v9ses, void v9fs_session_close(struct v9fs_session_info *v9ses) { - if (v9ses->mux) { - v9fs_mux_destroy(v9ses->mux); - v9ses->mux = NULL; - } - - if (v9ses->transport) { - v9ses->transport->close(v9ses->transport); - kfree(v9ses->transport); - v9ses->transport = NULL; + if (v9ses->clnt) { + p9_client_destroy(v9ses->clnt); + v9ses->clnt = NULL; } __putname(v9ses->name); @@ -446,9 +297,8 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) * and cancel all pending requests. */ void v9fs_session_cancel(struct v9fs_session_info *v9ses) { - dprintk(DEBUG_ERROR, "cancel session %p\n", v9ses); - v9ses->transport->status = Disconnected; - v9fs_mux_cancel(v9ses->mux, -EIO); + P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses); + p9_client_disconnect(v9ses->clnt); } extern int v9fs_error_init(void); @@ -460,24 +310,9 @@ extern int v9fs_error_init(void); static int __init init_v9fs(void) { - int ret; - - v9fs_error_init(); - printk(KERN_INFO "Installing v9fs 9p2000 file system support\n"); - ret = v9fs_mux_global_init(); - if (ret) { - printk(KERN_WARNING "v9fs: starting mux failed\n"); - return ret; - } - ret = register_filesystem(&v9fs_fs_type); - if (ret) { - printk(KERN_WARNING "v9fs: registering file system failed\n"); - v9fs_mux_global_exit(); - } - - return ret; + return register_filesystem(&v9fs_fs_type); } /** @@ -487,13 +322,13 @@ static int __init init_v9fs(void) static void __exit exit_v9fs(void) { - v9fs_mux_global_exit(); unregister_filesystem(&v9fs_fs_type); } module_init(init_v9fs) module_exit(exit_v9fs) +MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>"); MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>"); MODULE_LICENSE("GPL"); diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 820bf5c..abc4b16 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -22,16 +22,6 @@ */ /* - * Idpool structure provides lock and id management - * - */ - -struct v9fs_idpool { - struct semaphore lock; - struct idr pool; -}; - -/* * Session structure provides information for an opened session * */ @@ -54,15 +44,7 @@ struct v9fs_session_info { unsigned int uid; /* default uid/muid for legacy support */ unsigned int gid; /* default gid for legacy support */ - /* book keeping */ - struct v9fs_idpool fidpool; /* The FID pool for file descriptors */ - - struct v9fs_transport *transport; - struct v9fs_mux_data *mux; - - int inprogress; /* session in progress => true */ - int shutdown; /* session shutting down. no more attaches. */ - unsigned char session_hung; + struct p9_client *clnt; /* 9p client */ struct dentry *debugfs_dir; }; @@ -71,6 +53,7 @@ enum { PROTO_TCP, PROTO_UNIX, PROTO_FD, + PROTO_PCI, }; /* possible values of ->cache */ @@ -82,12 +65,9 @@ enum { extern struct dentry *v9fs_debugfs_root; -int v9fs_session_init(struct v9fs_session_info *, const char *, char *); -struct v9fs_session_info *v9fs_inode2v9ses(struct inode *); +struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, + char *); void v9fs_session_close(struct v9fs_session_info *v9ses); -int v9fs_get_idpool(struct v9fs_idpool *p); -void v9fs_put_idpool(int id, struct v9fs_idpool *p); -int v9fs_check_idpool(int id, struct v9fs_idpool *p); void v9fs_session_cancel(struct v9fs_session_info *v9ses); #define V9FS_MAGIC 0x01021997 @@ -97,3 +77,7 @@ void v9fs_session_cancel(struct v9fs_session_info *v9ses); #define V9FS_DEFUSER "nobody" #define V9FS_DEFANAME "" +static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode) +{ + return (inode->i_sb->s_fs_info); +} diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 6a82d39..fd01d90 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -45,10 +45,10 @@ extern struct dentry_operations v9fs_dentry_operations; extern struct dentry_operations v9fs_cached_dentry_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); -ino_t v9fs_qid2ino(struct v9fs_qid *qid); -void v9fs_stat2inode(struct v9fs_stat *, struct inode *, struct super_block *); +ino_t v9fs_qid2ino(struct p9_qid *qid); +void v9fs_stat2inode(struct p9_stat *, struct inode *, struct super_block *); int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_file_open(struct inode *inode, struct file *file); -void v9fs_inode2stat(struct inode *inode, struct v9fs_stat *stat); +void v9fs_inode2stat(struct inode *inode, struct p9_stat *stat); void v9fs_dentry_release(struct dentry *); int v9fs_uflags2omode(int uflags); diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 9ac4ffe..6248f0e 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -33,10 +33,10 @@ #include <linux/pagemap.h> #include <linux/idr.h> #include <linux/sched.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -50,55 +50,26 @@ static int v9fs_vfs_readpage(struct file *filp, struct page *page) { - char *buffer = NULL; - int retval = -EIO; - loff_t offset = page_offset(page); - int count = PAGE_CACHE_SIZE; - struct inode *inode = filp->f_path.dentry->d_inode; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - int rsize = v9ses->maxdata - V9FS_IOHDRSZ; - struct v9fs_fid *v9f = filp->private_data; - struct v9fs_fcall *fcall = NULL; - int fid = v9f->fid; - int total = 0; - int result = 0; - - dprintk(DEBUG_VFS, "\n"); + int retval; + loff_t offset; + char *buffer; + struct p9_fid *fid; + P9_DPRINTK(P9_DEBUG_VFS, "\n"); + fid = filp->private_data; buffer = kmap(page); - do { - if (count < rsize) - rsize = count; - - result = v9fs_t_read(v9ses, fid, offset, rsize, &fcall); - - if (result < 0) { - printk(KERN_ERR "v9fs_t_read returned %d\n", - result); - - kfree(fcall); - goto UnmapAndUnlock; - } else - offset += result; - - memcpy(buffer, fcall->params.rread.data, result); - - count -= result; - buffer += result; - total += result; - - kfree(fcall); + offset = page_offset(page); - if (result < rsize) - break; - } while (count); + retval = p9_client_readn(fid, buffer, offset, PAGE_CACHE_SIZE); + if (retval < 0) + goto done; - memset(buffer, 0, count); + memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval); flush_dcache_page(page); SetPageUptodate(page); retval = 0; -UnmapAndUnlock: +done: kunmap(page); unlock_page(page); return retval; diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index d939604..f9534f1 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -34,10 +34,10 @@ #include <linux/namei.h> #include <linux/idr.h> #include <linux/sched.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -52,7 +52,7 @@ static int v9fs_dentry_delete(struct dentry *dentry) { - dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); return 1; } @@ -69,7 +69,7 @@ static int v9fs_dentry_delete(struct dentry *dentry) static int v9fs_cached_dentry_delete(struct dentry *dentry) { struct inode *inode = dentry->d_inode; - dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); if(!inode) return 1; @@ -85,26 +85,19 @@ static int v9fs_cached_dentry_delete(struct dentry *dentry) void v9fs_dentry_release(struct dentry *dentry) { - int err; - - dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); - - if (dentry->d_fsdata != NULL) { - struct list_head *fid_list = dentry->d_fsdata; - struct v9fs_fid *temp = NULL; - struct v9fs_fid *current_fid = NULL; - - list_for_each_entry_safe(current_fid, temp, fid_list, list) { - err = v9fs_t_clunk(current_fid->v9ses, current_fid->fid); - - if (err < 0) - dprintk(DEBUG_ERROR, "clunk failed: %d name %s\n", - err, dentry->d_iname); - - v9fs_fid_destroy(current_fid); + struct v9fs_dentry *dent; + struct p9_fid *temp, *current_fid; + + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + dent = dentry->d_fsdata; + if (dent) { + list_for_each_entry_safe(current_fid, temp, &dent->fidlist, + dlist) { + p9_client_clunk(current_fid); } - kfree(dentry->d_fsdata); /* free the list_head */ + kfree(dent); + dentry->d_fsdata = NULL; } } diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 1dd86ee..0924d44 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -32,11 +32,10 @@ #include <linux/sched.h> #include <linux/inet.h> #include <linux/idr.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" -#include "conv.h" #include "v9fs_vfs.h" #include "fid.h" @@ -46,14 +45,14 @@ * */ -static inline int dt_type(struct v9fs_stat *mistat) +static inline int dt_type(struct p9_stat *mistat) { unsigned long perm = mistat->mode; int rettype = DT_REG; - if (perm & V9FS_DMDIR) + if (perm & P9_DMDIR) rettype = DT_DIR; - if (perm & V9FS_DMSYMLINK) + if (perm & P9_DMSYMLINK) rettype = DT_LNK; return rettype; @@ -69,106 +68,36 @@ static inline int dt_type(struct v9fs_stat *mistat) static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct v9fs_fcall *fcall = NULL; - struct inode *inode = filp->f_path.dentry->d_inode; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - struct v9fs_fid *file = filp->private_data; - unsigned int i, n, s; - int fid = -1; - int ret = 0; - struct v9fs_stat stat; - int over = 0; - - dprintk(DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); - - fid = file->fid; - - if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) { - kfree(file->rdir_fcall); - file->rdir_fcall = NULL; - } - - if (file->rdir_fcall) { - n = file->rdir_fcall->params.rread.count; - i = file->rdir_fpos; - while (i < n) { - s = v9fs_deserialize_stat( - file->rdir_fcall->params.rread.data + i, - n - i, &stat, v9ses->extended); - - if (s == 0) { - dprintk(DEBUG_ERROR, - "error while deserializing stat\n"); - ret = -EIO; - goto FreeStructs; - } - - over = filldir(dirent, stat.name.str, stat.name.len, - filp->f_pos, v9fs_qid2ino(&stat.qid), - dt_type(&stat)); - - if (over) { - file->rdir_fpos = i; - file->rdir_pos = filp->f_pos; - break; - } - - i += s; - filp->f_pos += s; - } - - if (!over) { - kfree(file->rdir_fcall); - file->rdir_fcall = NULL; - } - } - - while (!over) { - ret = v9fs_t_read(v9ses, fid, filp->f_pos, - v9ses->maxdata-V9FS_IOHDRSZ, &fcall); - if (ret < 0) { - dprintk(DEBUG_ERROR, "error while reading: %d: %p\n", - ret, fcall); - goto FreeStructs; - } else if (ret == 0) + int over; + struct p9_fid *fid; + struct v9fs_session_info *v9ses; + struct inode *inode; + struct p9_stat *st; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); + inode = filp->f_path.dentry->d_inode; + v9ses = v9fs_inode2v9ses(inode); + fid = filp->private_data; + while ((st = p9_client_dirread(fid, filp->f_pos)) != NULL) { + if (IS_ERR(st)) + return PTR_ERR(st); + + over = filldir(dirent, st->name.str, st->name.len, filp->f_pos, + v9fs_qid2ino(&st->qid), dt_type(st)); + + if (over) break; - n = ret; - i = 0; - while (i < n) { - s = v9fs_deserialize_stat(fcall->params.rread.data + i, - n - i, &stat, v9ses->extended); - - if (s == 0) { - dprintk(DEBUG_ERROR, - "error while deserializing stat\n"); - return -EIO; - } - - over = filldir(dirent, stat.name.str, stat.name.len, - filp->f_pos, v9fs_qid2ino(&stat.qid), - dt_type(&stat)); - - if (over) { - file->rdir_fcall = fcall; - file->rdir_fpos = i; - file->rdir_pos = filp->f_pos; - fcall = NULL; - break; - } - - i += s; - filp->f_pos += s; - } - - kfree(fcall); + filp->f_pos += st->size; + kfree(st); + st = NULL; } - FreeStructs: - kfree(fcall); - return ret; + kfree(st); + return 0; } + /** * v9fs_dir_release - close a directory * @inode: inode of the directory @@ -178,29 +107,13 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) int v9fs_dir_release(struct inode *inode, struct file *filp) { - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - struct v9fs_fid *fid = filp->private_data; - int fidnum = -1; - - dprintk(DEBUG_VFS, "inode: %p filp: %p fid: %d\n", inode, filp, - fid->fid); - fidnum = fid->fid; + struct p9_fid *fid; + fid = filp->private_data; + P9_DPRINTK(P9_DEBUG_VFS, + "inode: %p filp: %p fid: %d\n", inode, filp, fid->fid); filemap_write_and_wait(inode->i_mapping); - - if (fidnum >= 0) { - dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen, - fid->fid); - - if (v9fs_t_clunk(v9ses, fidnum)) - dprintk(DEBUG_ERROR, "clunk failed\n"); - - kfree(fid->rdir_fcall); - kfree(fid); - - filp->private_data = NULL; - } - + p9_client_clunk(fid); return 0; } diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 6e7678e..2a40c29 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -34,10 +34,10 @@ #include <linux/list.h> #include <asm/uaccess.h> #include <linux/idr.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -52,48 +52,40 @@ static const struct file_operations v9fs_cached_file_operations; int v9fs_file_open(struct inode *inode, struct file *file) { - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - struct v9fs_fid *vfid; - struct v9fs_fcall *fcall = NULL; - int omode; int err; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + int omode; - dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); - - vfid = v9fs_fid_clone(file->f_path.dentry); - if (IS_ERR(vfid)) - return PTR_ERR(vfid); - + P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file); + v9ses = v9fs_inode2v9ses(inode); omode = v9fs_uflags2omode(file->f_flags); - err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall); - if (err < 0) { - PRINT_FCALL_ERROR("open failed", fcall); - goto Clunk_Fid; + fid = file->private_data; + if (!fid) { + fid = v9fs_fid_clone(file->f_path.dentry); + if (IS_ERR(fid)) + return PTR_ERR(fid); + + err = p9_client_open(fid, omode); + if (err < 0) { + p9_client_clunk(fid); + return err; + } + if (omode & P9_OTRUNC) { + inode->i_size = 0; + inode->i_blocks = 0; + } } - file->private_data = vfid; - vfid->fidopen = 1; - vfid->fidclunked = 0; - vfid->iounit = fcall->params.ropen.iounit; - vfid->rdir_pos = 0; - vfid->rdir_fcall = NULL; - vfid->filp = file; - kfree(fcall); - - if((vfid->qid.version) && (v9ses->cache)) { - dprintk(DEBUG_VFS, "cached"); + file->private_data = fid; + if ((fid->qid.version) && (v9ses->cache)) { + P9_DPRINTK(P9_DEBUG_VFS, "cached"); /* enable cached file options */ if(file->f_op == &v9fs_file_operations) file->f_op = &v9fs_cached_file_operations; } return 0; - -Clunk_Fid: - v9fs_fid_clunk(v9ses, vfid); - kfree(fcall); - - return err; } /** @@ -110,7 +102,7 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) int res = 0; struct inode *inode = filp->f_path.dentry->d_inode; - dprintk(DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); + P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); /* No mandatory locks */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) @@ -136,55 +128,16 @@ static ssize_t v9fs_file_read(struct file *filp, char __user * data, size_t count, loff_t * offset) { - struct inode *inode = filp->f_path.dentry->d_inode; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - struct v9fs_fid *v9f = filp->private_data; - struct v9fs_fcall *fcall = NULL; - int fid = v9f->fid; - int rsize = 0; - int result = 0; - int total = 0; - int n; - - dprintk(DEBUG_VFS, "\n"); - - rsize = v9ses->maxdata - V9FS_IOHDRSZ; - if (v9f->iounit != 0 && rsize > v9f->iounit) - rsize = v9f->iounit; - - do { - if (count < rsize) - rsize = count; + int ret; + struct p9_fid *fid; - result = v9fs_t_read(v9ses, fid, *offset, rsize, &fcall); + P9_DPRINTK(P9_DEBUG_VFS, "\n"); + fid = filp->private_data; + ret = p9_client_uread(fid, data, *offset, count); + if (ret > 0) + *offset += ret; - if (result < 0) { - printk(KERN_ERR "9P2000: v9fs_t_read returned %d\n", - result); - - kfree(fcall); - return total; - } else - *offset += result; - - n = copy_to_user(data, fcall->params.rread.data, result); - if (n) { - dprintk(DEBUG_ERROR, "Problem copying to user %d\n", n); - kfree(fcall); - return -EFAULT; - } - - count -= result; - data += result; - total += result; - - kfree(fcall); - - if (result < rsize) - break; - } while (count); - - return total; + return ret; } /** @@ -200,50 +153,25 @@ static ssize_t v9fs_file_write(struct file *filp, const char __user * data, size_t count, loff_t * offset) { + int ret; + struct p9_fid *fid; struct inode *inode = filp->f_path.dentry->d_inode; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); - struct v9fs_fid *v9fid = filp->private_data; - struct v9fs_fcall *fcall; - int fid = v9fid->fid; - int result = -EIO; - int rsize = 0; - int total = 0; - - dprintk(DEBUG_VFS, "data %p count %d offset %x\n", data, (int)count, - (int)*offset); - rsize = v9ses->maxdata - V9FS_IOHDRSZ; - if (v9fid->iounit != 0 && rsize > v9fid->iounit) - rsize = v9fid->iounit; - - do { - if (count < rsize) - rsize = count; - result = v9fs_t_write(v9ses, fid, *offset, rsize, data, &fcall); - if (result < 0) { - PRINT_FCALL_ERROR("error while writing", fcall); - kfree(fcall); - return result; - } else - *offset += result; + P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data, + (int)count, (int)*offset); - kfree(fcall); - fcall = NULL; + fid = filp->private_data; + ret = p9_client_uwrite(fid, data, *offset, count); + if (ret > 0) + *offset += ret; - if (result != rsize) { - eprintk(KERN_ERR, - "short write: v9fs_t_write returned %d\n", - result); - break; - } - - count -= result; - data += result; - total += result; - } while (count); + if (*offset > inode->i_size) { + inode->i_size = *offset; + inode->i_blocks = (inode->i_size + 512 - 1) >> 9; + } invalidate_inode_pages2(inode->i_mapping); - return total; + return ret; } static const struct file_operations v9fs_cached_file_operations = { diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index c76cd8f..e5c45ee 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -34,10 +34,10 @@ #include <linux/namei.h> #include <linux/idr.h> #include <linux/sched.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -58,27 +58,27 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode) int res; res = mode & 0777; if (S_ISDIR(mode)) - res |= V9FS_DMDIR; + res |= P9_DMDIR; if (v9ses->extended) { if (S_ISLNK(mode)) - res |= V9FS_DMSYMLINK; + res |= P9_DMSYMLINK; if (v9ses->nodev == 0) { if (S_ISSOCK(mode)) - res |= V9FS_DMSOCKET; + res |= P9_DMSOCKET; if (S_ISFIFO(mode)) - res |= V9FS_DMNAMEDPIPE; + res |= P9_DMNAMEDPIPE; if (S_ISBLK(mode)) - res |= V9FS_DMDEVICE; + res |= P9_DMDEVICE; if (S_ISCHR(mode)) - res |= V9FS_DMDEVICE; + res |= P9_DMDEVICE; } if ((mode & S_ISUID) == S_ISUID) - res |= V9FS_DMSETUID; + res |= P9_DMSETUID; if ((mode & S_ISGID) == S_ISGID) - res |= V9FS_DMSETGID; - if ((mode & V9FS_DMLINK)) - res |= V9FS_DMLINK; + res |= P9_DMSETGID; + if ((mode & P9_DMLINK)) + res |= P9_DMLINK; } return res; @@ -97,27 +97,27 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) res = mode & 0777; - if ((mode & V9FS_DMDIR) == V9FS_DMDIR) + if ((mode & P9_DMDIR) == P9_DMDIR) res |= S_IFDIR; - else if ((mode & V9FS_DMSYMLINK) && (v9ses->extended)) + else if ((mode & P9_DMSYMLINK) && (v9ses->extended)) res |= S_IFLNK; - else if ((mode & V9FS_DMSOCKET) && (v9ses->extended) + else if ((mode & P9_DMSOCKET) && (v9ses->extended) && (v9ses->nodev == 0)) res |= S_IFSOCK; - else if ((mode & V9FS_DMNAMEDPIPE) && (v9ses->extended) + else if ((mode & P9_DMNAMEDPIPE) && (v9ses->extended) && (v9ses->nodev == 0)) res |= S_IFIFO; - else if ((mode & V9FS_DMDEVICE) && (v9ses->extended) + else if ((mode & P9_DMDEVICE) && (v9ses->extended) && (v9ses->nodev == 0)) res |= S_IFBLK; else res |= S_IFREG; if (v9ses->extended) { - if ((mode & V9FS_DMSETUID) == V9FS_DMSETUID) + if ((mode & P9_DMSETUID) == P9_DMSETUID) res |= S_ISUID; - if ((mode & V9FS_DMSETGID) == V9FS_DMSETGID) + if ((mode & P9_DMSETGID) == P9_DMSETGID) res |= S_ISGID; } @@ -132,26 +132,26 @@ int v9fs_uflags2omode(int uflags) switch (uflags&3) { default: case O_RDONLY: - ret = V9FS_OREAD; + ret = P9_OREAD; break; case O_WRONLY: - ret = V9FS_OWRITE; + ret = P9_OWRITE; break; case O_RDWR: - ret = V9FS_ORDWR; + ret = P9_ORDWR; break; } if (uflags & O_EXCL) - ret |= V9FS_OEXCL; + ret |= P9_OEXCL; if (uflags & O_TRUNC) - ret |= V9FS_OTRUNC; + ret |= P9_OTRUNC; if (uflags & O_APPEND) - ret |= V9FS_OAPPEND; + ret |= P9_OAPPEND; return ret; } @@ -164,7 +164,7 @@ int v9fs_uflags2omode(int uflags) */ static void -v9fs_blank_wstat(struct v9fs_wstat *wstat) +v9fs_blank_wstat(struct p9_wstat *wstat) { wstat->type = ~0; wstat->dev = ~0; @@ -197,7 +197,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode) struct inode *inode; struct v9fs_session_info *v9ses = sb->s_fs_info; - dprintk(DEBUG_VFS, "super block: %p mode: %o\n", sb, mode); + P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode); inode = new_inode(sb); if (inode) { @@ -215,7 +215,8 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode) case S_IFCHR: case S_IFSOCK: if(!v9ses->extended) { - dprintk(DEBUG_ERROR, "special files without extended mode\n"); + P9_DPRINTK(P9_DEBUG_ERROR, + "special files without extended mode\n"); return ERR_PTR(-EINVAL); } init_special_inode(inode, inode->i_mode, @@ -227,7 +228,8 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode) break; case S_IFLNK: if(!v9ses->extended) { - dprintk(DEBUG_ERROR, "extended modes used w/o 9P2000.u\n"); + P9_DPRINTK(P9_DEBUG_ERROR, + "extended modes used w/o 9P2000.u\n"); return ERR_PTR(-EINVAL); } inode->i_op = &v9fs_symlink_inode_operations; @@ -241,71 +243,19 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode) inode->i_fop = &v9fs_dir_operations; break; default: - dprintk(DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n", + P9_DPRINTK(P9_DEBUG_ERROR, + "BAD mode 0x%x S_IFMT 0x%x\n", mode, mode & S_IFMT); return ERR_PTR(-EINVAL); } } else { - eprintk(KERN_WARNING, "Problem allocating inode\n"); + P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n"); return ERR_PTR(-ENOMEM); } return inode; } -static int -v9fs_create(struct v9fs_session_info *v9ses, u32 pfid, char *name, u32 perm, - u8 mode, char *extension, u32 *fidp, struct v9fs_qid *qid, u32 *iounit) -{ - int fid; - int err; - struct v9fs_fcall *fcall; - - fid = v9fs_get_idpool(&v9ses->fidpool); - if (fid < 0) { - eprintk(KERN_WARNING, "no free fids available\n"); - return -ENOSPC; - } - - err = v9fs_t_walk(v9ses, pfid, fid, NULL, &fcall); - if (err < 0) { - PRINT_FCALL_ERROR("clone error", fcall); - if (fcall && fcall->id == RWALK) - goto clunk_fid; - else - goto put_fid; - } - kfree(fcall); - - err = v9fs_t_create(v9ses, fid, name, perm, mode, extension, &fcall); - if (err < 0) { - PRINT_FCALL_ERROR("create fails", fcall); - goto clunk_fid; - } - - if (iounit) - *iounit = fcall->params.rcreate.iounit; - - if (qid) - *qid = fcall->params.rcreate.qid; - - if (fidp) - *fidp = fid; - - kfree(fcall); - return 0; - -clunk_fid: - v9fs_t_clunk(v9ses, fid); - fid = V9FS_NOFID; - -put_fid: - if (fid != V9FS_NOFID) - v9fs_put_idpool(fid, &v9ses->fidpool); - - kfree(fcall); - return err; -} - +/* static struct v9fs_fid* v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry) { @@ -355,23 +305,25 @@ error: kfree(fcall); return ERR_PTR(err); } +*/ static struct inode * -v9fs_inode_from_fid(struct v9fs_session_info *v9ses, u32 fid, +v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, struct super_block *sb) { int err, umode; struct inode *ret; - struct v9fs_fcall *fcall; + struct p9_stat *st; ret = NULL; - err = v9fs_t_stat(v9ses, fid, &fcall); - if (err) { - PRINT_FCALL_ERROR("stat error", fcall); + st = p9_client_stat(fid); + if (IS_ERR(st)) { + err = PTR_ERR(st); + st = NULL; goto error; } - umode = p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode); + umode = p9mode2unixmode(v9ses, st->mode); ret = v9fs_get_inode(sb, umode); if (IS_ERR(ret)) { err = PTR_ERR(ret); @@ -379,12 +331,13 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, u32 fid, goto error; } - v9fs_stat2inode(&fcall->params.rstat.stat, ret, sb); - kfree(fcall); + v9fs_stat2inode(st, ret, sb); + ret->i_ino = v9fs_qid2ino(&st->qid); + kfree(st); return ret; error: - kfree(fcall); + kfree(st); if (ret) iput(ret); @@ -401,43 +354,20 @@ error: static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) { - struct v9fs_fcall *fcall = NULL; - struct super_block *sb = NULL; - struct v9fs_session_info *v9ses = NULL; - struct v9fs_fid *v9fid = NULL; - struct inode *file_inode = NULL; - int fid = -1; - int result = 0; + struct inode *file_inode; + struct v9fs_session_info *v9ses; + struct p9_fid *v9fid; - dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, + P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, rmdir); file_inode = file->d_inode; - sb = file_inode->i_sb; v9ses = v9fs_inode2v9ses(file_inode); v9fid = v9fs_fid_clone(file); if(IS_ERR(v9fid)) return PTR_ERR(v9fid); - fid = v9fid->fid; - if (fid < 0) { - dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n", - file_inode->i_ino); - return -EBADF; - } - - result = v9fs_t_remove(v9ses, fid, &fcall); - if (result < 0) { - PRINT_FCALL_ERROR("remove fails", fcall); - goto Error; - } - - v9fs_put_idpool(fid, &v9ses->fidpool); - v9fs_fid_destroy(v9fid); - -Error: - kfree(fcall); - return result; + return p9_client_remove(v9fid); } static int @@ -446,61 +376,59 @@ v9fs_open_created(struct inode *inode, struct file *file) return 0; } + /** - * v9fs_vfs_create - VFS hook to create files - * @inode: directory inode that is being deleted - * @dentry: dentry that is being deleted - * @mode: create permissions - * @nd: path information + * v9fs_create - Create a file + * @dentry: dentry that is being created + * @perm: create permissions + * @mode: open mode * */ - -static int -v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) +static struct p9_fid * +v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, + struct dentry *dentry, char *extension, u32 perm, u8 mode) { int err; - u32 fid, perm, iounit; - int flags; - struct v9fs_session_info *v9ses; - struct v9fs_fid *dfid, *vfid, *ffid; + char *name; + struct p9_fid *dfid, *ofid, *fid; struct inode *inode; - struct v9fs_qid qid; - struct file *filp; - inode = NULL; - vfid = NULL; - v9ses = v9fs_inode2v9ses(dir); + err = 0; + ofid = NULL; + fid = NULL; + name = (char *) dentry->d_name.name; dfid = v9fs_fid_clone(dentry->d_parent); if(IS_ERR(dfid)) { err = PTR_ERR(dfid); + dfid = NULL; goto error; } - perm = unixmode2p9mode(v9ses, mode); - if (nd && nd->flags & LOOKUP_OPEN) - flags = nd->intent.open.flags - 1; - else - flags = O_RDWR; - - err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, - perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); + /* clone a fid to use for creation */ + ofid = p9_client_walk(dfid, 0, NULL, 1); + if (IS_ERR(ofid)) { + err = PTR_ERR(ofid); + ofid = NULL; + goto error; + } - if (err) - goto clunk_dfid; + err = p9_client_fcreate(ofid, name, perm, mode, extension); + if (err < 0) + goto error; - vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); - v9fs_fid_clunk(v9ses, dfid); - if (IS_ERR(vfid)) { - err = PTR_ERR(vfid); - vfid = NULL; + /* now walk from the parent so we can get unopened fid */ + fid = p9_client_walk(dfid, 1, &name, 0); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; goto error; - } + } else + dfid = NULL; - inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); + /* instantiate inode and assign the unopened fid to the dentry */ + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); if (IS_ERR(inode)) { err = PTR_ERR(inode); - inode = NULL; goto error; } @@ -508,35 +436,78 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, dentry->d_op = &v9fs_cached_dentry_operations; else dentry->d_op = &v9fs_dentry_operations; + d_instantiate(dentry, inode); + v9fs_fid_add(dentry, fid); + return ofid; - if (nd && nd->flags & LOOKUP_OPEN) { - ffid = v9fs_fid_create(v9ses, fid); - if (!ffid) - return -ENOMEM; +error: + if (dfid) + p9_client_clunk(dfid); + + if (ofid) + p9_client_clunk(ofid); + + if (fid) + p9_client_clunk(fid); + + return ERR_PTR(err); +} + +/** + * v9fs_vfs_create - VFS hook to create files + * @inode: directory inode that is being created + * @dentry: dentry that is being deleted + * @mode: create permissions + * @nd: path information + * + */ +static int +v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + int err; + u32 perm; + int flags; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct file *filp; + + err = 0; + fid = NULL; + v9ses = v9fs_inode2v9ses(dir); + perm = unixmode2p9mode(v9ses, mode); + if (nd && nd->flags & LOOKUP_OPEN) + flags = nd->intent.open.flags - 1; + else + flags = O_RDWR; + + fid = v9fs_create(v9ses, dir, dentry, NULL, perm, + v9fs_uflags2omode(flags)); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; + goto error; + } + + /* if we are opening a file, assign the open fid to the file */ + if (nd && nd->flags & LOOKUP_OPEN) { filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created); if (IS_ERR(filp)) { - v9fs_fid_destroy(ffid); - return PTR_ERR(filp); + err = PTR_ERR(filp); + goto error; } - ffid->rdir_pos = 0; - ffid->rdir_fcall = NULL; - ffid->fidopen = 1; - ffid->iounit = iounit; - ffid->filp = filp; - filp->private_data = ffid; - } + filp->private_data = fid; + } else + p9_client_clunk(fid); return 0; -clunk_dfid: - v9fs_fid_clunk(v9ses, dfid); - error: - if (vfid) - v9fs_fid_destroy(vfid); + if (fid) + p9_client_clunk(fid); return err; } @@ -552,57 +523,23 @@ error: static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int err; - u32 fid, perm; + u32 perm; struct v9fs_session_info *v9ses; - struct v9fs_fid *dfid, *vfid; - struct inode *inode; + struct p9_fid *fid; - inode = NULL; - vfid = NULL; + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); + err = 0; v9ses = v9fs_inode2v9ses(dir); - dfid = v9fs_fid_clone(dentry->d_parent); - if(IS_ERR(dfid)) { - err = PTR_ERR(dfid); - goto error; - } - perm = unixmode2p9mode(v9ses, mode | S_IFDIR); - - err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, - perm, V9FS_OREAD, NULL, &fid, NULL, NULL); - - if (err) { - dprintk(DEBUG_ERROR, "create error %d\n", err); - goto clean_up_dfid; + fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + fid = NULL; } - vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); - if (IS_ERR(vfid)) { - err = PTR_ERR(vfid); - vfid = NULL; - goto clean_up_dfid; - } + if (fid) + p9_client_clunk(fid); - v9fs_fid_clunk(v9ses, dfid); - inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - inode = NULL; - v9fs_fid_destroy(vfid); - goto error; - } - - if(v9ses->cache) - dentry->d_op = &v9fs_cached_dentry_operations; - else - dentry->d_op = &v9fs_dentry_operations; - d_instantiate(dentry, inode); - return 0; - -clean_up_dfid: - v9fs_fid_clunk(v9ses, dfid); - -error: return err; } @@ -619,104 +556,54 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, { struct super_block *sb; struct v9fs_session_info *v9ses; - struct v9fs_fid *dirfid; - struct v9fs_fid *fid; + struct p9_fid *dfid, *fid; struct inode *inode; - struct v9fs_fcall *fcall = NULL; - int dirfidnum = -1; - int newfid = -1; + char *name; int result = 0; - dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", + P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", dir, dentry->d_name.name, dentry, nameidata); sb = dir->i_sb; v9ses = v9fs_inode2v9ses(dir); - dirfid = v9fs_fid_lookup(dentry->d_parent); - - if(IS_ERR(dirfid)) - return ERR_PTR(PTR_ERR(dirfid)); - - dirfidnum = dirfid->fid; - - newfid = v9fs_get_idpool(&v9ses->fidpool); - if (newfid < 0) { - eprintk(KERN_WARNING, "newfid fails!\n"); - result = -ENOSPC; - goto Release_Dirfid; - } - - result = v9fs_t_walk(v9ses, dirfidnum, newfid, - (char *)dentry->d_name.name, &fcall); - - up(&dirfid->lock); - - if (result < 0) { - if (fcall && fcall->id == RWALK) - v9fs_t_clunk(v9ses, newfid); - else - v9fs_put_idpool(newfid, &v9ses->fidpool); - + dfid = v9fs_fid_lookup(dentry->d_parent); + if (IS_ERR(dfid)) + return ERR_PTR(PTR_ERR(dfid)); + + name = (char *) dentry->d_name.name; + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + result = PTR_ERR(fid); if (result == -ENOENT) { d_add(dentry, NULL); - dprintk(DEBUG_VFS, - "Return negative dentry %p count %d\n", - dentry, atomic_read(&dentry->d_count)); - kfree(fcall); return NULL; } - dprintk(DEBUG_ERROR, "walk error:%d\n", result); - goto FreeFcall; - } - kfree(fcall); - - result = v9fs_t_stat(v9ses, newfid, &fcall); - if (result < 0) { - dprintk(DEBUG_ERROR, "stat error\n"); - goto FreeFcall; - } - - inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses, - fcall->params.rstat.stat.mode)); - if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { - eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", - PTR_ERR(inode)); - - result = -ENOSPC; - goto FreeFcall; + return ERR_PTR(result); } - inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid); - - fid = v9fs_fid_create(v9ses, newfid); - if (fid == NULL) { - dprintk(DEBUG_ERROR, "couldn't insert\n"); - result = -ENOMEM; - goto FreeFcall; + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + result = PTR_ERR(inode); + inode = NULL; + goto error; } - result = v9fs_fid_insert(fid, dentry); + result = v9fs_fid_add(dentry, fid); if (result < 0) - goto FreeFcall; + goto error; - fid->qid = fcall->params.rstat.stat.qid; - v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); if((fid->qid.version)&&(v9ses->cache)) dentry->d_op = &v9fs_cached_dentry_operations; else dentry->d_op = &v9fs_dentry_operations; d_add(dentry, inode); - kfree(fcall); - return NULL; -Release_Dirfid: - up(&dirfid->lock); - -FreeFcall: - kfree(fcall); +error: + if (fid) + p9_client_clunk(fid); return ERR_PTR(result); } @@ -758,73 +645,54 @@ static int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct inode *old_inode = old_dentry->d_inode; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); - struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); - struct v9fs_fid *olddirfid; - struct v9fs_fid *newdirfid; - struct v9fs_wstat wstat; - struct v9fs_fcall *fcall = NULL; - int fid = -1; - int olddirfidnum = -1; - int newdirfidnum = -1; - int retval = 0; - - dprintk(DEBUG_VFS, "\n"); + struct inode *old_inode; + struct v9fs_session_info *v9ses; + struct p9_fid *oldfid; + struct p9_fid *olddirfid; + struct p9_fid *newdirfid; + struct p9_wstat wstat; + int retval; + P9_DPRINTK(P9_DEBUG_VFS, "\n"); + retval = 0; + old_inode = old_dentry->d_inode; + v9ses = v9fs_inode2v9ses(old_inode); + oldfid = v9fs_fid_lookup(old_dentry); if(IS_ERR(oldfid)) return PTR_ERR(oldfid); olddirfid = v9fs_fid_clone(old_dentry->d_parent); if(IS_ERR(olddirfid)) { retval = PTR_ERR(olddirfid); - goto Release_lock; + goto done; } newdirfid = v9fs_fid_clone(new_dentry->d_parent); if(IS_ERR(newdirfid)) { retval = PTR_ERR(newdirfid); - goto Clunk_olddir; + goto clunk_olddir; } /* 9P can only handle file rename in the same directory */ if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { - dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); + P9_DPRINTK(P9_DEBUG_ERROR, + "old dir and new dir are different\n"); retval = -EXDEV; - goto Clunk_newdir; - } - - fid = oldfid->fid; - olddirfidnum = olddirfid->fid; - newdirfidnum = newdirfid->fid; - - if (fid < 0) { - dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", - old_inode->i_ino); - retval = -EBADF; - goto Clunk_newdir; + goto clunk_newdir; } v9fs_blank_wstat(&wstat); wstat.muid = v9ses->name; wstat.name = (char *) new_dentry->d_name.name; + retval = p9_client_wstat(oldfid, &wstat); - retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); +clunk_newdir: + p9_client_clunk(olddirfid); - if (retval < 0) - PRINT_FCALL_ERROR("wstat error", fcall); - - kfree(fcall); - -Clunk_newdir: - v9fs_fid_clunk(v9ses, newdirfid); - -Clunk_olddir: - v9fs_fid_clunk(v9ses, olddirfid); - -Release_lock: - up(&oldfid->lock); +clunk_olddir: + p9_client_clunk(newdirfid); +done: return retval; } @@ -840,28 +708,30 @@ static int v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct v9fs_fcall *fcall = NULL; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_clone(dentry); - int err = -EPERM; + int err; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct p9_stat *st; - dprintk(DEBUG_VFS, "dentry: %p\n", dentry); - if(IS_ERR(fid)) + P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); + err = -EPERM; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + if (v9ses->cache == CACHE_LOOSE) + return simple_getattr(mnt, dentry, stat); + + fid = v9fs_fid_lookup(dentry); + if (IS_ERR(fid)) return PTR_ERR(fid); - err = v9fs_t_stat(v9ses, fid->fid, &fcall); + st = p9_client_stat(fid); + if (IS_ERR(st)) + return PTR_ERR(st); - if (err < 0) - dprintk(DEBUG_ERROR, "stat error\n"); - else { - v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode, - dentry->d_inode->i_sb); + v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb); generic_fillattr(dentry->d_inode, stat); - } - kfree(fcall); - v9fs_fid_clunk(v9ses, fid); - return err; + kfree(st); + return 0; } /** @@ -873,13 +743,15 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) { - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_clone(dentry); - struct v9fs_fcall *fcall = NULL; - struct v9fs_wstat wstat; - int res = -EPERM; + int retval; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct p9_wstat wstat; - dprintk(DEBUG_VFS, "\n"); + P9_DPRINTK(P9_DEBUG_VFS, "\n"); + retval = -EPERM; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + fid = v9fs_fid_lookup(dentry); if(IS_ERR(fid)) return PTR_ERR(fid); @@ -904,17 +776,11 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) wstat.n_gid = iattr->ia_gid; } - res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); + retval = p9_client_wstat(fid, &wstat); + if (retval >= 0) + retval = inode_setattr(dentry->d_inode, iattr); - if (res < 0) - PRINT_FCALL_ERROR("wstat error", fcall); - - kfree(fcall); - if (res >= 0) - res = inode_setattr(dentry->d_inode, iattr); - - v9fs_fid_clunk(v9ses, fid); - return res; + return retval; } /** @@ -926,7 +792,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) */ void -v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, +v9fs_stat2inode(struct p9_stat *stat, struct inode *inode, struct super_block *sb) { int n; @@ -967,8 +833,9 @@ v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, case 'b': break; default: - dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n", - type, stat->extension.len, stat->extension.str); + P9_DPRINTK(P9_DEBUG_ERROR, + "Unknown special type %c (%.*s)\n", type, + stat->extension.len, stat->extension.str); }; inode->i_rdev = MKDEV(major, minor); } else @@ -976,8 +843,8 @@ v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, inode->i_size = stat->length; - inode->i_blocks = - (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + /* not real number of blocks, but 512 byte ones ... */ + inode->i_blocks = (inode->i_size + 512 - 1) >> 9; } /** @@ -987,7 +854,7 @@ v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, * BUG: potential for inode number collisions? */ -ino_t v9fs_qid2ino(struct v9fs_qid *qid) +ino_t v9fs_qid2ino(struct p9_qid *qid) { u64 path = qid->path + 2; ino_t i = 0; @@ -1010,56 +877,46 @@ ino_t v9fs_qid2ino(struct v9fs_qid *qid) static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) { - int retval = -EPERM; + int retval; - struct v9fs_fcall *fcall = NULL; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); - struct v9fs_fid *fid = v9fs_fid_clone(dentry); + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct p9_stat *st; + P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name); + retval = -EPERM; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + fid = v9fs_fid_lookup(dentry); if(IS_ERR(fid)) return PTR_ERR(fid); - if (!v9ses->extended) { - retval = -EBADF; - dprintk(DEBUG_ERROR, "not extended\n"); - goto ClunkFid; - } - - dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); - retval = v9fs_t_stat(v9ses, fid->fid, &fcall); - - if (retval < 0) { - dprintk(DEBUG_ERROR, "stat error\n"); - goto FreeFcall; - } + if (!v9ses->extended) + return -EBADF; - if (!fcall) { - retval = -EIO; - goto ClunkFid; - } + st = p9_client_stat(fid); + if (IS_ERR(st)) + return PTR_ERR(st); - if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { + if (!(st->mode & P9_DMSYMLINK)) { retval = -EINVAL; - goto FreeFcall; + goto done; } /* copy extension buffer into buffer */ - if (fcall->params.rstat.stat.extension.len < buflen) - buflen = fcall->params.rstat.stat.extension.len + 1; + if (st->extension.len < buflen) + buflen = st->extension.len + 1; - memmove(buffer, fcall->params.rstat.stat.extension.str, buflen - 1); + memmove(buffer, st->extension.str, buflen - 1); buffer[buflen-1] = 0; - dprintk(DEBUG_ERROR, "%s -> %.*s (%s)\n", dentry->d_name.name, fcall->params.rstat.stat.extension.len, - fcall->params.rstat.stat.extension.str, buffer); - retval = buflen; + P9_DPRINTK(P9_DEBUG_VFS, + "%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len, + st->extension.str, buffer); -FreeFcall: - kfree(fcall); - -ClunkFid: - v9fs_fid_clunk(v9ses, fid); + retval = buflen; +done: + kfree(st); return retval; } @@ -1084,14 +941,14 @@ static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, if (buflen > PATH_MAX) buflen = PATH_MAX; - dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); + P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); retval = v9fs_readlink(dentry, link, buflen); if (retval > 0) { if ((ret = copy_to_user(buffer, link, retval)) != 0) { - dprintk(DEBUG_ERROR, "problem copying to user: %d\n", - ret); + P9_DPRINTK(P9_DEBUG_ERROR, + "problem copying to user: %d\n", ret); retval = ret; } } @@ -1112,7 +969,7 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) int len = 0; char *link = __getname(); - dprintk(DEBUG_VFS, "%s n", dentry->d_name.name); + P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name); if (!link) link = ERR_PTR(-ENOMEM); @@ -1141,7 +998,7 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void { char *s = nd_get_link(nd); - dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); + P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); if (!IS_ERR(s)) __putname(s); } @@ -1149,66 +1006,24 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, int mode, const char *extension) { - int err; - u32 fid, perm; + u32 perm; struct v9fs_session_info *v9ses; - struct v9fs_fid *dfid, *vfid = NULL; - struct inode *inode = NULL; + struct p9_fid *fid; v9ses = v9fs_inode2v9ses(dir); if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); + P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n"); return -EPERM; } - dfid = v9fs_fid_clone(dentry->d_parent); - if(IS_ERR(dfid)) { - err = PTR_ERR(dfid); - goto error; - } - perm = unixmode2p9mode(v9ses, mode); + fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm, + P9_OREAD); + if (IS_ERR(fid)) + return PTR_ERR(fid); - err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, - perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); - - if (err) - goto clunk_dfid; - - err = v9fs_t_clunk(v9ses, fid); - if (err) - goto clunk_dfid; - - vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); - if (IS_ERR(vfid)) { - err = PTR_ERR(vfid); - vfid = NULL; - goto clunk_dfid; - } - - inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - inode = NULL; - goto free_vfid; - } - - if(v9ses->cache) - dentry->d_op = &v9fs_cached_dentry_operations; - else - dentry->d_op = &v9fs_dentry_operations; - d_instantiate(dentry, inode); + p9_client_clunk(fid); return 0; - -free_vfid: - v9fs_fid_destroy(vfid); - -clunk_dfid: - v9fs_fid_clunk(v9ses, dfid); - -error: - return err; - } /** @@ -1224,8 +1039,8 @@ error: static int v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, - symname); + P9_DPRINTK(P9_DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, + dentry->d_name.name, symname); return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); } @@ -1247,11 +1062,11 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { int retval; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fid *oldfid; + struct p9_fid *oldfid; char *name; - dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, + P9_DPRINTK(P9_DEBUG_VFS, + " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, old_dentry->d_name.name); oldfid = v9fs_fid_clone(old_dentry); @@ -1265,11 +1080,11 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, } sprintf(name, "%d\n", oldfid->fid); - retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); + retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name); __putname(name); clunk_fid: - v9fs_fid_clunk(v9ses, oldfid); + p9_client_clunk(oldfid); return retval; } @@ -1288,7 +1103,8 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) int retval; char *name; - dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, + P9_DPRINTK(P9_DEBUG_VFS, + " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); if (!new_valid_dev(rdev)) diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 7bdf8b3..ba90437 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -37,10 +37,10 @@ #include <linux/mount.h> #include <linux/idr.h> #include <linux/sched.h> +#include <net/9p/9p.h> +#include <net/9p/client.h> -#include "debug.h" #include "v9fs.h" -#include "9p.h" #include "v9fs_vfs.h" #include "fid.h" @@ -107,41 +107,48 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, struct vfsmount *mnt) { struct super_block *sb = NULL; - struct v9fs_fcall *fcall = NULL; struct inode *inode = NULL; struct dentry *root = NULL; struct v9fs_session_info *v9ses = NULL; - struct v9fs_fid *root_fid = NULL; + struct p9_stat *st = NULL; int mode = S_IRWXUGO | S_ISVTX; uid_t uid = current->fsuid; gid_t gid = current->fsgid; - int stat_result = 0; - int newfid = 0; + struct p9_fid *fid; int retval = 0; - dprintk(DEBUG_VFS, " \n"); + P9_DPRINTK(P9_DEBUG_VFS, " \n"); v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); if (!v9ses) return -ENOMEM; - if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { - dprintk(DEBUG_ERROR, "problem initiating session\n"); - retval = newfid; - goto out_free_session; + fid = v9fs_session_init(v9ses, dev_name, data); + if (IS_ERR(fid)) { + retval = PTR_ERR(fid); + fid = NULL; + kfree(v9ses); + v9ses = NULL; + goto error; + } + + st = p9_client_stat(fid); + if (IS_ERR(st)) { + retval = PTR_ERR(st); + goto error; } sb = sget(fs_type, NULL, v9fs_set_super, v9ses); if (IS_ERR(sb)) { retval = PTR_ERR(sb); - goto out_close_session; + goto error; } v9fs_fill_super(sb, v9ses, flags); inode = v9fs_get_inode(sb, S_IFDIR | mode); if (IS_ERR(inode)) { retval = PTR_ERR(inode); - goto put_back_sb; + goto error; } inode->i_uid = uid; @@ -150,54 +157,30 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, root = d_alloc_root(inode); if (!root) { retval = -ENOMEM; - goto put_back_sb; + goto error; } sb->s_root = root; + root->d_inode->i_ino = v9fs_qid2ino(&st->qid); + v9fs_stat2inode(st, root->d_inode, sb); + v9fs_fid_add(root, fid); - stat_result = v9fs_t_stat(v9ses, newfid, &fcall); - if (stat_result < 0) { - dprintk(DEBUG_ERROR, "stat error\n"); - v9fs_t_clunk(v9ses, newfid); - } else { - /* Setup the Root Inode */ - root_fid = v9fs_fid_create(v9ses, newfid); - if (root_fid == NULL) { - retval = -ENOMEM; - goto put_back_sb; - } - - retval = v9fs_fid_insert(root_fid, root); - if (retval < 0) { - kfree(fcall); - goto put_back_sb; - } - - root_fid->qid = fcall->params.rstat.stat.qid; - root->d_inode->i_ino = - v9fs_qid2ino(&fcall->params.rstat.stat.qid); - v9fs_stat2inode(&fcall->params.rstat.stat, root->d_inode, sb); - } + return simple_set_mnt(mnt, sb); - kfree(fcall); +error: + if (fid) + p9_client_clunk(fid); - if (stat_result < 0) { - retval = stat_result; - goto put_back_sb; + if (v9ses) { + v9fs_session_close(v9ses); + kfree(v9ses); } - return simple_set_mnt(mnt, sb); - -out_close_session: - v9fs_session_close(v9ses); -out_free_session: - kfree(v9ses); - return retval; + if (sb) { + up_write(&sb->s_umount); + deactivate_super(sb); + } -put_back_sb: - /* deactivate_super calls v9fs_kill_super which will frees the rest */ - up_write(&sb->s_umount); - deactivate_super(sb); return retval; } @@ -211,7 +194,7 @@ static void v9fs_kill_super(struct super_block *s) { struct v9fs_session_info *v9ses = s->s_fs_info; - dprintk(DEBUG_VFS, " %p\n", s); + P9_DPRINTK(P9_DEBUG_VFS, " %p\n", s); v9fs_dentry_release(s->s_root); /* clunk root */ @@ -219,7 +202,7 @@ static void v9fs_kill_super(struct super_block *s) v9fs_session_close(v9ses); kfree(v9ses); - dprintk(DEBUG_VFS, "exiting kill_super\n"); + P9_DPRINTK(P9_DEBUG_VFS, "exiting kill_super\n"); } /** @@ -234,7 +217,7 @@ static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt) struct v9fs_session_info *v9ses = mnt->mnt_sb->s_fs_info; if (v9ses->debug != 0) - seq_printf(m, ",debug=%u", v9ses->debug); + seq_printf(m, ",debug=%x", v9ses->debug); if (v9ses->port != V9FS_PORT) seq_printf(m, ",port=%u", v9ses->port); if (v9ses->maxdata != 9000) @@ -251,7 +251,7 @@ config JBD2 config JBD2_DEBUG bool "JBD2 (ext4dev/ext4) debugging support" - depends on JBD2 + depends on JBD2 && DEBUG_FS help If you are using the ext4dev/ext4 journaled file system (or potentially any other filesystem/device using JBD2), this option @@ -260,10 +260,10 @@ config JBD2_DEBUG By default, the debugging output will be turned off. If you select Y here, then you will be able to turn on debugging - with "echo N > /proc/sys/fs/jbd2-debug", where N is a number between - 1 and 5. The higher the number, the more debugging output is - generated. To turn debugging off again, do - "echo 0 > /proc/sys/fs/jbd2-debug". + with "echo N > /sys/kernel/debug/jbd2/jbd2-debug", where N is a + number between 1 and 5. The higher the number, the more debugging + output is generated. To turn debugging off again, do + "echo 0 > /sys/kernel/debug/jbd2/jbd2-debug". config FS_MBCACHE # Meta block cache for Extended Attributes (ext2/ext3/ext4) @@ -991,7 +991,7 @@ config TMPFS_POSIX_ACL config HUGETLBFS bool "HugeTLB file system support" - depends on X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN + depends on X86 || IA64 || PPC64 || SPARC64 || (SUPERH && MMU) || BROKEN help hugetlbfs is a filesystem backing for HugeTLB pages, based on ramfs. For architectures that support it, say Y here and read @@ -1700,7 +1700,8 @@ config NFSD_V3_ACL config NFSD_V4 bool "Provide NFSv4 server support (EXPERIMENTAL)" - depends on NFSD_V3 && EXPERIMENTAL + depends on NFSD && NFSD_V3 && EXPERIMENTAL + select RPCSEC_GSS_KRB5 help If you would like to include the NFSv4 server as well as the NFSv2 and NFSv3 servers, say Y here. This feature is experimental, and @@ -2074,7 +2075,7 @@ config AFS_DEBUG config 9P_FS tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" - depends on INET && EXPERIMENTAL + depends on INET && NET_9P && EXPERIMENTAL help If you say Y here, you will get experimental support for Plan 9 resource sharing via the 9P2000 protocol. diff --git a/fs/adfs/super.c b/fs/adfs/super.c index de2ed5c..1c9fd30 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -234,14 +234,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { adfs_inode_cachep = kmem_cache_create("adfs_inode_cache", sizeof(struct adfs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (adfs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/affs/super.c b/fs/affs/super.c index 6d0ebc3..c80191a 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -99,7 +99,7 @@ static int init_inodecache(void) sizeof(struct affs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (affs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 73ce561..a666710 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -8,6 +8,7 @@ kafs-objs := \ cmservice.o \ dir.o \ file.o \ + flock.o \ fsclient.o \ inode.o \ main.o \ diff --git a/fs/afs/afs.h b/fs/afs/afs.h index 2452579..c548aa3 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -37,6 +37,13 @@ typedef enum { AFS_FTYPE_SYMLINK = 3, } afs_file_type_t; +typedef enum { + AFS_LOCK_READ = 0, /* read lock request */ + AFS_LOCK_WRITE = 1, /* write lock request */ +} afs_lock_type_t; + +#define AFS_LOCKWAIT (5 * 60) /* time until a lock times out (seconds) */ + /* * AFS file identifier */ @@ -120,6 +127,7 @@ struct afs_file_status { struct afs_fid parent; /* parent dir ID for non-dirs only */ time_t mtime_client; /* last time client changed data */ time_t mtime_server; /* last time server changed data */ + s32 lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */ }; /* diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h index a18c374..eb64732 100644 --- a/fs/afs/afs_fs.h +++ b/fs/afs/afs_fs.h @@ -31,6 +31,9 @@ enum AFS_FS_Operations { FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */ FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */ FSGETROOTVOLUME = 151, /* AFS Get root volume name */ + FSSETLOCK = 156, /* AFS Request a file lock */ + FSEXTENDLOCK = 157, /* AFS Extend a file lock */ + FSRELEASELOCK = 158, /* AFS Release a file lock */ FSLOOKUP = 161, /* AFS lookup file in directory */ FSFETCHDATA64 = 65537, /* AFS Fetch file data */ FSSTOREDATA64 = 65538, /* AFS Store file data */ diff --git a/fs/afs/callback.c b/fs/afs/callback.c index bacf518..b824394 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -125,6 +125,9 @@ static void afs_break_callback(struct afs_server *server, spin_unlock(&server->cb_lock); queue_work(afs_callback_update_worker, &vnode->cb_broken_work); + if (list_empty(&vnode->granted_locks) && + !list_empty(&vnode->pending_locks)) + afs_lock_may_be_available(vnode); spin_unlock(&vnode->lock); } } diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 546c595..33fe39a 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -44,6 +44,7 @@ const struct file_operations afs_dir_file_operations = { .open = afs_dir_open, .release = afs_release, .readdir = afs_readdir, + .lock = afs_lock, }; const struct inode_operations afs_dir_inode_operations = { diff --git a/fs/afs/file.c b/fs/afs/file.c index aede7eb..525f7c5 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -34,6 +34,8 @@ const struct file_operations afs_file_operations = { .mmap = generic_file_readonly_mmap, .splice_read = generic_file_splice_read, .fsync = afs_fsync, + .lock = afs_lock, + .flock = afs_flock, }; const struct inode_operations afs_file_inode_operations = { diff --git a/fs/afs/flock.c b/fs/afs/flock.c new file mode 100644 index 0000000..4f77f3c --- /dev/null +++ b/fs/afs/flock.c @@ -0,0 +1,559 @@ +/* AFS file locking support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/smp_lock.h> +#include "internal.h" + +#define AFS_LOCK_GRANTED 0 +#define AFS_LOCK_PENDING 1 + +static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl); +static void afs_fl_release_private(struct file_lock *fl); + +static struct workqueue_struct *afs_lock_manager; + +static struct file_lock_operations afs_lock_ops = { + .fl_copy_lock = afs_fl_copy_lock, + .fl_release_private = afs_fl_release_private, +}; + +/* + * initialise the lock manager thread if it isn't already running + */ +static int afs_init_lock_manager(void) +{ + if (!afs_lock_manager) { + afs_lock_manager = create_singlethread_workqueue("kafs_lockd"); + if (!afs_lock_manager) + return -ENOMEM; + } + return 0; +} + +/* + * destroy the lock manager thread if it's running + */ +void __exit afs_kill_lock_manager(void) +{ + if (afs_lock_manager) + destroy_workqueue(afs_lock_manager); +} + +/* + * if the callback is broken on this vnode, then the lock may now be available + */ +void afs_lock_may_be_available(struct afs_vnode *vnode) +{ + _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); + + queue_delayed_work(afs_lock_manager, &vnode->lock_work, 0); +} + +/* + * the lock will time out in 5 minutes unless we extend it, so schedule + * extension in a bit less than that time + */ +static void afs_schedule_lock_extension(struct afs_vnode *vnode) +{ + queue_delayed_work(afs_lock_manager, &vnode->lock_work, + AFS_LOCKWAIT * HZ / 2); +} + +/* + * do work for a lock, including: + * - probing for a lock we're waiting on but didn't get immediately + * - extending a lock that's close to timing out + */ +void afs_lock_work(struct work_struct *work) +{ + struct afs_vnode *vnode = + container_of(work, struct afs_vnode, lock_work.work); + struct file_lock *fl; + afs_lock_type_t type; + struct key *key; + int ret; + + _enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode); + + spin_lock(&vnode->lock); + + if (test_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) { + _debug("unlock"); + spin_unlock(&vnode->lock); + + /* attempt to release the server lock; if it fails, we just + * wait 5 minutes and it'll time out anyway */ + ret = afs_vnode_release_lock(vnode, vnode->unlock_key); + if (ret < 0) + printk(KERN_WARNING "AFS:" + " Failed to release lock on {%x:%x} error %d\n", + vnode->fid.vid, vnode->fid.vnode, ret); + + spin_lock(&vnode->lock); + key_put(vnode->unlock_key); + vnode->unlock_key = NULL; + clear_bit(AFS_VNODE_UNLOCKING, &vnode->flags); + } + + /* if we've got a lock, then it must be time to extend that lock as AFS + * locks time out after 5 minutes */ + if (!list_empty(&vnode->granted_locks)) { + _debug("extend"); + + if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) + BUG(); + fl = list_entry(vnode->granted_locks.next, + struct file_lock, fl_u.afs.link); + key = key_get(fl->fl_file->private_data); + spin_unlock(&vnode->lock); + + ret = afs_vnode_extend_lock(vnode, key); + clear_bit(AFS_VNODE_LOCKING, &vnode->flags); + key_put(key); + switch (ret) { + case 0: + afs_schedule_lock_extension(vnode); + break; + default: + /* ummm... we failed to extend the lock - retry + * extension shortly */ + printk(KERN_WARNING "AFS:" + " Failed to extend lock on {%x:%x} error %d\n", + vnode->fid.vid, vnode->fid.vnode, ret); + queue_delayed_work(afs_lock_manager, &vnode->lock_work, + HZ * 10); + break; + } + _leave(" [extend]"); + return; + } + + /* if we don't have a granted lock, then we must've been called back by + * the server, and so if might be possible to get a lock we're + * currently waiting for */ + if (!list_empty(&vnode->pending_locks)) { + _debug("get"); + + if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) + BUG(); + fl = list_entry(vnode->pending_locks.next, + struct file_lock, fl_u.afs.link); + key = key_get(fl->fl_file->private_data); + type = (fl->fl_type == F_RDLCK) ? + AFS_LOCK_READ : AFS_LOCK_WRITE; + spin_unlock(&vnode->lock); + + ret = afs_vnode_set_lock(vnode, key, type); + clear_bit(AFS_VNODE_LOCKING, &vnode->flags); + switch (ret) { + case -EWOULDBLOCK: + _debug("blocked"); + break; + case 0: + _debug("acquired"); + if (type == AFS_LOCK_READ) + set_bit(AFS_VNODE_READLOCKED, &vnode->flags); + else + set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); + ret = AFS_LOCK_GRANTED; + default: + spin_lock(&vnode->lock); + /* the pending lock may have been withdrawn due to a + * signal */ + if (list_entry(vnode->pending_locks.next, + struct file_lock, fl_u.afs.link) == fl) { + fl->fl_u.afs.state = ret; + if (ret == AFS_LOCK_GRANTED) + list_move_tail(&fl->fl_u.afs.link, + &vnode->granted_locks); + else + list_del_init(&fl->fl_u.afs.link); + wake_up(&fl->fl_wait); + spin_unlock(&vnode->lock); + } else { + _debug("withdrawn"); + clear_bit(AFS_VNODE_READLOCKED, &vnode->flags); + clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); + spin_unlock(&vnode->lock); + afs_vnode_release_lock(vnode, key); + if (!list_empty(&vnode->pending_locks)) + afs_lock_may_be_available(vnode); + } + break; + } + key_put(key); + _leave(" [pend]"); + return; + } + + /* looks like the lock request was withdrawn on a signal */ + spin_unlock(&vnode->lock); + _leave(" [no locks]"); +} + +/* + * pass responsibility for the unlocking of a vnode on the server to the + * manager thread, lest a pending signal in the calling thread interrupt + * AF_RXRPC + * - the caller must hold the vnode lock + */ +static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) +{ + cancel_delayed_work(&vnode->lock_work); + if (!test_and_clear_bit(AFS_VNODE_READLOCKED, &vnode->flags) && + !test_and_clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags)) + BUG(); + if (test_and_set_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) + BUG(); + vnode->unlock_key = key_get(key); + afs_lock_may_be_available(vnode); +} + +/* + * request a lock on a file on the server + */ +static int afs_do_setlk(struct file *file, struct file_lock *fl) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); + afs_lock_type_t type; + struct key *key = file->private_data; + int ret; + + _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); + + /* only whole-file locks are supported */ + if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) + return -EINVAL; + + ret = afs_init_lock_manager(); + if (ret < 0) + return ret; + + fl->fl_ops = &afs_lock_ops; + INIT_LIST_HEAD(&fl->fl_u.afs.link); + fl->fl_u.afs.state = AFS_LOCK_PENDING; + + type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; + + lock_kernel(); + + /* make sure we've got a callback on this file and that our view of the + * data version is up to date */ + ret = afs_vnode_fetch_status(vnode, NULL, key); + if (ret < 0) + goto error; + + if (vnode->status.lock_count != 0 && !(fl->fl_flags & FL_SLEEP)) { + ret = -EAGAIN; + goto error; + } + + spin_lock(&vnode->lock); + + if (list_empty(&vnode->pending_locks)) { + /* if there's no-one else with a lock on this vnode, then we + * need to ask the server for a lock */ + if (list_empty(&vnode->granted_locks)) { + _debug("not locked"); + ASSERTCMP(vnode->flags & + ((1 << AFS_VNODE_LOCKING) | + (1 << AFS_VNODE_READLOCKED) | + (1 << AFS_VNODE_WRITELOCKED)), ==, 0); + list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); + set_bit(AFS_VNODE_LOCKING, &vnode->flags); + spin_unlock(&vnode->lock); + + ret = afs_vnode_set_lock(vnode, key, type); + clear_bit(AFS_VNODE_LOCKING, &vnode->flags); + switch (ret) { + case 0: + goto acquired_server_lock; + case -EWOULDBLOCK: + spin_lock(&vnode->lock); + ASSERT(list_empty(&vnode->granted_locks)); + ASSERTCMP(vnode->pending_locks.next, ==, + &fl->fl_u.afs.link); + goto wait; + default: + spin_lock(&vnode->lock); + list_del_init(&fl->fl_u.afs.link); + spin_unlock(&vnode->lock); + goto error; + } + } + + /* if we've already got a readlock on the server and no waiting + * writelocks, then we might be able to instantly grant another + * readlock */ + if (type == AFS_LOCK_READ && + vnode->flags & (1 << AFS_VNODE_READLOCKED)) { + _debug("instant readlock"); + ASSERTCMP(vnode->flags & + ((1 << AFS_VNODE_LOCKING) | + (1 << AFS_VNODE_WRITELOCKED)), ==, 0); + ASSERT(!list_empty(&vnode->granted_locks)); + goto sharing_existing_lock; + } + } + + /* otherwise, we need to wait for a local lock to become available */ + _debug("wait local"); + list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); +wait: + if (!(fl->fl_flags & FL_SLEEP)) { + _debug("noblock"); + ret = -EAGAIN; + goto abort_attempt; + } + spin_unlock(&vnode->lock); + + /* now we need to sleep and wait for the lock manager thread to get the + * lock from the server */ + _debug("sleep"); + ret = wait_event_interruptible(fl->fl_wait, + fl->fl_u.afs.state <= AFS_LOCK_GRANTED); + if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { + ret = fl->fl_u.afs.state; + if (ret < 0) + goto error; + spin_lock(&vnode->lock); + goto given_lock; + } + + /* we were interrupted, but someone may still be in the throes of + * giving us the lock */ + _debug("intr"); + ASSERTCMP(ret, ==, -ERESTARTSYS); + + spin_lock(&vnode->lock); + if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { + ret = fl->fl_u.afs.state; + if (ret < 0) { + spin_unlock(&vnode->lock); + goto error; + } + goto given_lock; + } + +abort_attempt: + /* we aren't going to get the lock, either because we're unwilling to + * wait, or because some signal happened */ + _debug("abort"); + if (list_empty(&vnode->granted_locks) && + vnode->pending_locks.next == &fl->fl_u.afs.link) { + if (vnode->pending_locks.prev != &fl->fl_u.afs.link) { + /* kick the next pending lock into having a go */ + list_del_init(&fl->fl_u.afs.link); + afs_lock_may_be_available(vnode); + } + } else { + list_del_init(&fl->fl_u.afs.link); + } + spin_unlock(&vnode->lock); + goto error; + +acquired_server_lock: + /* we've acquired a server lock, but it needs to be renewed after 5 + * mins */ + spin_lock(&vnode->lock); + afs_schedule_lock_extension(vnode); + if (type == AFS_LOCK_READ) + set_bit(AFS_VNODE_READLOCKED, &vnode->flags); + else + set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); +sharing_existing_lock: + /* the lock has been granted as far as we're concerned... */ + fl->fl_u.afs.state = AFS_LOCK_GRANTED; + list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); +given_lock: + /* ... but we do still need to get the VFS's blessing */ + ASSERT(!(vnode->flags & (1 << AFS_VNODE_LOCKING))); + ASSERT((vnode->flags & ((1 << AFS_VNODE_READLOCKED) | + (1 << AFS_VNODE_WRITELOCKED))) != 0); + ret = posix_lock_file(file, fl, NULL); + if (ret < 0) + goto vfs_rejected_lock; + spin_unlock(&vnode->lock); + + /* again, make sure we've got a callback on this file and, again, make + * sure that our view of the data version is up to date (we ignore + * errors incurred here and deal with the consequences elsewhere) */ + afs_vnode_fetch_status(vnode, NULL, key); + +error: + unlock_kernel(); + _leave(" = %d", ret); + return ret; + +vfs_rejected_lock: + /* the VFS rejected the lock we just obtained, so we have to discard + * what we just got */ + _debug("vfs refused %d", ret); + list_del_init(&fl->fl_u.afs.link); + if (list_empty(&vnode->granted_locks)) + afs_defer_unlock(vnode, key); + spin_unlock(&vnode->lock); + goto abort_attempt; +} + +/* + * unlock on a file on the server + */ +static int afs_do_unlk(struct file *file, struct file_lock *fl) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); + struct key *key = file->private_data; + int ret; + + _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); + + /* only whole-file unlocks are supported */ + if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) + return -EINVAL; + + fl->fl_ops = &afs_lock_ops; + INIT_LIST_HEAD(&fl->fl_u.afs.link); + fl->fl_u.afs.state = AFS_LOCK_PENDING; + + spin_lock(&vnode->lock); + ret = posix_lock_file(file, fl, NULL); + if (ret < 0) { + spin_unlock(&vnode->lock); + _leave(" = %d [vfs]", ret); + return ret; + } + + /* discard the server lock only if all granted locks are gone */ + if (list_empty(&vnode->granted_locks)) + afs_defer_unlock(vnode, key); + spin_unlock(&vnode->lock); + _leave(" = 0"); + return 0; +} + +/* + * return information about a lock we currently hold, if indeed we hold one + */ +static int afs_do_getlk(struct file *file, struct file_lock *fl) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); + struct key *key = file->private_data; + int ret, lock_count; + + _enter(""); + + fl->fl_type = F_UNLCK; + + mutex_lock(&vnode->vfs_inode.i_mutex); + + /* check local lock records first */ + ret = 0; + posix_test_lock(file, fl); + if (fl->fl_type == F_UNLCK) { + /* no local locks; consult the server */ + ret = afs_vnode_fetch_status(vnode, NULL, key); + if (ret < 0) + goto error; + lock_count = vnode->status.lock_count; + if (lock_count) { + if (lock_count > 0) + fl->fl_type = F_RDLCK; + else + fl->fl_type = F_WRLCK; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + } + } + +error: + mutex_unlock(&vnode->vfs_inode.i_mutex); + _leave(" = %d [%hd]", ret, fl->fl_type); + return ret; +} + +/* + * manage POSIX locks on a file + */ +int afs_lock(struct file *file, int cmd, struct file_lock *fl) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); + + _enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}", + vnode->fid.vid, vnode->fid.vnode, cmd, + fl->fl_type, fl->fl_flags, + (long long) fl->fl_start, (long long) fl->fl_end); + + /* AFS doesn't support mandatory locks */ + if ((vnode->vfs_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && + fl->fl_type != F_UNLCK) + return -ENOLCK; + + if (IS_GETLK(cmd)) + return afs_do_getlk(file, fl); + if (fl->fl_type == F_UNLCK) + return afs_do_unlk(file, fl); + return afs_do_setlk(file, fl); +} + +/* + * manage FLOCK locks on a file + */ +int afs_flock(struct file *file, int cmd, struct file_lock *fl) +{ + struct afs_vnode *vnode = AFS_FS_I(file->f_dentry->d_inode); + + _enter("{%x:%u},%d,{t=%x,fl=%x}", + vnode->fid.vid, vnode->fid.vnode, cmd, + fl->fl_type, fl->fl_flags); + + /* + * No BSD flocks over NFS allowed. + * Note: we could try to fake a POSIX lock request here by + * using ((u32) filp | 0x80000000) or some such as the pid. + * Not sure whether that would be unique, though, or whether + * that would break in other places. + */ + if (!(fl->fl_flags & FL_FLOCK)) + return -ENOLCK; + + /* we're simulating flock() locks using posix locks on the server */ + fl->fl_owner = (fl_owner_t) file; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + + if (fl->fl_type == F_UNLCK) + return afs_do_unlk(file, fl); + return afs_do_setlk(file, fl); +} + +/* + * the POSIX lock management core VFS code copies the lock record and adds the + * copy into its own list, so we need to add that copy to the vnode's lock + * queue in the same place as the original (which will be deleted shortly + * after) + */ +static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) +{ + _enter(""); + + list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); +} + +/* + * need to remove this lock from the vnode queue when it's removed from the + * VFS's list + */ +static void afs_fl_release_private(struct file_lock *fl) +{ + _enter(""); + + list_del_init(&fl->fl_u.afs.link); +} diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 5dff130..023b95b 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -67,7 +67,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, EXTRACT(status->group); bp++; /* sync counter */ data_version |= (u64) ntohl(*bp++) << 32; - bp++; /* lock count */ + EXTRACT(status->lock_count); size |= (u64) ntohl(*bp++) << 32; bp++; /* spare 4 */ *_bp = bp; @@ -1748,3 +1748,156 @@ int afs_fs_get_volume_status(struct afs_server *server, return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); } + +/* + * deliver reply data to an FS.SetLock, FS.ExtendLock or FS.ReleaseLock + */ +static int afs_deliver_fs_xxxx_lock(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + const __be32 *bp; + + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + afs_transfer_reply(call, skb); + if (!last) + return 0; + + if (call->reply_size != call->reply_max) + return -EBADMSG; + + /* unmarshall the reply once we've received all of it */ + bp = call->buffer; + /* xdr_decode_AFSVolSync(&bp, call->replyX); */ + + _leave(" = 0 [done]"); + return 0; +} + +/* + * FS.SetLock operation type + */ +static const struct afs_call_type afs_RXFSSetLock = { + .name = "FS.SetLock", + .deliver = afs_deliver_fs_xxxx_lock, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * FS.ExtendLock operation type + */ +static const struct afs_call_type afs_RXFSExtendLock = { + .name = "FS.ExtendLock", + .deliver = afs_deliver_fs_xxxx_lock, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * FS.ReleaseLock operation type + */ +static const struct afs_call_type afs_RXFSReleaseLock = { + .name = "FS.ReleaseLock", + .deliver = afs_deliver_fs_xxxx_lock, + .abort_to_error = afs_abort_to_error, + .destructor = afs_flat_call_destructor, +}; + +/* + * get a lock on a file + */ +int afs_fs_set_lock(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + afs_lock_type_t type, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; + + _enter(""); + + call = afs_alloc_flat_call(&afs_RXFSSetLock, 5 * 4, 6 * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSSETLOCK); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + *bp++ = htonl(type); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} + +/* + * extend a lock on a file + */ +int afs_fs_extend_lock(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; + + _enter(""); + + call = afs_alloc_flat_call(&afs_RXFSExtendLock, 4 * 4, 6 * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSEXTENDLOCK); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} + +/* + * release a lock on a file + */ +int afs_fs_release_lock(struct afs_server *server, + struct key *key, + struct afs_vnode *vnode, + const struct afs_wait_mode *wait_mode) +{ + struct afs_call *call; + __be32 *bp; + + _enter(""); + + call = afs_alloc_flat_call(&afs_RXFSReleaseLock, 4 * 4, 6 * 4); + if (!call) + return -ENOMEM; + + call->key = key; + call->reply = vnode; + call->service_id = FS_SERVICE; + call->port = htons(AFS_FS_PORT); + + /* marshall the parameters */ + bp = call->request; + *bp++ = htonl(FSRELEASELOCK); + *bp++ = htonl(vnode->fid.vid); + *bp++ = htonl(vnode->fid.vnode); + *bp++ = htonl(vnode->fid.unique); + + return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode); +} diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 2c55dd9..6306438 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -351,10 +351,18 @@ struct afs_vnode { #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ +#define AFS_VNODE_LOCKING 6 /* set if waiting for lock on vnode */ +#define AFS_VNODE_READLOCKED 7 /* set if vnode is read-locked on the server */ +#define AFS_VNODE_WRITELOCKED 8 /* set if vnode is write-locked on the server */ +#define AFS_VNODE_UNLOCKING 9 /* set if vnode is being unlocked on the server */ long acl_order; /* ACL check count (callback break count) */ struct list_head writebacks; /* alterations in pagecache that need writing */ + struct list_head pending_locks; /* locks waiting to be granted */ + struct list_head granted_locks; /* locks granted on this file */ + struct delayed_work lock_work; /* work to be done in locking */ + struct key *unlock_key; /* key to be used in unlocking */ /* outstanding callback notification on this file */ struct rb_node server_rb; /* link in server->fs_vnodes */ @@ -474,6 +482,15 @@ extern int afs_open(struct inode *, struct file *); extern int afs_release(struct inode *, struct file *); /* + * flock.c + */ +extern void __exit afs_kill_lock_manager(void); +extern void afs_lock_work(struct work_struct *); +extern void afs_lock_may_be_available(struct afs_vnode *); +extern int afs_lock(struct file *, int, struct file_lock *); +extern int afs_flock(struct file *, int, struct file_lock *); + +/* * fsclient.c */ extern int afs_fs_fetch_file_status(struct afs_server *, struct key *, @@ -513,6 +530,15 @@ extern int afs_fs_get_volume_status(struct afs_server *, struct key *, struct afs_vnode *, struct afs_volume_status *, const struct afs_wait_mode *); +extern int afs_fs_set_lock(struct afs_server *, struct key *, + struct afs_vnode *, afs_lock_type_t, + const struct afs_wait_mode *); +extern int afs_fs_extend_lock(struct afs_server *, struct key *, + struct afs_vnode *, + const struct afs_wait_mode *); +extern int afs_fs_release_lock(struct afs_server *, struct key *, + struct afs_vnode *, + const struct afs_wait_mode *); /* * inode.c @@ -681,6 +707,10 @@ extern int afs_vnode_store_data(struct afs_writeback *, pgoff_t, pgoff_t, extern int afs_vnode_setattr(struct afs_vnode *, struct key *, struct iattr *); extern int afs_vnode_get_volume_status(struct afs_vnode *, struct key *, struct afs_volume_status *); +extern int afs_vnode_set_lock(struct afs_vnode *, struct key *, + afs_lock_type_t); +extern int afs_vnode_extend_lock(struct afs_vnode *, struct key *); +extern int afs_vnode_release_lock(struct afs_vnode *, struct key *); /* * volume.c diff --git a/fs/afs/main.c b/fs/afs/main.c index cd21195..0f60f6b 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -168,6 +168,7 @@ static void __exit afs_exit(void) printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); afs_fs_exit(); + afs_kill_lock_manager(); afs_close_socket(); afs_purge_servers(); afs_callback_update_kill(); diff --git a/fs/afs/misc.c b/fs/afs/misc.c index d1a889c..2d33a5f 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -35,6 +35,7 @@ int afs_abort_to_error(u32 abort_code) case VOVERQUOTA: return -EDQUOT; case VBUSY: return -EBUSY; case VMOVED: return -ENXIO; + case 0x2f6df0a: return -EWOULDBLOCK; case 0x2f6df0c: return -EACCES; case 0x2f6df0f: return -EBUSY; case 0x2f6df10: return -EEXIST; diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 13df512..6edb566 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -201,23 +201,9 @@ static int afs_proc_cells_open(struct inode *inode, struct file *file) */ static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) { - struct list_head *_p; - loff_t pos = *_pos; - /* lock the list against modification */ down_read(&afs_proc_cells_sem); - - /* allow for the header line */ - if (!pos) - return (void *) 1; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &afs_proc_cells) - if (!pos--) - break; - - return _p != &afs_proc_cells ? _p : NULL; + return seq_list_start_head(&afs_proc_cells, *_pos); } /* @@ -225,14 +211,7 @@ static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) */ static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos) { - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = v == (void *) 1 ? afs_proc_cells.next : _p->next; - - return _p != &afs_proc_cells ? _p : NULL; + return seq_list_next(v, &afs_proc_cells, pos); } /* @@ -250,7 +229,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v) { struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link); - if (v == (void *) 1) { + if (v == &afs_proc_cells) { /* display header on line 1 */ seq_puts(m, "USE NAME\n"); return 0; @@ -503,26 +482,13 @@ static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file) */ static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) { - struct list_head *_p; struct afs_cell *cell = m->private; - loff_t pos = *_pos; _enter("cell=%p pos=%Ld", cell, *_pos); /* lock the list against modification */ down_read(&cell->vl_sem); - - /* allow for the header line */ - if (!pos) - return (void *) 1; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &cell->vl_list) - if (!pos--) - break; - - return _p != &cell->vl_list ? _p : NULL; + return seq_list_start_head(&cell->vl_list, *_pos); } /* @@ -531,17 +497,10 @@ static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, loff_t *_pos) { - struct list_head *_p; struct afs_cell *cell = p->private; _enter("cell=%p pos=%Ld", cell, *_pos); - - (*_pos)++; - - _p = v; - _p = (v == (void *) 1) ? cell->vl_list.next : _p->next; - - return (_p != &cell->vl_list) ? _p : NULL; + return seq_list_next(v, &cell->vl_list, _pos); } /* @@ -569,11 +528,12 @@ const char afs_vlocation_states[][4] = { */ static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) { + struct afs_cell *cell = m->private; struct afs_vlocation *vlocation = list_entry(v, struct afs_vlocation, link); /* display header on line 1 */ - if (v == (void *) 1) { + if (v == &cell->vl_list) { seq_puts(m, "USE STT VLID[0] VLID[1] VLID[2] NAME\n"); return 0; } @@ -734,26 +694,13 @@ static int afs_proc_cell_servers_release(struct inode *inode, static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) __acquires(m->private->servers_lock) { - struct list_head *_p; struct afs_cell *cell = m->private; - loff_t pos = *_pos; _enter("cell=%p pos=%Ld", cell, *_pos); /* lock the list against modification */ read_lock(&cell->servers_lock); - - /* allow for the header line */ - if (!pos) - return (void *) 1; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &cell->servers) - if (!pos--) - break; - - return _p != &cell->servers ? _p : NULL; + return seq_list_start_head(&cell->servers, *_pos); } /* @@ -762,17 +709,10 @@ static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, loff_t *_pos) { - struct list_head *_p; struct afs_cell *cell = p->private; _enter("cell=%p pos=%Ld", cell, *_pos); - - (*_pos)++; - - _p = v; - _p = v == (void *) 1 ? cell->servers.next : _p->next; - - return _p != &cell->servers ? _p : NULL; + return seq_list_next(v, &cell->servers, _pos); } /* @@ -791,11 +731,12 @@ static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) */ static int afs_proc_cell_servers_show(struct seq_file *m, void *v) { + struct afs_cell *cell = m->private; struct afs_server *server = list_entry(v, struct afs_server, link); char ipaddr[20]; /* display header on line 1 */ - if (v == (void *) 1) { + if (v == &cell->servers) { seq_puts(m, "USE ADDR STATE\n"); return 0; } diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 1b36f45..8ccee9e 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -792,6 +792,7 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len) { struct msghdr msg; struct iovec iov[1]; + int n; _enter(""); @@ -806,22 +807,20 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len) msg.msg_flags = 0; call->state = AFS_CALL_AWAIT_ACK; - switch (rxrpc_kernel_send_data(call->rxcall, &msg, len)) { - case 0: + n = rxrpc_kernel_send_data(call->rxcall, &msg, len); + if (n >= 0) { _leave(" [replied]"); return; - - case -ENOMEM: + } + if (n == -ENOMEM) { _debug("oom"); rxrpc_kernel_abort_call(call->rxcall, RX_USER_ABORT); - default: - rxrpc_kernel_end_call(call->rxcall); - call->rxcall = NULL; - call->type->destructor(call); - afs_free_call(call); - _leave(" [error]"); - return; } + rxrpc_kernel_end_call(call->rxcall); + call->rxcall = NULL; + call->type->destructor(call); + afs_free_call(call); + _leave(" [error]"); } /* diff --git a/fs/afs/super.c b/fs/afs/super.c index 2e8496b..b8808b4 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -89,8 +89,7 @@ int __init afs_fs_init(void) sizeof(struct afs_vnode), 0, SLAB_HWCACHE_ALIGN, - afs_i_init_once, - NULL); + afs_i_init_once); if (!afs_inode_cachep) { printk(KERN_NOTICE "kAFS: Failed to allocate inode cache\n"); return ret; @@ -460,6 +459,9 @@ static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, spin_lock_init(&vnode->writeback_lock); spin_lock_init(&vnode->lock); INIT_LIST_HEAD(&vnode->writebacks); + INIT_LIST_HEAD(&vnode->pending_locks); + INIT_LIST_HEAD(&vnode->granted_locks); + INIT_DELAYED_WORK(&vnode->lock_work, afs_lock_work); INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); } diff --git a/fs/afs/vnode.c b/fs/afs/vnode.c index 232c55d..2f05c4f 100644 --- a/fs/afs/vnode.c +++ b/fs/afs/vnode.c @@ -561,7 +561,7 @@ no_server: /* * create a hard link */ -extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, +int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode, struct key *key, const char *name) { struct afs_server *server; @@ -887,11 +887,6 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, vnode->fid.unique, key_serial(key)); - /* this op will fetch the status */ - spin_lock(&vnode->lock); - vnode->update_cnt++; - spin_unlock(&vnode->lock); - do { /* pick a server to query */ server = afs_volume_pick_fileserver(vnode); @@ -905,20 +900,127 @@ int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key, } while (!afs_volume_release_fileserver(vnode, server, ret)); /* adjust the flags */ - if (ret == 0) { - afs_vnode_finalise_status_update(vnode, server); + if (ret == 0) + afs_put_server(server); + + _leave(" = %d", ret); + return ret; + +no_server: + return PTR_ERR(server); +} + +/* + * get a lock on a file + */ +int afs_vnode_set_lock(struct afs_vnode *vnode, struct key *key, + afs_lock_type_t type) +{ + struct afs_server *server; + int ret; + + _enter("%s{%x:%u.%u},%x,%u", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key), type); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_set_lock(server, key, vnode, type, &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) + afs_put_server(server); + + _leave(" = %d", ret); + return ret; + +no_server: + return PTR_ERR(server); +} + +/* + * extend a lock on a file + */ +int afs_vnode_extend_lock(struct afs_vnode *vnode, struct key *key) +{ + struct afs_server *server; + int ret; + + _enter("%s{%x:%u.%u},%x", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key)); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_extend_lock(server, key, vnode, &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) + afs_put_server(server); + + _leave(" = %d", ret); + return ret; + +no_server: + return PTR_ERR(server); +} + +/* + * release a lock on a file + */ +int afs_vnode_release_lock(struct afs_vnode *vnode, struct key *key) +{ + struct afs_server *server; + int ret; + + _enter("%s{%x:%u.%u},%x", + vnode->volume->vlocation->vldb.name, + vnode->fid.vid, + vnode->fid.vnode, + vnode->fid.unique, + key_serial(key)); + + do { + /* pick a server to query */ + server = afs_volume_pick_fileserver(vnode); + if (IS_ERR(server)) + goto no_server; + + _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr)); + + ret = afs_fs_release_lock(server, key, vnode, &afs_sync_call); + + } while (!afs_volume_release_fileserver(vnode, server, ret)); + + /* adjust the flags */ + if (ret == 0) afs_put_server(server); - } else { - afs_vnode_status_update_failed(vnode, ret); - } _leave(" = %d", ret); return ret; no_server: - spin_lock(&vnode->lock); - vnode->update_cnt--; - ASSERTCMP(vnode->update_cnt, >=, 0); - spin_unlock(&vnode->lock); return PTR_ERR(server); } diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 40fe3a3..b4a7588 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -53,7 +53,7 @@ static struct dentry_operations anon_inodefs_dentry_operations = { }; /** - * anon_inode_getfd - creates a new file instance by hooking it up to and + * anon_inode_getfd - creates a new file instance by hooking it up to an * anonymous inode, and a dentry that describe the "class" * of the file * @@ -66,7 +66,7 @@ static struct dentry_operations anon_inodefs_dentry_operations = { * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. - * All the files created with anon_inode_getfd() will share a single inode, by + * All the files created with anon_inode_getfd() will share a single inode, * hence saving memory and avoiding code duplication for the file/inode/dentry * setup. */ @@ -139,11 +139,12 @@ err_put_filp: put_filp(file); return error; } +EXPORT_SYMBOL_GPL(anon_inode_getfd); /* - * A single inode exist for all anon_inode files. Contrary to pipes, - * anon_inode inodes has no per-instance data associated, so we can avoid - * the allocation of multiple of them. + * A single inode exists for all anon_inode files. Contrary to pipes, + * anon_inode inodes have no associated per-instance data, so we need + * only allocate one of them. */ static struct inode *anon_inode_mkinode(void) { @@ -42,7 +42,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) goto error; /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : @@ -52,7 +52,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) /* Check for setting the inode time. */ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) goto error; } fine: diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index a5c5171..a451418 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -414,7 +414,7 @@ befs_read_inode(struct inode *inode) } /* Initialize the inode cache. Called at fs setup. - * + * * Taken from NFS implementation by Al Viro. */ static int @@ -424,7 +424,7 @@ befs_init_inodecache(void) sizeof (struct befs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (befs_inode_cachep == NULL) { printk(KERN_ERR "befs_init_inodecache: " "Couldn't initalize inode slabcache\n"); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 58c7bd9..f346eb1 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -250,14 +250,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&bi->vfs_inode); } - + static int init_inodecache(void) { bfs_inode_cachep = kmem_cache_create("bfs_inode_cache", sizeof(struct bfs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (bfs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 08e4414..4482a06 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -148,6 +148,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, elf_addr_t *elf_info; int ei_index = 0; struct task_struct *tsk = current; + struct vm_area_struct *vma; /* * If this architecture has a platform capability string, copy it @@ -234,6 +235,15 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, sp = (elf_addr_t __user *)bprm->p; #endif + + /* + * Grow the stack manually; some architectures have a limit on how + * far ahead a user-space access may be in order to grow the stack. + */ + vma = find_extend_vma(current->mm, bprm->p); + if (!vma) + return -EFAULT; + /* Now, let's put argc (and argv, envp if appropriate) on the stack */ if (__put_user(argc, sp++)) return -EFAULT; @@ -254,8 +264,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, size_t len; if (__put_user((elf_addr_t)p, argv++)) return -EFAULT; - len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES); - if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) + len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); + if (!len || len > MAX_ARG_STRLEN) return 0; p += len; } @@ -266,8 +276,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, size_t len; if (__put_user((elf_addr_t)p, envp++)) return -EFAULT; - len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES); - if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) + len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); + if (!len || len > MAX_ARG_STRLEN) return 0; p += len; } @@ -777,10 +787,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) } /* OK, This is the point of no return */ - current->mm->start_data = 0; - current->mm->end_data = 0; - current->mm->end_code = 0; - current->mm->mmap = NULL; current->flags &= ~PF_FORKNOEXEC; current->mm->def_flags = def_flags; @@ -988,9 +994,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; - create_elf_tables(bprm, &loc->elf_ex, + retval = create_elf_tables(bprm, &loc->elf_ex, (interpreter_type == INTERPRETER_AOUT), load_addr, interp_load_addr); + if (retval < 0) { + send_sig(SIGKILL, current, 0); + goto out; + } /* N.B. passed_fileno might not be initialized? */ if (interpreter_type == INTERPRETER_AOUT) current->mm->arg_start += strlen(passed_fileno) + 1; @@ -1189,7 +1199,7 @@ static int dump_seek(struct file *file, loff_t off) * * I think we should skip something. But I am not sure how. H.J. */ -static int maydump(struct vm_area_struct *vma) +static int maydump(struct vm_area_struct *vma, unsigned long mm_flags) { /* The vma can be set up to tell us the answer directly. */ if (vma->vm_flags & VM_ALWAYSDUMP) @@ -1199,15 +1209,19 @@ static int maydump(struct vm_area_struct *vma) if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; - /* Dump shared memory only if mapped from an anonymous file. */ - if (vma->vm_flags & VM_SHARED) - return vma->vm_file->f_path.dentry->d_inode->i_nlink == 0; + /* By default, dump shared memory if mapped from an anonymous file. */ + if (vma->vm_flags & VM_SHARED) { + if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0) + return test_bit(MMF_DUMP_ANON_SHARED, &mm_flags); + else + return test_bit(MMF_DUMP_MAPPED_SHARED, &mm_flags); + } - /* If it hasn't been written to, don't write it out */ + /* By default, if it hasn't been written to, don't write it out. */ if (!vma->anon_vma) - return 0; + return test_bit(MMF_DUMP_MAPPED_PRIVATE, &mm_flags); - return 1; + return test_bit(MMF_DUMP_ANON_PRIVATE, &mm_flags); } /* An ELF note in memory */ @@ -1499,6 +1513,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) #endif int thread_status_size = 0; elf_addr_t *auxv; + unsigned long mm_flags; #ifdef ELF_CORE_WRITE_EXTRA_NOTES int extra_notes_size; #endif @@ -1642,6 +1657,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); + /* + * We must use the same mm->flags while dumping core to avoid + * inconsistency between the program headers and bodies, otherwise an + * unusable core file can be generated. + */ + mm_flags = current->mm->flags; + /* Write program headers for segments dump */ for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { @@ -1654,7 +1676,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma) ? sz : 0; + phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0; phdr.p_memsz = sz; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; @@ -1698,7 +1720,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) vma = next_vma(vma, gate_vma)) { unsigned long addr; - if (!maydump(vma)) + if (!maydump(vma, mm_flags)) continue; for (addr = vma->vm_start; diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 9d62fba..2f5d8db 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -621,8 +621,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm, p = (char __user *) current->mm->arg_start; for (loop = bprm->argc; loop > 0; loop--) { __put_user((elf_caddr_t) p, argv++); - len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); - if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) + len = strnlen_user(p, MAX_ARG_STRLEN); + if (!len || len > MAX_ARG_STRLEN) return -EINVAL; p += len; } @@ -633,8 +633,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm, current->mm->env_start = (unsigned long) p; for (loop = bprm->envc; loop > 0; loop--) { __put_user((elf_caddr_t)(unsigned long) p, envp++); - len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); - if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) + len = strnlen_user(p, MAX_ARG_STRLEN); + if (!len || len > MAX_ARG_STRLEN) return -EINVAL; p += len; } @@ -1181,8 +1181,10 @@ static int dump_seek(struct file *file, loff_t off) * * I think we should skip something. But I am not sure how. H.J. */ -static int maydump(struct vm_area_struct *vma) +static int maydump(struct vm_area_struct *vma, unsigned long mm_flags) { + int dump_ok; + /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) { kdcore("%08lx: %08lx: no (IO)", vma->vm_start, vma->vm_flags); @@ -1197,27 +1199,35 @@ static int maydump(struct vm_area_struct *vma) return 0; } - /* Dump shared memory only if mapped from an anonymous file. */ + /* By default, dump shared memory if mapped from an anonymous file. */ if (vma->vm_flags & VM_SHARED) { if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0) { - kdcore("%08lx: %08lx: no (share)", vma->vm_start, vma->vm_flags); - return 1; + dump_ok = test_bit(MMF_DUMP_ANON_SHARED, &mm_flags); + kdcore("%08lx: %08lx: %s (share)", vma->vm_start, + vma->vm_flags, dump_ok ? "yes" : "no"); + return dump_ok; } - kdcore("%08lx: %08lx: no (share)", vma->vm_start, vma->vm_flags); - return 0; + dump_ok = test_bit(MMF_DUMP_MAPPED_SHARED, &mm_flags); + kdcore("%08lx: %08lx: %s (share)", vma->vm_start, + vma->vm_flags, dump_ok ? "yes" : "no"); + return dump_ok; } #ifdef CONFIG_MMU - /* If it hasn't been written to, don't write it out */ + /* By default, if it hasn't been written to, don't write it out */ if (!vma->anon_vma) { - kdcore("%08lx: %08lx: no (!anon)", vma->vm_start, vma->vm_flags); - return 0; + dump_ok = test_bit(MMF_DUMP_MAPPED_PRIVATE, &mm_flags); + kdcore("%08lx: %08lx: %s (!anon)", vma->vm_start, + vma->vm_flags, dump_ok ? "yes" : "no"); + return dump_ok; } #endif - kdcore("%08lx: %08lx: yes", vma->vm_start, vma->vm_flags); - return 1; + dump_ok = test_bit(MMF_DUMP_ANON_PRIVATE, &mm_flags); + kdcore("%08lx: %08lx: %s", vma->vm_start, vma->vm_flags, + dump_ok ? "yes" : "no"); + return dump_ok; } /* An ELF note in memory */ @@ -1456,15 +1466,15 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) * dump the segments for an MMU process */ #ifdef CONFIG_MMU -static int elf_fdpic_dump_segments(struct file *file, struct mm_struct *mm, - size_t *size, unsigned long *limit) +static int elf_fdpic_dump_segments(struct file *file, size_t *size, + unsigned long *limit, unsigned long mm_flags) { struct vm_area_struct *vma; for (vma = current->mm->mmap; vma; vma = vma->vm_next) { unsigned long addr; - if (!maydump(vma)) + if (!maydump(vma, mm_flags)) continue; for (addr = vma->vm_start; @@ -1511,15 +1521,15 @@ end_coredump: * dump the segments for a NOMMU process */ #ifndef CONFIG_MMU -static int elf_fdpic_dump_segments(struct file *file, struct mm_struct *mm, - size_t *size, unsigned long *limit) +static int elf_fdpic_dump_segments(struct file *file, size_t *size, + unsigned long *limit, unsigned long mm_flags) { struct vm_list_struct *vml; for (vml = current->mm->context.vmlist; vml; vml = vml->next) { struct vm_area_struct *vma = vml->vma; - if (!maydump(vma)) + if (!maydump(vma, mm_flags)) continue; if ((*size += PAGE_SIZE) > *limit) @@ -1570,6 +1580,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs, struct vm_list_struct *vml; #endif elf_addr_t *auxv; + unsigned long mm_flags; /* * We no longer stop all VM operations. @@ -1707,6 +1718,13 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs, /* Page-align dumped data */ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); + /* + * We must use the same mm->flags while dumping core to avoid + * inconsistency between the program headers and bodies, otherwise an + * unusable core file can be generated. + */ + mm_flags = current->mm->flags; + /* write program headers for segments dump */ for ( #ifdef CONFIG_MMU @@ -1728,7 +1746,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs, phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma) ? sz : 0; + phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0; phdr.p_memsz = sz; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; @@ -1762,7 +1780,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs, DUMP_SEEK(dataoff); - if (elf_fdpic_dump_segments(file, current->mm, &size, &limit) < 0) + if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0) goto end_coredump; #ifdef ELF_CORE_WRITE_EXTRA_DATA diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 330fd3f..42e94b3 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -126,7 +126,9 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto _ret; if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) { - remove_arg_zero(bprm); + retval = remove_arg_zero(bprm); + if (retval) + goto _ret; } if (fmt->flags & MISC_FMT_OPEN_BINARY) { diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 304c885..4d0e0f6 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -67,7 +67,9 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs) * This is done in reverse order, because of how the * user environment and arguments are stored. */ - remove_arg_zero(bprm); + retval = remove_arg_zero(bprm); + if (retval) + return retval; retval = copy_strings_kernel(1, &bprm->interp, bprm); if (retval < 0) return retval; bprm->argc++; @@ -1187,7 +1187,7 @@ static void __init biovec_init_slabs(void) size = bvs->nr_vecs * sizeof(struct bio_vec); bvs->slab = kmem_cache_create(bvs->name, size, 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); + SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); } } diff --git a/fs/block_dev.c b/fs/block_dev.c index b3e9bfa..2980eab 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -517,7 +517,7 @@ void __init bdev_cache_init(void) bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD|SLAB_PANIC), - init_once, NULL); + init_once); err = register_filesystem(&bd_type); if (err) panic("Cannot register bdev pseudo-fs"); @@ -588,12 +588,10 @@ EXPORT_SYMBOL(bdget); long nr_blockdev_pages(void) { - struct list_head *p; + struct block_device *bdev; long ret = 0; spin_lock(&bdev_lock); - list_for_each(p, &all_bdevs) { - struct block_device *bdev; - bdev = list_entry(p, struct block_device, bd_list); + list_for_each_entry(bdev, &all_bdevs, bd_list) { ret += bdev->bd_inode->i_mapping->nrpages; } spin_unlock(&bdev_lock); @@ -874,7 +872,7 @@ static struct bd_holder *find_bd_holder(struct block_device *bdev, */ static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) { - int ret; + int err; if (!bo) return -EINVAL; @@ -882,15 +880,18 @@ static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) if (!bd_holder_grab_dirs(bdev, bo)) return -EBUSY; - ret = add_symlink(bo->sdir, bo->sdev); - if (ret == 0) { - ret = add_symlink(bo->hdir, bo->hdev); - if (ret) - del_symlink(bo->sdir, bo->sdev); + err = add_symlink(bo->sdir, bo->sdev); + if (err) + return err; + + err = add_symlink(bo->hdir, bo->hdev); + if (err) { + del_symlink(bo->sdir, bo->sdev); + return err; } - if (ret == 0) - list_add_tail(&bo->list, &bdev->bd_holder_list); - return ret; + + list_add_tail(&bo->list, &bdev->bd_holder_list); + return 0; } /** @@ -948,7 +949,7 @@ static struct bd_holder *del_bd_holder(struct block_device *bdev, static int bd_claim_by_kobject(struct block_device *bdev, void *holder, struct kobject *kobj) { - int res; + int err; struct bd_holder *bo, *found; if (!kobj) @@ -959,21 +960,24 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder, return -ENOMEM; mutex_lock(&bdev->bd_mutex); - res = bd_claim(bdev, holder); - if (res == 0) { - found = find_bd_holder(bdev, bo); - if (found == NULL) { - res = add_bd_holder(bdev, bo); - if (res) - bd_release(bdev); - } - } - if (res || found) - free_bd_holder(bo); - mutex_unlock(&bdev->bd_mutex); + err = bd_claim(bdev, holder); + if (err) + goto fail; - return res; + found = find_bd_holder(bdev, bo); + if (found) + goto fail; + + err = add_bd_holder(bdev, bo); + if (err) + bd_release(bdev); + else + bo = NULL; +fail: + mutex_unlock(&bdev->bd_mutex); + free_bd_holder(bo); + return err; } /** @@ -987,15 +991,12 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder, static void bd_release_from_kobject(struct block_device *bdev, struct kobject *kobj) { - struct bd_holder *bo; - if (!kobj) return; mutex_lock(&bdev->bd_mutex); bd_release(bdev); - if ((bo = del_bd_holder(bdev, kobj))) - free_bd_holder(bo); + free_bd_holder(del_bd_holder(bdev, kobj)); mutex_unlock(&bdev->bd_mutex); } diff --git a/fs/buffer.c b/fs/buffer.c index aa68206..0e5ec37 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -356,7 +356,7 @@ static void free_more_memory(void) for_each_online_pgdat(pgdat) { zones = pgdat->node_zonelists[gfp_zone(GFP_NOFS)].zones; if (*zones) - try_to_free_pages(zones, GFP_NOFS); + try_to_free_pages(zones, 0, GFP_NOFS); } } @@ -676,6 +676,39 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) EXPORT_SYMBOL(mark_buffer_dirty_inode); /* + * Mark the page dirty, and set it dirty in the radix tree, and mark the inode + * dirty. + * + * If warn is true, then emit a warning if the page is not uptodate and has + * not been truncated. + */ +static int __set_page_dirty(struct page *page, + struct address_space *mapping, int warn) +{ + if (unlikely(!mapping)) + return !TestSetPageDirty(page); + + if (TestSetPageDirty(page)) + return 0; + + write_lock_irq(&mapping->tree_lock); + if (page->mapping) { /* Race with truncate? */ + WARN_ON_ONCE(warn && !PageUptodate(page)); + + if (mapping_cap_account_dirty(mapping)) { + __inc_zone_page_state(page, NR_FILE_DIRTY); + task_io_account_write(PAGE_CACHE_SIZE); + } + radix_tree_tag_set(&mapping->page_tree, + page_index(page), PAGECACHE_TAG_DIRTY); + } + write_unlock_irq(&mapping->tree_lock); + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + + return 1; +} + +/* * Add a page to the dirty page list. * * It is a sad fact of life that this function is called from several places @@ -702,7 +735,7 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode); */ int __set_page_dirty_buffers(struct page *page) { - struct address_space * const mapping = page_mapping(page); + struct address_space *mapping = page_mapping(page); if (unlikely(!mapping)) return !TestSetPageDirty(page); @@ -719,21 +752,7 @@ int __set_page_dirty_buffers(struct page *page) } spin_unlock(&mapping->private_lock); - if (TestSetPageDirty(page)) - return 0; - - write_lock_irq(&mapping->tree_lock); - if (page->mapping) { /* Race with truncate? */ - if (mapping_cap_account_dirty(mapping)) { - __inc_zone_page_state(page, NR_FILE_DIRTY); - task_io_account_write(PAGE_CACHE_SIZE); - } - radix_tree_tag_set(&mapping->page_tree, - page_index(page), PAGECACHE_TAG_DIRTY); - } - write_unlock_irq(&mapping->tree_lock); - __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); - return 1; + return __set_page_dirty(page, mapping, 1); } EXPORT_SYMBOL(__set_page_dirty_buffers); @@ -982,7 +1001,7 @@ grow_dev_page(struct block_device *bdev, sector_t block, struct buffer_head *bh; page = find_or_create_page(inode->i_mapping, index, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); + (mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE); if (!page) return NULL; @@ -1026,11 +1045,6 @@ failed: /* * Create buffers for the specified block device block's page. If * that page was dirty, the buffers are set dirty also. - * - * Except that's a bug. Attaching dirty buffers to a dirty - * blockdev's page can result in filesystem corruption, because - * some of those buffers may be aliases of filesystem data. - * grow_dev_page() will go BUG() if this happens. */ static int grow_buffers(struct block_device *bdev, sector_t block, int size) @@ -1137,8 +1151,9 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size) */ void fastcall mark_buffer_dirty(struct buffer_head *bh) { + WARN_ON_ONCE(!buffer_uptodate(bh)); if (!buffer_dirty(bh) && !test_set_buffer_dirty(bh)) - __set_page_dirty_nobuffers(bh->b_page); + __set_page_dirty(bh->b_page, page_mapping(bh->b_page), 0); } /* @@ -2179,6 +2194,52 @@ int generic_commit_write(struct file *file, struct page *page, return 0; } +/* + * block_page_mkwrite() is not allowed to change the file size as it gets + * called from a page fault handler when a page is first dirtied. Hence we must + * be careful to check for EOF conditions here. We set the page up correctly + * for a written page which means we get ENOSPC checking when writing into + * holes and correct delalloc and unwritten extent mapping on filesystems that + * support these features. + * + * We are not allowed to take the i_mutex here so we have to play games to + * protect against truncate races as the page could now be beyond EOF. Because + * vmtruncate() writes the inode size before removing pages, once we have the + * page lock we can determine safely if the page is beyond EOF. If it is not + * beyond EOF, then the page is guaranteed safe against truncation until we + * unlock the page. + */ +int +block_page_mkwrite(struct vm_area_struct *vma, struct page *page, + get_block_t get_block) +{ + struct inode *inode = vma->vm_file->f_path.dentry->d_inode; + unsigned long end; + loff_t size; + int ret = -EINVAL; + + lock_page(page); + size = i_size_read(inode); + if ((page->mapping != inode->i_mapping) || + (page_offset(page) > size)) { + /* page got truncated out from underneath us */ + goto out_unlock; + } + + /* page is wholly or partially inside EOF */ + if (((page->index + 1) << PAGE_CACHE_SHIFT) > size) + end = size & ~PAGE_CACHE_MASK; + else + end = PAGE_CACHE_SIZE; + + ret = block_prepare_write(page, 0, end, get_block); + if (!ret) + ret = block_commit_write(page, 0, end); + +out_unlock: + unlock_page(page); + return ret; +} /* * nobh_prepare_write()'s prereads are special: the buffer_heads are freed @@ -2962,6 +3023,7 @@ EXPORT_SYMBOL(__brelse); EXPORT_SYMBOL(__wait_on_buffer); EXPORT_SYMBOL(block_commit_write); EXPORT_SYMBOL(block_prepare_write); +EXPORT_SYMBOL(block_page_mkwrite); EXPORT_SYMBOL(block_read_full_page); EXPORT_SYMBOL(block_sync_page); EXPORT_SYMBOL(block_truncate_page); diff --git a/fs/char_dev.c b/fs/char_dev.c index 164a45c..bbbf07b 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -321,14 +321,13 @@ void unregister_chrdev_region(dev_t from, unsigned count) } } -int unregister_chrdev(unsigned int major, const char *name) +void unregister_chrdev(unsigned int major, const char *name) { struct char_device_struct *cd; cd = __unregister_chrdev_region(major, 0, 256); if (cd && cd->cdev) cdev_del(cd->cdev); kfree(cd); - return 0; } static DEFINE_SPINLOCK(cdev_lock); diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index a9b6bc5..6d84ca2 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,10 @@ +Version 1.50 +------------ +Fix NTLMv2 signing. NFS server mounted over cifs works (if cifs mount is +done with "serverino" mount option). Add support for POSIX Unlink +(helps with certain sharing violation cases when server such as +Samba supports newer POSIX CIFS Protocol Extensions). + Version 1.49 ------------ IPv6 support. Enable ipv6 addresses to be passed on mount (put the ipv6 @@ -8,7 +15,11 @@ when Unix Extensions were ignored). This allows users to override the default uid and gid for files when they are certain that the uids or gids on the server do not match those of the client. Make "sec=none" mount override username (so that null user connection is attempted) -to match what documentation said. +to match what documentation said. Support for very large reads, over 127K, +available to some newer servers (such as Samba 3.0.26 and later but +note that it also requires setting CIFSMaxBufSize at module install +time to a larger value which may hurt performance in some cases). +Make sign option force signing (or fail if server does not support it). Version 1.48 ------------ diff --git a/fs/cifs/README b/fs/cifs/README index 4d01697..85f1eb1 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -301,10 +301,21 @@ A partial list of the supported mount options follows: during the local client kernel build will be used. If server does not support Unicode, this parameter is unused. - rsize default read size (usually 16K) - wsize default write size (usually 16K, 32K is often better over GigE) - maximum wsize currently allowed by CIFS is 57344 (14 4096 byte - pages) + rsize default read size (usually 16K). The client currently + can not use rsize larger than CIFSMaxBufSize. CIFSMaxBufSize + defaults to 16K and may be changed (from 8K to the maximum + kmalloc size allowed by your kernel) at module install time + for cifs.ko. Setting CIFSMaxBufSize to a very large value + will cause cifs to use more memory and may reduce performance + in some cases. To use rsize greater than 127K (the original + cifs protocol maximum) also requires that the server support + a new Unix Capability flag (for very large read) which some + newer servers (e.g. Samba 3.0.26 or later) do. rsize can be + set from a minimum of 2048 to a maximum of 130048 (127K or + CIFSMaxBufSize, whichever is smaller) + wsize default write size (default 57344) + maximum wsize currently allowed by CIFS is 57344 (fourteen + 4096 byte pages) rw mount the network share read-write (note that the server may still consider the share read-only) ro mount network share read-only @@ -359,7 +370,7 @@ A partial list of the supported mount options follows: Note that this does not affect the normal ACL check on the target machine done by the server software (of the server ACL against the user name provided at mount time). - serverino Use servers inode numbers instead of generating automatically + serverino Use server's inode numbers instead of generating automatically incrementing inode numbers on the client. Although this will make it easier to spot hardlinked files (as they will have the same inode numbers) and inode numbers may be persistent, @@ -367,12 +378,11 @@ A partial list of the supported mount options follows: are unique if multiple server side mounts are exported under a single share (since inode numbers on the servers might not be unique if multiple filesystems are mounted under the same - shared higher level directory). Note that this requires that - the server support the CIFS Unix Extensions as other servers - do not return a unique IndexNumber on SMB FindFirst (most - servers return zero as the IndexNumber). Parameter has no - effect to Windows servers and others which do not support the - CIFS Unix Extensions. + shared higher level directory). Note that some older + (e.g. pre-Windows 2000) do not support returning UniqueIDs + or the CIFS Unix Extensions equivalent and for those + this mount option will have no effect. Exporting cifs mounts + under nfsd requires this mount option on the cifs mount. noserverino Client generates inode numbers (rather than using the actual one from the server) by default. setuids If the CIFS Unix extensions are negotiated with the server @@ -582,10 +592,10 @@ the start of smb requests and responses can be enabled via: echo 1 > /proc/fs/cifs/traceSMB -Two other experimental features are under development and to test -require enabling CONFIG_CIFS_EXPERIMENTAL +Two other experimental features are under development. To test these +requires enabling CONFIG_CIFS_EXPERIMENTAL - More efficient write operations + ipv6 enablement DNOTIFY fcntl: needed for support of directory change notification and perhaps later for file leases) diff --git a/fs/cifs/TODO b/fs/cifs/TODO index 78b620e..d7bd515 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -18,9 +18,9 @@ better) d) Kerberos/SPNEGO session setup support - (started) -e) More testing of NTLMv2 authentication (mostly implemented - double check -that NTLMv2 signing works, also need to cleanup now unneeded SessSetup code in -fs/cifs/connect.c) +e) Cleanup now unneeded SessSetup code in +fs/cifs/connect.c and add back in NTLMSSP code if any servers +need it f) MD5-HMAC signing SMB PDUs when SPNEGO style SessionSetup used (Kerberos or NTLMSSP). Signing alreadyimplemented for NTLM @@ -106,6 +106,12 @@ but recognizes them succeed but still return access denied (appears to be Windows server not cifs client problem) and has not been reproduced recently. NTFS partitions do not have this problem. +4) Unix/POSIX capabilities are reset after reconnection, and affect +a few fields in the tree connection but we do do not know which +superblocks to apply these changes to. We should probably walk +the list of superblocks to set these. Also need to check the +flags on the second mount to the same share, and see if we +can do the same trick that NFS does to remount duplicate shares. Misc testing to do ================== diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index 2e75883..f50a88d 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -1,7 +1,7 @@ -/* +/* * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich - * + * * Copyright (c) 2000 RP Internet (www.rpi.net.au). * * This program is free software; you can redistribute it and/or modify @@ -80,7 +80,7 @@ static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; -/* +/* * ASN.1 context. */ struct asn1_ctx { @@ -190,7 +190,7 @@ asn1_header_decode(struct asn1_ctx *ctx, unsigned char **eoc, unsigned int *cls, unsigned int *con, unsigned int *tag) { - unsigned int def = 0; + unsigned int def = 0; unsigned int len = 0; if (!asn1_id_decode(ctx, cls, con, tag)) @@ -331,7 +331,7 @@ static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx, *integer |= ch; } return 1; -} +} static unsigned char asn1_octets_decode(struct asn1_ctx *ctx, @@ -376,7 +376,7 @@ asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid) return 1; } -static int +static int asn1_oid_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned long **oid, unsigned int *len) { @@ -459,7 +459,7 @@ decode_negTokenInit(unsigned char *security_blob, int length, unsigned int cls, con, tag, oidlen, rc; int use_ntlmssp = FALSE; - *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default */ + *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/ /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */ @@ -498,7 +498,8 @@ decode_negTokenInit(unsigned char *security_blob, int length, return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON) || (tag != ASN1_EOC)) { - cFYI(1,("cls = %d con = %d tag = %d end = %p (%d) exit 0", + cFYI(1, + ("cls = %d con = %d tag = %d end = %p (%d) exit 0", cls, con, tag, end, *end)); return 0; } @@ -508,7 +509,8 @@ decode_negTokenInit(unsigned char *security_blob, int length, return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_CON) || (tag != ASN1_SEQ)) { - cFYI(1,("cls = %d con = %d tag = %d end = %p (%d) exit 1", + cFYI(1, + ("cls = %d con = %d tag = %d end = %p (%d) exit 1", cls, con, tag, end, *end)); return 0; } @@ -540,32 +542,34 @@ decode_negTokenInit(unsigned char *security_blob, int length, rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag); if (!rc) { cFYI(1, - ("Error 1 decoding negTokenInit header exit 2")); + ("Error decoding negTokenInit hdr exit2")); return 0; } if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); - if(rc) { + if (rc) { cFYI(1, - ("OID len = %d oid = 0x%lx 0x%lx 0x%lx 0x%lx", - oidlen, *oid, *(oid + 1), *(oid + 2), - *(oid + 3))); - rc = compare_oid(oid, oidlen, NTLMSSP_OID, - NTLMSSP_OID_LEN); + ("OID len = %d oid = 0x%lx 0x%lx " + "0x%lx 0x%lx", + oidlen, *oid, *(oid + 1), + *(oid + 2), *(oid + 3))); + rc = compare_oid(oid, oidlen, + NTLMSSP_OID, NTLMSSP_OID_LEN); kfree(oid); if (rc) use_ntlmssp = TRUE; } } else { - cFYI(1,("This should be an oid what is going on? ")); + cFYI(1, ("Should be an oid what is going on?")); } } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, - ("Error decoding last part of negTokenInit exit 3")); + ("Error decoding last part negTokenInit exit3")); return 0; - } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) { /* tag = 3 indicating mechListMIC */ + } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) { + /* tag = 3 indicating mechListMIC */ cFYI(1, ("Exit 4 cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); @@ -573,7 +577,7 @@ decode_negTokenInit(unsigned char *security_blob, int length, } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, - ("Error decoding last part of negTokenInit exit 5")); + ("Error decoding last part negTokenInit exit5")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_CON) || (tag != ASN1_SEQ)) { @@ -584,7 +588,7 @@ decode_negTokenInit(unsigned char *security_blob, int length, if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, - ("Error decoding last part of negTokenInit exit 7")); + ("Error decoding last part negTokenInit exit 7")); return 0; } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) { cFYI(1, @@ -594,20 +598,21 @@ decode_negTokenInit(unsigned char *security_blob, int length, } if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) { cFYI(1, - ("Error decoding last part of negTokenInit exit 9")); + ("Error decoding last part negTokenInit exit9")); return 0; } else if ((cls != ASN1_UNI) || (con != ASN1_PRI) || (tag != ASN1_GENSTR)) { cFYI(1, - ("Exit 10 cls = %d con = %d tag = %d end = %p (%d)", + ("Exit10 cls = %d con = %d tag = %d end = %p (%d)", cls, con, tag, end, *end)); return 0; } - cFYI(1, ("Need to call asn1_octets_decode() function for this %s", ctx.pointer)); /* is this UTF-8 or ASCII? */ + cFYI(1, ("Need to call asn1_octets_decode() function for %s", + ctx.pointer)); /* is this UTF-8 or ASCII? */ } - /* if (use_kerberos) - *secType = Kerberos + /* if (use_kerberos) + *secType = Kerberos else */ if (use_ntlmssp) { *secType = NTLMSSP; diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 07838b2..1bf8cf5 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -58,7 +58,7 @@ cifs_dump_mem(char *label, void *data, int length) } #ifdef CONFIG_CIFS_DEBUG2 -void cifs_dump_detail(struct smb_hdr * smb) +void cifs_dump_detail(struct smb_hdr *smb) { cERROR(1, ("Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", smb->Command, smb->Status.CifsError, @@ -67,10 +67,10 @@ void cifs_dump_detail(struct smb_hdr * smb) } -void cifs_dump_mids(struct TCP_Server_Info * server) +void cifs_dump_mids(struct TCP_Server_Info *server) { struct list_head *tmp; - struct mid_q_entry * mid_entry; + struct mid_q_entry *mid_entry; if (server == NULL) return; @@ -114,12 +114,12 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, { struct list_head *tmp; struct list_head *tmp1; - struct mid_q_entry * mid_entry; + struct mid_q_entry *mid_entry; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; int i; int length = 0; - char * original_buf = buf; + char *original_buf = buf; *beginBuffer = buf + offset; @@ -145,7 +145,6 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset, (ses->serverNOS == NULL)) { buf += sprintf(buf, "\nentry for %s not fully " "displayed\n\t", ses->serverName); - } else { length = sprintf(buf, @@ -901,90 +900,14 @@ security_flags_write(struct file *file, const char __user *buffer, } /* flags look ok - update the global security flags for cifs module */ extended_security = flags; + if (extended_security & CIFSSEC_MUST_SIGN) { + /* requiring signing implies signing is allowed */ + extended_security |= CIFSSEC_MAY_SIGN; + cFYI(1, ("packet signing now required")); + } else if ((extended_security & CIFSSEC_MAY_SIGN) == 0) { + cFYI(1, ("packet signing disabled")); + } + /* BB should we turn on MAY flags for other MUST options? */ return count; } - -/* static int -ntlmv2_enabled_read(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - - len = sprintf(page, "%d\n", ntlmv2_support); - - len -= off; - *start = page + off; - - if (len > count) - len = count; - else - *eof = 1; - - if (len < 0) - len = 0; - - return len; -} -static int -ntlmv2_enabled_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - char c; - int rc; - - rc = get_user(c, buffer); - if (rc) - return rc; - if (c == '0' || c == 'n' || c == 'N') - ntlmv2_support = 0; - else if (c == '1' || c == 'y' || c == 'Y') - ntlmv2_support = 1; - else if (c == '2') - ntlmv2_support = 2; - - return count; -} - -static int -packet_signing_enabled_read(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - - len = sprintf(page, "%d\n", sign_CIFS_PDUs); - - len -= off; - *start = page + off; - - if (len > count) - len = count; - else - *eof = 1; - - if (len < 0) - len = 0; - - return len; -} -static int -packet_signing_enabled_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - char c; - int rc; - - rc = get_user(c, buffer); - if (rc) - return rc; - if (c == '0' || c == 'n' || c == 'N') - sign_CIFS_PDUs = 0; - else if (c == '1' || c == 'y' || c == 'Y') - sign_CIFS_PDUs = 1; - else if (c == '2') - sign_CIFS_PDUs = 2; - - return count; -} */ - - #endif diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 4cc2012..34af556 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -43,6 +43,6 @@ struct cifs_sb_info { mode_t mnt_dir_mode; int mnt_cifs_flags; int prepathlen; - char * prepath; + char *prepath; }; #endif /* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 701e9a9..b5903b8 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -66,7 +66,7 @@ cifs_strtoUCS(__le16 * to, const char *from, int len, { int charlen; int i; - wchar_t * wchar_to = (wchar_t *)to; /* needed to quiet sparse */ + wchar_t *wchar_to = (wchar_t *)to; /* needed to quiet sparse */ for (i = 0; len && *from; i++, from += charlen, len -= charlen) { diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 39e5b97..614c11f 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -5,20 +5,20 @@ * Convert a unicode character to upper or lower case using * compressed tables. * - * Copyright (c) International Business Machines Corp., 2000,2005555555555555555555555555555555555555555555555555555555 + * Copyright (c) International Business Machines Corp., 2000,2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * @@ -70,7 +70,7 @@ int cifs_strtoUCS(__le16 *, const char *, int, const struct nls_table *); * Address of the first string */ static inline wchar_t * -UniStrcat(wchar_t * ucs1, const wchar_t * ucs2) +UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) { wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ @@ -88,7 +88,7 @@ UniStrcat(wchar_t * ucs1, const wchar_t * ucs2) * or NULL if the character is not in the string */ static inline wchar_t * -UniStrchr(const wchar_t * ucs, wchar_t uc) +UniStrchr(const wchar_t *ucs, wchar_t uc) { while ((*ucs != uc) && *ucs) ucs++; @@ -107,7 +107,7 @@ UniStrchr(const wchar_t * ucs, wchar_t uc) * > 0: First string is greater than second */ static inline int -UniStrcmp(const wchar_t * ucs1, const wchar_t * ucs2) +UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) { while ((*ucs1 == *ucs2) && *ucs1) { ucs1++; @@ -120,7 +120,7 @@ UniStrcmp(const wchar_t * ucs1, const wchar_t * ucs2) * UniStrcpy: Copy a string */ static inline wchar_t * -UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2) +UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) { wchar_t *anchor = ucs1; /* save the start of result string */ @@ -132,7 +132,7 @@ UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2) * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) */ static inline size_t -UniStrlen(const wchar_t * ucs1) +UniStrlen(const wchar_t *ucs1) { int i = 0; @@ -142,10 +142,11 @@ UniStrlen(const wchar_t * ucs1) } /* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a string (length limited) + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) */ static inline size_t -UniStrnlen(const wchar_t * ucs1, int maxlen) +UniStrnlen(const wchar_t *ucs1, int maxlen) { int i = 0; @@ -161,7 +162,7 @@ UniStrnlen(const wchar_t * ucs1, int maxlen) * UniStrncat: Concatenate length limited string */ static inline wchar_t * -UniStrncat(wchar_t * ucs1, const wchar_t * ucs2, size_t n) +UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; /* save pointer to string 1 */ @@ -179,7 +180,7 @@ UniStrncat(wchar_t * ucs1, const wchar_t * ucs2, size_t n) * UniStrncmp: Compare length limited string */ static inline int -UniStrncmp(const wchar_t * ucs1, const wchar_t * ucs2, size_t n) +UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) { if (!n) return 0; /* Null strings are equal */ @@ -194,7 +195,7 @@ UniStrncmp(const wchar_t * ucs1, const wchar_t * ucs2, size_t n) * UniStrncmp_le: Compare length limited string - native to little-endian */ static inline int -UniStrncmp_le(const wchar_t * ucs1, const wchar_t * ucs2, size_t n) +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) { if (!n) return 0; /* Null strings are equal */ @@ -209,7 +210,7 @@ UniStrncmp_le(const wchar_t * ucs1, const wchar_t * ucs2, size_t n) * UniStrncpy: Copy length limited string with pad */ static inline wchar_t * -UniStrncpy(wchar_t * ucs1, const wchar_t * ucs2, size_t n) +UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; @@ -226,7 +227,7 @@ UniStrncpy(wchar_t * ucs1, const wchar_t * ucs2, size_t n) * UniStrncpy_le: Copy length limited string with pad to little-endian */ static inline wchar_t * -UniStrncpy_le(wchar_t * ucs1, const wchar_t * ucs2, size_t n) +UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) { wchar_t *anchor = ucs1; @@ -247,7 +248,7 @@ UniStrncpy_le(wchar_t * ucs1, const wchar_t * ucs2, size_t n) * NULL if no matching string is found */ static inline wchar_t * -UniStrstr(const wchar_t * ucs1, const wchar_t * ucs2) +UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) { const wchar_t *anchor1 = ucs1; const wchar_t *anchor2 = ucs2; @@ -297,7 +298,7 @@ UniToupper(register wchar_t uc) * UniStrupr: Upper case a unicode string */ static inline wchar_t * -UniStrupr(register wchar_t * upin) +UniStrupr(register wchar_t *upin) { register wchar_t *up; @@ -338,7 +339,7 @@ UniTolower(wchar_t uc) * UniStrlwr: Lower case a unicode string */ static inline wchar_t * -UniStrlwr(register wchar_t * upin) +UniStrlwr(register wchar_t *upin) { register wchar_t *up; diff --git a/fs/cifs/cifs_uniupr.h b/fs/cifs/cifs_uniupr.h index da2ad5b..18a9d97 100644 --- a/fs/cifs/cifs_uniupr.h +++ b/fs/cifs/cifs_uniupr.h @@ -3,16 +3,16 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * uniupr.h - Unicode compressed case ranges @@ -53,7 +53,7 @@ signed char CifsUniUpperTable[512] = { 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ }; diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index fdeda51..3627229 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -21,7 +21,7 @@ #include <linux/fs.h> #include "cifspdu.h" -#include "cifsglob.h" +#include "cifsglob.h" #include "cifs_debug.h" #include "md5.h" #include "cifs_unicode.h" @@ -29,54 +29,57 @@ #include <linux/ctype.h> #include <linux/random.h> -/* Calculate and return the CIFS signature based on the mac key and the smb pdu */ +/* Calculate and return the CIFS signature based on the mac key and SMB PDU */ /* the 16 byte signature must be allocated by the caller */ /* Note we only use the 1st eight bytes */ -/* Note that the smb header signature field on input contains the +/* Note that the smb header signature field on input contains the sequence number before this function is called */ extern void mdfour(unsigned char *out, unsigned char *in, int n); extern void E_md4hash(const unsigned char *passwd, unsigned char *p16); extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, - unsigned char *p24); - -static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu, - const char * key, char * signature) + unsigned char *p24); + +static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, + const struct mac_key *key, char *signature) { struct MD5Context context; - if((cifs_pdu == NULL) || (signature == NULL)) + if ((cifs_pdu == NULL) || (signature == NULL) || (key == NULL)) return -EINVAL; MD5Init(&context); - MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16); - MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length); - MD5Final(signature,&context); + MD5Update(&context, (char *)&key->data, key->len); + MD5Update(&context, cifs_pdu->Protocol, cifs_pdu->smb_buf_length); + + MD5Final(signature, &context); return 0; } -int cifs_sign_smb(struct smb_hdr * cifs_pdu, struct TCP_Server_Info * server, - __u32 * pexpected_response_sequence_number) +int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; - if((cifs_pdu == NULL) || (server == NULL)) + if ((cifs_pdu == NULL) || (server == NULL)) return -EINVAL; - if((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) + if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) return rc; spin_lock(&GlobalMid_Lock); - cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(server->sequence_number); + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(server->sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; - + *pexpected_response_sequence_number = server->sequence_number++; server->sequence_number++; spin_unlock(&GlobalMid_Lock); - rc = cifs_calculate_signature(cifs_pdu, server->mac_signing_key,smb_signature); - if(rc) + rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key, + smb_signature); + if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); else memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); @@ -84,115 +87,119 @@ int cifs_sign_smb(struct smb_hdr * cifs_pdu, struct TCP_Server_Info * server, return rc; } -static int cifs_calc_signature2(const struct kvec * iov, int n_vec, - const char * key, char * signature) +static int cifs_calc_signature2(const struct kvec *iov, int n_vec, + const struct mac_key *key, char *signature) { struct MD5Context context; int i; - if((iov == NULL) || (signature == NULL)) + if ((iov == NULL) || (signature == NULL) || (key == NULL)) return -EINVAL; MD5Init(&context); - MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16); - for(i=0;i<n_vec;i++) { - if(iov[i].iov_base == NULL) { - cERROR(1,("null iovec entry")); + MD5Update(&context, (char *)&key->data, key->len); + for (i = 0; i < n_vec; i++) { + if (iov[i].iov_base == NULL) { + cERROR(1, ("null iovec entry")); return -EIO; - } else if(iov[i].iov_len == 0) + } else if (iov[i].iov_len == 0) break; /* bail out if we are sent nothing to sign */ - /* The first entry includes a length field (which does not get + /* The first entry includes a length field (which does not get signed that occupies the first 4 bytes before the header */ - if(i==0) { + if (i == 0) { if (iov[0].iov_len <= 8 ) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ - MD5Update(&context,iov[0].iov_base+4, iov[0].iov_len-4); + MD5Update(&context, iov[0].iov_base+4, + iov[0].iov_len-4); } else - MD5Update(&context,iov[i].iov_base, iov[i].iov_len); + MD5Update(&context, iov[i].iov_base, iov[i].iov_len); } - MD5Final(signature,&context); + MD5Final(signature, &context); return 0; } -int cifs_sign_smb2(struct kvec * iov, int n_vec, struct TCP_Server_Info *server, +int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, __u32 * pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; - struct smb_hdr * cifs_pdu = iov[0].iov_base; + struct smb_hdr *cifs_pdu = iov[0].iov_base; - if((cifs_pdu == NULL) || (server == NULL)) + if ((cifs_pdu == NULL) || (server == NULL)) return -EINVAL; - if((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) + if ((cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) == 0) return rc; - spin_lock(&GlobalMid_Lock); - cifs_pdu->Signature.Sequence.SequenceNumber = + spin_lock(&GlobalMid_Lock); + cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(server->sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; + cifs_pdu->Signature.Sequence.Reserved = 0; - *pexpected_response_sequence_number = server->sequence_number++; - server->sequence_number++; - spin_unlock(&GlobalMid_Lock); + *pexpected_response_sequence_number = server->sequence_number++; + server->sequence_number++; + spin_unlock(&GlobalMid_Lock); - rc = cifs_calc_signature2(iov, n_vec, server->mac_signing_key, + rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key, smb_signature); - if(rc) - memset(cifs_pdu->Signature.SecuritySignature, 0, 8); - else - memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); - - return rc; + if (rc) + memset(cifs_pdu->Signature.SecuritySignature, 0, 8); + else + memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); + return rc; } -int cifs_verify_signature(struct smb_hdr * cifs_pdu, const char * mac_key, - __u32 expected_sequence_number) +int cifs_verify_signature(struct smb_hdr *cifs_pdu, + const struct mac_key *mac_key, + __u32 expected_sequence_number) { unsigned int rc; char server_response_sig[8]; char what_we_think_sig_should_be[20]; - if((cifs_pdu == NULL) || (mac_key == NULL)) + if ((cifs_pdu == NULL) || (mac_key == NULL)) return -EINVAL; if (cifs_pdu->Command == SMB_COM_NEGOTIATE) return 0; if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { - struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)cifs_pdu; - if(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) + struct smb_com_lock_req *pSMB = + (struct smb_com_lock_req *)cifs_pdu; + if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) return 0; } - /* BB what if signatures are supposed to be on for session but server does not - send one? BB */ - + /* BB what if signatures are supposed to be on for session but + server does not send one? BB */ + /* Do not need to verify session setups with signature "BSRSPYL " */ - if(memcmp(cifs_pdu->Signature.SecuritySignature,"BSRSPYL ",8)==0) - cFYI(1,("dummy signature received for smb command 0x%x",cifs_pdu->Command)); + if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) + cFYI(1, ("dummy signature received for smb command 0x%x", + cifs_pdu->Command)); /* save off the origiginal signature so we can modify the smb and check its signature against what the server sent */ - memcpy(server_response_sig,cifs_pdu->Signature.SecuritySignature,8); + memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); - cifs_pdu->Signature.Sequence.SequenceNumber = cpu_to_le32(expected_sequence_number); + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(expected_sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; rc = cifs_calculate_signature(cifs_pdu, mac_key, what_we_think_sig_should_be); - if(rc) + if (rc) return rc; - -/* cifs_dump_mem("what we think it should be: ",what_we_think_sig_should_be,16); */ +/* cifs_dump_mem("what we think it should be: ", + what_we_think_sig_should_be, 16); */ - if(memcmp(server_response_sig, what_we_think_sig_should_be, 8)) + if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) return -EACCES; else return 0; @@ -200,89 +207,94 @@ int cifs_verify_signature(struct smb_hdr * cifs_pdu, const char * mac_key, } /* We fill in key by putting in 40 byte array which was allocated by caller */ -int cifs_calculate_mac_key(char * key, const char * rn, const char * password) +int cifs_calculate_mac_key(struct mac_key *key, const char *rn, + const char *password) { char temp_key[16]; if ((key == NULL) || (rn == NULL)) return -EINVAL; E_md4hash(password, temp_key); - mdfour(key,temp_key,16); - memcpy(key+16,rn, CIFS_SESS_KEY_SIZE); + mdfour(key->data.ntlm, temp_key, 16); + memcpy(key->data.ntlm+16, rn, CIFS_SESS_KEY_SIZE); + key->len = 40; return 0; } -int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, - const struct nls_table * nls_info) +int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses, + const struct nls_table *nls_info) { char temp_hash[16]; struct HMACMD5Context ctx; - char * ucase_buf; - __le16 * unicode_buf; - unsigned int i,user_name_len,dom_name_len; + char *ucase_buf; + __le16 *unicode_buf; + unsigned int i, user_name_len, dom_name_len; - if(ses == NULL) + if (ses == NULL) return -EINVAL; E_md4hash(ses->password, temp_hash); hmac_md5_init_limK_to_64(temp_hash, 16, &ctx); user_name_len = strlen(ses->userName); - if(user_name_len > MAX_USERNAME_SIZE) + if (user_name_len > MAX_USERNAME_SIZE) return -EINVAL; - if(ses->domainName == NULL) + if (ses->domainName == NULL) return -EINVAL; /* BB should we use CIFS_LINUX_DOM */ dom_name_len = strlen(ses->domainName); - if(dom_name_len > MAX_USERNAME_SIZE) + if (dom_name_len > MAX_USERNAME_SIZE) return -EINVAL; - + ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); - if(ucase_buf == NULL) + if (ucase_buf == NULL) return -ENOMEM; unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); - if(unicode_buf == NULL) { + if (unicode_buf == NULL) { kfree(ucase_buf); return -ENOMEM; } - - for(i=0;i<user_name_len;i++) + + for (i = 0; i < user_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->userName[i]]; ucase_buf[i] = 0; - user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + user_name_len = cifs_strtoUCS(unicode_buf, ucase_buf, + MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len] = 0; user_name_len++; - for(i=0;i<dom_name_len;i++) + for (i = 0; i < dom_name_len; i++) ucase_buf[i] = nls_info->charset2upper[(int)ses->domainName[i]]; ucase_buf[i] = 0; - dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, MAX_USERNAME_SIZE*2, nls_info); + dom_name_len = cifs_strtoUCS(unicode_buf+user_name_len, ucase_buf, + MAX_USERNAME_SIZE*2, nls_info); unicode_buf[user_name_len + dom_name_len] = 0; hmac_md5_update((const unsigned char *) unicode_buf, - (user_name_len+dom_name_len)*2,&ctx); + (user_name_len+dom_name_len)*2, &ctx); - hmac_md5_final(ses->server->mac_signing_key,&ctx); + hmac_md5_final(ses->server->ntlmv2_hash, &ctx); kfree(ucase_buf); kfree(unicode_buf); return 0; } #ifdef CONFIG_CIFS_WEAK_PW_HASH -void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key) +void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key) { int i; char password_with_pad[CIFS_ENCPWD_SIZE]; - if(ses->server == NULL) + if (ses->server == NULL) return; memset(password_with_pad, 0, CIFS_ENCPWD_SIZE); - if(ses->password) + if (ses->password) strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE); - if((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0) - if(extended_security & CIFSSEC_MAY_PLNTXT) { - memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); + if ((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0) + if (extended_security & CIFSSEC_MAY_PLNTXT) { + memcpy(lnm_session_key, password_with_pad, + CIFS_ENCPWD_SIZE); return; } @@ -297,7 +309,7 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key) utf8 and other multibyte codepages each need their own strupper function since a byte at a time will ont work. */ - for(i = 0; i < CIFS_ENCPWD_SIZE; i++) { + for (i = 0; i < CIFS_ENCPWD_SIZE; i++) { password_with_pad[i] = toupper(password_with_pad[i]); } @@ -307,19 +319,19 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key) } #endif /* CIFS_WEAK_PW_HASH */ -static int calc_ntlmv2_hash(struct cifsSesInfo *ses, - const struct nls_table * nls_cp) +static int calc_ntlmv2_hash(struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { int rc = 0; int len; char nt_hash[16]; - struct HMACMD5Context * pctxt; - wchar_t * user; - wchar_t * domain; + struct HMACMD5Context *pctxt; + wchar_t *user; + wchar_t *domain; pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL); - if(pctxt == NULL) + if (pctxt == NULL) return -ENOMEM; /* calculate md4 hash of password */ @@ -331,41 +343,45 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses, /* convert ses->userName to unicode and uppercase */ len = strlen(ses->userName); user = kmalloc(2 + (len * 2), GFP_KERNEL); - if(user == NULL) + if (user == NULL) goto calc_exit_2; len = cifs_strtoUCS(user, ses->userName, len, nls_cp); UniStrupr(user); hmac_md5_update((char *)user, 2*len, pctxt); /* convert ses->domainName to unicode and uppercase */ - if(ses->domainName) { + if (ses->domainName) { len = strlen(ses->domainName); - domain = kmalloc(2 + (len * 2), GFP_KERNEL); - if(domain == NULL) + domain = kmalloc(2 + (len * 2), GFP_KERNEL); + if (domain == NULL) goto calc_exit_1; len = cifs_strtoUCS(domain, ses->domainName, len, nls_cp); - UniStrupr(domain); + /* the following line was removed since it didn't work well + with lower cased domain name that passed as an option. + Maybe converting the domain name earlier makes sense */ + /* UniStrupr(domain); */ hmac_md5_update((char *)domain, 2*len, pctxt); - + kfree(domain); } calc_exit_1: kfree(user); calc_exit_2: - /* BB FIXME what about bytes 24 through 40 of the signing key? + /* BB FIXME what about bytes 24 through 40 of the signing key? compare with the NTLM example */ - hmac_md5_final(ses->server->mac_signing_key, pctxt); + hmac_md5_final(ses->server->ntlmv2_hash, pctxt); return rc; } -void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf, - const struct nls_table * nls_cp) +void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, + const struct nls_table *nls_cp) { int rc; - struct ntlmv2_resp * buf = (struct ntlmv2_resp *)resp_buf; + struct ntlmv2_resp *buf = (struct ntlmv2_resp *)resp_buf; + struct HMACMD5Context context; buf->blob_signature = cpu_to_le32(0x00000101); buf->reserved = 0; @@ -379,21 +395,31 @@ void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf, /* calculate buf->ntlmv2_hash */ rc = calc_ntlmv2_hash(ses, nls_cp); - if(rc) - cERROR(1,("could not get v2 hash rc %d",rc)); + if (rc) + cERROR(1, ("could not get v2 hash rc %d", rc)); CalcNTLMv2_response(ses, resp_buf); + + /* now calculate the MAC key for NTLMv2 */ + hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); + hmac_md5_update(resp_buf, 16, &context); + hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context); + + memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf, + sizeof(struct ntlmv2_resp)); + ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp); } -void CalcNTLMv2_response(const struct cifsSesInfo * ses, char * v2_session_response) +void CalcNTLMv2_response(const struct cifsSesInfo *ses, + char *v2_session_response) { struct HMACMD5Context context; /* rest of v2 struct already generated */ - memcpy(v2_session_response + 8, ses->server->cryptKey,8); - hmac_md5_init_limK_to_64(ses->server->mac_signing_key, 16, &context); + memcpy(v2_session_response + 8, ses->server->cryptKey, 8); + hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); - hmac_md5_update(v2_session_response+8, + hmac_md5_update(v2_session_response+8, sizeof(struct ntlmv2_resp) - 8, &context); - hmac_md5_final(v2_session_response,&context); + hmac_md5_final(v2_session_response, &context); /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8b0cbf4..cabb6a5 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -64,23 +64,27 @@ unsigned int multiuser_mount = 0; unsigned int extended_security = CIFSSEC_DEF; /* unsigned int ntlmv2_support = 0; */ unsigned int sign_CIFS_PDUs = 1; -extern struct task_struct * oplockThread; /* remove sparse warning */ -struct task_struct * oplockThread = NULL; +extern struct task_struct *oplockThread; /* remove sparse warning */ +struct task_struct *oplockThread = NULL; /* extern struct task_struct * dnotifyThread; remove sparse warning */ -static struct task_struct * dnotifyThread = NULL; +static struct task_struct *dnotifyThread = NULL; static const struct super_operations cifs_super_ops; unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; module_param(CIFSMaxBufSize, int, 0); -MODULE_PARM_DESC(CIFSMaxBufSize,"Network buffer size (not including header). Default: 16384 Range: 8192 to 130048"); +MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). " + "Default: 16384 Range: 8192 to 130048"); unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; module_param(cifs_min_rcv, int, 0); -MODULE_PARM_DESC(cifs_min_rcv,"Network buffers in pool. Default: 4 Range: 1 to 64"); +MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " + "1 to 64"); unsigned int cifs_min_small = 30; module_param(cifs_min_small, int, 0); -MODULE_PARM_DESC(cifs_min_small,"Small network buffers in pool. Default: 30 Range: 2 to 256"); +MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " + "Range: 2 to 256"); unsigned int cifs_max_pending = CIFS_MAX_REQ; module_param(cifs_max_pending, int, 0); -MODULE_PARM_DESC(cifs_max_pending,"Simultaneous requests to server. Default: 50 Range: 2 to 256"); +MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. " + "Default: 50 Range: 2 to 256"); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; @@ -95,10 +99,10 @@ cifs_read_super(struct super_block *sb, void *data, struct inode *inode; struct cifs_sb_info *cifs_sb; int rc = 0; - + /* BB should we make this contingent on mount parm? */ sb->s_flags |= MS_NODIRATIME | MS_NOATIME; - sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info),GFP_KERNEL); + sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) return -ENOMEM; @@ -114,12 +118,9 @@ cifs_read_super(struct super_block *sb, void *data, sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_op = &cifs_super_ops; -#ifdef CONFIG_CIFS_EXPERIMENTAL - if (experimEnabled != 0) - sb->s_export_op = &cifs_export_ops; -#endif /* EXPERIMENTAL */ /* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512) - sb->s_blocksize = cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */ + sb->s_blocksize = + cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */ #ifdef CONFIG_CIFS_QUOTA sb->s_qcop = &cifs_quotactl_ops; #endif @@ -139,6 +140,13 @@ cifs_read_super(struct super_block *sb, void *data, goto out_no_root; } +#ifdef CONFIG_CIFS_EXPERIMENTAL + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cFYI(1, ("export ops supported")); + sb->s_export_op = &cifs_export_ops; + } +#endif /* EXPERIMENTAL */ + return 0; out_no_root: @@ -149,7 +157,7 @@ out_no_root: out_mount_failed: if (cifs_sb) { if (cifs_sb->local_nls) - unload_nls(cifs_sb->local_nls); + unload_nls(cifs_sb->local_nls); kfree(cifs_sb); } return rc; @@ -164,10 +172,10 @@ cifs_put_super(struct super_block *sb) cFYI(1, ("In cifs_put_super")); cifs_sb = CIFS_SB(sb); if (cifs_sb == NULL) { - cFYI(1,("Empty cifs superblock info passed to unmount")); + cFYI(1, ("Empty cifs superblock info passed to unmount")); return; } - rc = cifs_umount(sb, cifs_sb); + rc = cifs_umount(sb, cifs_sb); if (rc) { cERROR(1, ("cifs_umount failed with return code %d", rc)); } @@ -180,7 +188,7 @@ static int cifs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; - int xid; + int xid; int rc = -EOPNOTSUPP; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -193,7 +201,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = CIFS_MAGIC_NUMBER; /* instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO */ - buf->f_namelen = PATH_MAX; /* PATH_MAX may be too long - it would + buf->f_namelen = PATH_MAX; /* PATH_MAX may be too long - it would presumably be total path, but note that some servers (includinng Samba 3) have a shorter maximum path */ @@ -217,8 +225,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) bypassed it because we detected that this was an older LANMAN sess */ if (rc) rc = SMBOldQFSInfo(xid, pTcon, buf); - /* - int f_type; + /* int f_type; __fsid_t f_fsid; int f_namelen; */ /* BB get from info in tcon struct at mount time call to QFSAttrInfo */ @@ -227,7 +234,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) longer available? */ } -static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd) +static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct cifs_sb_info *cifs_sb; @@ -235,10 +242,10 @@ static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { return 0; - } else /* file mode might have been restricted at mount time - on the client (above and beyond ACL on servers) for + } else /* file mode might have been restricted at mount time + on the client (above and beyond ACL on servers) for servers which do not support setting and viewing mode bits, - so allowing client to check permissions is useful */ + so allowing client to check permissions is useful */ return generic_permission(inode, mask, NULL); } @@ -267,7 +274,7 @@ cifs_alloc_inode(struct super_block *sb) cifs_inode->clientCanCacheRead = FALSE; cifs_inode->clientCanCacheAll = FALSE; cifs_inode->vfs_inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ - + /* Can not set i_flags here - they get immediately overwritten to zero by the VFS */ /* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/ @@ -309,26 +316,26 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) seq_printf(s, ",posixpaths"); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) || - !(cifs_sb->tcon->ses->capabilities & CAP_UNIX)) + !(cifs_sb->tcon->unix_ext)) seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) || - !(cifs_sb->tcon->ses->capabilities & CAP_UNIX)) + !(cifs_sb->tcon->unix_ext)) seq_printf(s, ",gid=%d", cifs_sb->mnt_gid); - seq_printf(s, ",rsize=%d",cifs_sb->rsize); - seq_printf(s, ",wsize=%d",cifs_sb->wsize); + seq_printf(s, ",rsize=%d", cifs_sb->rsize); + seq_printf(s, ",wsize=%d", cifs_sb->wsize); } return 0; } #ifdef CONFIG_CIFS_QUOTA -int cifs_xquota_set(struct super_block * sb, int quota_type, qid_t qid, - struct fs_disk_quota * pdquota) +int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid, + struct fs_disk_quota *pdquota) { int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; - + if (cifs_sb) pTcon = cifs_sb->tcon; else @@ -337,7 +344,7 @@ int cifs_xquota_set(struct super_block * sb, int quota_type, qid_t qid, xid = GetXid(); if (pTcon) { - cFYI(1,("set type: 0x%x id: %d",quota_type,qid)); + cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else { return -EIO; } @@ -346,8 +353,8 @@ int cifs_xquota_set(struct super_block * sb, int quota_type, qid_t qid, return rc; } -int cifs_xquota_get(struct super_block * sb, int quota_type, qid_t qid, - struct fs_disk_quota * pdquota) +int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid, + struct fs_disk_quota *pdquota) { int xid; int rc = 0; @@ -361,7 +368,7 @@ int cifs_xquota_get(struct super_block * sb, int quota_type, qid_t qid, xid = GetXid(); if (pTcon) { - cFYI(1,("set type: 0x%x id: %d",quota_type,qid)); + cFYI(1, ("set type: 0x%x id: %d", quota_type, qid)); } else { rc = -EIO; } @@ -370,9 +377,9 @@ int cifs_xquota_get(struct super_block * sb, int quota_type, qid_t qid, return rc; } -int cifs_xstate_set(struct super_block * sb, unsigned int flags, int operation) +int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation) { - int xid; + int xid; int rc = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifsTconInfo *pTcon; @@ -384,7 +391,7 @@ int cifs_xstate_set(struct super_block * sb, unsigned int flags, int operation) xid = GetXid(); if (pTcon) { - cFYI(1,("flags: 0x%x operation: 0x%x",flags,operation)); + cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation)); } else { rc = -EIO; } @@ -393,7 +400,7 @@ int cifs_xstate_set(struct super_block * sb, unsigned int flags, int operation) return rc; } -int cifs_xstate_get(struct super_block * sb, struct fs_quota_stat *qstats) +int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats) { int xid; int rc = 0; @@ -407,7 +414,7 @@ int cifs_xstate_get(struct super_block * sb, struct fs_quota_stat *qstats) } xid = GetXid(); if (pTcon) { - cFYI(1,("pqstats %p",qstats)); + cFYI(1, ("pqstats %p", qstats)); } else { rc = -EIO; } @@ -424,10 +431,10 @@ static struct quotactl_ops cifs_quotactl_ops = { }; #endif -static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags) +static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) { struct cifs_sb_info *cifs_sb; - struct cifsTconInfo * tcon; + struct cifsTconInfo *tcon; if (!(flags & MNT_FORCE)) return; @@ -445,9 +452,8 @@ static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags) /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_notify_requests(tcon); */ - if (tcon->ses && tcon->ses->server) - { - cFYI(1,("wake up tasks now - umount begin not complete")); + if (tcon->ses && tcon->ses->server) { + cFYI(1, ("wake up tasks now - umount begin not complete")); wake_up_all(&tcon->ses->server->request_q); wake_up_all(&tcon->ses->server->response_q); msleep(1); /* yield */ @@ -480,10 +486,11 @@ static const struct super_operations cifs_super_ops = { .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, -/* .drop_inode = generic_delete_inode, - .delete_inode = cifs_delete_inode, *//* Do not need the above two functions - unless later we add lazy close of inodes or unless the kernel forgets to call - us with the same number of releases (closes) as opens */ +/* .drop_inode = generic_delete_inode, + .delete_inode = cifs_delete_inode, */ /* Do not need above two + functions unless later we add lazy close of inodes or unless the + kernel forgets to call us with the same number of releases (closes) + as opens */ .show_options = cifs_show_options, .umount_begin = cifs_umount_begin, .remount_fs = cifs_remount, @@ -586,11 +593,11 @@ const struct inode_operations cifs_file_inode_ops = { .getxattr = cifs_getxattr, .listxattr = cifs_listxattr, .removexattr = cifs_removexattr, -#endif +#endif }; const struct inode_operations cifs_symlink_inode_ops = { - .readlink = generic_readlink, + .readlink = generic_readlink, .follow_link = cifs_follow_link, .put_link = cifs_put_link, .permission = cifs_permission, @@ -602,7 +609,7 @@ const struct inode_operations cifs_symlink_inode_ops = { .getxattr = cifs_getxattr, .listxattr = cifs_listxattr, .removexattr = cifs_removexattr, -#endif +#endif }; const struct file_operations cifs_file_ops = { @@ -628,7 +635,7 @@ const struct file_operations cifs_file_ops = { }; const struct file_operations cifs_file_direct_ops = { - /* no mmap, no aio, no readv - + /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, @@ -668,7 +675,7 @@ const struct file_operations cifs_file_nobrl_ops = { }; const struct file_operations cifs_file_direct_nobrl_ops = { - /* no mmap, no aio, no readv - + /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ .read = cifs_user_read, .write = cifs_user_write, @@ -693,11 +700,11 @@ const struct file_operations cifs_dir_ops = { #ifdef CONFIG_CIFS_EXPERIMENTAL .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ - .ioctl = cifs_ioctl, + .ioctl = cifs_ioctl, }; static void -cifs_init_once(void *inode, struct kmem_cache * cachep, unsigned long flags) +cifs_init_once(void *inode, struct kmem_cache *cachep, unsigned long flags) { struct cifsInodeInfo *cifsi = inode; @@ -712,7 +719,7 @@ cifs_init_inodecache(void) sizeof (struct cifsInodeInfo), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - cifs_init_once, NULL); + cifs_init_once); if (cifs_inode_cachep == NULL) return -ENOMEM; @@ -741,7 +748,7 @@ cifs_init_request_bufs(void) cifs_req_cachep = kmem_cache_create("cifs_request", CIFSMaxBufSize + MAX_CIFS_HDR_SIZE, 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + SLAB_HWCACHE_ALIGN, NULL); if (cifs_req_cachep == NULL) return -ENOMEM; @@ -749,7 +756,7 @@ cifs_init_request_bufs(void) cifs_min_rcv = 1; else if (cifs_min_rcv > 64) { cifs_min_rcv = 64; - cERROR(1,("cifs_min_rcv set to maximum (64)")); + cERROR(1, ("cifs_min_rcv set to maximum (64)")); } cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, @@ -762,25 +769,25 @@ cifs_init_request_bufs(void) /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and almost all handle based requests (but not write response, nor is it sufficient for path based requests). A smaller size would have - been more efficient (compacting multiple slab items on one 4k page) + been more efficient (compacting multiple slab items on one 4k page) for the case in which debug was on, but this larger size allows more SMBs to use small buffer alloc and is still much more - efficient to alloc 1 per page off the slab compared to 17K (5page) + efficient to alloc 1 per page off the slab compared to 17K (5page) alloc of large cifs buffers even when page debugging is on */ cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq", - MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, + NULL); if (cifs_sm_req_cachep == NULL) { mempool_destroy(cifs_req_poolp); kmem_cache_destroy(cifs_req_cachep); - return -ENOMEM; + return -ENOMEM; } if (cifs_min_small < 2) cifs_min_small = 2; else if (cifs_min_small > 256) { cifs_min_small = 256; - cFYI(1,("cifs_min_small set to maximum (256)")); + cFYI(1, ("cifs_min_small set to maximum (256)")); } cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, @@ -810,7 +817,7 @@ cifs_init_mids(void) { cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", sizeof (struct mid_q_entry), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + SLAB_HWCACHE_ALIGN, NULL); if (cifs_mid_cachep == NULL) return -ENOMEM; @@ -823,7 +830,7 @@ cifs_init_mids(void) cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs", sizeof (struct oplock_q_entry), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + SLAB_HWCACHE_ALIGN, NULL); if (cifs_oplock_cachep == NULL) { mempool_destroy(cifs_mid_poolp); kmem_cache_destroy(cifs_mid_cachep); @@ -841,41 +848,43 @@ cifs_destroy_mids(void) kmem_cache_destroy(cifs_oplock_cachep); } -static int cifs_oplock_thread(void * dummyarg) +static int cifs_oplock_thread(void *dummyarg) { - struct oplock_q_entry * oplock_item; + struct oplock_q_entry *oplock_item; struct cifsTconInfo *pTcon; - struct inode * inode; + struct inode *inode; __u16 netfid; int rc; + set_freezable(); do { - if (try_to_freeze()) + if (try_to_freeze()) continue; - + spin_lock(&GlobalMid_Lock); if (list_empty(&GlobalOplock_Q)) { spin_unlock(&GlobalMid_Lock); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(39*HZ); } else { - oplock_item = list_entry(GlobalOplock_Q.next, + oplock_item = list_entry(GlobalOplock_Q.next, struct oplock_q_entry, qhead); if (oplock_item) { - cFYI(1,("found oplock item to write out")); + cFYI(1, ("found oplock item to write out")); pTcon = oplock_item->tcon; inode = oplock_item->pinode; netfid = oplock_item->netfid; spin_unlock(&GlobalMid_Lock); DeleteOplockQEntry(oplock_item); /* can not grab inode sem here since it would - deadlock when oplock received on delete + deadlock when oplock received on delete since vfs_unlink holds the i_mutex across the call */ /* mutex_lock(&inode->i_mutex);*/ if (S_ISREG(inode->i_mode)) { rc = filemap_fdatawrite(inode->i_mapping); - if (CIFS_I(inode)->clientCanCacheRead == 0) { + if (CIFS_I(inode)->clientCanCacheRead + == 0) { filemap_fdatawait(inode->i_mapping); invalidate_remote_inode(inode); } @@ -884,20 +893,22 @@ static int cifs_oplock_thread(void * dummyarg) /* mutex_unlock(&inode->i_mutex);*/ if (rc) CIFS_I(inode)->write_behind_rc = rc; - cFYI(1,("Oplock flush inode %p rc %d",inode,rc)); - - /* releasing a stale oplock after recent reconnection - of smb session using a now incorrect file - handle is not a data integrity issue but do - not bother sending an oplock release if session - to server still is disconnected since oplock + cFYI(1, ("Oplock flush inode %p rc %d", + inode, rc)); + + /* releasing stale oplock after recent reconnect + of smb session using a now incorrect file + handle is not a data integrity issue but do + not bother sending an oplock release if session + to server still is disconnected since oplock already released by the server in that case */ if (pTcon->tidStatus != CifsNeedReconnect) { rc = CIFSSMBLock(0, pTcon, netfid, - 0 /* len */ , 0 /* offset */, 0, + 0 /* len */ , 0 /* offset */, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, 0 /* wait flag */); - cFYI(1,("Oplock release rc = %d ",rc)); + cFYI(1, + ("Oplock release rc = %d ", rc)); } } else spin_unlock(&GlobalMid_Lock); @@ -909,7 +920,7 @@ static int cifs_oplock_thread(void * dummyarg) return 0; } -static int cifs_dnotify_thread(void * dummyarg) +static int cifs_dnotify_thread(void *dummyarg) { struct list_head *tmp; struct cifsSesInfo *ses; @@ -924,9 +935,9 @@ static int cifs_dnotify_thread(void * dummyarg) to be woken up and wakeq so the thread can wake up and error out */ list_for_each(tmp, &GlobalSMBSessionList) { - ses = list_entry(tmp, struct cifsSesInfo, + ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); - if (ses && ses->server && + if (ses && ses->server && atomic_read(&ses->server->inFlight)) wake_up_all(&ses->server->response_q); } @@ -950,13 +961,13 @@ init_cifs(void) #ifdef CONFIG_CIFS_EXPERIMENTAL INIT_LIST_HEAD(&GlobalDnotifyReqList); INIT_LIST_HEAD(&GlobalDnotifyRsp_Q); -#endif +#endif /* * Initialize Global counters */ atomic_set(&sesInfoAllocCount, 0); atomic_set(&tconInfoAllocCount, 0); - atomic_set(&tcpSesAllocCount,0); + atomic_set(&tcpSesAllocCount, 0); atomic_set(&tcpSesReconnectCount, 0); atomic_set(&tconInfoReconnectCount, 0); @@ -977,10 +988,10 @@ init_cifs(void) if (cifs_max_pending < 2) { cifs_max_pending = 2; - cFYI(1,("cifs_max_pending set to min of 2")); + cFYI(1, ("cifs_max_pending set to min of 2")); } else if (cifs_max_pending > 256) { cifs_max_pending = 256; - cFYI(1,("cifs_max_pending set to max of 256")); + cFYI(1, ("cifs_max_pending set to max of 256")); } rc = cifs_init_inodecache(); @@ -1002,14 +1013,14 @@ init_cifs(void) oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); if (IS_ERR(oplockThread)) { rc = PTR_ERR(oplockThread); - cERROR(1,("error %d create oplock thread", rc)); + cERROR(1, ("error %d create oplock thread", rc)); goto out_unregister_filesystem; } dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); if (IS_ERR(dnotifyThread)) { rc = PTR_ERR(dnotifyThread); - cERROR(1,("error %d create dnotify thread", rc)); + cERROR(1, ("error %d create dnotify thread", rc)); goto out_stop_oplock_thread; } @@ -1035,7 +1046,7 @@ init_cifs(void) static void __exit exit_cifs(void) { - cFYI(0, ("In unregister ie exit_cifs")); + cFYI(0, ("exit_cifs")); #ifdef CONFIG_PROC_FS cifs_proc_clean(); #endif @@ -1048,9 +1059,10 @@ exit_cifs(void) } MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>"); -MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ +MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ MODULE_DESCRIPTION - ("VFS to access servers complying with the SNIA CIFS Specification e.g. Samba and Windows"); + ("VFS to access servers complying with the SNIA CIFS Specification " + "e.g. Samba and Windows"); MODULE_VERSION(CIFS_VERSION); module_init(init_cifs) module_exit(exit_cifs) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index c235d32..a20de77 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CIFSFS_H @@ -43,9 +43,9 @@ extern void cifs_read_inode(struct inode *); /* Functions related to inodes */ extern const struct inode_operations cifs_dir_inode_ops; -extern int cifs_create(struct inode *, struct dentry *, int, +extern int cifs_create(struct inode *, struct dentry *, int, struct nameidata *); -extern struct dentry * cifs_lookup(struct inode *, struct dentry *, +extern struct dentry *cifs_lookup(struct inode *, struct dentry *, struct nameidata *); extern int cifs_unlink(struct inode *, struct dentry *); extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); @@ -63,16 +63,16 @@ extern const struct inode_operations cifs_symlink_inode_ops; /* Functions related to files and directories */ extern const struct file_operations cifs_file_ops; -extern const struct file_operations cifs_file_direct_ops; /* if directio mount */ +extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ extern const struct file_operations cifs_file_nobrl_ops; -extern const struct file_operations cifs_file_direct_nobrl_ops; /* if directio mount */ +extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); extern ssize_t cifs_user_read(struct file *file, char __user *read_data, - size_t read_size, loff_t * poffset); + size_t read_size, loff_t *poffset); extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, - size_t write_size, loff_t * poffset); + size_t write_size, loff_t *poffset); extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, struct dentry *, int); extern int cifs_flush(struct file *, fl_owner_t id); @@ -88,8 +88,9 @@ extern struct dentry_operations cifs_ci_dentry_ops; /* Functions related to symlinks */ extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); -extern void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *); -extern int cifs_readlink(struct dentry *direntry, char __user *buffer, +extern void cifs_put_link(struct dentry *direntry, + struct nameidata *nd, void *); +extern int cifs_readlink(struct dentry *direntry, char __user *buffer, int buflen); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); @@ -98,7 +99,7 @@ extern int cifs_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); -extern int cifs_ioctl (struct inode * inode, struct file * filep, +extern int cifs_ioctl (struct inode *inode, struct file *filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.49" +#define CIFS_VERSION "1.50" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 23655de..b98742f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifsglob.h * - * Copyright (C) International Business Machines Corp., 2002,2006 + * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * Jeremy Allison (jra@samba.org) * @@ -14,7 +14,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. - * + * */ #include <linux/in.h> #include <linux/in6.h> @@ -28,7 +28,7 @@ #define MAX_TREE_SIZE 2 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1 #define MAX_SERVER_SIZE 15 -#define MAX_SHARE_SIZE 64 /* used to be 20 - this should still be enough */ +#define MAX_SHARE_SIZE 64 /* used to be 20, this should still be enough */ #define MAX_USERNAME_SIZE 32 /* 32 is to allow for 15 char names + null termination then *2 for unicode versions */ #define MAX_PASSWORD_SIZE 16 @@ -38,13 +38,13 @@ /* * MAX_REQ is the maximum number of requests that WE will send * on one socket concurently. It also matches the most common - * value of max multiplex returned by servers. We may + * value of max multiplex returned by servers. We may * eventually want to use the negotiated value (in case * future servers can handle more) when we are more confident that * we will not have problems oveloading the socket with pending * write data. */ -#define CIFS_MAX_REQ 50 +#define CIFS_MAX_REQ 50 #define SERVER_NAME_LENGTH 15 #define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) @@ -104,6 +104,17 @@ enum protocolEnum { /* Netbios frames protocol not supported at this time */ }; +struct mac_key { + unsigned int len; + union { + char ntlm[CIFS_SESS_KEY_SIZE + 16]; + struct { + char key[16]; + struct ntlmv2_resp resp; + } ntlmv2; + } data; +}; + /* ***************************************************************** * Except the CIFS PDUs themselves all the @@ -120,13 +131,13 @@ struct TCP_Server_Info { struct sockaddr_in sockAddr; struct sockaddr_in6 sockAddr6; } addr; - wait_queue_head_t response_q; + wait_queue_head_t response_q; wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ struct list_head pending_mid_q; void *Server_NlsInfo; /* BB - placeholder for future NLS info */ unsigned short server_codepage; /* codepage for the server */ unsigned long ip_address; /* IP addr for the server if known */ - enum protocolEnum protocolType; + enum protocolEnum protocolType; char versionMajor; char versionMinor; unsigned svlocal:1; /* local server or remote */ @@ -159,14 +170,15 @@ struct TCP_Server_Info { /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ - char mac_signing_key[CIFS_SESS_KEY_SIZE + 16]; + struct mac_key mac_signing_key; + char ntlmv2_hash[16]; unsigned long lstrp; /* when we got last response from this server */ }; /* * The following is our shortcut to user information. We surface the uid, * and name. We always get the password on the fly in case it - * has changed. We also hang a list of sessions owned by this user off here. + * has changed. We also hang a list of sessions owned by this user off here. */ struct cifsUidInfo { struct list_head userList; @@ -197,11 +209,11 @@ struct cifsSesInfo { int Suid; /* remote smb uid */ uid_t linux_uid; /* local Linux uid */ int capabilities; - char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for + char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ char userName[MAX_USERNAME_SIZE + 1]; - char * domainName; - char * password; + char *domainName; + char *password; }; /* no more than one of the following three session flags may be set */ #define CIFS_SES_NT4 1 @@ -213,7 +225,7 @@ struct cifsSesInfo { #define CIFS_SES_LANMAN 8 /* * there is one of these for each connection to a resource on a particular - * session + * session */ struct cifsTconInfo { struct list_head cifsConnectionList; @@ -269,7 +281,9 @@ struct cifsTconInfo { FILE_SYSTEM_UNIX_INFO fsUnixInfo; unsigned retry:1; unsigned nocase:1; - /* BB add field for back pointer to sb struct? */ + unsigned unix_ext:1; /* if off disable Linux extensions to CIFS protocol + for this mount even if server would support */ + /* BB add field for back pointer to sb struct(s)? */ }; /* @@ -291,9 +305,9 @@ struct cifs_search_info { __u16 entries_in_buffer; __u16 info_level; __u32 resume_key; - char * ntwrk_buf_start; - char * srch_entries_start; - char * presume_name; + char *ntwrk_buf_start; + char *srch_entries_start; + char *presume_name; unsigned int resume_name_len; unsigned endOfSearch:1; unsigned emptyDir:1; @@ -309,15 +323,15 @@ struct cifsFileInfo { __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ - struct file * pfile; /* needed for writepage */ - struct inode * pInode; /* needed for oplock break */ + struct file *pfile; /* needed for writepage */ + struct inode *pInode; /* needed for oplock break */ struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ unsigned closePend:1; /* file is marked to close */ unsigned invalidHandle:1; /* file closed via session abend */ atomic_t wrtPending; /* handle in use - defer close */ struct semaphore fh_sem; /* prevents reopen race after dead ses*/ - char * search_resume_name; /* BB removeme BB */ + char *search_resume_name; /* BB removeme BB */ struct cifs_search_info srch_inf; }; @@ -327,7 +341,7 @@ struct cifsFileInfo { struct cifsInodeInfo { struct list_head lockList; - /* BB add in lists for dirty pages - i.e. write caching info for oplock */ + /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; int write_behind_rc; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ @@ -381,9 +395,9 @@ static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon, } #else -#define cifs_stats_inc(field) do {} while(0) -#define cifs_stats_bytes_written(tcon, bytes) do {} while(0) -#define cifs_stats_bytes_read(tcon, bytes) do {} while(0) +#define cifs_stats_inc(field) do {} while (0) +#define cifs_stats_bytes_written(tcon, bytes) do {} while (0) +#define cifs_stats_bytes_read(tcon, bytes) do {} while (0) #endif @@ -410,8 +424,8 @@ struct mid_q_entry { struct oplock_q_entry { struct list_head qhead; - struct inode * pinode; - struct cifsTconInfo * tcon; + struct inode *pinode; + struct cifsTconInfo *tcon; __u16 netfid; }; @@ -426,7 +440,7 @@ struct dir_notify_req { __u16 netfid; __u32 filter; /* CompletionFilter (for multishot) */ int multishot; - struct file * pfile; + struct file *pfile; }; #define MID_FREE 0 @@ -464,7 +478,7 @@ require use of the stronger protocol */ #define CIFSSEC_MUST_LANMAN 0x10010 #define CIFSSEC_MUST_PLNTXT 0x20020 #define CIFSSEC_MASK 0x37037 /* current flags supported if weak */ -#else +#else #define CIFSSEC_MASK 0x07007 /* flags supported if no weak config */ #endif /* WEAK_PW_HASH */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ @@ -502,7 +516,7 @@ require use of the stronger protocol */ * ---------- * sesSem operations on smb session * tconSem operations on tree connection - * fh_sem file handle reconnection operations + * fh_sem file handle reconnection operations * ****************************************************************************/ @@ -515,7 +529,7 @@ require use of the stronger protocol */ /* * The list of servers that did not respond with NT LM 0.12. * This list helps improve performance and eliminate the messages indicating - * that we had a communications error talking to the server in this list. + * that we had a communications error talking to the server in this list. */ /* Feature not supported */ /* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */ @@ -568,12 +582,12 @@ GLOBAL_EXTERN atomic_t midCount; /* Misc globals */ GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions to be established on existing mount if we - have the uid/password or Kerberos credential + have the uid/password or Kerberos credential or equivalent for current user */ GLOBAL_EXTERN unsigned int oplockEnabled; GLOBAL_EXTERN unsigned int experimEnabled; GLOBAL_EXTERN unsigned int lookupCacheEnabled; -GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent +GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent with more secure ntlmssp2 challenge/resp */ GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index d619ca7..6a2056e 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -144,7 +144,7 @@ #define SMBOPEN_OAPPEND 0x0001 /* - * SMB flag definitions + * SMB flag definitions */ #define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ #define SMBFLG_RCV_POSTED 0x02 /* obsolete */ @@ -157,9 +157,9 @@ #define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ /* - * SMB flag2 definitions + * SMB flag2 definitions */ -#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) +#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3) path names in response */ #define SMBFLG2_KNOWS_EAS cpu_to_le16(2) #define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) @@ -260,7 +260,7 @@ #define ATTR_SPARSE 0x0200 #define ATTR_REPARSE 0x0400 #define ATTR_COMPRESSED 0x0800 -#define ATTR_OFFLINE 0x1000 /* ie file not immediately available - +#define ATTR_OFFLINE 0x1000 /* ie file not immediately available - on offline storage */ #define ATTR_NOT_CONTENT_INDEXED 0x2000 #define ATTR_ENCRYPTED 0x4000 @@ -300,7 +300,7 @@ #define CREATE_DELETE_ON_CLOSE 0x00001000 #define CREATE_OPEN_BY_ID 0x00002000 #define OPEN_REPARSE_POINT 0x00200000 -#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTIONS_MASK 0x007FFFFF #define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ /* ImpersonationLevel flags */ @@ -366,17 +366,19 @@ struct smb_hdr { #define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) + 2 ) /* - * Computer Name Length + * Computer Name Length (since Netbios name was length 16 with last byte 0x20) + * No longer as important, now that TCP names are more commonly used to + * resolve hosts. */ #define CNLEN 15 /* - * Share Name Length @S8A - * Note: This length is limited by the SMB used to get @S8A - * the Share info. NetShareEnum only returns 13 @S8A - * chars, including the null termination. @S8A + * Share Name Length (SNLEN) + * Note: This length was limited by the SMB used to get + * the Share info. NetShareEnum only returned 13 + * chars, including the null termination. + * This was removed because it no longer is limiting. */ -#define SNLEN 12 /*@S8A */ /* * Comment Length @@ -394,8 +396,8 @@ struct smb_hdr { * * The Naming convention is the lower case version of the * smb command code name for the struct and this is typedef to the - * uppercase version of the same name with the prefix SMB_ removed - * for brevity. Although typedefs are not commonly used for + * uppercase version of the same name with the prefix SMB_ removed + * for brevity. Although typedefs are not commonly used for * structure definitions in the Linux kernel, their use in the * CIFS standards document, which this code is based on, may * make this one of the cases where typedefs for structures make @@ -403,7 +405,7 @@ struct smb_hdr { * Typedefs can always be removed later if they are too distracting * and they are only used for the CIFSs PDUs themselves, not * internal cifs vfs structures - * + * */ typedef struct negotiate_req { @@ -511,7 +513,7 @@ typedef union smb_com_session_setup_andx { unsigned char SecurityBlob[1]; /* followed by */ /* STRING NativeOS */ /* STRING NativeLanMan */ - } __attribute__((packed)) req; /* NTLM request format (with + } __attribute__((packed)) req; /* NTLM request format (with extended security */ struct { /* request format */ @@ -549,7 +551,7 @@ typedef union smb_com_session_setup_andx { /* unsigned char * NativeOS; */ /* unsigned char * NativeLanMan; */ /* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) resp; /* NTLM response + } __attribute__((packed)) resp; /* NTLM response (with or without extended sec) */ struct { /* request format */ @@ -618,7 +620,7 @@ struct ntlmv2_resp { #define CAP_NT_SMBS 0x00000010 #define CAP_STATUS32 0x00000040 #define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_NT_FIND 0x00000200 /* reserved should be zero +#define CAP_NT_FIND 0x00000200 /* reserved should be zero (because NT_SMBs implies the same thing?) */ #define CAP_BULK_TRANSFER 0x20000000 #define CAP_EXTENDED_SECURITY 0x80000000 @@ -676,7 +678,7 @@ typedef struct smb_com_logoff_andx_rsp { __u16 ByteCount; } __attribute__((packed)) LOGOFF_ANDX_RSP; -typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on +typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on tree_connect PDU to effect disconnect */ /* tdis is probably simplest SMB PDU */ struct { @@ -712,6 +714,7 @@ typedef struct smb_com_findclose_req { #define REQ_OPLOCK 0x00000002 #define REQ_BATCHOPLOCK 0x00000004 #define REQ_OPENDIRONLY 0x00000008 +#define REQ_EXTENDED_INFO 0x00000010 typedef struct smb_com_open_req { /* also handles create */ struct smb_hdr hdr; /* wct = 24 */ @@ -799,27 +802,28 @@ typedef struct smb_com_openx_rsp { __u32 FileId; __u16 Reserved; __u16 ByteCount; -} __attribute__((packed)) OPENX_RSP; +} __attribute__((packed)) OPENX_RSP; /* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ /* Legacy write request for older servers */ typedef struct smb_com_writex_req { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; /* Timeout */ - __le16 WriteMode; /* 1 = write through */ - __le16 Remaining; - __le16 Reserved2; - __le16 DataLengthLow; - __le16 DataOffset; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ - char Data[0]; + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; /* Timeout */ + __le16 WriteMode; /* 1 = write through */ + __le16 Remaining; + __le16 Reserved2; + __le16 DataLengthLow; + __le16 DataOffset; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[0]; } __attribute__((packed)) WRITEX_REQ; typedef struct smb_com_write_req { @@ -837,7 +841,8 @@ typedef struct smb_com_write_req { __le16 DataOffset; __le32 OffsetHigh; __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ char Data[0]; } __attribute__((packed)) WRITE_REQ; @@ -855,17 +860,17 @@ typedef struct smb_com_write_rsp { /* legacy read request for older servers */ typedef struct smb_com_readx_req { - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 Reserved; - __le16 Remaining; - __le16 ByteCount; + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 Reserved; + __le16 Remaining; + __le16 ByteCount; } __attribute__((packed)) READX_REQ; typedef struct smb_com_read_req { @@ -896,7 +901,8 @@ typedef struct smb_com_read_rsp { __le16 DataLengthHigh; __u64 Reserved2; __u16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD boundary and optimum performance here */ + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ char Data[1]; } __attribute__((packed)) READ_RSP; @@ -967,7 +973,7 @@ typedef struct smb_com_rename_req { #define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ #define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ #define COPY_VERIFY_WRITES 0x0010 -#define COPY_TREE 0x0020 +#define COPY_TREE 0x0020 typedef struct smb_com_copy_req { struct smb_hdr hdr; /* wct = 3 */ @@ -975,7 +981,7 @@ typedef struct smb_com_copy_req { __le16 OpenFunction; __le16 Flags; __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ + __u8 BufferFormat; /* 4 = ASCII or Unicode */ unsigned char OldFileName[1]; /* followed by __u8 BufferFormat2 */ /* followed by NewFileName string */ @@ -1083,28 +1089,28 @@ typedef struct smb_com_setattr_rsp { /*******************************************************/ /* NT Transact structure defintions follow */ -/* Currently only ioctl, acl (get security descriptor) */ +/* Currently only ioctl, acl (get security descriptor) */ /* and notify are implemented */ /*******************************************************/ typedef struct smb_com_ntransact_req { - struct smb_hdr hdr; /* wct >= 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - /* SetupCount words follow then */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Parms[0]; + struct smb_hdr hdr; /* wct >= 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + /* SetupCount words follow then */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Parms[0]; } __attribute__((packed)) NTRANSACT_REQ; typedef struct smb_com_ntransact_rsp { @@ -1120,7 +1126,7 @@ typedef struct smb_com_ntransact_rsp { __le32 DataDisplacement; __u8 SetupCount; /* 0 */ __u16 ByteCount; - /* __u8 Pad[3]; */ + /* __u8 Pad[3]; */ /* parms and data follow */ } __attribute__((packed)) NTRANSACT_RSP; @@ -1215,7 +1221,7 @@ typedef struct smb_com_transaction_change_notify_req { /* __u8 Data[1];*/ } __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ; -/* BB eventually change to use generic ntransact rsp struct +/* BB eventually change to use generic ntransact rsp struct and validation routine */ typedef struct smb_com_transaction_change_notify_rsp { struct smb_hdr hdr; /* wct = 18 */ @@ -1262,7 +1268,7 @@ struct file_notify_information { __le32 Action; __le32 FileNameLength; __u8 FileName[0]; -} __attribute__((packed)); +} __attribute__((packed)); struct reparse_data { __u32 ReparseTag; @@ -1331,7 +1337,7 @@ struct trans2_resp { __u8 Reserved1; /* SetupWords[SetupCount]; __u16 ByteCount; - __u16 Reserved2;*/ + __u16 Reserved2;*/ /* data area follows */ } __attribute__((packed)); @@ -1370,9 +1376,9 @@ struct smb_t2_rsp { #define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee #define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 #define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ -#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 #define SMB_QUERY_FILE_MODE_INFO 0x3f8 -#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 #define SMB_SET_FILE_BASIC_INFO 0x101 @@ -1506,35 +1512,35 @@ struct smb_com_transaction2_sfi_req { __u16 Pad1; __u16 Fid; __le16 InformationLevel; - __u16 Reserved4; + __u16 Reserved4; } __attribute__((packed)); struct smb_com_transaction2_sfi_rsp { struct smb_hdr hdr; /* wct = 10 + SetupCount */ struct trans2_resp t2; __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - + __u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */ } __attribute__((packed)); struct smb_t2_qfi_req { - struct smb_hdr hdr; - struct trans2_req t2; + struct smb_hdr hdr; + struct trans2_req t2; __u8 Pad; __u16 Fid; __le16 InformationLevel; } __attribute__((packed)); struct smb_t2_qfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - - present for infolevels > 100 */ + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - + present for infolevels > 100 */ } __attribute__((packed)); /* - * Flags on T2 FINDFIRST and FINDNEXT + * Flags on T2 FINDFIRST and FINDNEXT */ #define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 #define CIFS_SEARCH_CLOSE_AT_END 0x0002 @@ -1743,7 +1749,9 @@ typedef struct smb_com_transaction2_get_dfs_refer_req { __u8 Reserved3; __le16 SubCommand; /* one setup word */ __le16 ByteCount; - __u8 Pad[3]; /* Win2K has sent 0x0F01 (max resp length perhaps?) followed by one byte pad - doesn't seem to matter though */ + __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length + perhaps?) followed by one byte pad - doesn't + seem to matter though */ __le16 MaxReferralLevel; char RequestFileName[1]; } __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ; @@ -1752,7 +1760,10 @@ typedef struct dfs_referral_level_3 { __le16 VersionNumber; __le16 ReferralSize; __le16 ServerType; /* 0x0001 = CIFS server */ - __le16 ReferralFlags; /* or proximity - not clear which since always set to zero - SNIA spec says 0x01 means strip off PathConsumed chars before submitting RequestFileName to remote node */ + __le16 ReferralFlags; /* or proximity - not clear which since it is + always set to zero - SNIA spec says 0x01 + means strip off PathConsumed chars before + submitting RequestFileName to remote node */ __le16 TimeToLive; __le16 Proximity; __le16 DfsPathOffset; @@ -1778,11 +1789,13 @@ typedef struct smb_com_transaction_get_dfs_refer_rsp { #define DFSREF_STORAGE_SERVER 0x0002 /* IOCTL information */ -/* List of ioctl function codes that look to be of interest to remote clients like this. */ -/* Need to do some experimentation to make sure they all work remotely. */ -/* Some of the following such as the encryption/compression ones would be */ -/* invoked from tools via a specialized hook into the VFS rather than via the */ -/* standard vfs entry points */ +/* + * List of ioctl function codes that look to be of interest to remote clients + * like this one. Need to do some experimentation to make sure they all work + * remotely. Some of the following, such as the encryption/compression ones + * would be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ #define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 #define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 #define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 @@ -1811,7 +1824,7 @@ typedef struct smb_com_transaction_get_dfs_refer_rsp { /* ************************************************************************ * All structs for everything above the SMB PDUs themselves - * (such as the T2 level specific data) go here + * (such as the T2 level specific data) go here ************************************************************************ */ @@ -1857,7 +1870,7 @@ typedef struct { __le64 FreeAllocationUnits; __le32 SectorsPerAllocationUnit; __le32 BytesPerSector; -} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */ +} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */ typedef struct { __le32 fsid; @@ -1871,7 +1884,7 @@ typedef struct { __le16 MajorVersionNumber; __le16 MinorVersionNumber; __le64 Capability; -} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extensions info, level 0x200 */ +} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/ /* Version numbers for CIFS UNIX major and minor. */ #define CIFS_UNIX_MAJOR_VERSION 1 @@ -1885,16 +1898,20 @@ typedef struct { #define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ #define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based calls including posix open - and posix unlink */ + and posix unlink */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up + to 0xFFFF00 */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 + #ifdef CONFIG_CIFS_POSIX /* Can not set pathnames cap yet until we send new posix create SMB since otherwise server can treat such handles opened with older ntcreatex (by a new client which knows how to send posix path ops) as non-posix handles (can affect write behavior with byte range locks. We can add back in POSIX_PATH_OPS cap when Posix Create/Mkdir finished */ -/* #define CIFS_UNIX_CAP_MASK 0x0000003b */ -#define CIFS_UNIX_CAP_MASK 0x0000001b -#else +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000000db +#else #define CIFS_UNIX_CAP_MASK 0x00000013 #endif /* CONFIG_CIFS_POSIX */ @@ -1904,10 +1921,10 @@ typedef struct { typedef struct { /* For undefined recommended transfer size return -1 in that field */ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ - __le32 BlockSize; + __le32 BlockSize; /* The next three fields are in terms of the block size. (above). If block size is unknown, 4096 would be a - reasonable block size for a server to report. + reasonable block size for a server to report. Note that returning the blocks/blocksavail removes need to make a second call (to QFSInfo level 0x103 to get this info. UserBlockAvail is typically less than or equal to BlocksAvail, @@ -2062,9 +2079,9 @@ struct file_alt_name_info { struct file_stream_info { __le32 number_of_streams; /* BB check sizes and verify location */ - /* followed by info on streams themselves + /* followed by info on streams themselves u64 size; - u64 allocation_size + u64 allocation_size stream info */ }; /* level 0x109 */ @@ -2083,7 +2100,7 @@ struct cifs_posix_ace { /* access control entry (ACE) */ __u8 cifs_e_tag; __u8 cifs_e_perm; __le64 cifs_uid; /* or gid */ -} __attribute__((packed)); +} __attribute__((packed)); struct cifs_posix_acl { /* access conrol list (ACL) */ __le16 version; @@ -2138,6 +2155,12 @@ typedef struct { /* struct following varies based on requested level */ } __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ +#define SMB_POSIX_UNLINK_FILE_TARGET 0 +#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 + +struct unlink_psx_rq { /* level 0x20a SetPathInfo */ + __le16 type; +} __attribute__((packed)); struct file_internal_info { __u64 UniqueId; /* inode number */ @@ -2154,7 +2177,7 @@ struct file_attrib_tag { /********************************************************/ -/* FindFirst/FindNext transact2 data buffer formats */ +/* FindFirst/FindNext transact2 data buffer formats */ /********************************************************/ typedef struct { @@ -2232,7 +2255,7 @@ typedef struct { __le64 EndOfFile; __le64 AllocationSize; __le32 ExtFileAttributes; - __le32 FileNameLength; + __le32 FileNameLength; __le32 EaSize; /* length of the xattrs */ __u8 ShortNameLength; __u8 Reserved; @@ -2259,7 +2282,7 @@ typedef struct { struct win_dev { unsigned char type[8]; /* IntxCHR or IntxBLK */ __le64 major; - __le64 minor; + __le64 minor; } __attribute__((packed)); struct gea { @@ -2291,36 +2314,36 @@ struct fealist { struct data_blob { __u8 *data; size_t length; - void (*free) (struct data_blob * data_blob); + void (*free) (struct data_blob *data_blob); } __attribute__((packed)); #ifdef CONFIG_CIFS_POSIX -/* +/* For better POSIX semantics from Linux client, (even better than the existing CIFS Unix Extensions) we need updated PDUs for: - + 1) PosixCreateX - to set and return the mode, inode#, device info and perhaps add a CreateDevice - to create Pipes and other special .inodes Also note POSIX open flags - 2) Close - to return the last write time to do cache across close + 2) Close - to return the last write time to do cache across close more safely - 3) FindFirst return unique inode number - what about resume key, two + 3) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes) 4) Mkdir - set mode - - And under consideration: + + And under consideration: 5) FindClose2 (return nanosecond timestamp ??) - 6) Use nanosecond timestamps throughout all time fields if + 6) Use nanosecond timestamps throughout all time fields if corresponding attribute flag is set 7) sendfile - handle based copy 8) Direct i/o 9) Misc fcntls? - + what about fixing 64 bit alignment - + There are also various legacy SMB/CIFS requests used as is - + From existing Lanman and NTLM dialects: -------------------------------------- NEGOTIATE @@ -2341,48 +2364,48 @@ struct data_blob { (BB verify that never need to set allocation size) SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via Unix ext?) - + COPY (note support for copy across directories) - FUTURE, OPTIONAL setting/getting OS/2 EAs - FUTURE (BB can this handle setting Linux xattrs perfectly) - OPTIONAL dnotify - FUTURE, OPTIONAL quota - FUTURE, OPTIONAL - - Note that various requests implemented for NT interop such as + + Note that various requests implemented for NT interop such as NT_TRANSACT (IOCTL) QueryReparseInfo are unneeded to servers compliant with the CIFS POSIX extensions - + From CIFS Unix Extensions: ------------------------- T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) - BB check for missing inode fields - Actually need QUERY_FILE_UNIX_INFO since has inode num - BB what about a) blksize/blkbits/blocks + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing + inode fields + Actually a need QUERY_FILE_UNIX_INFO + since has inode num + BB what about a) blksize/blkbits/blocks b) i_version c) i_rdev d) notify mask? e) generation f) size_seqcount T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX - TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended + TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL - - */ /* xsymlink is a symlink format (used by MacOS) that can be used - to save symlink info in a regular file when + to save symlink info in a regular file when mounted to operating systems that do not support the cifs Unix extensions or EAs (for xattr based symlinks). For such a file to be recognized - as containing symlink data: + as containing symlink data: - 1) file size must be 1067, + 1) file size must be 1067, 2) signature must begin file data, 3) length field must be set to ASCII representation - of a number which is less than or equal to 1024, + of a number which is less than or equal to 1024, 4) md5 must match that of the path data */ struct xsymlink { @@ -2393,10 +2416,10 @@ struct xsymlink { char length[4]; char cr1; /* \n */ /* md5 of valid subset of path ie path[0] through path[length-1] */ - __u8 md5[32]; + __u8 md5[32]; char cr2; /* \n */ /* if room left, then end with \n then 0x20s by convention but not required */ - char path[1024]; + char path[1024]; } __attribute__((packed)); typedef struct file_xattr_info { @@ -2405,7 +2428,8 @@ typedef struct file_xattr_info { __u32 xattr_value_len; char xattr_name[0]; /* followed by xattr_value[xattr_value_len], no pad */ -} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute, info level 0x205 */ +} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info + level 0x205 */ /* flags for chattr command */ @@ -2431,8 +2455,9 @@ typedef struct file_xattr_info { typedef struct file_chattr_info { __le64 mask; /* list of all possible attribute bits */ __le64 mode; /* list of actual attribute bits on this inode */ -} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes (chattr, chflags) level 0x206 */ +} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes + (chattr, chflags) level 0x206 */ -#endif +#endif #endif /* _CIFSPDU_H */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 5d163e2..04a69da 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CIFSPROTO_H #define _CIFSPROTO_H @@ -49,9 +49,9 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* out */ , int * /* bytes returned */ , const int long_op); extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, - struct kvec *, int /* nvec to send */, + struct kvec *, int /* nvec to send */, int * /* type of buf returned */ , const int long_op); -extern int SendReceiveBlockingLock(const unsigned int /* xid */ , +extern int SendReceiveBlockingLock(const unsigned int /* xid */ , struct cifsTconInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , @@ -64,19 +64,19 @@ extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, enum securityEnum *secType); -extern int cifs_inet_pton(int, char * source, void *dst); +extern int cifs_inet_pton(int, char *source, void *dst); extern int map_smb_to_linux_error(struct smb_hdr *smb); extern void header_assemble(struct smb_hdr *, char /* command */ , const struct cifsTconInfo *, int /* length of fixed section (word count) in two byte units */); extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifsSesInfo *ses, - void ** request_buf); + void **request_buf); extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, - const int stage, + const int stage, const struct nls_table *nls_cp); extern __u16 GetNextMid(struct TCP_Server_Info *server); -extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16, +extern struct oplock_q_entry *AllocOplockQEntry(struct inode *, u16, struct cifsTconInfo *); extern void DeleteOplockQEntry(struct oplock_q_entry *); extern struct timespec cifs_NTtimeToUnix(u64 /* utc nanoseconds since 1601 */ ); @@ -85,12 +85,12 @@ extern __le64 cnvrtDosCifsTm(__u16 date, __u16 time); extern struct timespec cnvrtDosUnixTm(__u16 date, __u16 time); extern int cifs_get_inode_info(struct inode **pinode, - const unsigned char *search_path, + const unsigned char *search_path, FILE_ALL_INFO * pfile_info, struct super_block *sb, int xid); extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, - struct super_block *sb,int xid); + struct super_block *sb, int xid); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); @@ -98,8 +98,8 @@ extern int cifs_umount(struct super_block *, struct cifs_sb_info *); void cifs_proc_init(void); void cifs_proc_clean(void); -extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, - struct nls_table * nls_info); +extern int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, + struct nls_table *nls_info); extern int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses); extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, @@ -108,11 +108,11 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, const char *searchName, const struct nls_table *nls_codepage, - __u16 *searchHandle, struct cifs_search_info * psrch_inf, + __u16 *searchHandle, struct cifs_search_info *psrch_inf, int map, const char dirsep); extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, - __u16 searchHandle, struct cifs_search_info * psrch_inf); + __u16 searchHandle, struct cifs_search_info *psrch_inf); extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, const __u16 search_handle); @@ -123,9 +123,9 @@ extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon, int legacy /* whether to use old info level */, const struct nls_table *nls_codepage, int remap); extern int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - FILE_ALL_INFO * findData, - const struct nls_table *nls_codepage, int remap); + const unsigned char *searchName, + FILE_ALL_INFO *findData, + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon, @@ -143,13 +143,13 @@ extern int connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, const struct nls_table *nls_codepage, int remap); extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, - const char *old_path, + const char *old_path, const struct nls_table *nls_codepage, - unsigned int *pnum_referrals, - unsigned char ** preferrals, + unsigned int *pnum_referrals, + unsigned char **preferrals, int remap); extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, - struct super_block * sb, struct smb_vol * vol); + struct super_block *sb, struct smb_vol *vol); extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData); extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, @@ -181,11 +181,11 @@ extern int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, - __u64 size, __u16 fileHandle,__u32 opener_pid, + __u64 size, __u16 fileHandle, __u32 opener_pid, int AllocSizeFlag); extern int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *pTcon, char *full_path, __u64 mode, __u64 uid, - __u64 gid, dev_t dev, + __u64 gid, dev_t dev, const struct nls_table *nls_codepage, int remap_special_chars); @@ -196,7 +196,10 @@ extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, int remap_special_chars); - +extern int CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, + const char *name, __u16 type, + const struct nls_table *nls_codepage, + int remap_special_chars); extern int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, @@ -205,8 +208,8 @@ extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, - int netfid, char * target_name, +extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, + int netfid, char *target_name, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSCreateHardLink(const int xid, @@ -217,7 +220,7 @@ extern int CIFSCreateHardLink(const int xid, extern int CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon, const char *fromName, const char *toName, - const struct nls_table *nls_codepage, + const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon, @@ -228,7 +231,7 @@ extern int CIFSSMBUnixQuerySymLink(const int xid, const unsigned char *searchName, char *syminfo, const int buflen, const struct nls_table *nls_codepage); -extern int CIFSSMBQueryReparseLinkInfo(const int xid, +extern int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *symlinkinfo, const int buflen, __u16 fid, @@ -244,35 +247,35 @@ extern int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, const int access_flags, const int omode, __u16 * netfid, int *pOplock, FILE_ALL_INFO *, const struct nls_table *nls_codepage, int remap); -extern int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, +extern int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, u32 posix_flags, __u64 mode, __u16 * netfid, FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, const char *name, - const struct nls_table *nls_codepage, int remap); + const struct nls_table *nls_codepage, int remap); extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, const int smb_file_id); extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, - const int netfid, unsigned int count, - const __u64 lseek, unsigned int *nbytes, char **buf, - int * return_buf_type); + const int netfid, unsigned int count, + const __u64 lseek, unsigned int *nbytes, char **buf, + int *return_buf_type); extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 lseek, unsigned int *nbytes, - const char *buf, const char __user *ubuf, + const char *buf, const char __user *ubuf, const int long_op); extern int CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, - const __u64 offset, unsigned int *nbytes, + const __u64 offset, unsigned int *nbytes, struct kvec *iov, const int nvec, const int long_op); extern int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, __u64 * inode_number, - const struct nls_table *nls_codepage, + const struct nls_table *nls_codepage, int remap_special_chars); extern int cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, - const struct nls_table * codepage); -extern int cifsConvertToUCS(__le16 * target, const char *source, int maxlen, - const struct nls_table * cp, int mapChars); + const struct nls_table *codepage); +extern int cifsConvertToUCS(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars); extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 netfid, const __u64 len, @@ -281,7 +284,7 @@ extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const int waitFlag); extern int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const int get_flag, - const __u64 len, struct file_lock *, + const __u64 len, struct file_lock *, const __u16 lock_type, const int waitFlag); extern int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon); extern int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses); @@ -291,54 +294,56 @@ extern void sesInfoFree(struct cifsSesInfo *); extern struct cifsTconInfo *tconInfoAlloc(void); extern void tconInfoFree(struct cifsTconInfo *); -extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *,__u32 *); +extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *, __u32 *); -extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key, - __u32 expected_sequence_number); -extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass); -extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, +extern int cifs_verify_signature(struct smb_hdr *, + const struct mac_key *mac_key, + __u32 expected_sequence_number); +extern int cifs_calculate_mac_key(struct mac_key *key, const char *rn, + const char *pass); +extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, const struct nls_table *); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char * ); -extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, +extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, const struct nls_table *); #ifdef CONFIG_CIFS_WEAK_PW_HASH -extern void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key); +extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key); #endif /* CIFS_WEAK_PW_HASH */ extern int CIFSSMBCopy(int xid, struct cifsTconInfo *source_tcon, const char *fromName, const __u16 target_tid, const char *toName, const int flags, - const struct nls_table *nls_codepage, + const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, - const int notify_subdirs,const __u16 netfid, - __u32 filter, struct file * file, int multishot, +extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, + const int notify_subdirs, const __u16 netfid, + __u32 filter, struct file *file, int multishot, const struct nls_table *nls_codepage); extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, char * EAData, + const unsigned char *searchName, char *EAData, size_t bufsize, const struct nls_table *nls_codepage, int remap_special_chars); -extern ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, - const unsigned char * searchName,const unsigned char * ea_name, - unsigned char * ea_value, size_t buf_size, +extern ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, const unsigned char *ea_name, + unsigned char *ea_value, size_t buf_size, const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, - const char *fileName, const char * ea_name, - const void * ea_value, const __u16 ea_value_len, +extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, + const char *fileName, const char *ea_name, + const void *ea_value, const __u16 ea_value_len, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, char *acl_inf, const int buflen, const int acl_type /* ACCESS vs. DEFAULT */); extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, - char *acl_inf, const int buflen,const int acl_type, + char *acl_inf, const int buflen, const int acl_type, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *fileName, const char *local_acl, const int buflen, const int acl_type, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, - const int netfid, __u64 * pExtAttrBits, __u64 *pMask); + const int netfid, __u64 * pExtAttrBits, __u64 *pMask); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 57419a1..8eb102f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -48,7 +48,7 @@ static struct { {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ - {CIFS_PROT, "\2NT LM 0.12"}, + {CIFS_PROT, "\2NT LM 0.12"}, {POSIX_PROT, "\2POSIX 2"}, {BAD_PROT, "\2"} }; @@ -61,7 +61,7 @@ static struct { {LANMAN_PROT, "\2LM1.2X002"}, {LANMAN2_PROT, "\2LANMAN2.1"}, #endif /* weak password hashing for legacy clients */ - {CIFS_PROT, "\2NT LM 0.12"}, + {CIFS_PROT, "\2NT LM 0.12"}, {BAD_PROT, "\2"} }; #endif @@ -84,17 +84,17 @@ static struct { /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ -static void mark_open_files_invalid(struct cifsTconInfo * pTcon) +static void mark_open_files_invalid(struct cifsTconInfo *pTcon) { struct cifsFileInfo *open_file = NULL; - struct list_head * tmp; - struct list_head * tmp1; + struct list_head *tmp; + struct list_head *tmp1; /* list all files open on tree connection and mark them invalid */ write_lock(&GlobalSMBSeslock); list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { - open_file = list_entry(tmp,struct cifsFileInfo, tlist); - if(open_file) { + open_file = list_entry(tmp, struct cifsFileInfo, tlist); + if (open_file) { open_file->invalidHandle = TRUE; } } @@ -113,75 +113,78 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ - if(tcon) { - if(tcon->tidStatus == CifsExiting) { + if (tcon) { + if (tcon->tidStatus == CifsExiting) { /* only tree disconnect, open, and write, (and ulogoff which does not have tcon) are allowed as we start force umount */ - if((smb_command != SMB_COM_WRITE_ANDX) && - (smb_command != SMB_COM_OPEN_ANDX) && + if ((smb_command != SMB_COM_WRITE_ANDX) && + (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { - cFYI(1,("can not send cmd %d while umounting", + cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } - if((tcon->ses) && (tcon->ses->status != CifsExiting) && - (tcon->ses->server)){ + if ((tcon->ses) && (tcon->ses->status != CifsExiting) && + (tcon->ses->server)) { struct nls_table *nls_codepage; - /* Give Demultiplex thread up to 10 seconds to + /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ - while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { + while (tcon->ses->server->tcpStatus == + CifsNeedReconnect) { wait_event_interruptible_timeout(tcon->ses->server->response_q, - (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); - if(tcon->ses->server->tcpStatus == CifsNeedReconnect) { + (tcon->ses->server->tcpStatus == + CifsGood), 10 * HZ); + if (tcon->ses->server->tcpStatus == + CifsNeedReconnect) { /* on "soft" mounts we wait once */ - if((tcon->retry == FALSE) || + if ((tcon->retry == FALSE) || (tcon->ses->status == CifsExiting)) { - cFYI(1,("gave up waiting on reconnect in smb_init")); + cFYI(1, ("gave up waiting on " + "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes back on-line */ } else /* TCP session is reestablished now */ break; - } - + nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); - if(tcon->ses->status == CifsNeedReconnect) - rc = cifs_setup_session(0, tcon->ses, + if (tcon->ses->status == CifsNeedReconnect) + rc = cifs_setup_session(0, tcon->ses, nls_codepage); - if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { + if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); - rc = CIFSTCon(0, tcon->ses, tcon->treeName, + rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); up(&tcon->ses->sesSem); /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps(0 /* no xid */, - tcon, + tcon, NULL /* we do not know sb */, - NULL /* no vol info */); + NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ - if(rc == 0) + if (rc == 0) atomic_inc(&tconInfoReconnectCount); cFYI(1, ("reconnect tcon rc = %d", rc)); - /* Removed call to reopen open files here - - it is safer (and faster) to reopen files + /* Removed call to reopen open files here. + It is safer (and faster) to reopen files one at a time as needed in read and write */ - /* Check if handle based operation so we + /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ - switch(smb_command) { + switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: @@ -200,7 +203,7 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, return -EIO; } } - if(rc) + if (rc) return rc; *request_buf = cifs_small_buf_get(); @@ -209,23 +212,24 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, return -ENOMEM; } - header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,wct); + header_assemble((struct smb_hdr *) *request_buf, smb_command, + tcon, wct); - if(tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); return rc; } int -small_smb_init_no_tc(const int smb_command, const int wct, +small_smb_init_no_tc(const int smb_command, const int wct, struct cifsSesInfo *ses, void **request_buf) { int rc; - struct smb_hdr * buffer; + struct smb_hdr *buffer; rc = small_smb_init(smb_command, wct, NULL, request_buf); - if(rc) + if (rc) return rc; buffer = (struct smb_hdr *)*request_buf; @@ -237,7 +241,7 @@ small_smb_init_no_tc(const int smb_command, const int wct, /* uid, tid can stay at zero as set in header assemble */ - /* BB add support for turning on the signing when + /* BB add support for turning on the signing when this function is used after 1st of session setup requests */ return rc; @@ -254,52 +258,53 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for tcp and smb session status done differently for those three - in the calling routine */ - if(tcon) { - if(tcon->tidStatus == CifsExiting) { + if (tcon) { + if (tcon->tidStatus == CifsExiting) { /* only tree disconnect, open, and write, (and ulogoff which does not have tcon) are allowed as we start force umount */ - if((smb_command != SMB_COM_WRITE_ANDX) && + if ((smb_command != SMB_COM_WRITE_ANDX) && (smb_command != SMB_COM_OPEN_ANDX) && (smb_command != SMB_COM_TREE_DISCONNECT)) { - cFYI(1,("can not send cmd %d while umounting", + cFYI(1, ("can not send cmd %d while umounting", smb_command)); return -ENODEV; } } - if((tcon->ses) && (tcon->ses->status != CifsExiting) && - (tcon->ses->server)){ + if ((tcon->ses) && (tcon->ses->status != CifsExiting) && + (tcon->ses->server)) { struct nls_table *nls_codepage; /* Give Demultiplex thread up to 10 seconds to reconnect, should be greater than cifs socket timeout which is 7 seconds */ - while(tcon->ses->server->tcpStatus == CifsNeedReconnect) { + while (tcon->ses->server->tcpStatus == + CifsNeedReconnect) { wait_event_interruptible_timeout(tcon->ses->server->response_q, - (tcon->ses->server->tcpStatus == CifsGood), 10 * HZ); - if(tcon->ses->server->tcpStatus == + (tcon->ses->server->tcpStatus == + CifsGood), 10 * HZ); + if (tcon->ses->server->tcpStatus == CifsNeedReconnect) { /* on "soft" mounts we wait once */ - if((tcon->retry == FALSE) || + if ((tcon->retry == FALSE) || (tcon->ses->status == CifsExiting)) { - cFYI(1,("gave up waiting on reconnect in smb_init")); + cFYI(1, ("gave up waiting on " + "reconnect in smb_init")); return -EHOSTDOWN; } /* else "hard" mount - keep retrying until process is killed or server comes on-line */ } else /* TCP session is reestablished now */ break; - } - nls_codepage = load_nls_default(); /* need to prevent multiple threads trying to simultaneously reconnect the same SMB session */ down(&tcon->ses->sesSem); - if(tcon->ses->status == CifsNeedReconnect) - rc = cifs_setup_session(0, tcon->ses, + if (tcon->ses->status == CifsNeedReconnect) + rc = cifs_setup_session(0, tcon->ses, nls_codepage); - if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { + if (!rc && (tcon->tidStatus == CifsNeedReconnect)) { mark_open_files_invalid(tcon); rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); @@ -307,24 +312,24 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) reset_cifs_unix_caps(0 /* no xid */, - tcon, + tcon, NULL /* do not know sb */, NULL /* no vol info */); /* BB FIXME add code to check if wsize needs update due to negotiated smb buffer size shrinking */ - if(rc == 0) + if (rc == 0) atomic_inc(&tconInfoReconnectCount); cFYI(1, ("reconnect tcon rc = %d", rc)); - /* Removed call to reopen open files here - - it is safer (and faster) to reopen files + /* Removed call to reopen open files here. + It is safer (and faster) to reopen files one at a time as needed in read and write */ - /* Check if handle based operation so we + /* Check if handle based operation so we know whether we can continue or not without returning to caller to reset file handle */ - switch(smb_command) { + switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: @@ -343,7 +348,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, return -EIO; } } - if(rc) + if (rc) return rc; *request_buf = cifs_buf_get(); @@ -355,48 +360,48 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* potential retries of smb operations it turns out we can determine */ /* from the mid flags when the request buffer can be resent without */ /* having to use a second distinct buffer for the response */ - if(response_buf) - *response_buf = *request_buf; + if (response_buf) + *response_buf = *request_buf; header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, wct /*wct */ ); - if(tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); return rc; } -static int validate_t2(struct smb_t2_rsp * pSMB) +static int validate_t2(struct smb_t2_rsp *pSMB) { int rc = -EINVAL; int total_size; - char * pBCC; + char *pBCC; /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ - if(pSMB->hdr.WordCount >= 10) { - if((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && + if (pSMB->hdr.WordCount >= 10) { + if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { /* check that bcc is at least as big as parms + data */ /* check that bcc is less than negotiated smb buffer */ total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); - if(total_size < 512) { - total_size+=le16_to_cpu(pSMB->t2_rsp.DataCount); + if (total_size < 512) { + total_size += + le16_to_cpu(pSMB->t2_rsp.DataCount); /* BCC le converted in SendReceive */ - pBCC = (pSMB->hdr.WordCount * 2) + + pBCC = (pSMB->hdr.WordCount * 2) + sizeof(struct smb_hdr) + (char *)pSMB; - if((total_size <= (*(u16 *)pBCC)) && - (total_size < + if ((total_size <= (*(u16 *)pBCC)) && + (total_size < CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { return 0; } - } } } - cifs_dump_mem("Invalid transact2 SMB: ",(char *)pSMB, + cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, sizeof(struct smb_t2_rsp) + 16); return rc; } @@ -408,12 +413,12 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) int rc = 0; int bytes_returned; int i; - struct TCP_Server_Info * server; + struct TCP_Server_Info *server; u16 count; unsigned int secFlags; u16 dialect; - if(ses->server) + if (ses->server) server = ses->server; else { rc = -EIO; @@ -425,20 +430,20 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) return rc; /* if any of auth flags (ie not sign or seal) are overriden use them */ - if(ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - secFlags = ses->overrideSecFlg; + if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) + secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */ else /* if override flags set only sign/seal OR them with global auth */ secFlags = extended_security | ses->overrideSecFlg; - cFYI(1,("secFlags 0x%x",secFlags)); + cFYI(1, ("secFlags 0x%x", secFlags)); pSMB->hdr.Mid = GetNextMid(server); pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - + count = 0; - for(i=0;i<CIFS_NUM_PROT;i++) { + for (i = 0; i < CIFS_NUM_PROT; i++) { strncpy(pSMB->DialectsArray+count, protocols[i].name, 16); count += strlen(protocols[i].name) + 1; /* null at end of source and target buffers anyway */ @@ -448,26 +453,26 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc != 0) + if (rc != 0) goto neg_err_exit; dialect = le16_to_cpu(pSMBr->DialectIndex); - cFYI(1,("Dialect: %d", dialect)); + cFYI(1, ("Dialect: %d", dialect)); /* Check wct = 1 error case */ - if((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) { + if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) { /* core returns wct = 1, but we do not ask for core - otherwise - small wct just comes when dialect index is -1 indicating we + small wct just comes when dialect index is -1 indicating we could not negotiate a common dialect */ rc = -EOPNOTSUPP; goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - } else if((pSMBr->hdr.WordCount == 13) +#ifdef CONFIG_CIFS_WEAK_PW_HASH + } else if ((pSMBr->hdr.WordCount == 13) && ((dialect == LANMAN_PROT) || (dialect == LANMAN2_PROT))) { __s16 tmp; - struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr; + struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; - if((secFlags & CIFSSEC_MAY_LANMAN) || + if ((secFlags & CIFSSEC_MAY_LANMAN) || (secFlags & CIFSSEC_MAY_PLNTXT)) server->secType = LANMAN; else { @@ -475,7 +480,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) " in /proc/fs/cifs/SecurityFlags")); rc = -EOPNOTSUPP; goto neg_err_exit; - } + } server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode); server->maxReq = le16_to_cpu(rsp->MaxMpxCount); server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), @@ -483,7 +488,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); /* even though we do not use raw we might as well set this accurately, in case we ever find a need for it */ - if((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { + if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { server->maxRw = 0xFF00; server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; } else { @@ -504,29 +509,29 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) utc = CURRENT_TIME; ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date), le16_to_cpu(rsp->SrvTime.Time)); - cFYI(1,("SrvTime: %d sec since 1970 (utc: %d) diff: %d", - (int)ts.tv_sec, (int)utc.tv_sec, + cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d", + (int)ts.tv_sec, (int)utc.tv_sec, (int)(utc.tv_sec - ts.tv_sec))); val = (int)(utc.tv_sec - ts.tv_sec); seconds = val < 0 ? -val : val; result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; remain = seconds % MIN_TZ_ADJ; - if(remain >= (MIN_TZ_ADJ / 2)) + if (remain >= (MIN_TZ_ADJ / 2)) result += MIN_TZ_ADJ; - if(val < 0) + if (val < 0) result = - result; server->timeAdj = result; } else { server->timeAdj = (int)tmp; server->timeAdj *= 60; /* also in seconds */ } - cFYI(1,("server->timeAdj: %d seconds", server->timeAdj)); + cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj)); /* BB get server time for time conversions and add - code to use it and timezone since this is not UTC */ + code to use it and timezone since this is not UTC */ - if (rsp->EncryptionKeyLength == + if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { memcpy(server->cryptKey, rsp->EncryptionKey, CIFS_CRYPTO_KEY_SIZE); @@ -535,39 +540,39 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) goto neg_err_exit; } - cFYI(1,("LANMAN negotiated")); + cFYI(1, ("LANMAN negotiated")); /* we will not end up setting signing flags - as no signing was in LANMAN and server did not return the flags on */ goto signing_check; #else /* weak security disabled */ - } else if(pSMBr->hdr.WordCount == 13) { - cERROR(1,("mount failed, cifs module not built " + } else if (pSMBr->hdr.WordCount == 13) { + cERROR(1, ("mount failed, cifs module not built " "with CIFS_WEAK_PW_HASH support")); rc = -EOPNOTSUPP; #endif /* WEAK_PW_HASH */ goto neg_err_exit; - } else if(pSMBr->hdr.WordCount != 17) { + } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; goto neg_err_exit; } /* else wct == 17 NTLM */ server->secMode = pSMBr->SecurityMode; - if((server->secMode & SECMODE_USER) == 0) - cFYI(1,("share mode security")); + if ((server->secMode & SECMODE_USER) == 0) + cFYI(1, ("share mode security")); - if((server->secMode & SECMODE_PW_ENCRYPT) == 0) + if ((server->secMode & SECMODE_PW_ENCRYPT) == 0) #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0) #endif /* CIFS_WEAK_PW_HASH */ - cERROR(1,("Server requests plain text password" + cERROR(1, ("Server requests plain text password" " but client support disabled")); - if((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) + if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) server->secType = NTLMv2; - else if(secFlags & CIFSSEC_MAY_NTLM) + else if (secFlags & CIFSSEC_MAY_NTLM) server->secType = NTLM; - else if(secFlags & CIFSSEC_MAY_NTLMV2) + else if (secFlags & CIFSSEC_MAY_NTLMV2) server->secType = NTLMv2; /* else krb5 ... any others ... */ @@ -596,7 +601,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) /* BB might be helpful to save off the domain of server here */ - if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && + if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (server->capabilities & CAP_EXTENDED_SECURITY)) { count = pSMBr->ByteCount; if (count < 16) @@ -620,7 +625,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) SecurityBlob, count - 16, &server->secType); - if(rc == 1) { + if (rc == 1) { /* BB Need to fill struct for sessetup here */ rc = -EOPNOTSUPP; } else { @@ -633,26 +638,37 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) #ifdef CONFIG_CIFS_WEAK_PW_HASH signing_check: #endif - if(sign_CIFS_PDUs == FALSE) { - if(server->secMode & SECMODE_SIGN_REQUIRED) - cERROR(1,("Server requires " - "/proc/fs/cifs/PacketSigningEnabled to be on")); - server->secMode &= + if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { + /* MUST_SIGN already includes the MAY_SIGN FLAG + so if this is zero it means that signing is disabled */ + cFYI(1, ("Signing disabled")); + if (server->secMode & SECMODE_SIGN_REQUIRED) + cERROR(1, ("Server requires " + "/proc/fs/cifs/PacketSigningEnabled " + "to be on")); + server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if(sign_CIFS_PDUs == 1) { - if((server->secMode & SECMODE_SIGN_REQUIRED) == 0) - server->secMode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if(sign_CIFS_PDUs == 2) { - if((server->secMode & + } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { + /* signing required */ + cFYI(1, ("Must sign - secFlags 0x%x", secFlags)); + if ((server->secMode & (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { - cERROR(1,("signing required but server lacks support")); - } + cERROR(1, + ("signing required but server lacks support")); + rc = -EOPNOTSUPP; + } else + server->secMode |= SECMODE_SIGN_REQUIRED; + } else { + /* signing optional ie CIFSSEC_MAY_SIGN */ + if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0) + server->secMode &= + ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); } -neg_err_exit: + +neg_err_exit: cifs_buf_release(pSMB); - cFYI(1,("negprot rc %d",rc)); + cFYI(1, ("negprot rc %d", rc)); return rc; } @@ -669,7 +685,7 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) * If last user of the connection and * connection alive - disconnect it * If this is the last connection on the server session disconnect it - * (and inside session disconnect we should check if tcp socket needs + * (and inside session disconnect we should check if tcp socket needs * to be freed and kernel thread woken up). */ if (tcon) @@ -683,18 +699,18 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) return -EBUSY; } - /* No need to return error on this operation if tid invalidated and + /* No need to return error on this operation if tid invalidated and closed on server already e.g. due to tcp session crashing */ - if(tcon->tidStatus == CifsNeedReconnect) { + if (tcon->tidStatus == CifsNeedReconnect) { up(&tcon->tconSem); - return 0; + return 0; } - if((tcon->ses == NULL) || (tcon->ses->server == NULL)) { + if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) { up(&tcon->tconSem); return -EIO; } - rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, + rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, (void **)&smb_buffer); if (rc) { up(&tcon->tconSem); @@ -711,7 +727,7 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) cifs_small_buf_release(smb_buffer); up(&tcon->tconSem); - /* No need to return error on this operation if tid invalidated and + /* No need to return error on this operation if tid invalidated and closed on server already e.g. due to tcp session crashing */ if (rc == -EAGAIN) rc = 0; @@ -745,11 +761,11 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) } smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */ - - if(ses->server) { + + if (ses->server) { pSMB->hdr.Mid = GetNextMid(ses->server); - if(ses->server->secMode & + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } @@ -772,7 +788,7 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) cifs_small_buf_release(pSMB); /* if session dead then we do not need to do ulogoff, - since server closed smb session, no sense reporting + since server closed smb session, no sense reporting error */ if (rc == -EAGAIN) rc = 0; @@ -780,6 +796,82 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) } int +CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, + __u16 type, const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + struct unlink_psx_rq *pRqD; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cFYI(1, ("In POSIX delete")); +PsxDelete: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB add path length overrun check */ + name_len = strnlen(fileName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, fileName, name_len); + } + + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = 0; /* BB double check this with jra */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* Setup pointer to Request Data (inode type) */ + pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset); + pRqD->type = cpu_to_le16(type); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); + + pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); + pSMB->Reserved4 = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Posix delete returned %d", rc)); + } + cifs_buf_release(pSMB); + + cifs_stats_inc(&tcon->num_deletes); + + if (rc == -EAGAIN) + goto PsxDelete; + + return rc; +} + +int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, const struct nls_table *nls_codepage, int remap) { @@ -797,7 +889,7 @@ DelFileRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, + cifsConvertToUCS((__le16 *) pSMB->fileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -816,7 +908,7 @@ DelFileRetry: cifs_stats_inc(&tcon->num_deletes); if (rc) { cFYI(1, ("Error in RMFile = %d", rc)); - } + } cifs_buf_release(pSMB); if (rc == -EAGAIN) @@ -826,7 +918,7 @@ DelFileRetry: } int -CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName, +CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName, const struct nls_table *nls_codepage, int remap) { DELETE_DIRECTORY_REQ *pSMB = NULL; @@ -887,7 +979,7 @@ MkDirRetry: return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name, + name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -916,7 +1008,7 @@ MkDirRetry: int CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags, __u64 mode, __u16 * netfid, FILE_UNIX_BASIC_INFO *pRetData, - __u32 *pOplock, const char *name, + __u32 *pOplock, const char *name, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; @@ -924,7 +1016,6 @@ CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags, int name_len; int rc = 0; int bytes_returned = 0; - char *data_offset; __u16 params, param_offset, offset, byte_count, count; OPEN_PSX_REQ * pdata; OPEN_PSX_RSP * psx_rsp; @@ -958,13 +1049,12 @@ PsxCreat: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset); pdata->Level = SMB_QUERY_FILE_UNIX_BASIC; pdata->Permissions = cpu_to_le64(mode); - pdata->PosixOpenFlags = cpu_to_le32(posix_flags); + pdata->PosixOpenFlags = cpu_to_le32(posix_flags); pdata->OpenFlags = cpu_to_le32(*pOplock); pSMB->ParameterOffset = cpu_to_le16(param_offset); pSMB->DataOffset = cpu_to_le16(offset); @@ -979,7 +1069,7 @@ PsxCreat: pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); pSMB->Reserved4 = 0; - pSMB->hdr.smb_buf_length += byte_count; + pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -988,7 +1078,7 @@ PsxCreat: goto psx_create_err; } - cFYI(1,("copying inode info")); + cFYI(1, ("copying inode info")); rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) { @@ -997,34 +1087,33 @@ PsxCreat: } /* copy return information to pRetData */ - psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol + psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset)); - + *pOplock = le16_to_cpu(psx_rsp->OplockFlags); - if(netfid) + if (netfid) *netfid = psx_rsp->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ - if(cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) + if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) *pOplock |= CIFS_CREATE_ACTION; /* check to make sure response data is there */ - if(psx_rsp->ReturnedLevel != SMB_QUERY_FILE_UNIX_BASIC) { + if (psx_rsp->ReturnedLevel != SMB_QUERY_FILE_UNIX_BASIC) { pRetData->Type = -1; /* unknown */ #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1,("unknown type")); + cFYI(1, ("unknown type")); #endif } else { - if(pSMBr->ByteCount < sizeof(OPEN_PSX_RSP) + if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP) + sizeof(FILE_UNIX_BASIC_INFO)) { - cERROR(1,("Open response data too small")); + cERROR(1, ("Open response data too small")); pRetData->Type = -1; goto psx_create_err; } - memcpy((char *) pRetData, + memcpy((char *) pRetData, (char *)psx_rsp + sizeof(OPEN_PSX_RSP), sizeof (FILE_UNIX_BASIC_INFO)); } - psx_create_err: cifs_buf_release(pSMB); @@ -1034,7 +1123,7 @@ psx_create_err: if (rc == -EAGAIN) goto PsxCreat; - return rc; + return rc; } static __u16 convert_disposition(int disposition) @@ -1061,7 +1150,7 @@ static __u16 convert_disposition(int disposition) ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; break; default: - cFYI(1,("unknown disposition %d",disposition)); + cFYI(1, ("unknown disposition %d", disposition)); ofun = SMBOPEN_OAPPEND; /* regular open */ } return ofun; @@ -1071,7 +1160,7 @@ int SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, const int access_flags, const int create_options, __u16 * netfid, - int *pOplock, FILE_ALL_INFO * pfile_info, + int *pOplock, FILE_ALL_INFO * pfile_info, const struct nls_table *nls_codepage, int remap) { int rc = -EACCES; @@ -1113,16 +1202,16 @@ OldOpenRetry: 1 = write 2 = rw 3 = execute - */ + */ pSMB->Mode = cpu_to_le16(2); pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ /* set file as system file if special file such as fifo and server expecting SFU style and no Unix extensions */ - if(create_options & CREATE_OPTION_SPECIAL) - pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); - else + if (create_options & CREATE_OPTION_SPECIAL) + pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM); + else pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); /* BB FIXME */ /* if ((omode & S_IWUGO) == 0) @@ -1132,7 +1221,8 @@ OldOpenRetry: being created */ /* BB FIXME BB */ -/* pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); */ +/* pSMB->CreateOptions = cpu_to_le32(create_options & + CREATE_OPTIONS_MASK); */ /* BB FIXME END BB */ pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); @@ -1143,7 +1233,7 @@ OldOpenRetry: pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 1); + (struct smb_hdr *) pSMBr, &bytes_returned, 1); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, ("Error in Open = %d", rc)); @@ -1156,17 +1246,17 @@ OldOpenRetry: /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ /* BB FIXME BB */ -/* if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) +/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) *pOplock |= CIFS_CREATE_ACTION; */ /* BB FIXME END */ - if(pfile_info) { + if (pfile_info) { pfile_info->CreationTime = 0; /* BB convert CreateTime*/ pfile_info->LastAccessTime = 0; /* BB fixme */ pfile_info->LastWriteTime = 0; /* BB fixme */ pfile_info->ChangeTime = 0; /* BB fixme */ pfile_info->Attributes = - cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); + cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes)); /* the file_info buf is endian converted by caller */ pfile_info->AllocationSize = cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile)); @@ -1185,7 +1275,7 @@ int CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, const char *fileName, const int openDisposition, const int access_flags, const int create_options, __u16 * netfid, - int *pOplock, FILE_ALL_INFO * pfile_info, + int *pOplock, FILE_ALL_INFO * pfile_info, const struct nls_table *nls_codepage, int remap) { int rc = -EACCES; @@ -1228,7 +1318,7 @@ openRetry: /* set file as system file if special file such as fifo and server expecting SFU style and no Unix extensions */ - if(create_options & CREATE_OPTION_SPECIAL) + if (create_options & CREATE_OPTION_SPECIAL) pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); else pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); @@ -1266,10 +1356,10 @@ openRetry: *netfid = pSMBr->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ - if(cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) - *pOplock |= CIFS_CREATE_ACTION; - if(pfile_info) { - memcpy((char *)pfile_info,(char *)&pSMBr->CreationTime, + if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; + if (pfile_info) { + memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, 36 /* CreationTime to Attributes */); /* the file_info buf is endian converted by caller */ pfile_info->AllocationSize = pSMBr->AllocationSize; @@ -1285,10 +1375,9 @@ openRetry: } int -CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, - const int netfid, const unsigned int count, - const __u64 lseek, unsigned int *nbytes, char **buf, - int * pbuf_type) +CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, + const unsigned int count, const __u64 lseek, unsigned int *nbytes, + char **buf, int *pbuf_type) { int rc = -EACCES; READ_REQ *pSMB = NULL; @@ -1298,8 +1387,8 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, int resp_buf_type = 0; struct kvec iov[1]; - cFYI(1,("Reading %d bytes on fid %d",count,netfid)); - if(tcon->ses->capabilities & CAP_LARGE_FILES) + cFYI(1, ("Reading %d bytes on fid %d", count, netfid)); + if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 12; else wct = 10; /* old style read */ @@ -1316,28 +1405,28 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); - if(wct == 12) + if (wct == 12) pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); - else if((lseek >> 32) > 0) /* can not handle this big offset for old */ + else if ((lseek >> 32) > 0) /* can not handle this big offset for old */ return -EIO; pSMB->Remaining = 0; pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); pSMB->MaxCountHigh = cpu_to_le32(count >> 16); - if(wct == 12) + if (wct == 12) pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ else { /* old style read */ - struct smb_com_readx_req * pSMBW = + struct smb_com_readx_req *pSMBW = (struct smb_com_readx_req *)pSMB; pSMBW->ByteCount = 0; } iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; - rc = SendReceive2(xid, tcon->ses, iov, + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, 0); + &resp_buf_type, 0); cifs_stats_inc(&tcon->num_reads); pSMBr = (READ_RSP *)iov[0].iov_base; if (rc) { @@ -1351,33 +1440,34 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, /*check that DataLength would not go beyond end of SMB */ if ((data_length > CIFSMaxBufSize) || (data_length > count)) { - cFYI(1,("bad length %d for count %d",data_length,count)); + cFYI(1, ("bad length %d for count %d", + data_length, count)); rc = -EIO; *nbytes = 0; } else { pReadData = (char *) (&pSMBr->hdr.Protocol) + le16_to_cpu(pSMBr->DataOffset); -/* if(rc = copy_to_user(buf, pReadData, data_length)) { - cERROR(1,("Faulting on read rc = %d",rc)); - rc = -EFAULT; +/* if (rc = copy_to_user(buf, pReadData, data_length)) { + cERROR(1,("Faulting on read rc = %d",rc)); + rc = -EFAULT; }*/ /* can not use copy_to_user when using page cache*/ - if(*buf) - memcpy(*buf,pReadData,data_length); + if (*buf) + memcpy(*buf, pReadData, data_length); } } /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ - if(*buf) { - if(resp_buf_type == CIFS_SMALL_BUFFER) + if (*buf) { + if (resp_buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); - else if(resp_buf_type == CIFS_LARGE_BUFFER) + else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); - } else if(resp_buf_type != CIFS_NO_BUFFER) { - /* return buffer to caller to free */ - *buf = iov[0].iov_base; - if(resp_buf_type == CIFS_SMALL_BUFFER) + } else if (resp_buf_type != CIFS_NO_BUFFER) { + /* return buffer to caller to free */ + *buf = iov[0].iov_base; + if (resp_buf_type == CIFS_SMALL_BUFFER) *pbuf_type = CIFS_SMALL_BUFFER; - else if(resp_buf_type == CIFS_LARGE_BUFFER) + else if (resp_buf_type == CIFS_LARGE_BUFFER) *pbuf_type = CIFS_LARGE_BUFFER; } /* else no valid buffer on return - leave as null */ @@ -1391,7 +1481,7 @@ int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, const int netfid, const unsigned int count, const __u64 offset, unsigned int *nbytes, const char *buf, - const char __user * ubuf, const int long_op) + const char __user *ubuf, const int long_op) { int rc = -EACCES; WRITE_REQ *pSMB = NULL; @@ -1401,10 +1491,10 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, __u16 byte_count; /* cFYI(1,("write at %lld %d bytes",offset,count));*/ - if(tcon->ses == NULL) + if (tcon->ses == NULL) return -ECONNABORTED; - if(tcon->ses->capabilities & CAP_LARGE_FILES) + if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 14; else wct = 12; @@ -1420,20 +1510,20 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if(wct == 14) + if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - else if((offset >> 32) > 0) /* can not handle this big offset for old */ + else if ((offset >> 32) > 0) /* can not handle big offset for old srv */ return -EIO; - + pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; - /* Can increase buffer size if buffer is big enough in some cases - ie we + /* Can increase buffer size if buffer is big enough in some cases ie we can send more if LARGE_WRITE_X capability returned by the server and if our buffer is big enough or if we convert to iovecs on socket writes and eliminate the copy to the CIFS buffer */ - if(tcon->ses->capabilities & CAP_LARGE_WRITE_X) { + if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); } else { bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) @@ -1443,11 +1533,11 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, if (bytes_sent > count) bytes_sent = count; pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); - if(buf) - memcpy(pSMB->Data,buf,bytes_sent); - else if(ubuf) { - if(copy_from_user(pSMB->Data,ubuf,bytes_sent)) { + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + if (buf) + memcpy(pSMB->Data, buf, bytes_sent); + else if (ubuf) { + if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) { cifs_buf_release(pSMB); return -EFAULT; } @@ -1456,7 +1546,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, cifs_buf_release(pSMB); return -EINVAL; } /* else setting file size with write of zero bytes */ - if(wct == 14) + if (wct == 14) byte_count = bytes_sent + 1; /* pad */ else /* wct == 12 */ { byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */ @@ -1465,10 +1555,11 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); pSMB->hdr.smb_buf_length += byte_count; - if(wct == 14) + if (wct == 14) pSMB->ByteCount = cpu_to_le16(byte_count); - else { /* old style write has byte count 4 bytes earlier so 4 bytes pad */ - struct smb_com_writex_req * pSMBW = + else { /* old style write has byte count 4 bytes earlier + so 4 bytes pad */ + struct smb_com_writex_req *pSMBW = (struct smb_com_writex_req *)pSMB; pSMBW->ByteCount = cpu_to_le16(byte_count); } @@ -1487,7 +1578,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, cifs_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; @@ -1505,9 +1596,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, int smb_hdr_len; int resp_buf_type = 0; - cFYI(1,("write2 at %lld %d bytes", (long long)offset, count)); + cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count)); - if(tcon->ses->capabilities & CAP_LARGE_FILES) + if (tcon->ses->capabilities & CAP_LARGE_FILES) wct = 14; else wct = 12; @@ -1521,37 +1612,37 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if(wct == 14) + if (wct == 14) pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - else if((offset >> 32) > 0) /* can not handle this big offset for old */ + else if ((offset >> 32) > 0) /* can not handle big offset for old srv */ return -EIO; pSMB->Reserved = 0xFFFFFFFF; pSMB->WriteMode = 0; pSMB->Remaining = 0; pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req,Data) - 4); + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); pSMB->DataLengthHigh = cpu_to_le16(count >> 16); smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */ - if(wct == 14) + if (wct == 14) pSMB->hdr.smb_buf_length += count+1; else /* wct == 12 */ - pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ - if(wct == 14) + pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */ + if (wct == 14) pSMB->ByteCount = cpu_to_le16(count + 1); else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ { - struct smb_com_writex_req * pSMBW = + struct smb_com_writex_req *pSMBW = (struct smb_com_writex_req *)pSMB; pSMBW->ByteCount = cpu_to_le16(count + 5); } iov[0].iov_base = pSMB; - if(wct == 14) + if (wct == 14) iov[0].iov_len = smb_hdr_len + 4; else /* wct == 12 pad bigger by four bytes */ iov[0].iov_len = smb_hdr_len + 8; - + rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, long_op); @@ -1559,7 +1650,7 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, if (rc) { cFYI(1, ("Send error Write2 = %d", rc)); *nbytes = 0; - } else if(resp_buf_type == 0) { + } else if (resp_buf_type == 0) { /* presumably this can not happen, but best to be safe */ rc = -EIO; *nbytes = 0; @@ -1568,15 +1659,15 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, *nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = (*nbytes) << 16; *nbytes += le16_to_cpu(pSMBr->Count); - } + } /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ - if(resp_buf_type == CIFS_SMALL_BUFFER) + if (resp_buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); - else if(resp_buf_type == CIFS_LARGE_BUFFER) + else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); - /* Note: On -EAGAIN error only caller can retry on handle based calls + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; @@ -1596,7 +1687,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, int timeout = 0; __u16 count; - cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d",waitFlag,numLock)); + cFYI(1, ("In CIFSSMBLock - timeout %d numLock %d", waitFlag, numLock)); rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); if (rc) @@ -1604,7 +1695,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */ - if(lockType == LOCKING_ANDX_OPLOCK_RELEASE) { + if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { timeout = -1; /* no response expected */ pSMB->Timeout = 0; } else if (waitFlag == TRUE) { @@ -1620,7 +1711,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = smb_file_id; /* netfid stays le */ - if((numLock != 0) || (numUnlock != 0)) { + if ((numLock != 0) || (numUnlock != 0)) { pSMB->Locks[0].Pid = cpu_to_le16(current->tgid); /* BB where to store pid high? */ pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); @@ -1648,7 +1739,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, } cifs_small_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } @@ -1656,12 +1747,11 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const int get_flag, const __u64 len, - struct file_lock *pLockData, const __u16 lock_type, + struct file_lock *pLockData, const __u16 lock_type, const int waitFlag) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - char *data_offset; struct cifs_posix_lock *parm_data; int rc = 0; int timeout = 0; @@ -1670,7 +1760,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("Posix Lock")); - if(pLockData == NULL) + if (pLockData == NULL) return EINVAL; rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); @@ -1680,7 +1770,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; - params = 6; + params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; @@ -1688,14 +1778,12 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; - count = sizeof(struct cifs_posix_lock); pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */ pSMB->SetupCount = 1; pSMB->Reserved3 = 0; - if(get_flag) + if (get_flag) pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); else pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); @@ -1705,11 +1793,11 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); - parm_data = (struct cifs_posix_lock *) + parm_data = (struct cifs_posix_lock *) (((char *) &pSMB->hdr.Protocol) + offset); parm_data->lock_type = cpu_to_le16(lock_type); - if(waitFlag) { + if (waitFlag) { timeout = 3; /* blocking operation, no timeout */ parm_data->lock_flags = cpu_to_le16(1); pSMB->Timeout = cpu_to_le32(-1); @@ -1746,22 +1834,22 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, rc = -EIO; /* bad smb */ goto plk_err_exit; } - if(pLockData == NULL) { + if (pLockData == NULL) { rc = -EINVAL; goto plk_err_exit; } data_offset = le16_to_cpu(pSMBr->t2.DataOffset); data_count = le16_to_cpu(pSMBr->t2.DataCount); - if(data_count < sizeof(struct cifs_posix_lock)) { + if (data_count < sizeof(struct cifs_posix_lock)) { rc = -EIO; goto plk_err_exit; } parm_data = (struct cifs_posix_lock *) ((char *)&pSMBr->hdr.Protocol + data_offset); - if(parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) + if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) pLockData->fl_type = F_UNLCK; } - + plk_err_exit: if (pSMB) cifs_small_buf_release(pSMB); @@ -1784,7 +1872,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) /* do not retry on dead session on close */ rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); - if(rc == -EAGAIN) + if (rc == -EAGAIN) return 0; if (rc) return rc; @@ -1798,7 +1886,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_closes); if (rc) { - if(rc!=-EINTR) { + if (rc != -EINTR) { /* EINTR is expected when user ctl-c to kill app */ cERROR(1, ("Send error in Close = %d", rc)); } @@ -1807,7 +1895,7 @@ CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id) cifs_small_buf_release(pSMB); /* Since session is dead, file will be closed on server already */ - if(rc == -EAGAIN) + if (rc == -EAGAIN) rc = 0; return rc; @@ -1839,7 +1927,7 @@ renameRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, + cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -1851,7 +1939,7 @@ renameRetry: toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); @@ -1872,7 +1960,7 @@ renameRetry: cifs_stats_inc(&tcon->num_renames); if (rc) { cFYI(1, ("Send error in rename = %d", rc)); - } + } cifs_buf_release(pSMB); @@ -1882,13 +1970,13 @@ renameRetry: return rc; } -int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, - int netfid, char * target_name, - const struct nls_table * nls_codepage, int remap) +int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon, + int netfid, char *target_name, + const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - struct set_file_rename * rename_info; + struct set_file_rename *rename_info; char *data_offset; char dummy_string[30]; int rc = 0; @@ -1927,13 +2015,14 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, rename_info->overwrite = cpu_to_le32(1); rename_info->root_fid = 0; /* unicode only call */ - if(target_name == NULL) { - sprintf(dummy_string,"cifs%x",pSMB->hdr.Mid); - len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, + if (target_name == NULL) { + sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); + len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, dummy_string, 24, nls_codepage, remap); } else { len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name, - target_name, PATH_MAX, nls_codepage, remap); + target_name, PATH_MAX, nls_codepage, + remap); } rename_info->target_name_len = cpu_to_le32(2 * len_of_str); count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2; @@ -1947,10 +2036,10 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&pTcon->num_t2renames); if (rc) { - cFYI(1,("Send error in Rename (by file handle) = %d", rc)); + cFYI(1, ("Send error in Rename (by file handle) = %d", rc)); } cifs_buf_release(pSMB); @@ -1962,9 +2051,9 @@ int CIFSSMBRenameOpenFile(const int xid,struct cifsTconInfo *pTcon, } int -CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char * fromName, - const __u16 target_tid, const char *toName, const int flags, - const struct nls_table *nls_codepage, int remap) +CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName, + const __u16 target_tid, const char *toName, const int flags, + const struct nls_table *nls_codepage, int remap) { int rc = 0; COPY_REQ *pSMB = NULL; @@ -1986,7 +2075,7 @@ copyRetry: pSMB->Flags = cpu_to_le16(flags & COPY_TREE); if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, + name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ @@ -1994,11 +2083,12 @@ copyRetry: pSMB->OldFileName[name_len] = 0x04; /* pad */ /* protocol requires ASCII signature byte on Unicode string */ pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], + name_len2 = + cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); @@ -2058,7 +2148,7 @@ createSymLinkRetry: name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fromName, name_len); @@ -2070,7 +2160,7 @@ createSymLinkRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; @@ -2081,7 +2171,7 @@ createSymLinkRetry: , nls_codepage); name_len_target++; /* trailing null */ name_len_target *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len_target = strnlen(toName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, toName, name_len_target); @@ -2108,9 +2198,7 @@ createSymLinkRetry: (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_symlinks); if (rc) { - cFYI(1, - ("Send error in SetPathInfo (create symlink) = %d", - rc)); + cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc)); } if (pSMB) @@ -2149,7 +2237,7 @@ createHardLinkRetry: name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(toName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, toName, name_len); @@ -2161,7 +2249,7 @@ createHardLinkRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; @@ -2171,7 +2259,7 @@ createHardLinkRetry: nls_codepage, remap); name_len_target++; /* trailing null */ name_len_target *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len_target = strnlen(fromName, PATH_MAX); name_len_target++; /* trailing null */ strncpy(data_offset, fromName, name_len_target); @@ -2243,13 +2331,13 @@ winCreateHardLinkRetry: name_len++; /* trailing null */ name_len *= 2; pSMB->OldFileName[name_len] = 0; /* pad */ - pSMB->OldFileName[name_len + 1] = 0x04; + pSMB->OldFileName[name_len + 1] = 0x04; name_len2 = - cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], + cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2], toName, PATH_MAX, nls_codepage, remap); name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; name_len2 *= 2; /* convert to bytes */ - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fromName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->OldFileName, fromName, name_len); @@ -2302,12 +2390,11 @@ querySymLinkRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifs_strtoUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX - /* find define for this maxpathcomponent */ - , nls_codepage); + cifs_strtoUCS((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); @@ -2324,7 +2411,7 @@ querySymLinkRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -2355,16 +2442,16 @@ querySymLinkRetry: if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = UniStrnlen((wchar_t *) ((char *) - &pSMBr->hdr.Protocol +data_offset), - min_t(const int, buflen,count) / 2); + &pSMBr->hdr.Protocol + data_offset), + min_t(const int, buflen, count) / 2); /* BB FIXME investigate remapping reserved chars here */ cifs_strfromUCS_le(symlinkinfo, - (__le16 *) ((char *)&pSMBr->hdr.Protocol + - data_offset), + (__le16 *) ((char *)&pSMBr->hdr.Protocol + + data_offset), name_len, nls_codepage); } else { strncpy(symlinkinfo, - (char *) &pSMBr->hdr.Protocol + + (char *) &pSMBr->hdr.Protocol + data_offset, min_t(const int, buflen, count)); } @@ -2385,14 +2472,14 @@ querySymLinkRetry: Setup words themselves and ByteCount MaxSetupCount (size of returned setup area) and MaxParameterCount (returned parms size) must be set by caller */ -static int +static int smb_init_ntransact(const __u16 sub_command, const int setup_count, const int parm_len, struct cifsTconInfo *tcon, - void ** ret_buf) + void **ret_buf) { int rc; __u32 temp_offset; - struct smb_com_ntransact_req * pSMB; + struct smb_com_ntransact_req *pSMB; rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, (void **)&pSMB); @@ -2416,47 +2503,47 @@ smb_init_ntransact(const __u16 sub_command, const int setup_count, } static int -validate_ntransact(char * buf, char ** ppparm, char ** ppdata, - int * pdatalen, int * pparmlen) +validate_ntransact(char *buf, char **ppparm, char **ppdata, + int *pdatalen, int *pparmlen) { - char * end_of_smb; + char *end_of_smb; __u32 data_count, data_offset, parm_count, parm_offset; - struct smb_com_ntransact_rsp * pSMBr; + struct smb_com_ntransact_rsp *pSMBr; - if(buf == NULL) + if (buf == NULL) return -EINVAL; pSMBr = (struct smb_com_ntransact_rsp *)buf; /* ByteCount was converted from little endian in SendReceive */ - end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + + end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + (char *)&pSMBr->ByteCount; - data_offset = le32_to_cpu(pSMBr->DataOffset); data_count = le32_to_cpu(pSMBr->DataCount); - parm_offset = le32_to_cpu(pSMBr->ParameterOffset); + parm_offset = le32_to_cpu(pSMBr->ParameterOffset); parm_count = le32_to_cpu(pSMBr->ParameterCount); *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; /* should we also check that parm and data areas do not overlap? */ - if(*ppparm > end_of_smb) { - cFYI(1,("parms start after end of smb")); + if (*ppparm > end_of_smb) { + cFYI(1, ("parms start after end of smb")); return -EINVAL; - } else if(parm_count + *ppparm > end_of_smb) { - cFYI(1,("parm end after end of smb")); + } else if (parm_count + *ppparm > end_of_smb) { + cFYI(1, ("parm end after end of smb")); return -EINVAL; - } else if(*ppdata > end_of_smb) { - cFYI(1,("data starts after end of smb")); + } else if (*ppdata > end_of_smb) { + cFYI(1, ("data starts after end of smb")); return -EINVAL; - } else if(data_count + *ppdata > end_of_smb) { + } else if (data_count + *ppdata > end_of_smb) { cFYI(1,("data %p + count %d (%p) ends after end of smb %p start %p", - *ppdata, data_count, (data_count + *ppdata), end_of_smb, pSMBr)); /* BB FIXME */ + *ppdata, data_count, (data_count + *ppdata), + end_of_smb, pSMBr)); return -EINVAL; - } else if(parm_count + data_count > pSMBr->ByteCount) { - cFYI(1,("parm count and data count larger than SMB")); + } else if (parm_count + data_count > pSMBr->ByteCount) { + cFYI(1, ("parm count and data count larger than SMB")); return -EINVAL; } return 0; @@ -2465,14 +2552,14 @@ validate_ntransact(char * buf, char ** ppparm, char ** ppdata, int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, - char *symlinkinfo, const int buflen,__u16 fid, + char *symlinkinfo, const int buflen, __u16 fid, const struct nls_table *nls_codepage) { int rc = 0; int bytes_returned; int name_len; - struct smb_com_transaction_ioctl_req * pSMB; - struct smb_com_transaction_ioctl_rsp * pSMBr; + struct smb_com_transaction_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName)); rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, @@ -2511,47 +2598,53 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, /* BB also check enough total bytes returned */ rc = -EIO; /* bad smb */ else { - if(data_count && (data_count < 2048)) { - char * end_of_smb = 2 /* sizeof byte count */ + + if (data_count && (data_count < 2048)) { + char *end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount + (char *)&pSMBr->ByteCount; - struct reparse_data * reparse_buf = (struct reparse_data *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if((char*)reparse_buf >= end_of_smb) { + struct reparse_data *reparse_buf = + (struct reparse_data *) + ((char *)&pSMBr->hdr.Protocol + + data_offset); + if ((char *)reparse_buf >= end_of_smb) { rc = -EIO; goto qreparse_out; } - if((reparse_buf->LinkNamesBuf + + if ((reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset + reparse_buf->TargetNameLen) > end_of_smb) { - cFYI(1,("reparse buf extended beyond SMB")); + cFYI(1,("reparse buf goes beyond SMB")); rc = -EIO; goto qreparse_out; } - + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = UniStrnlen((wchar_t *) - (reparse_buf->LinkNamesBuf + - reparse_buf->TargetNameOffset), - min(buflen/2, reparse_buf->TargetNameLen / 2)); + (reparse_buf->LinkNamesBuf + + reparse_buf->TargetNameOffset), + min(buflen/2, + reparse_buf->TargetNameLen / 2)); cifs_strfromUCS_le(symlinkinfo, - (__le16 *) (reparse_buf->LinkNamesBuf + + (__le16 *) (reparse_buf->LinkNamesBuf + reparse_buf->TargetNameOffset), name_len, nls_codepage); } else { /* ASCII names */ - strncpy(symlinkinfo,reparse_buf->LinkNamesBuf + - reparse_buf->TargetNameOffset, - min_t(const int, buflen, reparse_buf->TargetNameLen)); + strncpy(symlinkinfo, + reparse_buf->LinkNamesBuf + + reparse_buf->TargetNameOffset, + min_t(const int, buflen, + reparse_buf->TargetNameLen)); } } else { rc = -EIO; - cFYI(1,("Invalid return data count on get reparse info ioctl")); + cFYI(1, ("Invalid return data count on " + "get reparse info ioctl")); } symlinkinfo[buflen] = 0; /* just in case so the caller does not go off the end of the buffer */ - cFYI(1,("readlink result - %s",symlinkinfo)); + cFYI(1, ("readlink result - %s", symlinkinfo)); } } qreparse_out: @@ -2566,7 +2659,8 @@ qreparse_out: #ifdef CONFIG_CIFS_POSIX /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ -static void cifs_convert_ace(posix_acl_xattr_entry * ace, struct cifs_posix_ace * cifs_ace) +static void cifs_convert_ace(posix_acl_xattr_entry *ace, + struct cifs_posix_ace *cifs_ace) { /* u8 cifs fields do not need le conversion */ ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); @@ -2578,30 +2672,31 @@ static void cifs_convert_ace(posix_acl_xattr_entry * ace, struct cifs_posix_ace } /* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ -static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen, - const int acl_type,const int size_of_data_area) +static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, + const int acl_type, const int size_of_data_area) { int size = 0; int i; __u16 count; - struct cifs_posix_ace * pACE; - struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)src; - posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)trgt; + struct cifs_posix_ace *pACE; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; + posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt; if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) return -EOPNOTSUPP; - if(acl_type & ACL_TYPE_ACCESS) { + if (acl_type & ACL_TYPE_ACCESS) { count = le16_to_cpu(cifs_acl->access_entry_count); pACE = &cifs_acl->ace_array[0]; size = sizeof(struct cifs_posix_acl); size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ - if(size_of_data_area < size) { - cFYI(1,("bad CIFS POSIX ACL size %d vs. %d",size_of_data_area,size)); + if (size_of_data_area < size) { + cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d", + size_of_data_area, size)); return -EINVAL; } - } else if(acl_type & ACL_TYPE_DEFAULT) { + } else if (acl_type & ACL_TYPE_DEFAULT) { count = le16_to_cpu(cifs_acl->access_entry_count); size = sizeof(struct cifs_posix_acl); size += sizeof(struct cifs_posix_ace) * count; @@ -2610,7 +2705,7 @@ static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen, count = le16_to_cpu(cifs_acl->default_entry_count); size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ - if(size_of_data_area < size) + if (size_of_data_area < size) return -EINVAL; } else { /* illegal type */ @@ -2618,76 +2713,77 @@ static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen, } size = posix_acl_xattr_size(count); - if((buflen == 0) || (local_acl == NULL)) { - /* used to query ACL EA size */ - } else if(size > buflen) { + if ((buflen == 0) || (local_acl == NULL)) { + /* used to query ACL EA size */ + } else if (size > buflen) { return -ERANGE; } else /* buffer big enough */ { local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); - for(i = 0;i < count ;i++) { - cifs_convert_ace(&local_acl->a_entries[i],pACE); - pACE ++; + for (i = 0; i < count ; i++) { + cifs_convert_ace(&local_acl->a_entries[i], pACE); + pACE++; } } return size; } -static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace * cifs_ace, - const posix_acl_xattr_entry * local_ace) +static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, + const posix_acl_xattr_entry *local_ace) { __u16 rc = 0; /* 0 = ACL converted ok */ cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); /* BB is there a better way to handle the large uid? */ - if(local_ace->e_id == cpu_to_le32(-1)) { + if (local_ace->e_id == cpu_to_le32(-1)) { /* Probably no need to le convert -1 on any arch but can not hurt */ cifs_ace->cifs_uid = cpu_to_le64(-1); - } else + } else cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); - /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/ + /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/ return rc; } /* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ -static __u16 ACL_to_cifs_posix(char * parm_data,const char * pACL,const int buflen, - const int acl_type) +static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, + const int buflen, const int acl_type) { __u16 rc = 0; - struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)parm_data; - posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)pACL; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; + posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL; int count; int i; - if((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) + if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) return 0; count = posix_acl_xattr_count((size_t)buflen); - cFYI(1,("setting acl with %d entries from buf of length %d and version of %d", + cFYI(1, ("setting acl with %d entries from buf of length %d and " + "version of %d", count, buflen, le32_to_cpu(local_acl->a_version))); - if(le32_to_cpu(local_acl->a_version) != 2) { - cFYI(1,("unknown POSIX ACL version %d", + if (le32_to_cpu(local_acl->a_version) != 2) { + cFYI(1, ("unknown POSIX ACL version %d", le32_to_cpu(local_acl->a_version))); return 0; } cifs_acl->version = cpu_to_le16(1); - if(acl_type == ACL_TYPE_ACCESS) + if (acl_type == ACL_TYPE_ACCESS) cifs_acl->access_entry_count = cpu_to_le16(count); - else if(acl_type == ACL_TYPE_DEFAULT) + else if (acl_type == ACL_TYPE_DEFAULT) cifs_acl->default_entry_count = cpu_to_le16(count); else { - cFYI(1,("unknown ACL type %d",acl_type)); + cFYI(1, ("unknown ACL type %d", acl_type)); return 0; } - for(i=0;i<count;i++) { + for (i = 0; i < count; i++) { rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &local_acl->a_entries[i]); - if(rc != 0) { + if (rc != 0) { /* ACE not converted */ break; } } - if(rc == 0) { + if (rc == 0) { rc = (__u16)(count * sizeof(struct cifs_posix_ace)); rc += sizeof(struct cifs_posix_acl); /* BB add check to make sure ACL does not overflow SMB */ @@ -2697,9 +2793,9 @@ static __u16 ACL_to_cifs_posix(char * parm_data,const char * pACL,const int bufl int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - char *acl_inf, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap) + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap) { /* SMB_QUERY_POSIX_ACL */ TRANSACTION2_QPI_REQ *pSMB = NULL; @@ -2708,7 +2804,7 @@ CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, int bytes_returned; int name_len; __u16 params, byte_count; - + cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName)); queryAclRetry: @@ -2716,16 +2812,16 @@ queryAclRetry: (void **) &pSMBr); if (rc) return rc; - + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; pSMB->FileName[name_len] = 0; pSMB->FileName[name_len+1] = 0; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); @@ -2734,7 +2830,7 @@ queryAclRetry: params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max data count below from sess structure BB */ + /* BB find exact max data count below from sess structure BB */ pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; @@ -2742,7 +2838,8 @@ queryAclRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + offsetof(struct smb_com_transaction2_qpi_req, + InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -2763,7 +2860,7 @@ queryAclRetry: cFYI(1, ("Send error in Query POSIX ACL = %d", rc)); } else { /* decode response */ - + rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 2)) /* BB also check enough total bytes returned */ @@ -2773,7 +2870,7 @@ queryAclRetry: __u16 count = le16_to_cpu(pSMBr->t2.DataCount); rc = cifs_copy_posix_acl(acl_inf, (char *)&pSMBr->hdr.Protocol+data_offset, - buflen,acl_type,count); + buflen, acl_type, count); } } cifs_buf_release(pSMB); @@ -2784,10 +2881,10 @@ queryAclRetry: int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, - const unsigned char *fileName, - const char *local_acl, const int buflen, - const int acl_type, - const struct nls_table *nls_codepage, int remap) + const unsigned char *fileName, + const char *local_acl, const int buflen, + const int acl_type, + const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; @@ -2800,16 +2897,16 @@ CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName)); setAclRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); + (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, + cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); @@ -2823,15 +2920,15 @@ setAclRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; parm_data = ((char *) &pSMB->hdr.Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); /* convert to on the wire format for POSIX ACL */ - data_count = ACL_to_cifs_posix(parm_data,local_acl,buflen,acl_type); + data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); - if(data_count == 0) { + if (data_count == 0) { rc = -EOPNOTSUPP; goto setACLerrorExit; } @@ -2849,7 +2946,7 @@ setAclRetry: pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Set POSIX ACL returned %d", rc)); } @@ -2864,86 +2961,85 @@ setACLerrorExit: /* BB fix tabs in this function FIXME BB */ int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, - const int netfid, __u64 * pExtAttrBits, __u64 *pMask) + const int netfid, __u64 * pExtAttrBits, __u64 *pMask) { - int rc = 0; - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int bytes_returned; - __u16 params, byte_count; + int rc = 0; + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int bytes_returned; + __u16 params, byte_count; - cFYI(1,("In GetExtAttr")); - if(tcon == NULL) - return -ENODEV; + cFYI(1, ("In GetExtAttr")); + if (tcon == NULL) + return -ENODEV; GetExtAttrRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ +2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(4000); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); - pSMB->Pad = 0; + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ +2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(4000); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); + pSMB->Pad = 0; pSMB->Fid = netfid; - pSMB->hdr.smb_buf_length += byte_count; - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cFYI(1, ("error %d in GetExtAttr", rc)); - } else { - /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 2)) - /* BB also check enough total bytes returned */ - /* If rc should we check for EOPNOSUPP and - disable the srvino flag? or in caller? */ - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_chattr_info * pfinfo; - /* BB Do we need a cast or hash here ? */ - if(count != 16) { - cFYI(1, ("Illegal size ret in GetExtAttr")); - rc = -EIO; - goto GetExtAttrOut; - } - pfinfo = (struct file_chattr_info *) - (data_offset + (char *) &pSMBr->hdr.Protocol); - *pExtAttrBits = le64_to_cpu(pfinfo->mode); + pSMB->hdr.smb_buf_length += byte_count; + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("error %d in GetExtAttr", rc)); + } else { + /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc || (pSMBr->ByteCount < 2)) + /* BB also check enough total bytes returned */ + /* If rc should we check for EOPNOSUPP and + disable the srvino flag? or in caller? */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + struct file_chattr_info *pfinfo; + /* BB Do we need a cast or hash here ? */ + if (count != 16) { + cFYI(1, ("Illegal size ret in GetExtAttr")); + rc = -EIO; + goto GetExtAttrOut; + } + pfinfo = (struct file_chattr_info *) + (data_offset + (char *) &pSMBr->hdr.Protocol); + *pExtAttrBits = le64_to_cpu(pfinfo->mode); *pMask = le64_to_cpu(pfinfo->mask); - } - } + } + } GetExtAttrOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto GetExtAttrRetry; - return rc; + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto GetExtAttrRetry; + return rc; } - #endif /* CONFIG_POSIX */ @@ -2955,7 +3051,7 @@ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {32, 545, 0, 0}}; /* Convert CIFS ACL to POSIX form */ -static int parse_sec_desc(struct cifs_sid * psec_desc, int acl_len) +static int parse_sec_desc(struct cifs_sid *psec_desc, int acl_len) { return 0; } @@ -2963,7 +3059,7 @@ static int parse_sec_desc(struct cifs_sid * psec_desc, int acl_len) /* Get Security Descriptor (by handle) from remote server for a file or dir */ int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, - /* BB fix up return info */ char *acl_inf, const int buflen, + /* BB fix up return info */ char *acl_inf, const int buflen, const int acl_type /* ACCESS/DEFAULT not sure implication */) { int rc = 0; @@ -2973,7 +3069,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, cFYI(1, ("GetCifsACL")); - rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc; @@ -2994,23 +3090,23 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, if (rc) { cFYI(1, ("Send error in QuerySecDesc = %d", rc)); } else { /* decode response */ - struct cifs_sid * psec_desc; + struct cifs_sid *psec_desc; __le32 * parm; int parm_len; int data_len; int acl_len; - struct smb_com_ntransact_rsp * pSMBr; + struct smb_com_ntransact_rsp *pSMBr; /* validate_nttransact */ - rc = validate_ntransact(iov[0].iov_base, (char **)&parm, + rc = validate_ntransact(iov[0].iov_base, (char **)&parm, (char **)&psec_desc, &parm_len, &data_len); - - if(rc) + if (rc) goto qsec_out; pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; - cERROR(1,("smb %p parm %p data %p",pSMBr,parm,psec_desc)); /* BB removeme BB */ + cERROR(1, ("smb %p parm %p data %p", + pSMBr, parm, psec_desc)); /* BB removeme BB */ if (le32_to_cpu(pSMBr->ParameterCount) != 4) { rc = -EIO; /* bad smb */ @@ -3020,14 +3116,14 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, /* BB check that data area is minimum length and as big as acl_len */ acl_len = le32_to_cpu(*(__le32 *)parm); - /* BB check if(acl_len > bufsize) */ + /* BB check if (acl_len > bufsize) */ parse_sec_desc(psec_desc, acl_len); } qsec_out: - if(buf_type == CIFS_SMALL_BUFFER) + if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(iov[0].iov_base); - else if(buf_type == CIFS_LARGE_BUFFER) + else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ return rc; @@ -3036,9 +3132,9 @@ qsec_out: /* Legacy Query Path Information call for lookup to old servers such as Win9x/WinME */ int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - FILE_ALL_INFO * pFinfo, - const struct nls_table *nls_codepage, int remap) + const unsigned char *searchName, + FILE_ALL_INFO *pFinfo, + const struct nls_table *nls_codepage, int remap) { QUERY_INFORMATION_REQ * pSMB; QUERY_INFORMATION_RSP * pSMBr; @@ -3046,31 +3142,31 @@ int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon, int bytes_returned; int name_len; - cFYI(1, ("In SMBQPath path %s", searchName)); + cFYI(1, ("In SMBQPath path %s", searchName)); QInfRetry: rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); + (void **) &pSMBr); if (rc) return rc; if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { + } else { name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } pSMB->BufferFormat = 0x04; - name_len++; /* account for buffer type byte */ + name_len++; /* account for buffer type byte */ pSMB->hdr.smb_buf_length += (__u16) name_len; pSMB->ByteCount = cpu_to_le16(name_len); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); + (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { cFYI(1, ("Send error in QueryInfo = %d", rc)); } else if (pFinfo) { /* decode response */ @@ -3127,17 +3223,17 @@ QPathInfoRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ; + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */ @@ -3147,7 +3243,7 @@ QPathInfoRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -3156,7 +3252,7 @@ QPathInfoRetry: byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; - if(legacy) + if (legacy) pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); else pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); @@ -3173,16 +3269,18 @@ QPathInfoRetry: if (rc) /* BB add auto retry on EOPNOTSUPP? */ rc = -EIO; - else if (!legacy && (pSMBr->ByteCount < 40)) + else if (!legacy && (pSMBr->ByteCount < 40)) rc = -EIO; /* bad smb */ - else if(legacy && (pSMBr->ByteCount < 24)) - rc = -EIO; /* 24 or 26 expected but we do not read last field */ - else if (pFindData){ + else if (legacy && (pSMBr->ByteCount < 24)) + rc = -EIO; /* 24 or 26 expected but we do not read + last field */ + else if (pFindData) { int size; __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - if(legacy) /* we do not read the last field, EAsize, fortunately - since it varies by subdialect and on Set vs. Get, is - two bytes or 4 bytes depending but we don't care here */ + if (legacy) /* we do not read the last field, EAsize, + fortunately since it varies by subdialect + and on Set vs. Get, is two bytes or 4 + bytes depending but we don't care here */ size = sizeof(FILE_INFO_STANDARD); else size = sizeof(FILE_ALL_INFO); @@ -3226,24 +3324,24 @@ UnixQPathInfoRetry: PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); } - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ; + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxDataCount = cpu_to_le16(4000); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -3303,12 +3401,11 @@ findUniqueRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX - /* find define for this maxpathcomponent */ - , nls_codepage); + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); @@ -3324,7 +3421,7 @@ findUniqueRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_ffirst_req,InformationLevel) - 4); + offsetof(struct smb_com_transaction2_ffirst_req, InformationLevel)-4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; /* one byte, no need to le convert */ @@ -3364,10 +3461,10 @@ findUniqueRetry: /* xid, tcon, searchName and codepage are input parms, rest are returned */ int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, - const char *searchName, + const char *searchName, const struct nls_table *nls_codepage, - __u16 * pnetfid, - struct cifs_search_info * psrch_inf, int remap, const char dirsep) + __u16 *pnetfid, + struct cifs_search_info *psrch_inf, int remap, const char dirsep) { /* level 257 SMB_ */ TRANSACTION2_FFIRST_REQ *pSMB = NULL; @@ -3378,7 +3475,7 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, int name_len; __u16 params, byte_count; - cFYI(1, ("In FindFirst for %s",searchName)); + cFYI(1, ("In FindFirst for %s", searchName)); findFirstRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -3388,7 +3485,7 @@ findFirstRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName,searchName, + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); /* We can not add the asterik earlier in case it got remapped to 0xF03A as if it were part of the @@ -3405,7 +3502,7 @@ findFirstRetry: } else { /* BB add check for overrun of SMB buf BB */ name_len = strnlen(searchName, PATH_MAX); /* BB fix here and in unicode clause above ie - if(name_len > buffersize-header) + if (name_len > buffersize-header) free buffer exit; BB */ strncpy(pSMB->FileName, searchName, name_len); pSMB->FileName[name_len] = dirsep; @@ -3438,8 +3535,8 @@ findFirstRetry: pSMB->SearchAttributes = cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); - pSMB->SearchCount= cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); - pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | + pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); + pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); @@ -3466,7 +3563,7 @@ findFirstRetry: } else { /* decode response */ /* BB remember to free buffer if error BB */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if(rc == 0) { + if (rc == 0) { if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) psrch_inf->unicode = TRUE; else @@ -3474,18 +3571,19 @@ findFirstRetry: psrch_inf->ntwrk_buf_start = (char *)pSMBr; psrch_inf->smallBuf = 0; - psrch_inf->srch_entries_start = - (char *) &pSMBr->hdr.Protocol + + psrch_inf->srch_entries_start = + (char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset); parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.ParameterOffset)); - if(parms->EndofSearch) + if (parms->EndofSearch) psrch_inf->endOfSearch = TRUE; else psrch_inf->endOfSearch = FALSE; - psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + psrch_inf->entries_in_buffer; *pnetfid = parms->SearchHandle; @@ -3498,7 +3596,7 @@ findFirstRetry: } int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, - __u16 searchHandle, struct cifs_search_info * psrch_inf) + __u16 searchHandle, struct cifs_search_info *psrch_inf) { TRANSACTION2_FNEXT_REQ *pSMB = NULL; TRANSACTION2_FNEXT_RSP *pSMBr = NULL; @@ -3510,7 +3608,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("In FindNext")); - if(psrch_inf->endOfSearch == TRUE) + if (psrch_inf->endOfSearch == TRUE) return -ENOENT; rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, @@ -3518,12 +3616,13 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, if (rc) return rc; - params = 14; /* includes 2 bytes of null string, converted to LE below */ + params = 14; /* includes 2 bytes of null string, converted to LE below*/ byte_count = 0; pSMB->TotalDataCount = 0; /* no EAs */ pSMB->MaxParameterCount = cpu_to_le16(8); pSMB->MaxDataCount = - cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFF00); + cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & + 0xFFFFFF00); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; @@ -3539,15 +3638,6 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->SearchHandle = searchHandle; /* always kept as le */ pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO)); - /* test for Unix extensions */ -/* if (tcon->ses->capabilities & CAP_UNIX) { - pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX); - psrch_inf->info_level = SMB_FIND_FILE_UNIX; - } else { - pSMB->InformationLevel = - cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO); - psrch_inf->info_level = SMB_FIND_FILE_DIRECTORY_INFO; - } */ pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); pSMB->ResumeKey = psrch_inf->resume_key; pSMB->SearchFlags = @@ -3555,7 +3645,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, name_len = psrch_inf->resume_name_len; params += name_len; - if(name_len < PATH_MAX) { + if (name_len < PATH_MAX) { memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); byte_count += name_len; /* 14 byte parm len above enough for 2 byte null terminator */ @@ -3570,20 +3660,20 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); - + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_fnext); if (rc) { if (rc == -EBADF) { psrch_inf->endOfSearch = TRUE; - rc = 0; /* search probably was closed at end of search above */ + rc = 0; /* search probably was closed at end of search*/ } else cFYI(1, ("FindNext returned = %d", rc)); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if(rc == 0) { + + if (rc == 0) { /* BB fixme add lock for file (srch_info) struct here */ if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) psrch_inf->unicode = TRUE; @@ -3594,7 +3684,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, parms = (T2_FNEXT_RSP_PARMS *)response_data; response_data = (char *)&pSMBr->hdr.Protocol + le16_to_cpu(pSMBr->t2.DataOffset); - if(psrch_inf->smallBuf) + if (psrch_inf->smallBuf) cifs_small_buf_release( psrch_inf->ntwrk_buf_start); else @@ -3602,15 +3692,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, psrch_inf->srch_entries_start = response_data; psrch_inf->ntwrk_buf_start = (char *)pSMB; psrch_inf->smallBuf = 0; - if(parms->EndofSearch) + if (parms->EndofSearch) psrch_inf->endOfSearch = TRUE; else psrch_inf->endOfSearch = FALSE; - - psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount); + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); psrch_inf->index_of_last_entry += psrch_inf->entries_in_buffer; -/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",psrch_inf->entries_in_buffer,psrch_inf->index_of_last_entry)); */ +/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d", + psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */ /* BB fixme add unlock here */ } @@ -3625,12 +3716,12 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, FNext2_err_exit: if (rc != 0) cifs_buf_release(pSMB); - return rc; } int -CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle) +CIFSFindClose(const int xid, struct cifsTconInfo *tcon, + const __u16 searchHandle) { int rc = 0; FINDCLOSE_REQ *pSMB = NULL; @@ -3642,7 +3733,7 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle /* no sense returning error if session restarted as file handle has been closed */ - if(rc == -EAGAIN) + if (rc == -EAGAIN) return 0; if (rc) return rc; @@ -3667,9 +3758,9 @@ CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle int CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, - const unsigned char *searchName, - __u64 * inode_number, - const struct nls_table *nls_codepage, int remap) + const unsigned char *searchName, + __u64 * inode_number, + const struct nls_table *nls_codepage, int remap) { int rc = 0; TRANSACTION2_QPI_REQ *pSMB = NULL; @@ -3677,24 +3768,23 @@ CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon, int name_len, bytes_returned; __u16 params, byte_count; - cFYI(1,("In GetSrvInodeNum for %s",searchName)); - if(tcon == NULL) - return -ENODEV; + cFYI(1, ("In GetSrvInodeNum for %s", searchName)); + if (tcon == NULL) + return -ENODEV; GetInodeNumberRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); + (void **) &pSMBr); if (rc) return rc; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, - PATH_MAX,nls_codepage, remap); + PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, searchName, name_len); @@ -3711,7 +3801,7 @@ GetInodeNumberRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -3737,12 +3827,12 @@ GetInodeNumberRetry: /* If rc should we check for EOPNOSUPP and disable the srvino flag? or in caller? */ rc = -EIO; /* bad smb */ - else { + else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_internal_info * pfinfo; + struct file_internal_info *pfinfo; /* BB Do we need a cast or hash here ? */ - if(count < 8) { + if (count < 8) { cFYI(1, ("Illegal size ret in QryIntrnlInf")); rc = -EIO; goto GetInodeNumOut; @@ -3769,12 +3859,12 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, /* TRANS2_GET_DFS_REFERRAL */ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; - struct dfs_referral_level_3 * referrals = NULL; + struct dfs_referral_level_3 *referrals = NULL; int rc = 0; int bytes_returned; int name_len; unsigned int i; - char * temp; + char *temp; __u16 params, byte_count; *number_of_UNC_in_array = 0; *targetUNCs = NULL; @@ -3787,8 +3877,8 @@ getDFSRetry: (void **) &pSMBr); if (rc) return rc; - - /* server pointer checked in called function, + + /* server pointer checked in called function, but should never be null here anyway */ pSMB->hdr.Mid = GetNextMid(ses->server); pSMB->hdr.Tid = ses->ipc_tid; @@ -3807,19 +3897,19 @@ getDFSRetry: searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(searchName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->RequestFileName, searchName, name_len); } - if(ses->server) { - if(ses->server->secMode & + if (ses->server) { + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } - pSMB->hdr.Uid = ses->Suid; + pSMB->hdr.Uid = ses->Suid; params = 2 /* level */ + name_len /*includes null */ ; pSMB->TotalDataCount = 0; @@ -3833,7 +3923,7 @@ getDFSRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); + struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); @@ -3852,74 +3942,87 @@ getDFSRetry: /* BB Add logic to parse referrals here */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 17)) /* BB also check enough total bytes returned */ + /* BB Also check if enough total bytes returned? */ + if (rc || (pSMBr->ByteCount < 17)) rc = -EIO; /* bad smb */ else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount); cFYI(1, - ("Decoding GetDFSRefer response. BCC: %d Offset %d", + ("Decoding GetDFSRefer response BCC: %d Offset %d", pSMBr->ByteCount, data_offset)); - referrals = - (struct dfs_referral_level_3 *) + referrals = + (struct dfs_referral_level_3 *) (8 /* sizeof start of data block */ + data_offset + - (char *) &pSMBr->hdr.Protocol); - cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \nfor referral one refer size: 0x%x srv type: 0x%x refer flags: 0x%x ttl: 0x%x", - le16_to_cpu(pSMBr->NumberOfReferrals),le16_to_cpu(pSMBr->DFSFlags), le16_to_cpu(referrals->ReferralSize),le16_to_cpu(referrals->ServerType),le16_to_cpu(referrals->ReferralFlags),le16_to_cpu(referrals->TimeToLive))); + (char *) &pSMBr->hdr.Protocol); + cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n" + "for referral one refer size: 0x%x srv " + "type: 0x%x refer flags: 0x%x ttl: 0x%x", + le16_to_cpu(pSMBr->NumberOfReferrals), + le16_to_cpu(pSMBr->DFSFlags), + le16_to_cpu(referrals->ReferralSize), + le16_to_cpu(referrals->ServerType), + le16_to_cpu(referrals->ReferralFlags), + le16_to_cpu(referrals->TimeToLive))); /* BB This field is actually two bytes in from start of data block so we could do safety check that DataBlock begins at address of pSMBr->NumberOfReferrals */ - *number_of_UNC_in_array = le16_to_cpu(pSMBr->NumberOfReferrals); + *number_of_UNC_in_array = + le16_to_cpu(pSMBr->NumberOfReferrals); /* BB Fix below so can return more than one referral */ - if(*number_of_UNC_in_array > 1) + if (*number_of_UNC_in_array > 1) *number_of_UNC_in_array = 1; /* get the length of the strings describing refs */ name_len = 0; - for(i=0;i<*number_of_UNC_in_array;i++) { + for (i = 0; i < *number_of_UNC_in_array; i++) { /* make sure that DfsPathOffset not past end */ - __u16 offset = le16_to_cpu(referrals->DfsPathOffset); + __u16 offset = + le16_to_cpu(referrals->DfsPathOffset); if (offset > data_count) { - /* if invalid referral, stop here and do + /* if invalid referral, stop here and do not try to copy any more */ *number_of_UNC_in_array = i; break; - } + } temp = ((char *)referrals) + offset; if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len += UniStrnlen((wchar_t *)temp,data_count); + name_len += UniStrnlen((wchar_t *)temp, + data_count); } else { - name_len += strnlen(temp,data_count); + name_len += strnlen(temp, data_count); } referrals++; - /* BB add check that referral pointer does not fall off end PDU */ - + /* BB add check that referral pointer does + not fall off end PDU */ } /* BB add check for name_len bigger than bcc */ - *targetUNCs = - kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL); - if(*targetUNCs == NULL) { + *targetUNCs = + kmalloc(name_len+1+(*number_of_UNC_in_array), + GFP_KERNEL); + if (*targetUNCs == NULL) { rc = -ENOMEM; goto GetDFSRefExit; } /* copy the ref strings */ - referrals = - (struct dfs_referral_level_3 *) - (8 /* sizeof data hdr */ + - data_offset + + referrals = (struct dfs_referral_level_3 *) + (8 /* sizeof data hdr */ + data_offset + (char *) &pSMBr->hdr.Protocol); - for(i=0;i<*number_of_UNC_in_array;i++) { - temp = ((char *)referrals) + le16_to_cpu(referrals->DfsPathOffset); + for (i = 0; i < *number_of_UNC_in_array; i++) { + temp = ((char *)referrals) + + le16_to_cpu(referrals->DfsPathOffset); if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { cifs_strfromUCS_le(*targetUNCs, - (__le16 *) temp, name_len, nls_codepage); + (__le16 *) temp, + name_len, + nls_codepage); } else { - strncpy(*targetUNCs,temp,name_len); + strncpy(*targetUNCs, temp, name_len); } /* BB update target_uncs pointers */ referrals++; @@ -3996,18 +4099,17 @@ oldQFSInfoRetry: rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - cFYI(1,("qfsinf resp BCC: %d Offset %d", + cFYI(1, ("qfsinf resp BCC: %d Offset %d", pSMBr->ByteCount, data_offset)); - response_data = - (FILE_SYSTEM_ALLOC_INFO *) + response_data = (FILE_SYSTEM_ALLOC_INFO *) (((char *) &pSMBr->hdr.Protocol) + data_offset); FSData->f_bsize = le16_to_cpu(response_data->BytesPerSector) * le32_to_cpu(response_data-> SectorsPerAllocationUnit); FSData->f_blocks = - le32_to_cpu(response_data->TotalAllocationUnits); + le32_to_cpu(response_data->TotalAllocationUnits); FSData->f_bfree = FSData->f_bavail = le32_to_cpu(response_data->FreeAllocationUnits); cFYI(1, @@ -4056,7 +4158,7 @@ QFSInfoRetry: pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -4071,7 +4173,7 @@ QFSInfoRetry: if (rc) { cFYI(1, ("Send error in QFSInfo = %d", rc)); } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc || (pSMBr->ByteCount < 24)) rc = -EIO; /* bad smb */ @@ -4136,7 +4238,7 @@ QFSAttributeRetry: pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -4153,7 +4255,8 @@ QFSAttributeRetry: } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < 13)) { /* BB also check enough bytes returned */ + if (rc || (pSMBr->ByteCount < 13)) { + /* BB also check if enough bytes returned */ rc = -EIO; /* bad smb */ } else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); @@ -4204,7 +4307,7 @@ QFSDeviceRetry: pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); + struct smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; @@ -4274,8 +4377,8 @@ QFSUnixRetry: byte_count = params + 1 /* pad */ ; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); @@ -4335,7 +4438,8 @@ SETFSUnixRetry: pSMB->Flags = 0; pSMB->Timeout = 0; pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) - 4; + param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum) + - 4; offset = param_offset + params; pSMB->MaxParameterCount = cpu_to_le16(4); @@ -4417,8 +4521,8 @@ QFSPosixRetry: byte_count = params + 1 /* pad */ ; pSMB->ParameterCount = cpu_to_le16(params); pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); pSMB->SetupCount = 1; pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); @@ -4447,18 +4551,18 @@ QFSPosixRetry: le64_to_cpu(response_data->TotalBlocks); FSData->f_bfree = le64_to_cpu(response_data->BlocksAvail); - if(response_data->UserBlocksAvail == cpu_to_le64(-1)) { + if (response_data->UserBlocksAvail == cpu_to_le64(-1)) { FSData->f_bavail = FSData->f_bfree; } else { FSData->f_bavail = - le64_to_cpu(response_data->UserBlocksAvail); + le64_to_cpu(response_data->UserBlocksAvail); } - if(response_data->TotalFileNodes != cpu_to_le64(-1)) + if (response_data->TotalFileNodes != cpu_to_le64(-1)) FSData->f_files = - le64_to_cpu(response_data->TotalFileNodes); - if(response_data->FreeFileNodes != cpu_to_le64(-1)) + le64_to_cpu(response_data->TotalFileNodes); + if (response_data->FreeFileNodes != cpu_to_le64(-1)) FSData->f_ffree = - le64_to_cpu(response_data->FreeFileNodes); + le64_to_cpu(response_data->FreeFileNodes); } } cifs_buf_release(pSMB); @@ -4470,15 +4574,15 @@ QFSPosixRetry: } -/* We can not use write of zero bytes trick to - set file size due to need for large file support. Also note that - this SetPathInfo is preferred to SetFileInfo based method in next +/* We can not use write of zero bytes trick to + set file size due to need for large file support. Also note that + this SetPathInfo is preferred to SetFileInfo based method in next routine which is only needed to work around a sharing violation bug in Samba which this routine can run into */ int CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName, - __u64 size, int SetAllocation, + __u64 size, int SetAllocation, const struct nls_table *nls_codepage, int remap) { struct smb_com_transaction2_spi_req *pSMB = NULL; @@ -4517,22 +4621,22 @@ SetEOFRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; - if(SetAllocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { + if (SetAllocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); } parm_data = @@ -4567,8 +4671,8 @@ SetEOFRetry: } int -CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, - __u16 fid, __u32 pid_of_opener, int SetAllocation) +CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, + __u16 fid, __u32 pid_of_opener, int SetAllocation) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; @@ -4589,7 +4693,7 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - + params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; @@ -4599,7 +4703,7 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + data_offset = (char *) (&pSMB->hdr.Protocol) + offset; count = sizeof(struct file_end_of_file_info); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -4614,25 +4718,25 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, pSMB->TotalParameterCount = pSMB->ParameterCount; pSMB->ParameterOffset = cpu_to_le16(param_offset); parm_data = - (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + - offset); + (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + + offset); pSMB->DataOffset = cpu_to_le16(offset); parm_data->FileSize = cpu_to_le64(size); pSMB->Fid = fid; - if(SetAllocation) { + if (SetAllocation) { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); else pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { + } else /* Set File Size */ { if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); else pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); } pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; @@ -4648,21 +4752,21 @@ CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size, if (pSMB) cifs_small_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; } -/* Some legacy servers such as NT4 require that the file times be set on +/* Some legacy servers such as NT4 require that the file times be set on an open handle, rather than by pathname - this is awkward due to potential access conflicts on the open, but it is unavoidable for these old servers since the only other choice is to go from 100 nanosecond DCE time and resort to the original setpathinfo level which takes the ancient DOS time format with 2 second granularity */ int -CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_INFO * data, - __u16 fid) +CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, + const FILE_BASIC_INFO *data, __u16 fid) { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; @@ -4684,7 +4788,7 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_I use an existing handle (rather than opening one on the fly) */ /* pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));*/ - + params = 6; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; @@ -4694,7 +4798,7 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_I param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + data_offset = (char *) (&pSMB->hdr.Protocol) + offset; count = sizeof (FILE_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -4717,16 +4821,16 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_I pSMB->Reserved4 = 0; pSMB->hdr.smb_buf_length += byte_count; pSMB->ByteCount = cpu_to_le16(byte_count); - memcpy(data_offset,data,sizeof(FILE_BASIC_INFO)); + memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { - cFYI(1,("Send error in Set Time (SetFileInfo) = %d",rc)); + cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc)); } cifs_small_buf_release(pSMB); - /* Note: On -EAGAIN error only caller can retry on handle based calls + /* Note: On -EAGAIN error only caller can retry on handle based calls since file handle passed in no longer valid */ return rc; @@ -4735,7 +4839,7 @@ CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon, const FILE_BASIC_I int CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName, - const FILE_BASIC_INFO * data, + const FILE_BASIC_INFO *data, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; @@ -4760,7 +4864,7 @@ SetTimesRetry: PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); @@ -4776,7 +4880,7 @@ SetTimesRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; data_offset = (char *) (&pSMB->hdr.Protocol) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); @@ -4837,11 +4941,11 @@ SetAttrLgcyRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - ConvertToUCS((__le16 *) pSMB->fileName, fileName, + ConvertToUCS((__le16 *) pSMB->fileName, fileName, PATH_MAX, nls_codepage); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->fileName, fileName, name_len); @@ -4867,8 +4971,8 @@ SetAttrLgcyRetry: int CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon, - char *fileName, __u64 mode, __u64 uid, __u64 gid, - dev_t device, const struct nls_table *nls_codepage, + char *fileName, __u64 mode, __u64 uid, __u64 gid, + dev_t device, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_SPI_REQ *pSMB = NULL; @@ -4888,7 +4992,7 @@ setPermsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, + cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -4908,7 +5012,7 @@ setPermsRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; data_offset = (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol + @@ -4931,7 +5035,7 @@ setPermsRetry: older clients, but we should be precise - we use SetFileSize to set file size and do not want to truncate file size to zero accidently as happened on one Samba server beta by putting - zero instead of -1 here */ + zero instead of -1 here */ data_offset->EndOfFile = NO_CHANGE_64; data_offset->NumOfBytes = NO_CHANGE_64; data_offset->LastStatusChange = NO_CHANGE_64; @@ -4943,20 +5047,20 @@ setPermsRetry: data_offset->DevMajor = cpu_to_le64(MAJOR(device)); data_offset->DevMinor = cpu_to_le64(MINOR(device)); data_offset->Permissions = cpu_to_le64(mode); - - if(S_ISREG(mode)) + + if (S_ISREG(mode)) data_offset->Type = cpu_to_le32(UNIX_FILE); - else if(S_ISDIR(mode)) + else if (S_ISDIR(mode)) data_offset->Type = cpu_to_le32(UNIX_DIR); - else if(S_ISLNK(mode)) + else if (S_ISLNK(mode)) data_offset->Type = cpu_to_le32(UNIX_SYMLINK); - else if(S_ISCHR(mode)) + else if (S_ISCHR(mode)) data_offset->Type = cpu_to_le32(UNIX_CHARDEV); - else if(S_ISBLK(mode)) + else if (S_ISBLK(mode)) data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); - else if(S_ISFIFO(mode)) + else if (S_ISFIFO(mode)) data_offset->Type = cpu_to_le32(UNIX_FIFO); - else if(S_ISSOCK(mode)) + else if (S_ISSOCK(mode)) data_offset->Type = cpu_to_le32(UNIX_SOCKET); @@ -4974,20 +5078,20 @@ setPermsRetry: return rc; } -int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, +int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, const int notify_subdirs, const __u16 netfid, - __u32 filter, struct file * pfile, int multishot, + __u32 filter, struct file *pfile, int multishot, const struct nls_table *nls_codepage) { int rc = 0; - struct smb_com_transaction_change_notify_req * pSMB = NULL; - struct smb_com_ntransaction_change_notify_rsp * pSMBr = NULL; + struct smb_com_transaction_change_notify_req *pSMB = NULL; + struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL; struct dir_notify_req *dnotify_req; int bytes_returned; - cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid)); + cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid)); rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); + (void **) &pSMBr); if (rc) return rc; @@ -5008,7 +5112,7 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, pSMB->SetupCount = 4; /* single byte does not need le conversion */ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE); pSMB->ParameterCount = pSMB->TotalParameterCount; - if(notify_subdirs) + if (notify_subdirs) pSMB->WatchTree = 1; /* one byte - no le conversion needed */ pSMB->Reserved2 = 0; pSMB->CompletionFilter = cpu_to_le32(filter); @@ -5021,11 +5125,11 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, cFYI(1, ("Error in Notify = %d", rc)); } else { /* Add file to outstanding requests */ - /* BB change to kmem cache alloc */ + /* BB change to kmem cache alloc */ dnotify_req = kmalloc( sizeof(struct dir_notify_req), GFP_KERNEL); - if(dnotify_req) { + if (dnotify_req) { dnotify_req->Pid = pSMB->hdr.Pid; dnotify_req->PidHigh = pSMB->hdr.PidHigh; dnotify_req->Mid = pSMB->hdr.Mid; @@ -5036,20 +5140,20 @@ int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, dnotify_req->filter = filter; dnotify_req->multishot = multishot; spin_lock(&GlobalMid_Lock); - list_add_tail(&dnotify_req->lhead, + list_add_tail(&dnotify_req->lhead, &GlobalDnotifyReqList); spin_unlock(&GlobalMid_Lock); - } else + } else rc = -ENOMEM; } cifs_buf_release(pSMB); - return rc; + return rc; } #ifdef CONFIG_CIFS_XATTR ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, - char * EAData, size_t buf_size, + char *EAData, size_t buf_size, const struct nls_table *nls_codepage, int remap) { /* BB assumes one setup word */ @@ -5058,8 +5162,8 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, int rc = 0; int bytes_returned; int name_len; - struct fea * temp_fea; - char * temp_ptr; + struct fea *temp_fea; + char *temp_ptr; __u16 params, byte_count; cFYI(1, ("In Query All EAs path %s", searchName)); @@ -5071,7 +5175,7 @@ QAllEAsRetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -5081,7 +5185,7 @@ QAllEAsRetry: strncpy(pSMB->FileName, searchName, name_len); } - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ; + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */ @@ -5091,7 +5195,7 @@ QAllEAsRetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -5115,7 +5219,7 @@ QAllEAsRetry: /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ - if (rc || (pSMBr->ByteCount < 4)) + if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, @@ -5128,39 +5232,40 @@ QAllEAsRetry: /* check that each element of each entry does not go beyond end of list */ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - struct fealist * ea_response_data; + struct fealist *ea_response_data; rc = 0; /* validate_trans2_offsets() */ - /* BB to check if(start of smb + data_offset > &bcc+ bcc)*/ + /* BB check if start of smb + data_offset > &bcc+ bcc */ ea_response_data = (struct fealist *) (((char *) &pSMBr->hdr.Protocol) + data_offset); name_len = le32_to_cpu(ea_response_data->list_len); - cFYI(1,("ea length %d", name_len)); - if(name_len <= 8) { + cFYI(1, ("ea length %d", name_len)); + if (name_len <= 8) { /* returned EA size zeroed at top of function */ - cFYI(1,("empty EA list returned from server")); + cFYI(1, ("empty EA list returned from server")); } else { /* account for ea list len */ name_len -= 4; temp_fea = ea_response_data->list; temp_ptr = (char *)temp_fea; - while(name_len > 0) { + while (name_len > 0) { __u16 value_len; name_len -= 4; temp_ptr += 4; rc += temp_fea->name_len; /* account for prefix user. and trailing null */ - rc = rc + 5 + 1; - if(rc<(int)buf_size) { - memcpy(EAData,"user.",5); - EAData+=5; - memcpy(EAData,temp_ptr,temp_fea->name_len); - EAData+=temp_fea->name_len; + rc = rc + 5 + 1; + if (rc < (int)buf_size) { + memcpy(EAData, "user.", 5); + EAData += 5; + memcpy(EAData, temp_ptr, + temp_fea->name_len); + EAData += temp_fea->name_len; /* null terminate name */ *EAData = 0; EAData = EAData + 1; - } else if(buf_size == 0) { + } else if (buf_size == 0) { /* skip copy - calc size only */ } else { /* stop before overrun buffer */ @@ -5172,11 +5277,15 @@ QAllEAsRetry: /* account for trailing null */ name_len--; temp_ptr++; - value_len = le16_to_cpu(temp_fea->value_len); + value_len = + le16_to_cpu(temp_fea->value_len); name_len -= value_len; temp_ptr += value_len; - /* BB check that temp_ptr is still within smb BB*/ - /* no trailing null to account for in value len */ + /* BB check that temp_ptr is still + within the SMB BB*/ + + /* no trailing null to account for + in value len */ /* go on to next EA */ temp_fea = (struct fea *)temp_ptr; } @@ -5191,9 +5300,9 @@ QAllEAsRetry: return (ssize_t)rc; } -ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, - const unsigned char * searchName,const unsigned char * ea_name, - unsigned char * ea_value, size_t buf_size, +ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, const unsigned char *ea_name, + unsigned char *ea_value, size_t buf_size, const struct nls_table *nls_codepage, int remap) { TRANSACTION2_QPI_REQ *pSMB = NULL; @@ -5201,8 +5310,8 @@ ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, int rc = 0; int bytes_returned; int name_len; - struct fea * temp_fea; - char * temp_ptr; + struct fea *temp_fea; + char *temp_ptr; __u16 params, byte_count; cFYI(1, ("In Query EA path %s", searchName)); @@ -5214,7 +5323,7 @@ QEARetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, + cifsConvertToUCS((__le16 *) pSMB->FileName, searchName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; @@ -5224,7 +5333,7 @@ QEARetry: strncpy(pSMB->FileName, searchName, name_len); } - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */ ; + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; pSMB->TotalDataCount = 0; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */ @@ -5234,7 +5343,7 @@ QEARetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); pSMB->DataCount = 0; pSMB->DataOffset = 0; pSMB->SetupCount = 1; @@ -5258,7 +5367,7 @@ QEARetry: /* BB also check enough total bytes returned */ /* BB we need to improve the validity checking of these trans2 responses */ - if (rc || (pSMBr->ByteCount < 4)) + if (rc || (pSMBr->ByteCount < 4)) rc = -EIO; /* bad smb */ /* else if (pFindData){ memcpy((char *) pFindData, @@ -5271,18 +5380,18 @@ QEARetry: /* check that each element of each entry does not go beyond end of list */ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - struct fealist * ea_response_data; + struct fealist *ea_response_data; rc = -ENODATA; /* validate_trans2_offsets() */ - /* BB to check if(start of smb + data_offset > &bcc+ bcc)*/ + /* BB check if start of smb + data_offset > &bcc+ bcc*/ ea_response_data = (struct fealist *) (((char *) &pSMBr->hdr.Protocol) + data_offset); name_len = le32_to_cpu(ea_response_data->list_len); - cFYI(1,("ea length %d", name_len)); - if(name_len <= 8) { + cFYI(1, ("ea length %d", name_len)); + if (name_len <= 8) { /* returned EA size zeroed at top of function */ - cFYI(1,("empty EA list returned from server")); + cFYI(1, ("empty EA list returned from server")); } else { /* account for ea list len */ name_len -= 4; @@ -5290,28 +5399,30 @@ QEARetry: temp_ptr = (char *)temp_fea; /* loop through checking if we have a matching name and then return the associated value */ - while(name_len > 0) { + while (name_len > 0) { __u16 value_len; name_len -= 4; temp_ptr += 4; - value_len = le16_to_cpu(temp_fea->value_len); - /* BB validate that value_len falls within SMB, - even though maximum for name_len is 255 */ - if(memcmp(temp_fea->name,ea_name, + value_len = + le16_to_cpu(temp_fea->value_len); + /* BB validate that value_len falls within SMB, + even though maximum for name_len is 255 */ + if (memcmp(temp_fea->name, ea_name, temp_fea->name_len) == 0) { /* found a match */ rc = value_len; /* account for prefix user. and trailing null */ - if(rc<=(int)buf_size) { + if (rc <= (int)buf_size) { memcpy(ea_value, temp_fea->name+temp_fea->name_len+1, rc); - /* ea values, unlike ea names, - are not null terminated */ - } else if(buf_size == 0) { + /* ea values, unlike ea + names, are not null + terminated */ + } else if (buf_size == 0) { /* skip copy - calc size only */ } else { - /* stop before overrun buffer */ + /* stop before overrun buffer */ rc = -ERANGE; } break; @@ -5323,11 +5434,11 @@ QEARetry: temp_ptr++; name_len -= value_len; temp_ptr += value_len; - /* no trailing null to account for in value len */ - /* go on to next EA */ + /* No trailing null to account for in + value_len. Go on to next EA */ temp_fea = (struct fea *)temp_ptr; } - } + } } } if (pSMB) @@ -5340,9 +5451,9 @@ QEARetry: int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName, - const char * ea_name, const void * ea_value, - const __u16 ea_value_len, const struct nls_table *nls_codepage, - int remap) + const char *ea_name, const void *ea_value, + const __u16 ea_value_len, const struct nls_table *nls_codepage, + int remap) { struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; @@ -5361,11 +5472,11 @@ SetEARetry: if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { name_len = - cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, + cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, PATH_MAX, nls_codepage, remap); name_len++; /* trailing null */ name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ + } else { /* BB improve the check for buffer overruns BB */ name_len = strnlen(fileName, PATH_MAX); name_len++; /* trailing null */ strncpy(pSMB->FileName, fileName, name_len); @@ -5376,10 +5487,10 @@ SetEARetry: /* done calculating parms using name_len of file name, now use name_len to calculate length of ea name we are going to create in the inode xattrs */ - if(ea_name == NULL) + if (ea_name == NULL) name_len = 0; else - name_len = strnlen(ea_name,255); + name_len = strnlen(ea_name, 255); count = sizeof(*parm_data) + ea_value_len + name_len + 1; pSMB->MaxParameterCount = cpu_to_le16(2); @@ -5390,7 +5501,7 @@ SetEARetry: pSMB->Timeout = 0; pSMB->Reserved2 = 0; param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; + InformationLevel) - 4; offset = param_offset + params; pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_EA); @@ -5410,17 +5521,19 @@ SetEARetry: /* we checked above that name len is less than 255 */ parm_data->list[0].name_len = (__u8)name_len; /* EA names are always ASCII */ - if(ea_name) - strncpy(parm_data->list[0].name,ea_name,name_len); + if (ea_name) + strncpy(parm_data->list[0].name, ea_name, name_len); parm_data->list[0].name[name_len] = 0; parm_data->list[0].value_len = cpu_to_le16(ea_value_len); /* caller ensures that ea_value_len is less than 64K but we need to ensure that it fits within the smb */ - /*BB add length check that it would fit in negotiated SMB buffer size BB */ - /* if(ea_value_len > buffer_size - 512 (enough for header)) */ - if(ea_value_len) - memcpy(parm_data->list[0].name+name_len+1,ea_value,ea_value_len); + /*BB add length check to see if it would fit in + negotiated SMB buffer size BB */ + /* if (ea_value_len > buffer_size - 512 (enough for header)) */ + if (ea_value_len) + memcpy(parm_data->list[0].name+name_len+1, + ea_value, ea_value_len); pSMB->TotalDataCount = pSMB->DataCount; pSMB->ParameterCount = cpu_to_le16(params); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f4e9266..4af3588 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@ /* * fs/cifs/connect.c * - * Copyright (C) International Business Machines Corp., 2002,2006 + * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/fs.h> #include <linux/net.h> @@ -85,6 +85,7 @@ struct smb_vol { unsigned direct_io:1; unsigned remap:1; /* set to remap seven reserved chars in filenames */ unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ + unsigned no_linux_ext:1; unsigned sfu_emul:1; unsigned nullauth:1; /* attempt to authenticate with null user */ unsigned nocase; /* request case insensitive filenames */ @@ -93,20 +94,20 @@ struct smb_vol { unsigned int wsize; unsigned int sockopt; unsigned short int port; - char * prepath; + char *prepath; }; -static int ipv4_connect(struct sockaddr_in *psin_server, +static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netb_name, - char * server_netb_name); -static int ipv6_connect(struct sockaddr_in6 *psin_server, + char *netb_name, + char *server_netb_name); +static int ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket); - /* + /* * cifs tcp session reconnection - * + * * mark tcp session as reconnecting so temporarily locked * mark all smb sessions as reconnecting for tcp session * reconnect tcp session @@ -120,11 +121,11 @@ cifs_reconnect(struct TCP_Server_Info *server) struct list_head *tmp; struct cifsSesInfo *ses; struct cifsTconInfo *tcon; - struct mid_q_entry * mid_entry; - + struct mid_q_entry *mid_entry; + spin_lock(&GlobalMid_Lock); - if( kthread_should_stop() ) { - /* the demux thread will exit normally + if ( kthread_should_stop() ) { + /* the demux thread will exit normally next time through the loop */ spin_unlock(&GlobalMid_Lock); return rc; @@ -150,18 +151,19 @@ cifs_reconnect(struct TCP_Server_Info *server) } list_for_each(tmp, &GlobalTreeConnectionList) { tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); - if((tcon) && (tcon->ses) && (tcon->ses->server == server)) { + if ((tcon) && (tcon->ses) && (tcon->ses->server == server)) { tcon->tidStatus = CifsNeedReconnect; } } read_unlock(&GlobalSMBSeslock); /* do not want to be sending data on a socket we are freeing */ - down(&server->tcpSem); - if(server->ssocket) { - cFYI(1,("State: 0x%x Flags: 0x%lx", server->ssocket->state, + down(&server->tcpSem); + if (server->ssocket) { + cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state, server->ssocket->flags)); - server->ssocket->ops->shutdown(server->ssocket,SEND_SHUTDOWN); - cFYI(1,("Post shutdown state: 0x%x Flags: 0x%lx", server->ssocket->state, + server->ssocket->ops->shutdown(server->ssocket, SEND_SHUTDOWN); + cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx", + server->ssocket->state, server->ssocket->flags)); sock_release(server->ssocket); server->ssocket = NULL; @@ -172,8 +174,8 @@ cifs_reconnect(struct TCP_Server_Info *server) mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - if(mid_entry) { - if(mid_entry->midState == MID_REQUEST_SUBMITTED) { + if (mid_entry) { + if (mid_entry->midState == MID_REQUEST_SUBMITTED) { /* Mark other intransit requests as needing retry so we do not immediately mark the session bad again (ie after we reconnect @@ -183,29 +185,29 @@ cifs_reconnect(struct TCP_Server_Info *server) } } spin_unlock(&GlobalMid_Lock); - up(&server->tcpSem); + up(&server->tcpSem); - while ( (!kthread_should_stop()) && (server->tcpStatus != CifsGood)) - { + while ( (!kthread_should_stop()) && (server->tcpStatus != CifsGood)) { try_to_freeze(); - if(server->protocolType == IPV6) { - rc = ipv6_connect(&server->addr.sockAddr6,&server->ssocket); + if (server->protocolType == IPV6) { + rc = ipv6_connect(&server->addr.sockAddr6, + &server->ssocket); } else { - rc = ipv4_connect(&server->addr.sockAddr, + rc = ipv4_connect(&server->addr.sockAddr, &server->ssocket, server->workstation_RFC1001_name, server->server_RFC1001_name); } - if(rc) { - cFYI(1,("reconnect error %d",rc)); + if (rc) { + cFYI(1, ("reconnect error %d", rc)); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); spin_lock(&GlobalMid_Lock); - if( !kthread_should_stop() ) + if ( !kthread_should_stop() ) server->tcpStatus = CifsGood; server->sequence_number = 0; - spin_unlock(&GlobalMid_Lock); + spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); } @@ -213,27 +215,27 @@ cifs_reconnect(struct TCP_Server_Info *server) return rc; } -/* +/* return codes: 0 not a transact2, or all data present >0 transact2 with that much data missing -EINVAL = invalid transact2 */ -static int check2ndT2(struct smb_hdr * pSMB, unsigned int maxBufSize) +static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize) { - struct smb_t2_rsp * pSMBt; - int total_data_size; + struct smb_t2_rsp *pSMBt; + int total_data_size; int data_in_this_rsp; int remaining; - if(pSMB->Command != SMB_COM_TRANSACTION2) + if (pSMB->Command != SMB_COM_TRANSACTION2) return 0; - /* check for plausible wct, bcc and t2 data and parm sizes */ - /* check for parm and data offset going beyond end of smb */ - if(pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ - cFYI(1,("invalid transact2 word count")); + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ + cFYI(1, ("invalid transact2 word count")); return -EINVAL; } @@ -244,25 +246,25 @@ static int check2ndT2(struct smb_hdr * pSMB, unsigned int maxBufSize) remaining = total_data_size - data_in_this_rsp; - if(remaining == 0) + if (remaining == 0) return 0; - else if(remaining < 0) { - cFYI(1,("total data %d smaller than data in frame %d", + else if (remaining < 0) { + cFYI(1, ("total data %d smaller than data in frame %d", total_data_size, data_in_this_rsp)); return -EINVAL; } else { - cFYI(1,("missing %d bytes from transact2, check next response", + cFYI(1, ("missing %d bytes from transact2, check next response", remaining)); - if(total_data_size > maxBufSize) { - cERROR(1,("TotalDataSize %d is over maximum buffer %d", - total_data_size,maxBufSize)); - return -EINVAL; + if (total_data_size > maxBufSize) { + cERROR(1, ("TotalDataSize %d is over maximum buffer %d", + total_data_size, maxBufSize)); + return -EINVAL; } return remaining; } } -static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB) +static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; @@ -270,43 +272,43 @@ static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB) int total_in_buf; int remaining; int total_in_buf2; - char * data_area_of_target; - char * data_area_of_buf2; + char *data_area_of_target; + char *data_area_of_buf2; __u16 byte_count; total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); - if(total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { - cFYI(1,("total data sizes of primary and secondary t2 differ")); + if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { + cFYI(1, ("total data size of primary and secondary t2 differ")); } total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; - - if(remaining < 0) + + if (remaining < 0) return -EINVAL; - if(remaining == 0) /* nothing to do, ignore */ + if (remaining == 0) /* nothing to do, ignore */ return 0; - + total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); - if(remaining < total_in_buf2) { - cFYI(1,("transact2 2nd response contains too much data")); + if (remaining < total_in_buf2) { + cFYI(1, ("transact2 2nd response contains too much data")); } /* find end of first SMB data area */ - data_area_of_target = (char *)&pSMBt->hdr.Protocol + + data_area_of_target = (char *)&pSMBt->hdr.Protocol + le16_to_cpu(pSMBt->t2_rsp.DataOffset); /* validate target area */ data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol + - le16_to_cpu(pSMB2->t2_rsp.DataOffset); + le16_to_cpu(pSMB2->t2_rsp.DataOffset); data_area_of_target += total_in_buf; /* copy second buffer into end of first buffer */ - memcpy(data_area_of_target,data_area_of_buf2,total_in_buf2); + memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); byte_count = le16_to_cpu(BCC_LE(pTargetSMB)); @@ -317,11 +319,11 @@ static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB) byte_count += total_in_buf2; /* BB also add check that we are not beyond maximum buffer size */ - + pTargetSMB->smb_buf_length = byte_count; - if(remaining == total_in_buf2) { - cFYI(1,("found the last secondary response")); + if (remaining == total_in_buf2) { + cFYI(1, ("found the last secondary response")); return 0; /* we are done */ } else /* more responses to go */ return 1; @@ -348,21 +350,21 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) int isMultiRsp; int reconnect; - allow_signal(SIGKILL); current->flags |= PF_MEMALLOC; server->tsk = current; /* save process info to wake at shutdown */ cFYI(1, ("Demultiplex PID: %d", current->pid)); - write_lock(&GlobalSMBSeslock); + write_lock(&GlobalSMBSeslock); atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; write_unlock(&GlobalSMBSeslock); complete(&cifsd_complete); - if(length > 1) { + if (length > 1) { mempool_resize(cifs_req_poolp, length + cifs_min_rcv, GFP_KERNEL); } + set_freezable(); while (!kthread_should_stop()) { if (try_to_freeze()) continue; @@ -425,10 +427,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) break; } if (!try_to_freeze() && (length == -EINTR)) { - cFYI(1,("cifsd thread killed")); + cFYI(1, ("cifsd thread killed")); break; } - cFYI(1,("Reconnect after unexpected peek error %d", + cFYI(1, ("Reconnect after unexpected peek error %d", length)); cifs_reconnect(server); csocket = server->ssocket; @@ -452,26 +454,26 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) with the most common, zero, as regular data */ temp = *((char *) smb_buffer); - /* Note that FC 1001 length is big endian on the wire, + /* Note that FC 1001 length is big endian on the wire, but we convert it here so it is always manipulated as host byte order */ pdu_length = ntohl(smb_buffer->smb_buf_length); smb_buffer->smb_buf_length = pdu_length; - cFYI(1,("rfc1002 length 0x%x)", pdu_length+4)); + cFYI(1, ("rfc1002 length 0x%x", pdu_length+4)); if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) { - continue; + continue; } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { - cFYI(1,("Good RFC 1002 session rsp")); + cFYI(1, ("Good RFC 1002 session rsp")); continue; } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { - /* we get this from Windows 98 instead of + /* we get this from Windows 98 instead of an error on SMB negprot response */ - cFYI(1,("Negative RFC1002 Session Response Error 0x%x)", + cFYI(1, ("Negative RFC1002 Session Response Error 0x%x)", pdu_length)); - if(server->tcpStatus == CifsNew) { - /* if nack on negprot (rather than + if (server->tcpStatus == CifsNew) { + /* if nack on negprot (rather than ret of smb negprot error) reconnecting not going to help, ret error to mount */ break; @@ -481,10 +483,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) msleep(1000); /* always try 445 first on reconnect since we get NACK on some if we ever - connected to port 139 (the NACK is + connected to port 139 (the NACK is since we do not begin with RFC1001 session initialize frame) */ - server->addr.sockAddr.sin_port = + server->addr.sockAddr.sin_port = htons(CIFS_PORT); cifs_reconnect(server); csocket = server->ssocket; @@ -492,7 +494,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) continue; } } else if (temp != (char) 0) { - cERROR(1,("Unknown RFC 1002 frame")); + cERROR(1, ("Unknown RFC 1002 frame")); cifs_dump_mem(" Received Data: ", (char *)smb_buffer, length); cifs_reconnect(server); @@ -501,7 +503,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } /* else we have an SMB response */ - if((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) || + if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) || (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) { cERROR(1, ("Invalid size SMB length %d pdu_length %d", length, pdu_length+4)); @@ -509,12 +511,12 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) csocket = server->ssocket; wake_up(&server->response_q); continue; - } + } /* else length ok */ reconnect = 0; - if(pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { + if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { isLargeBuf = TRUE; memcpy(bigbuf, smallbuf, 4); smb_buffer = bigbuf; @@ -522,11 +524,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) length = 0; iov.iov_base = 4 + (char *)smb_buffer; iov.iov_len = pdu_length; - for (total_read = 0; total_read < pdu_length; + for (total_read = 0; total_read < pdu_length; total_read += length) { length = kernel_recvmsg(csocket, &smb_msg, &iov, 1, pdu_length - total_read, 0); - if( kthread_should_stop() || + if ( kthread_should_stop() || (length == -EINTR)) { /* then will exit */ reconnect = 2; @@ -534,19 +536,19 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } else if (server->tcpStatus == CifsNeedReconnect) { cifs_reconnect(server); csocket = server->ssocket; - /* Reconnect wakes up rspns q */ + /* Reconnect wakes up rspns q */ /* Now we will reread sock */ reconnect = 1; break; - } else if ((length == -ERESTARTSYS) || + } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) { msleep(1); /* minimum sleep to prevent looping, - allowing socket to clear and app + allowing socket to clear and app threads to set tcpStatus CifsNeedReconnect if server hung*/ continue; } else if (length <= 0) { - cERROR(1,("Received no data, expecting %d", + cERROR(1, ("Received no data, expecting %d", pdu_length - total_read)); cifs_reconnect(server); csocket = server->ssocket; @@ -554,13 +556,13 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) break; } } - if(reconnect == 2) + if (reconnect == 2) break; - else if(reconnect == 1) + else if (reconnect == 1) continue; length += 4; /* account for rfc1002 hdr */ - + dump_smb(smb_buffer, length); if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) { @@ -574,28 +576,28 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - if ((mid_entry->mid == smb_buffer->Mid) && + if ((mid_entry->mid == smb_buffer->Mid) && (mid_entry->midState == MID_REQUEST_SUBMITTED) && (mid_entry->command == smb_buffer->Command)) { - if(check2ndT2(smb_buffer,server->maxBuf) > 0) { + if (check2ndT2(smb_buffer,server->maxBuf) > 0) { /* We have a multipart transact2 resp */ isMultiRsp = TRUE; - if(mid_entry->resp_buf) { + if (mid_entry->resp_buf) { /* merge response - fix up 1st*/ - if(coalesce_t2(smb_buffer, + if (coalesce_t2(smb_buffer, mid_entry->resp_buf)) { mid_entry->multiRsp = 1; break; } else { /* all parts received */ mid_entry->multiEnd = 1; - goto multi_t2_fnd; + goto multi_t2_fnd; } } else { - if(!isLargeBuf) { + if (!isLargeBuf) { cERROR(1,("1st trans2 resp needs bigbuf")); /* BB maybe we can fix this up, switch - to already allocated large buffer? */ + to already allocated large buffer? */ } else { /* Have first buffer */ mid_entry->resp_buf = @@ -605,9 +607,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } } break; - } + } mid_entry->resp_buf = smb_buffer; - if(isLargeBuf) + if (isLargeBuf) mid_entry->largeBuf = 1; else mid_entry->largeBuf = 0; @@ -627,24 +629,25 @@ multi_t2_fnd: spin_unlock(&GlobalMid_Lock); if (task_to_wake) { /* Was previous buf put in mpx struct for multi-rsp? */ - if(!isMultiRsp) { + if (!isMultiRsp) { /* smb buffer will be freed by user thread */ - if(isLargeBuf) { + if (isLargeBuf) { bigbuf = NULL; } else smallbuf = NULL; } wake_up_process(task_to_wake); } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE) - && (isMultiRsp == FALSE)) { - cERROR(1, ("No task to wake, unknown frame rcvd! NumMids %d", midCount.counter)); - cifs_dump_mem("Received Data is: ",(char *)smb_buffer, + && (isMultiRsp == FALSE)) { + cERROR(1, ("No task to wake, unknown frame received! " + "NumMids %d", midCount.counter)); + cifs_dump_mem("Received Data is: ", (char *)smb_buffer, sizeof(struct smb_hdr)); #ifdef CONFIG_CIFS_DEBUG2 cifs_dump_detail(smb_buffer); cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ - + } } /* end while !EXITING */ @@ -654,12 +657,12 @@ multi_t2_fnd: /* check if we have blocked requests that need to free */ /* Note that cifs_max_pending is normally 50, but can be set at module install time to as little as two */ - if(atomic_read(&server->inFlight) >= cifs_max_pending) + if (atomic_read(&server->inFlight) >= cifs_max_pending) atomic_set(&server->inFlight, cifs_max_pending - 1); /* We do not want to set the max_pending too low or we could end up with the counter going negative */ spin_unlock(&GlobalMid_Lock); - /* Although there should not be any requests blocked on + /* Although there should not be any requests blocked on this queue it can not hurt to be paranoid and try to wake up requests that may haven been blocked when more than 50 at time were on the wire to the same server - they now will see the session is in exit state @@ -667,8 +670,8 @@ multi_t2_fnd: wake_up_all(&server->request_q); /* give those requests time to exit */ msleep(125); - - if(server->ssocket) { + + if (server->ssocket) { sock_release(csocket); server->ssocket = NULL; } @@ -708,10 +711,10 @@ multi_t2_fnd: list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid_entry->midState == MID_REQUEST_SUBMITTED) { - cFYI(1, - ("Clearing Mid 0x%x - waking up ",mid_entry->mid)); + cFYI(1, ("Clearing Mid 0x%x - waking up ", + mid_entry->mid)); task_to_wake = mid_entry->tsk; - if(task_to_wake) { + if (task_to_wake) { wake_up_process(task_to_wake); } } @@ -723,7 +726,7 @@ multi_t2_fnd: } if (!list_empty(&server->pending_mid_q)) { - /* mpx threads have not exited yet give them + /* mpx threads have not exited yet give them at least the smb send timeout time for long ops */ /* due to delays on oplock break requests, we need to wait at least 45 seconds before giving up @@ -741,7 +744,7 @@ multi_t2_fnd: /* last chance to mark ses pointers invalid if there are any pointing to this (e.g - if a crazy root user tried to kill cifsd + if a crazy root user tried to kill cifsd kernel thread explicitly this might happen) */ list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, @@ -753,17 +756,18 @@ multi_t2_fnd: write_unlock(&GlobalSMBSeslock); kfree(server); - if(length > 0) { + if (length > 0) { mempool_resize(cifs_req_poolp, length + cifs_min_rcv, GFP_KERNEL); } - + return 0; } static int -cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) +cifs_parse_mount_options(char *options, const char *devname, + struct smb_vol *vol) { char *value; char *data; @@ -771,15 +775,15 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) char separator[2]; separator[0] = ','; - separator[1] = 0; + separator[1] = 0; if (Local_System_Name[0] != 0) - memcpy(vol->source_rfc1001_name, Local_System_Name,15); + memcpy(vol->source_rfc1001_name, Local_System_Name, 15); else { char *nodename = utsname()->nodename; - int n = strnlen(nodename,15); - memset(vol->source_rfc1001_name,0x20,15); - for(i=0 ; i < n ; i++) { + int n = strnlen(nodename, 15); + memset(vol->source_rfc1001_name, 0x20, 15); + for (i = 0; i < n; i++) { /* does not have to be perfect mapping since field is informational, only used for servers that do not support port 445 and it can be overridden at mount time */ @@ -804,31 +808,32 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) if (!options) return 1; - if(strncmp(options,"sep=",4) == 0) { - if(options[4] != 0) { + if (strncmp(options, "sep=", 4) == 0) { + if (options[4] != 0) { separator[0] = options[4]; options += 5; } else { - cFYI(1,("Null separator not allowed")); + cFYI(1, ("Null separator not allowed")); } } - + while ((data = strsep(&options, separator)) != NULL) { if (!*data) continue; if ((value = strchr(data, '=')) != NULL) *value++ = '\0'; - if (strnicmp(data, "user_xattr",10) == 0) {/*parse before user*/ + /* Have to parse this before we parse for "user" */ + if (strnicmp(data, "user_xattr", 10) == 0) { vol->no_xattr = 0; - } else if (strnicmp(data, "nouser_xattr",12) == 0) { + } else if (strnicmp(data, "nouser_xattr", 12) == 0) { vol->no_xattr = 1; } else if (strnicmp(data, "user", 4) == 0) { if (!value) { printk(KERN_WARNING "CIFS: invalid or missing username\n"); return 1; /* needs_arg; */ - } else if(!*value) { + } else if (!*value) { /* null user, ie anonymous, authentication */ vol->nullauth = 1; } @@ -842,12 +847,12 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) if (!value) { vol->password = NULL; continue; - } else if(value[0] == 0) { + } else if (value[0] == 0) { /* check if string begins with double comma since that would mean the password really does start with a comma, and would not indicate an empty string */ - if(value[1] != separator[0]) { + if (value[1] != separator[0]) { vol->password = NULL; continue; } @@ -856,7 +861,7 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* removed password length check, NTLM passwords can be arbitrarily long */ - /* if comma in password, the string will be + /* if comma in password, the string will be prematurely null terminated. Commas in password are specified across the cifs mount interface by a double comma ie ,, and a comma used as in other cases ie ',' @@ -866,18 +871,18 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) /* NB: password legally can have multiple commas and the only illegal character in a password is null */ - if ((value[temp_len] == 0) && + if ((value[temp_len] == 0) && (value[temp_len+1] == separator[0])) { /* reinsert comma */ value[temp_len] = separator[0]; - temp_len+=2; /* move after the second comma */ - while(value[temp_len] != 0) { + temp_len += 2; /* move after second comma */ + while (value[temp_len] != 0) { if (value[temp_len] == separator[0]) { - if (value[temp_len+1] == + if (value[temp_len+1] == separator[0]) { /* skip second comma */ temp_len++; - } else { + } else { /* single comma indicating start of next parm */ break; @@ -885,24 +890,25 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) } temp_len++; } - if(value[temp_len] == 0) { + if (value[temp_len] == 0) { options = NULL; } else { value[temp_len] = 0; /* point option to start of next parm */ options = value + temp_len + 1; } - /* go from value to value + temp_len condensing + /* go from value to value + temp_len condensing double commas to singles. Note that this ends up allocating a few bytes too many, which is ok */ vol->password = kzalloc(temp_len, GFP_KERNEL); - if(vol->password == NULL) { - printk("CIFS: no memory for pass\n"); + if (vol->password == NULL) { + printk(KERN_WARNING "CIFS: no memory " + "for password\n"); return 1; } - for(i=0,j=0;i<temp_len;i++,j++) { + for (i = 0, j = 0; i < temp_len; i++, j++) { vol->password[j] = value[i]; - if(value[i] == separator[0] + if (value[i] == separator[0] && value[i+1] == separator[0]) { /* skip second comma */ i++; @@ -911,8 +917,9 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->password[j] = 0; } else { vol->password = kzalloc(temp_len+1, GFP_KERNEL); - if(vol->password == NULL) { - printk("CIFS: no memory for pass\n"); + if (vol->password == NULL) { + printk(KERN_WARNING "CIFS: no memory " + "for password\n"); return 1; } strcpy(vol->password, value); @@ -923,20 +930,21 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) } else if (strnlen(value, 35) < 35) { vol->UNCip = value; } else { - printk(KERN_WARNING "CIFS: ip address too long\n"); + printk(KERN_WARNING "CIFS: ip address " + "too long\n"); return 1; } - } else if (strnicmp(data, "sec", 3) == 0) { - if (!value || !*value) { - cERROR(1,("no security value specified")); - continue; - } else if (strnicmp(value, "krb5i", 5) == 0) { - vol->secFlg |= CIFSSEC_MAY_KRB5 | + } else if (strnicmp(data, "sec", 3) == 0) { + if (!value || !*value) { + cERROR(1, ("no security value specified")); + continue; + } else if (strnicmp(value, "krb5i", 5) == 0) { + vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; } else if (strnicmp(value, "krb5p", 5) == 0) { - /* vol->secFlg |= CIFSSEC_MUST_SEAL | - CIFSSEC_MAY_KRB5; */ - cERROR(1,("Krb5 cifs privacy not supported")); + /* vol->secFlg |= CIFSSEC_MUST_SEAL | + CIFSSEC_MAY_KRB5; */ + cERROR(1, ("Krb5 cifs privacy not supported")); return 1; } else if (strnicmp(value, "krb5", 4) == 0) { vol->secFlg |= CIFSSEC_MAY_KRB5; @@ -956,33 +964,34 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->secFlg |= CIFSSEC_MAY_NTLMV2; #ifdef CONFIG_CIFS_WEAK_PW_HASH } else if (strnicmp(value, "lanman", 6) == 0) { - vol->secFlg |= CIFSSEC_MAY_LANMAN; + vol->secFlg |= CIFSSEC_MAY_LANMAN; #endif } else if (strnicmp(value, "none", 4) == 0) { vol->nullauth = 1; - } else { - cERROR(1,("bad security option: %s", value)); - return 1; - } + } else { + cERROR(1, ("bad security option: %s", value)); + return 1; + } } else if ((strnicmp(data, "unc", 3) == 0) || (strnicmp(data, "target", 6) == 0) || (strnicmp(data, "path", 4) == 0)) { if (!value || !*value) { - printk(KERN_WARNING - "CIFS: invalid path to network resource\n"); + printk(KERN_WARNING "CIFS: invalid path to " + "network resource\n"); return 1; /* needs_arg; */ } if ((temp_len = strnlen(value, 300)) < 300) { - vol->UNC = kmalloc(temp_len+1,GFP_KERNEL); + vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); if (vol->UNC == NULL) return 1; - strcpy(vol->UNC,value); + strcpy(vol->UNC, value); if (strncmp(vol->UNC, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; - } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { + } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { printk(KERN_WARNING - "CIFS: UNC Path does not begin with // or \\\\ \n"); + "CIFS: UNC Path does not begin " + "with // or \\\\ \n"); return 1; } } else { @@ -1001,43 +1010,47 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->domainname = value; cFYI(1, ("Domain name set")); } else { - printk(KERN_WARNING "CIFS: domain name too long\n"); + printk(KERN_WARNING "CIFS: domain name too " + "long\n"); return 1; } - } else if (strnicmp(data, "prefixpath", 10) == 0) { - if (!value || !*value) { - printk(KERN_WARNING - "CIFS: invalid path prefix\n"); - return 1; /* needs_arg; */ - } - if ((temp_len = strnlen(value, 1024)) < 1024) { + } else if (strnicmp(data, "prefixpath", 10) == 0) { + if (!value || !*value) { + printk(KERN_WARNING + "CIFS: invalid path prefix\n"); + return 1; /* needs_argument */ + } + if ((temp_len = strnlen(value, 1024)) < 1024) { if (value[0] != '/') temp_len++; /* missing leading slash */ - vol->prepath = kmalloc(temp_len+1,GFP_KERNEL); - if (vol->prepath == NULL) - return 1; + vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); + if (vol->prepath == NULL) + return 1; if (value[0] != '/') { vol->prepath[0] = '/'; - strcpy(vol->prepath+1,value); + strcpy(vol->prepath+1, value); } else - strcpy(vol->prepath,value); - cFYI(1,("prefix path %s",vol->prepath)); - } else { - printk(KERN_WARNING "CIFS: prefix too long\n"); - return 1; - } + strcpy(vol->prepath, value); + cFYI(1, ("prefix path %s", vol->prepath)); + } else { + printk(KERN_WARNING "CIFS: prefix too long\n"); + return 1; + } } else if (strnicmp(data, "iocharset", 9) == 0) { if (!value || !*value) { - printk(KERN_WARNING "CIFS: invalid iocharset specified\n"); + printk(KERN_WARNING "CIFS: invalid iocharset " + "specified\n"); return 1; /* needs_arg; */ } if (strnlen(value, 65) < 65) { - if (strnicmp(value,"default",7)) + if (strnicmp(value, "default", 7)) vol->iocharset = value; - /* if iocharset not set load_nls_default used by caller */ - cFYI(1, ("iocharset set to %s",value)); + /* if iocharset not set then load_nls_default + is used by caller */ + cFYI(1, ("iocharset set to %s", value)); } else { - printk(KERN_WARNING "CIFS: iocharset name too long.\n"); + printk(KERN_WARNING "CIFS: iocharset name " + "too long.\n"); return 1; } } else if (strnicmp(data, "uid", 3) == 0) { @@ -1089,54 +1102,59 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) } } else if (strnicmp(data, "netbiosname", 4) == 0) { if (!value || !*value || (*value == ' ')) { - cFYI(1,("invalid (empty) netbiosname specified")); + cFYI(1, ("invalid (empty) netbiosname")); } else { - memset(vol->source_rfc1001_name,0x20,15); - for(i=0;i<15;i++) { - /* BB are there cases in which a comma can be + memset(vol->source_rfc1001_name, 0x20, 15); + for (i = 0; i < 15; i++) { + /* BB are there cases in which a comma can be valid in this workstation netbios name (and need special handling)? */ /* We do not uppercase netbiosname for user */ - if (value[i]==0) + if (value[i] == 0) break; - else - vol->source_rfc1001_name[i] = value[i]; + else + vol->source_rfc1001_name[i] = + value[i]; } /* The string has 16th byte zero still from set at top of the function */ - if ((i==15) && (value[i] != 0)) - printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n"); + if ((i == 15) && (value[i] != 0)) + printk(KERN_WARNING "CIFS: netbiosname" + " longer than 15 truncated.\n"); } } else if (strnicmp(data, "servern", 7) == 0) { /* servernetbiosname specified override *SMBSERVER */ if (!value || !*value || (*value == ' ')) { - cFYI(1,("empty server netbiosname specified")); + cFYI(1, ("empty server netbiosname specified")); } else { /* last byte, type, is 0x20 for servr type */ - memset(vol->target_rfc1001_name,0x20,16); + memset(vol->target_rfc1001_name, 0x20, 16); - for(i=0;i<15;i++) { + for (i = 0; i < 15; i++) { /* BB are there cases in which a comma can be - valid in this workstation netbios name (and need - special handling)? */ + valid in this workstation netbios name + (and need special handling)? */ - /* user or mount helper must uppercase netbiosname */ - if (value[i]==0) + /* user or mount helper must uppercase + the netbiosname */ + if (value[i] == 0) break; else - vol->target_rfc1001_name[i] = value[i]; + vol->target_rfc1001_name[i] = + value[i]; } /* The string has 16th byte zero still from set at top of the function */ - if ((i==15) && (value[i] != 0)) - printk(KERN_WARNING "CIFS: server netbiosname longer than 15 truncated.\n"); + if ((i == 15) && (value[i] != 0)) + printk(KERN_WARNING "CIFS: server net" + "biosname longer than 15 truncated.\n"); } } else if (strnicmp(data, "credentials", 4) == 0) { /* ignore */ } else if (strnicmp(data, "version", 3) == 0) { /* ignore */ - } else if (strnicmp(data, "guest",5) == 0) { + } else if (strnicmp(data, "guest", 5) == 0) { /* ignore */ } else if (strnicmp(data, "rw", 2) == 0) { vol->rw = TRUE; @@ -1148,11 +1166,11 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) (strnicmp(data, "noauto", 6) == 0) || (strnicmp(data, "dev", 3) == 0)) { /* The mount tool or mount.cifs helper (if present) - uses these opts to set flags, and the flags are read - by the kernel vfs layer before we get here (ie - before read super) so there is no point trying to - parse these options again and set anything and it - is ok to just ignore them */ + uses these opts to set flags, and the flags are read + by the kernel vfs layer before we get here (ie + before read super) so there is no point trying to + parse these options again and set anything and it + is ok to just ignore them */ continue; } else if (strnicmp(data, "ro", 2) == 0) { vol->rw = FALSE; @@ -1168,26 +1186,31 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->remap = 1; } else if (strnicmp(data, "nomapchars", 10) == 0) { vol->remap = 0; - } else if (strnicmp(data, "sfu", 3) == 0) { - vol->sfu_emul = 1; - } else if (strnicmp(data, "nosfu", 5) == 0) { - vol->sfu_emul = 0; + } else if (strnicmp(data, "sfu", 3) == 0) { + vol->sfu_emul = 1; + } else if (strnicmp(data, "nosfu", 5) == 0) { + vol->sfu_emul = 0; } else if (strnicmp(data, "posixpaths", 10) == 0) { vol->posix_paths = 1; } else if (strnicmp(data, "noposixpaths", 12) == 0) { vol->posix_paths = 0; - } else if ((strnicmp(data, "nocase", 6) == 0) || + } else if (strnicmp(data, "nounix", 6) == 0) { + vol->no_linux_ext = 1; + } else if (strnicmp(data, "nolinux", 7) == 0) { + vol->no_linux_ext = 1; + } else if ((strnicmp(data, "nocase", 6) == 0) || (strnicmp(data, "ignorecase", 10) == 0)) { - vol->nocase = 1; + vol->nocase = 1; } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; - } else if ((strnicmp(data, "nobrl", 5) == 0) || + } else if ((strnicmp(data, "nobrl", 5) == 0) || (strnicmp(data, "nolock", 6) == 0)) { vol->nobrl = 1; /* turn off mandatory locking in mode if remote locking is turned off since the local vfs will do advisory */ - if(vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) + if (vol->file_mode == + (S_IALLUGO & ~(S_ISUID | S_IXGRP))) vol->file_mode = S_IALLUGO; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; @@ -1201,55 +1224,61 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) vol->intr = 0; } else if (strnicmp(data, "intr", 4) == 0) { vol->intr = 1; - } else if (strnicmp(data, "serverino",7) == 0) { + } else if (strnicmp(data, "serverino", 7) == 0) { vol->server_ino = 1; - } else if (strnicmp(data, "noserverino",9) == 0) { + } else if (strnicmp(data, "noserverino", 9) == 0) { vol->server_ino = 0; - } else if (strnicmp(data, "cifsacl",7) == 0) { + } else if (strnicmp(data, "cifsacl", 7) == 0) { vol->cifs_acl = 1; } else if (strnicmp(data, "nocifsacl", 9) == 0) { vol->cifs_acl = 0; - } else if (strnicmp(data, "acl",3) == 0) { + } else if (strnicmp(data, "acl", 3) == 0) { vol->no_psx_acl = 0; - } else if (strnicmp(data, "noacl",5) == 0) { + } else if (strnicmp(data, "noacl", 5) == 0) { vol->no_psx_acl = 1; - } else if (strnicmp(data, "sign",4) == 0) { + } else if (strnicmp(data, "sign", 4) == 0) { vol->secFlg |= CIFSSEC_MUST_SIGN; /* } else if (strnicmp(data, "seal",4) == 0) { vol->secFlg |= CIFSSEC_MUST_SEAL; */ - } else if (strnicmp(data, "direct",6) == 0) { + } else if (strnicmp(data, "direct", 6) == 0) { vol->direct_io = 1; - } else if (strnicmp(data, "forcedirectio",13) == 0) { + } else if (strnicmp(data, "forcedirectio", 13) == 0) { vol->direct_io = 1; - } else if (strnicmp(data, "in6_addr",8) == 0) { + } else if (strnicmp(data, "in6_addr", 8) == 0) { if (!value || !*value) { vol->in6_addr = NULL; } else if (strnlen(value, 49) == 48) { vol->in6_addr = value; } else { - printk(KERN_WARNING "CIFS: ip v6 address not 48 characters long\n"); + printk(KERN_WARNING "CIFS: ip v6 address not " + "48 characters long\n"); return 1; } } else if (strnicmp(data, "noac", 4) == 0) { - printk(KERN_WARNING "CIFS: Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); + printk(KERN_WARNING "CIFS: Mount option noac not " + "supported. Instead set " + "/proc/fs/cifs/LookupCacheEnabled to 0\n"); } else - printk(KERN_WARNING "CIFS: Unknown mount option %s\n",data); + printk(KERN_WARNING "CIFS: Unknown mount option %s\n", + data); } if (vol->UNC == NULL) { if (devname == NULL) { - printk(KERN_WARNING "CIFS: Missing UNC name for mount target\n"); + printk(KERN_WARNING "CIFS: Missing UNC name for mount " + "target\n"); return 1; } if ((temp_len = strnlen(devname, 300)) < 300) { - vol->UNC = kmalloc(temp_len+1,GFP_KERNEL); + vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); if (vol->UNC == NULL) return 1; - strcpy(vol->UNC,devname); + strcpy(vol->UNC, devname); if (strncmp(vol->UNC, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; } else if (strncmp(vol->UNC, "\\\\", 2) != 0) { - printk(KERN_WARNING "CIFS: UNC Path does not begin with // or \\\\ \n"); + printk(KERN_WARNING "CIFS: UNC Path does not " + "begin with // or \\\\ \n"); return 1; } } else { @@ -1257,14 +1286,14 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) return 1; } } - if(vol->UNCip == NULL) + if (vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; return 0; } static struct cifsSesInfo * -cifs_find_tcp_session(struct in_addr * target_ip_addr, +cifs_find_tcp_session(struct in_addr *target_ip_addr, struct in6_addr *target_ip6_addr, char *userName, struct TCP_Server_Info **psrvTcp) { @@ -1276,19 +1305,25 @@ cifs_find_tcp_session(struct in_addr * target_ip_addr, list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); if (ses->server) { - if((target_ip_addr && + if ((target_ip_addr && (ses->server->addr.sockAddr.sin_addr.s_addr == target_ip_addr->s_addr)) || (target_ip6_addr && memcmp(&ses->server->addr.sockAddr6.sin6_addr, - target_ip6_addr,sizeof(*target_ip6_addr)))){ - /* BB lock server and tcp session and increment use count here?? */ - *psrvTcp = ses->server; /* found a match on the TCP session */ + target_ip6_addr, sizeof(*target_ip6_addr)))) { + /* BB lock server and tcp session and increment + use count here?? */ + + /* found a match on the TCP session */ + *psrvTcp = ses->server; + /* BB check if reconnection needed */ if (strncmp (ses->userName, userName, MAX_USERNAME_SIZE) == 0){ read_unlock(&GlobalSMBSeslock); - return ses; /* found exact match on both tcp and SMB sessions */ + /* Found exact match on both TCP and + SMB sessions */ + return ses; } } } @@ -1319,7 +1354,8 @@ find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) /* BB lock tcon, server and tcp session and increment use count here? */ /* found a match on the TCP session */ /* BB check if reconnection needed */ - cFYI(1,("IP match, old UNC: %s new: %s", + cFYI(1, + ("IP match, old UNC: %s new: %s", tcon->treeName, uncName)); if (strncmp (tcon->treeName, uncName, @@ -1354,11 +1390,11 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, unsigned int num_referrals; int rc = 0; - rc = get_dfs_path(xid, pSesInfo,old_path, nls_codepage, + rc = get_dfs_path(xid, pSesInfo, old_path, nls_codepage, &num_referrals, &referrals, remap); /* BB Add in code to: if valid refrl, if not ip address contact - the helper that resolves tcp names, mount to it, try to + the helper that resolves tcp names, mount to it, try to tcon to it unmount it if fail */ kfree(referrals); @@ -1367,10 +1403,9 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, } int -get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, - const char *old_path, const struct nls_table *nls_codepage, - unsigned int *pnum_referrals, - unsigned char ** preferrals, int remap) +get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, + const struct nls_table *nls_codepage, unsigned int *pnum_referrals, + unsigned char **preferrals, int remap) { char *temp_unc; int rc = 0; @@ -1379,7 +1414,8 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, if (pSesInfo->ipc_tid == 0) { temp_unc = kmalloc(2 /* for slashes */ + - strnlen(pSesInfo->serverName,SERVER_NAME_LEN_WITH_NULL * 2) + strnlen(pSesInfo->serverName, + SERVER_NAME_LEN_WITH_NULL * 2) + 1 + 4 /* slash IPC$ */ + 2, GFP_KERNEL); if (temp_unc == NULL) @@ -1390,7 +1426,7 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$"); rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage); cFYI(1, - ("CIFS Tcon rc = %d ipc_tid = %d", rc,pSesInfo->ipc_tid)); + ("CIFS Tcon rc = %d ipc_tid = %d", rc, pSesInfo->ipc_tid)); kfree(temp_unc); } if (rc == 0) @@ -1401,62 +1437,63 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, } /* See RFC1001 section 14 on representation of Netbios names */ -static void rfc1002mangle(char * target,char * source, unsigned int length) +static void rfc1002mangle(char *target, char *source, unsigned int length) { - unsigned int i,j; + unsigned int i, j; - for(i=0,j=0;i<(length);i++) { + for (i = 0, j = 0; i < (length); i++) { /* mask a nibble at a time and encode */ target[j] = 'A' + (0x0F & (source[i] >> 4)); target[j+1] = 'A' + (0x0F & source[i]); - j+=2; + j += 2; } } static int -ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netbios_name, char * target_name) +ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, + char *netbios_name, char *target_name) { int rc = 0; int connected = 0; __be16 orig_port = 0; - if(*csocket == NULL) { - rc = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, csocket); + if (*csocket == NULL) { + rc = sock_create_kern(PF_INET, SOCK_STREAM, + IPPROTO_TCP, csocket); if (rc < 0) { - cERROR(1, ("Error %d creating socket",rc)); + cERROR(1, ("Error %d creating socket", rc)); *csocket = NULL; return rc; } else { /* BB other socket options to set KEEPALIVE, NODELAY? */ - cFYI(1,("Socket created")); - (*csocket)->sk->sk_allocation = GFP_NOFS; + cFYI(1, ("Socket created")); + (*csocket)->sk->sk_allocation = GFP_NOFS; } } psin_server->sin_family = AF_INET; - if(psin_server->sin_port) { /* user overrode default port */ + if (psin_server->sin_port) { /* user overrode default port */ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) psin_server, - sizeof (struct sockaddr_in),0); + sizeof (struct sockaddr_in), 0); if (rc >= 0) connected = 1; - } + } - if(!connected) { - /* save original port so we can retry user specified port + if (!connected) { + /* save original port so we can retry user specified port later if fall back ports fail this time */ orig_port = psin_server->sin_port; /* do not retry on the same port we just failed on */ - if(psin_server->sin_port != htons(CIFS_PORT)) { + if (psin_server->sin_port != htons(CIFS_PORT)) { psin_server->sin_port = htons(CIFS_PORT); rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) psin_server, - sizeof (struct sockaddr_in),0); + sizeof (struct sockaddr_in), 0); if (rc >= 0) connected = 1; } @@ -1464,60 +1501,63 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, if (!connected) { psin_server->sin_port = htons(RFC1001_PORT); rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) - psin_server, sizeof (struct sockaddr_in),0); - if (rc >= 0) + psin_server, + sizeof (struct sockaddr_in), 0); + if (rc >= 0) connected = 1; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { - if(orig_port) + if (orig_port) psin_server->sin_port = orig_port; - cFYI(1,("Error %d connecting to server via ipv4",rc)); + cFYI(1, ("Error %d connecting to server via ipv4", rc)); sock_release(*csocket); *csocket = NULL; return rc; } - /* Eventually check for other socket options to change from - the default. sock_setsockopt not used because it expects + /* Eventually check for other socket options to change from + the default. sock_setsockopt not used because it expects user space buffer */ - cFYI(1,("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",(*csocket)->sk->sk_sndbuf, + cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx", + (*csocket)->sk->sk_sndbuf, (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); (*csocket)->sk->sk_rcvtimeo = 7 * HZ; /* make the bufsizes depend on wsize/rsize and max requests */ - if((*csocket)->sk->sk_sndbuf < (200 * 1024)) + if ((*csocket)->sk->sk_sndbuf < (200 * 1024)) (*csocket)->sk->sk_sndbuf = 200 * 1024; - if((*csocket)->sk->sk_rcvbuf < (140 * 1024)) + if ((*csocket)->sk->sk_rcvbuf < (140 * 1024)) (*csocket)->sk->sk_rcvbuf = 140 * 1024; /* send RFC1001 sessinit */ - if(psin_server->sin_port == htons(RFC1001_PORT)) { + if (psin_server->sin_port == htons(RFC1001_PORT)) { /* some servers require RFC1001 sessinit before sending - negprot - BB check reconnection in case where second + negprot - BB check reconnection in case where second sessinit is sent but no second negprot */ - struct rfc1002_session_packet * ses_init_buf; - struct smb_hdr * smb_buf; - ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), GFP_KERNEL); - if(ses_init_buf) { + struct rfc1002_session_packet *ses_init_buf; + struct smb_hdr *smb_buf; + ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), + GFP_KERNEL); + if (ses_init_buf) { ses_init_buf->trailer.session_req.called_len = 32; - if(target_name && (target_name[0] != 0)) { + if (target_name && (target_name[0] != 0)) { rfc1002mangle(ses_init_buf->trailer.session_req.called_name, target_name, 16); } else { rfc1002mangle(ses_init_buf->trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME,16); + DEFAULT_CIFS_CALLED_NAME, 16); } ses_init_buf->trailer.session_req.calling_len = 32; /* calling name ends in null (byte 16) from old smb convention. */ - if(netbios_name && (netbios_name[0] !=0)) { + if (netbios_name && (netbios_name[0] != 0)) { rfc1002mangle(ses_init_buf->trailer.session_req.calling_name, - netbios_name,16); + netbios_name, 16); } else { rfc1002mangle(ses_init_buf->trailer.session_req.calling_name, - "LINUX_CIFS_CLNT",16); + "LINUX_CIFS_CLNT", 16); } ses_init_buf->trailer.session_req.scope1 = 0; ses_init_buf->trailer.session_req.scope2 = 0; @@ -1527,20 +1567,20 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, rc = smb_send(*csocket, smb_buf, 0x44, (struct sockaddr *)psin_server); kfree(ses_init_buf); - msleep(1); /* RFC1001 layer in at least one server + msleep(1); /* RFC1001 layer in at least one server requires very short break before negprot presumably because not expecting negprot to follow so fast. This is a simple - solution that works without + solution that works without complicating the code and causes no significant slowing down on mount for everyone else */ } - /* else the negprot may still work without this + /* else the negprot may still work without this even though malloc failed */ - + } - + return rc; } @@ -1551,41 +1591,42 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) int connected = 0; __be16 orig_port = 0; - if(*csocket == NULL) { - rc = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, csocket); + if (*csocket == NULL) { + rc = sock_create_kern(PF_INET6, SOCK_STREAM, + IPPROTO_TCP, csocket); if (rc < 0) { - cERROR(1, ("Error %d creating ipv6 socket",rc)); + cERROR(1, ("Error %d creating ipv6 socket", rc)); *csocket = NULL; return rc; } else { /* BB other socket options to set KEEPALIVE, NODELAY? */ - cFYI(1,("ipv6 Socket created")); + cFYI(1, ("ipv6 Socket created")); (*csocket)->sk->sk_allocation = GFP_NOFS; } } psin_server->sin6_family = AF_INET6; - if(psin_server->sin6_port) { /* user overrode default port */ + if (psin_server->sin6_port) { /* user overrode default port */ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) psin_server, - sizeof (struct sockaddr_in6),0); + sizeof (struct sockaddr_in6), 0); if (rc >= 0) connected = 1; - } + } - if(!connected) { - /* save original port so we can retry user specified port + if (!connected) { + /* save original port so we can retry user specified port later if fall back ports fail this time */ orig_port = psin_server->sin6_port; /* do not retry on the same port we just failed on */ - if(psin_server->sin6_port != htons(CIFS_PORT)) { + if (psin_server->sin6_port != htons(CIFS_PORT)) { psin_server->sin6_port = htons(CIFS_PORT); rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) psin_server, - sizeof (struct sockaddr_in6),0); + sizeof (struct sockaddr_in6), 0); if (rc >= 0) connected = 1; } @@ -1593,31 +1634,31 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) if (!connected) { psin_server->sin6_port = htons(RFC1001_PORT); rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *) - psin_server, sizeof (struct sockaddr_in6),0); - if (rc >= 0) + psin_server, sizeof (struct sockaddr_in6), 0); + if (rc >= 0) connected = 1; } /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { - if(orig_port) + if (orig_port) psin_server->sin6_port = orig_port; - cFYI(1,("Error %d connecting to server via ipv6",rc)); + cFYI(1, ("Error %d connecting to server via ipv6", rc)); sock_release(*csocket); *csocket = NULL; return rc; } - /* Eventually check for other socket options to change from - the default. sock_setsockopt not used because it expects + /* Eventually check for other socket options to change from + the default. sock_setsockopt not used because it expects user space buffer */ (*csocket)->sk->sk_rcvtimeo = 7 * HZ; - + return rc; } -void reset_cifs_unix_caps(int xid, struct cifsTconInfo * tcon, - struct super_block * sb, struct smb_vol * vol_info) +void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, + struct super_block *sb, struct smb_vol *vol_info) { /* if we are reconnecting then should we check to see if * any requested capabilities changed locally e.g. via @@ -1629,65 +1670,87 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo * tcon, * What if we wanted to mount the server share twice once with * and once without posixacls or posix paths? */ __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - - - if(!CIFSSMBQFSUnixInfo(xid, tcon)) { + + if (vol_info && vol_info->no_linux_ext) { + tcon->fsUnixInfo.Capability = 0; + tcon->unix_ext = 0; /* Unix Extensions disabled */ + cFYI(1, ("Linux protocol extensions disabled")); + return; + } else if (vol_info) + tcon->unix_ext = 1; /* Unix Extensions supported */ + + if (tcon->unix_ext == 0) { + cFYI(1, ("Unix extensions disabled so not set on reconnect")); + return; + } + + if (!CIFSSMBQFSUnixInfo(xid, tcon)) { __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - + /* check for reconnect case in which we do not want to change the mount behavior if we can avoid it */ - if(vol_info == NULL) { - /* turn off POSIX ACL and PATHNAMES if not set + if (vol_info == NULL) { + /* turn off POSIX ACL and PATHNAMES if not set originally at mount time */ if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - - - - } - + cap &= CIFS_UNIX_CAP_MASK; - if(vol_info && vol_info->no_psx_acl) + if (vol_info && vol_info->no_psx_acl) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - else if(CIFS_UNIX_POSIX_ACL_CAP & cap) { - cFYI(1,("negotiated posix acl support")); - if(sb) + else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { + cFYI(1, ("negotiated posix acl support")); + if (sb) sb->s_flags |= MS_POSIXACL; } - if(vol_info && vol_info->posix_paths == 0) + if (vol_info && vol_info->posix_paths == 0) cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { - cFYI(1,("negotiate posix pathnames")); - if(sb) - CIFS_SB(sb)->mnt_cifs_flags |= + else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cFYI(1, ("negotiate posix pathnames")); + if (sb) + CIFS_SB(sb)->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; } - + /* We might be setting the path sep back to a different form if we are reconnecting and the server switched its - posix path capability for this share */ - if(sb && (CIFS_SB(sb)->prepathlen > 0)) + posix path capability for this share */ + if (sb && (CIFS_SB(sb)->prepathlen > 0)) CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb)); - - cFYI(1,("Negotiate caps 0x%x",(int)cap)); + + if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) { + if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) { + CIFS_SB(sb)->rsize = 127 * 1024; +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("larger reads not supported by srv")); +#endif + } + } + + + cFYI(1, ("Negotiate caps 0x%x", (int)cap)); #ifdef CONFIG_CIFS_DEBUG2 - if(cap & CIFS_UNIX_FCNTL_CAP) - cFYI(1,("FCNTL cap")); - if(cap & CIFS_UNIX_EXTATTR_CAP) - cFYI(1,("EXTATTR cap")); - if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cFYI(1,("POSIX path cap")); - if(cap & CIFS_UNIX_XATTR_CAP) - cFYI(1,("XATTR cap")); - if(cap & CIFS_UNIX_POSIX_ACL_CAP) - cFYI(1,("POSIX ACL cap")); + if (cap & CIFS_UNIX_FCNTL_CAP) + cFYI(1, ("FCNTL cap")); + if (cap & CIFS_UNIX_EXTATTR_CAP) + cFYI(1, ("EXTATTR cap")); + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cFYI(1, ("POSIX path cap")); + if (cap & CIFS_UNIX_XATTR_CAP) + cFYI(1, ("XATTR cap")); + if (cap & CIFS_UNIX_POSIX_ACL_CAP) + cFYI(1, ("POSIX ACL cap")); + if (cap & CIFS_UNIX_LARGE_READ_CAP) + cFYI(1, ("very large read cap")); + if (cap & CIFS_UNIX_LARGE_WRITE_CAP) + cFYI(1, ("very large write cap")); #endif /* CIFS_DEBUG2 */ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - cFYI(1,("setting capabilities failed")); + cFYI(1, ("setting capabilities failed")); } } } @@ -1711,8 +1774,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, xid = GetXid(); /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */ - - memset(&volume_info,0,sizeof(struct smb_vol)); + + memset(&volume_info, 0, sizeof(struct smb_vol)); if (cifs_parse_mount_options(mount_data, devname, &volume_info)) { kfree(volume_info.UNC); kfree(volume_info.password); @@ -1722,15 +1785,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } if (volume_info.nullauth) { - cFYI(1,("null user")); + cFYI(1, ("null user")); volume_info.username = NULL; } else if (volume_info.username) { /* BB fixme parse for domain name here */ - cFYI(1, ("Username: %s ", volume_info.username)); + cFYI(1, ("Username: %s", volume_info.username)); } else { cifserror("No username specified"); - /* In userspace mount helper we can get user name from alternate - locations such as env variables and files on disk */ + /* In userspace mount helper we can get user name from alternate + locations such as env variables and files on disk */ kfree(volume_info.UNC); kfree(volume_info.password); kfree(volume_info.prepath); @@ -1739,18 +1802,20 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } if (volume_info.UNCip && volume_info.UNC) { - rc = cifs_inet_pton(AF_INET, volume_info.UNCip,&sin_server.sin_addr.s_addr); + rc = cifs_inet_pton(AF_INET, volume_info.UNCip, + &sin_server.sin_addr.s_addr); - if(rc <= 0) { + if (rc <= 0) { /* not ipv4 address, try ipv6 */ - rc = cifs_inet_pton(AF_INET6,volume_info.UNCip,&sin_server6.sin6_addr.in6_u); - if(rc > 0) + rc = cifs_inet_pton(AF_INET6, volume_info.UNCip, + &sin_server6.sin6_addr.in6_u); + if (rc > 0) address_type = AF_INET6; } else { address_type = AF_INET; } - - if(rc <= 0) { + + if (rc <= 0) { /* we failed translating address */ kfree(volume_info.UNC); kfree(volume_info.password); @@ -1762,9 +1827,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip)); /* success */ rc = 0; - } else if (volume_info.UNCip){ - /* BB using ip addr as server name connect to the DFS root below */ - cERROR(1,("Connecting to DFS root not implemented yet")); + } else if (volume_info.UNCip) { + /* BB using ip addr as server name to connect to the + DFS root below */ + cERROR(1, ("Connecting to DFS root not implemented yet")); kfree(volume_info.UNC); kfree(volume_info.password); kfree(volume_info.prepath); @@ -1772,7 +1838,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, return -EINVAL; } else /* which servers DFS root would we conect to */ { cERROR(1, - ("CIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified")); + ("CIFS mount error: No UNC path (e.g. -o " + "unc=//192.168.1.100/public) specified")); kfree(volume_info.UNC); kfree(volume_info.password); kfree(volume_info.prepath); @@ -1781,13 +1848,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } /* this is needed for ASCII cp to Unicode converts */ - if(volume_info.iocharset == NULL) { + if (volume_info.iocharset == NULL) { cifs_sb->local_nls = load_nls_default(); /* load_nls_default can not return null */ } else { cifs_sb->local_nls = load_nls(volume_info.iocharset); - if(cifs_sb->local_nls == NULL) { - cERROR(1,("CIFS mount error: iocharset %s not found",volume_info.iocharset)); + if (cifs_sb->local_nls == NULL) { + cERROR(1, ("CIFS mount error: iocharset %s not found", + volume_info.iocharset)); kfree(volume_info.UNC); kfree(volume_info.password); kfree(volume_info.prepath); @@ -1796,12 +1864,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } } - if(address_type == AF_INET) + if (address_type == AF_INET) existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, NULL /* no ipv6 addr */, volume_info.username, &srvTcp); - else if(address_type == AF_INET6) { - cFYI(1,("looking for ipv6 address")); + else if (address_type == AF_INET6) { + cFYI(1, ("looking for ipv6 address")); existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, &sin_server6.sin6_addr, volume_info.username, &srvTcp); @@ -1813,26 +1881,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, return -EINVAL; } - if (srvTcp) { - cFYI(1, ("Existing tcp session with server found")); + cFYI(1, ("Existing tcp session with server found")); } else { /* create socket */ if (volume_info.port) sin_server.sin_port = htons(volume_info.port); else sin_server.sin_port = 0; if (address_type == AF_INET6) { - cFYI(1,("attempting ipv6 connect")); + cFYI(1, ("attempting ipv6 connect")); /* BB should we allow ipv6 on port 139? */ /* other OS never observed in Wild doing 139 with v6 */ - rc = ipv6_connect(&sin_server6,&csocket); - } else - rc = ipv4_connect(&sin_server,&csocket, + rc = ipv6_connect(&sin_server6, &csocket); + } else + rc = ipv4_connect(&sin_server, &csocket, volume_info.source_rfc1001_name, volume_info.target_rfc1001_name); if (rc < 0) { - cERROR(1, - ("Error connecting to IPv4 socket. Aborting operation")); + cERROR(1, ("Error connecting to IPv4 socket. " + "Aborting operation")); if (csocket != NULL) sock_release(csocket); kfree(volume_info.UNC); @@ -1853,8 +1920,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, return rc; } else { memset(srvTcp, 0, sizeof (struct TCP_Server_Info)); - memcpy(&srvTcp->addr.sockAddr, &sin_server, sizeof (struct sockaddr_in)); - atomic_set(&srvTcp->inFlight,0); + memcpy(&srvTcp->addr.sockAddr, &sin_server, + sizeof (struct sockaddr_in)); + atomic_set(&srvTcp->inFlight, 0); /* BB Add code for ipv6 case too */ srvTcp->ssocket = csocket; srvTcp->protocolType = IPV4; @@ -1869,7 +1937,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd"); if ( IS_ERR(srvTcp->tsk) ) { rc = PTR_ERR(srvTcp->tsk); - cERROR(1,("error %d create cifsd thread", rc)); + cERROR(1, ("error %d create cifsd thread", rc)); srvTcp->tsk = NULL; sock_release(csocket); kfree(volume_info.UNC); @@ -1880,8 +1948,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } wait_for_completion(&cifsd_complete); rc = 0; - memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); - memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16); + memcpy(srvTcp->workstation_RFC1001_name, + volume_info.source_rfc1001_name, 16); + memcpy(srvTcp->server_RFC1001_name, + volume_info.target_rfc1001_name, 16); srvTcp->sequence_number = 0; } } @@ -1902,16 +1972,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, NIPQUAD(sin_server.sin_addr.s_addr)); } - if (!rc){ - /* volume_info.password freed at unmount */ + if (!rc) { + /* volume_info.password freed at unmount */ if (volume_info.password) pSesInfo->password = volume_info.password; if (volume_info.username) strncpy(pSesInfo->userName, - volume_info.username,MAX_USERNAME_SIZE); + volume_info.username, + MAX_USERNAME_SIZE); if (volume_info.domainname) { int len = strlen(volume_info.domainname); - pSesInfo->domainName = + pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL); if (pSesInfo->domainName) strcpy(pSesInfo->domainName, @@ -1921,46 +1992,48 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, pSesInfo->overrideSecFlg = volume_info.secFlg; down(&pSesInfo->sesSem); /* BB FIXME need to pass vol->secFlgs BB */ - rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls); + rc = cifs_setup_session(xid, pSesInfo, + cifs_sb->local_nls); up(&pSesInfo->sesSem); if (!rc) atomic_inc(&srvTcp->socketUseCount); } else kfree(volume_info.password); } - + /* search for existing tcon to this server share */ if (!rc) { if (volume_info.rsize > CIFSMaxBufSize) { - cERROR(1,("rsize %d too large, using MaxBufSize", + cERROR(1, ("rsize %d too large, using MaxBufSize", volume_info.rsize)); cifs_sb->rsize = CIFSMaxBufSize; - } else if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize)) + } else if ((volume_info.rsize) && + (volume_info.rsize <= CIFSMaxBufSize)) cifs_sb->rsize = volume_info.rsize; else /* default */ cifs_sb->rsize = CIFSMaxBufSize; if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { - cERROR(1,("wsize %d too large using 4096 instead", + cERROR(1, ("wsize %d too large, using 4096 instead", volume_info.wsize)); cifs_sb->wsize = 4096; } else if (volume_info.wsize) cifs_sb->wsize = volume_info.wsize; else - cifs_sb->wsize = + cifs_sb->wsize = min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, 127*1024); /* old default of CIFSMaxBufSize was too small now - that SMB Write2 can send multiple pages in kvec. + that SMB Write2 can send multiple pages in kvec. RFC1001 does not describe what happens when frame bigger than 128K is sent so use that as max in conjunction with 52K kvec constraint on arch with 4K page size */ if (cifs_sb->rsize < 2048) { - cifs_sb->rsize = 2048; + cifs_sb->rsize = 2048; /* Windows ME may prefer this */ - cFYI(1,("readsize set to minimum 2048")); + cFYI(1, ("readsize set to minimum: 2048")); } /* calculate prepath */ cifs_sb->prepath = volume_info.prepath; @@ -1968,14 +2041,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cifs_sb->prepathlen = strlen(cifs_sb->prepath); cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb); volume_info.prepath = NULL; - } else + } else cifs_sb->prepathlen = 0; cifs_sb->mnt_uid = volume_info.linux_uid; cifs_sb->mnt_gid = volume_info.linux_gid; cifs_sb->mnt_file_mode = volume_info.file_mode; cifs_sb->mnt_dir_mode = volume_info.dir_mode; - cFYI(1,("file mode: 0x%x dir mode: 0x%x", - cifs_sb->mnt_file_mode,cifs_sb->mnt_dir_mode)); + cFYI(1, ("file mode: 0x%x dir mode: 0x%x", + cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode)); if (volume_info.noperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; @@ -1998,7 +2071,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (volume_info.override_gid) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; if (volume_info.direct_io) { - cFYI(1,("mounting share using direct i/o")); + cFYI(1, ("mounting share using direct i/o")); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } @@ -2009,7 +2082,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, cFYI(1, ("Found match on UNC path")); /* we can have only one retry value for a connection to a share so for resources mounted more than once - to the same server share the last value passed in + to the same server share the last value passed in for the retry flag is used */ tcon->retry = volume_info.retry; tcon->nocase = volume_info.nocase; @@ -2018,17 +2091,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (tcon == NULL) rc = -ENOMEM; else { - /* check for null share name ie connecting to + /* check for null share name ie connecting to * dfs root */ - /* BB check if this works for exactly length + /* BB check if this works for exactly length * three strings */ if ((strchr(volume_info.UNC + 3, '\\') == NULL) && (strchr(volume_info.UNC + 3, '/') == NULL)) { rc = connect_to_dfs_path(xid, pSesInfo, "", cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(volume_info.UNC); FreeXid(xid); @@ -2037,7 +2110,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* BB Do we need to wrap sesSem around * this TCon call and Unix SetFS as * we do on SessSetup and reconnect? */ - rc = CIFSTCon(xid, pSesInfo, + rc = CIFSTCon(xid, pSesInfo, volume_info.UNC, tcon, cifs_sb->local_nls); cFYI(1, ("CIFS Tcon rc = %d", rc)); @@ -2074,9 +2147,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, always wake up processes blocked in tcp in recv_mesg then we could remove the send_sig call */ - send_sig(SIGKILL,srvTcp->tsk,1); + force_sig(SIGKILL, srvTcp->tsk); tsk = srvTcp->tsk; - if(tsk) + if (tsk) kthread_stop(tsk); } } @@ -2085,15 +2158,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, tconInfoFree(tcon); if (existingCifsSes == NULL) { if (pSesInfo) { - if ((pSesInfo->server) && + if ((pSesInfo->server) && (pSesInfo->status == CifsGood)) { int temp_rc; temp_rc = CIFSSMBLogoff(xid, pSesInfo); /* if the socketUseCount is now zero */ if ((temp_rc == -ESHUTDOWN) && - (pSesInfo->server) && (pSesInfo->server->tsk)) { + (pSesInfo->server) && + (pSesInfo->server->tsk)) { struct task_struct *tsk; - send_sig(SIGKILL,pSesInfo->server->tsk,1); + force_sig(SIGKILL, + pSesInfo->server->tsk); tsk = pSesInfo->server->tsk; if (tsk) kthread_stop(tsk); @@ -2112,19 +2187,29 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* do not care if following two calls succeed - informational */ CIFSSMBQFSDeviceInfo(xid, tcon); CIFSSMBQFSAttributeInfo(xid, tcon); - + /* tell server which Unix caps we support */ if (tcon->ses->capabilities & CAP_UNIX) + /* reset of caps checks mount to see if unix extensions + disabled for just this mount */ reset_cifs_unix_caps(xid, tcon, sb, &volume_info); - + else + tcon->unix_ext = 0; /* server does not support them */ + + if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) { + cifs_sb->rsize = 1024 * 127; +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("no very large read support, rsize now 127K")); +#endif + } if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) cifs_sb->wsize = min(cifs_sb->wsize, (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)); if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) - cifs_sb->rsize = min(cifs_sb->rsize, - (tcon->ses->server->maxBuf - - MAX_CIFS_HDR_SIZE)); + cifs_sb->rsize = min(cifs_sb->rsize, + (tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE)); } /* volume_info.password is freed above when existing session found @@ -2177,7 +2262,8 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq); - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -2196,7 +2282,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, } pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - pSMB->req_no_secext.CaseInsensitivePasswordLength = + pSMB->req_no_secext.CaseInsensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = @@ -2214,9 +2300,9 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, } if (user == NULL) bytes_returned = 0; /* skip null user */ - else + else bytes_returned = - cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, + cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, nls_codepage); /* convert number of 16 bit words to bytes */ bcc_ptr += 2 * bytes_returned; @@ -2246,7 +2332,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; } else { - if (user != NULL) { + if (user != NULL) { strncpy(bcc_ptr, user, 200); bcc_ptr += strnlen(user, 200); } @@ -2281,11 +2367,12 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, __u16 action = le16_to_cpu(pSMBr->resp.Action); __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); /* do we want to mark SesInfo struct ? */ - ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */ + cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */ + ses->Suid = smb_buffer_response->Uid; /* UID left in wire format + (little endian) */ cFYI(1, ("UID = %d ", ses->Suid)); - /* response can have either 3 or 4 word count - Samba sends 3 */ - bcc_ptr = pByteArea(smb_buffer_response); + /* response can have either 3 or 4 word count - Samba sends 3 */ + bcc_ptr = pByteArea(smb_buffer_response); if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) && (blob_len < pSMBr->resp.ByteCount))) { @@ -2295,8 +2382,10 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { if ((long) (bcc_ptr) % 2) { remaining_words = - (BCC(smb_buffer_response) - 1) /2; - bcc_ptr++; /* Unicode strings must be word aligned */ + (BCC(smb_buffer_response) - 1) / 2; + /* Unicode strings must be word + aligned */ + bcc_ptr++; } else { remaining_words = BCC(smb_buffer_response) / 2; @@ -2307,13 +2396,15 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ - if(ses->serverOS) + if (ses->serverOS) kfree(ses->serverOS); - ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); - if(ses->serverOS == NULL) + ses->serverOS = kzalloc(2 * (len + 1), + GFP_KERNEL); + if (ses->serverOS == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverOS, - (__le16 *)bcc_ptr, len,nls_codepage); + (__le16 *)bcc_ptr, + len, nls_codepage); bcc_ptr += 2 * (len + 1); remaining_words -= len + 1; ses->serverOS[2 * len] = 0; @@ -2322,42 +2413,49 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, len = UniStrnlen((wchar_t *)bcc_ptr, remaining_words-1); kfree(ses->serverNOS); - ses->serverNOS = kzalloc(2 * (len + 1),GFP_KERNEL); - if(ses->serverNOS == NULL) + ses->serverNOS = kzalloc(2 * (len + 1), + GFP_KERNEL); + if (ses->serverNOS == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverNOS, - (__le16 *)bcc_ptr,len,nls_codepage); + (__le16 *)bcc_ptr, + len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverNOS[2 * len] = 0; ses->serverNOS[1 + (2 * len)] = 0; - if(strncmp(ses->serverNOS, - "NT LAN Manager 4",16) == 0) { - cFYI(1,("NT4 server")); + if (strncmp(ses->serverNOS, + "NT LAN Manager 4", 16) == 0) { + cFYI(1, ("NT4 server")); ses->flags |= CIFS_SES_NT4; } remaining_words -= len + 1; if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); - /* last string is not always null terminated (for e.g. for Windows XP & 2000) */ - if(ses->serverDomain) + /* last string is not always null terminated + (for e.g. for Windows XP & 2000) */ + if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = - kzalloc(2*(len+1),GFP_KERNEL); - if(ses->serverDomain == NULL) + kzalloc(2*(len+1), + GFP_KERNEL); + if (ses->serverDomain == NULL) goto sesssetup_nomem; cifs_strfromUCS_le(ses->serverDomain, - (__le16 *)bcc_ptr,len,nls_codepage); + (__le16 *)bcc_ptr, + len, nls_codepage); bcc_ptr += 2 * (len + 1); ses->serverDomain[2*len] = 0; ses->serverDomain[1+(2*len)] = 0; - } /* else no more room so create dummy domain string */ - else { - if(ses->serverDomain) + } else { /* else no more room so create + dummy domain string */ + if (ses->serverDomain) kfree(ses->serverDomain); - ses->serverDomain = + ses->serverDomain = kzalloc(2, GFP_KERNEL); } - } else { /* no room so create dummy domain and NOS string */ + } else { /* no room so create dummy domain + and NOS string */ + /* if these kcallocs fail not much we can do, but better to not fail the sesssetup itself */ @@ -2374,19 +2472,22 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { kfree(ses->serverOS); - ses->serverOS = kzalloc(len + 1,GFP_KERNEL); - if(ses->serverOS == NULL) + ses->serverOS = kzalloc(len + 1, + GFP_KERNEL); + if (ses->serverOS == NULL) goto sesssetup_nomem; - strncpy(ses->serverOS,bcc_ptr, len); + strncpy(ses->serverOS, bcc_ptr, len); bcc_ptr += len; - bcc_ptr[0] = 0; /* null terminate the string */ + /* null terminate the string */ + bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); kfree(ses->serverNOS); - ses->serverNOS = kzalloc(len + 1,GFP_KERNEL); - if(ses->serverNOS == NULL) + ses->serverNOS = kzalloc(len + 1, + GFP_KERNEL); + if (ses->serverNOS == NULL) goto sesssetup_nomem; strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len; @@ -2394,23 +2495,27 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr++; len = strnlen(bcc_ptr, 1024); - if(ses->serverDomain) + if (ses->serverDomain) kfree(ses->serverDomain); - ses->serverDomain = kzalloc(len + 1,GFP_KERNEL); - if(ses->serverDomain == NULL) + ses->serverDomain = kzalloc(len + 1, + GFP_KERNEL); + if (ses->serverDomain == NULL) goto sesssetup_nomem; - strncpy(ses->serverDomain, bcc_ptr, len); + strncpy(ses->serverDomain, bcc_ptr, + len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, - ("Variable field of length %d extends beyond end of smb ", + ("Variable field of length %d " + "extends beyond end of smb ", len)); } } else { cERROR(1, - (" Security Blob Length extends beyond end of SMB")); + (" Security Blob Length extends beyond " + "end of SMB")); } } else { cERROR(1, @@ -2429,7 +2534,7 @@ sesssetup_nomem: /* do not return an error on nomem for the info strings, static int CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, - struct cifsSesInfo *ses, int * pNTLMv2_flag, + struct cifsSesInfo *ses, int *pNTLMv2_flag, const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; @@ -2449,7 +2554,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, __u16 count; cFYI(1, ("In NTLMSSP sesssetup (negotiate)")); - if(ses == NULL) + if (ses == NULL) return -EINVAL; domain = ses->domainName; *pNTLMv2_flag = FALSE; @@ -2473,7 +2578,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf); pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -2501,9 +2606,9 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_56 | /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128; - if(sign_CIFS_PDUs) + if (sign_CIFS_PDUs) negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN; -/* if(ntlmv2_support) +/* if (ntlmv2_support) negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/ /* setup pointers to domain name and workstation name */ bcc_ptr += SecurityBlobLength; @@ -2573,11 +2678,11 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); - /* Do we want to set anything in SesInfo struct when guest login? */ + cFYI(1, (" Guest login")); + /* Do we want to set anything in SesInfo struct when guest login? */ - bcc_ptr = pByteArea(smb_buffer_response); - /* response can have either 3 or 4 word count - Samba sends 3 */ + bcc_ptr = pByteArea(smb_buffer_response); + /* response can have either 3 or 4 word count - Samba sends 3 */ SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr; if (SecurityBlob2->MessageType != NtLmChallenge) { @@ -2585,7 +2690,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, ("Unexpected NTLMSSP message type received %d", SecurityBlob2->MessageType)); } else if (ses) { - ses->Suid = smb_buffer_response->Uid; /* UID left in le format */ + ses->Suid = smb_buffer_response->Uid; /* UID left in le format */ cFYI(1, ("UID = %d", ses->Suid)); if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) @@ -2603,18 +2708,18 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, memcpy(ses->server->cryptKey, SecurityBlob2->Challenge, CIFS_CRYPTO_KEY_SIZE); - if(SecurityBlob2->NegotiateFlags & + if (SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2)) *pNTLMv2_flag = TRUE; - if((SecurityBlob2->NegotiateFlags & - cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) + if ((SecurityBlob2->NegotiateFlags & + cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) || (sign_CIFS_PDUs > 1)) - ses->server->secMode |= - SECMODE_SIGN_REQUIRED; - if ((SecurityBlob2->NegotiateFlags & + ses->server->secMode |= + SECMODE_SIGN_REQUIRED; + if ((SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs)) - ses->server->secMode |= + ses->server->secMode |= SECMODE_SIGN_ENABLED; if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { @@ -2622,7 +2727,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, remaining_words = (BCC(smb_buffer_response) - 1) / 2; - bcc_ptr++; /* Unicode strings must be word aligned */ + /* Must word align unicode strings */ + bcc_ptr++; } else { remaining_words = BCC @@ -2634,7 +2740,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ - if(ses->serverOS) + if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); @@ -2667,8 +2773,9 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, (2 * len)] = 0; remaining_words -= len + 1; if (remaining_words > 0) { - len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); - /* last string is not always null terminated (for e.g. for Windows XP & 2000) */ + len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); + /* last string not always null terminated + (for e.g. for Windows XP & 2000) */ kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * @@ -2706,7 +2813,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { - if(ses->serverOS) + if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, @@ -2733,18 +2840,20 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, ses->serverDomain = kzalloc(len + 1, GFP_KERNEL); - strncpy(ses->serverDomain, bcc_ptr, len); + strncpy(ses->serverDomain, + bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, - ("Variable field of length %d extends beyond end of smb", + ("field of length %d " + "extends beyond end of smb", len)); } } else { - cERROR(1, - (" Security Blob Length extends beyond end of SMB")); + cERROR(1, ("Security Blob Length extends beyond" + " end of SMB")); } } else { cERROR(1, ("No session structure passed in.")); @@ -2783,7 +2892,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, __u16 count; cFYI(1, ("In NTLMSSPSessSetup (Authenticate)")); - if(ses == NULL) + if (ses == NULL) return -EINVAL; user = ses->userName; domain = ses->domainName; @@ -2808,7 +2917,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, pSMB->req.hdr.Uid = ses->Suid; - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | @@ -2832,13 +2941,13 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8); SecurityBlob->MessageType = NtLmAuthenticate; bcc_ptr += SecurityBlobLength; - negotiate_flags = + negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | 0x80000000 | NTLMSSP_NEGOTIATE_128; - if(sign_CIFS_PDUs) + if (sign_CIFS_PDUs) negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; - if(ntlmv2_flag) + if (ntlmv2_flag) negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ @@ -2902,13 +3011,17 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, cpu_to_le16(len); } - /* SecurityBlob->WorkstationName.Length = cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage); + /* SecurityBlob->WorkstationName.Length = + cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage); SecurityBlob->WorkstationName.Length *= 2; - SecurityBlob->WorkstationName.MaximumLength = cpu_to_le16(SecurityBlob->WorkstationName.Length); - SecurityBlob->WorkstationName.Buffer = cpu_to_le32(SecurityBlobLength); + SecurityBlob->WorkstationName.MaximumLength = + cpu_to_le16(SecurityBlob->WorkstationName.Length); + SecurityBlob->WorkstationName.Buffer = + cpu_to_le32(SecurityBlobLength); bcc_ptr += SecurityBlob->WorkstationName.Length; SecurityBlobLength += SecurityBlob->WorkstationName.Length; - SecurityBlob->WorkstationName.Length = cpu_to_le16(SecurityBlob->WorkstationName.Length); */ + SecurityBlob->WorkstationName.Length = + cpu_to_le16(SecurityBlob->WorkstationName.Length); */ if ((long) bcc_ptr % 2) { *bcc_ptr = 0; @@ -2994,17 +3107,20 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); if (action & GUEST_LOGIN) - cFYI(1, (" Guest login")); /* BB do we want to set anything in SesInfo struct ? */ -/* if(SecurityBlob2->MessageType != NtLm??){ - cFYI("Unexpected message type on auth response is %d ")); - } */ + cFYI(1, (" Guest login")); /* BB Should we set anything + in SesInfo struct ? */ +/* if (SecurityBlob2->MessageType != NtLm??) { + cFYI("Unexpected message type on auth response is %d")); + } */ + if (ses) { cFYI(1, - ("Does UID on challenge %d match auth response UID %d ", + ("Check challenge UID %d vs auth response UID %d", ses->Suid, smb_buffer_response->Uid)); - ses->Suid = smb_buffer_response->Uid; /* UID left in wire format */ - bcc_ptr = pByteArea(smb_buffer_response); - /* response can have either 3 or 4 word count - Samba sends 3 */ + /* UID left in wire format */ + ses->Suid = smb_buffer_response->Uid; + bcc_ptr = pByteArea(smb_buffer_response); + /* response can have either 3 or 4 word count - Samba sends 3 */ if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) && (blob_len < @@ -3034,7 +3150,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ - if(ses->serverOS) + if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); @@ -3066,9 +3182,9 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, ses->serverNOS[1+(2*len)] = 0; remaining_words -= len + 1; if (remaining_words > 0) { - len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); + len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string not always null terminated (e.g. for Windows XP & 2000) */ - if(ses->serverDomain) + if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2 * @@ -3096,12 +3212,12 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, = 0; } /* else no more room so create dummy domain string */ else { - if(ses->serverDomain) + if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2,GFP_KERNEL); } } else { /* no room so create dummy domain and NOS string */ - if(ses->serverDomain) + if (ses->serverDomain) kfree(ses->serverDomain); ses->serverDomain = kzalloc(2, GFP_KERNEL); kfree(ses->serverNOS); @@ -3109,10 +3225,10 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, } } else { /* ASCII */ len = strnlen(bcc_ptr, 1024); - if (((long) bcc_ptr + len) - - (long) pByteArea(smb_buffer_response) - <= BCC(smb_buffer_response)) { - if(ses->serverOS) + if (((long) bcc_ptr + len) - + (long) pByteArea(smb_buffer_response) + <= BCC(smb_buffer_response)) { + if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1,GFP_KERNEL); strncpy(ses->serverOS,bcc_ptr, len); @@ -3123,28 +3239,35 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, len = strnlen(bcc_ptr, 1024); kfree(ses->serverNOS); - ses->serverNOS = kzalloc(len+1,GFP_KERNEL); - strncpy(ses->serverNOS, bcc_ptr, len); + ses->serverNOS = kzalloc(len+1, + GFP_KERNEL); + strncpy(ses->serverNOS, + bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; len = strnlen(bcc_ptr, 1024); - if(ses->serverDomain) + if (ses->serverDomain) kfree(ses->serverDomain); - ses->serverDomain = kzalloc(len+1,GFP_KERNEL); - strncpy(ses->serverDomain, bcc_ptr, len); + ses->serverDomain = + kzalloc(len+1, + GFP_KERNEL); + strncpy(ses->serverDomain, + bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; bcc_ptr++; } else cFYI(1, - ("Variable field of length %d extends beyond end of smb ", + ("field of length %d " + "extends beyond end of smb ", len)); } } else { cERROR(1, - (" Security Blob Length extends beyond end of SMB")); + (" Security Blob extends beyond end " + "of SMB")); } } else { cERROR(1, ("No session structure passed in.")); @@ -3196,7 +3319,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, pSMB->AndXCommand = 0xFF; pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); bcc_ptr = &pSMB->Password[0]; - if((ses->server->secMode) & SECMODE_USER) { + if ((ses->server->secMode) & SECMODE_USER) { pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ *bcc_ptr = 0; /* password is null byte */ bcc_ptr++; /* skip password */ @@ -3210,7 +3333,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, by Samba (not sure whether other servers allow NTLMv2 password here) */ #ifdef CONFIG_CIFS_WEAK_PW_HASH - if((extended_security & CIFSSEC_MAY_LANMAN) && + if ((extended_security & CIFSSEC_MAY_LANMAN) && (ses->server->secType == LANMAN)) calc_lanman_hash(ses, bcc_ptr); else @@ -3220,14 +3343,14 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; - if(ses->capabilities & CAP_UNICODE) { + if (ses->capabilities & CAP_UNICODE) { /* must align unicode strings */ *bcc_ptr = 0; /* null byte password */ bcc_ptr++; } } - if(ses->server->secMode & + if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; @@ -3240,8 +3363,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; length = - cifs_strtoUCS((__le16 *) bcc_ptr, tree, - 6 /* max utf8 char length in bytes */ * + cifs_strtoUCS((__le16 *) bcc_ptr, tree, + 6 /* max utf8 char length in bytes */ * (/* server len*/ + 256 /* share len */), nls_codepage); bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ bcc_ptr += 2; /* skip trailing null */ @@ -3265,8 +3388,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); - /* skip service field (NB: this field is always ASCII) */ - bcc_ptr += length + 1; + /* skip service field (NB: this field is always ASCII) */ + bcc_ptr += length + 1; strncpy(tcon->treeName, tree, MAX_TREE_SIZE); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { length = UniStrnlen((wchar_t *) bcc_ptr, 512); @@ -3284,7 +3407,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr[1] = 0; bcc_ptr += 2; } - /* else do not bother copying these informational fields */ + /* else do not bother copying these information fields*/ } else { length = strnlen(bcc_ptr, 1024); if ((bcc_ptr + length) - @@ -3296,9 +3419,9 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, strncpy(tcon->nativeFileSystem, bcc_ptr, length); } - /* else do not bother copying these informational fields */ + /* else do not bother copying these information fields*/ } - if((smb_buffer_response->WordCount == 3) || + if ((smb_buffer_response->WordCount == 3) || (smb_buffer_response->WordCount == 7)) /* field is in same location */ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); @@ -3306,7 +3429,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, tcon->Flags = 0; cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags)); } else if ((rc == 0) && tcon == NULL) { - /* all we need to save for IPC$ connection */ + /* all we need to save for IPC$ connection */ ses->ipc_tid = smb_buffer_response->Tid; } @@ -3322,7 +3445,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) int xid; struct cifsSesInfo *ses = NULL; struct task_struct *cifsd_task; - char * tmp; + char *tmp; xid = GetXid(); @@ -3343,9 +3466,9 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) FreeXid(xid); return 0; } else if (rc == -ESHUTDOWN) { - cFYI(1,("Waking up socket by sending it signal")); + cFYI(1, ("Waking up socket by sending signal")); if (cifsd_task) { - send_sig(SIGKILL,cifsd_task,1); + force_sig(SIGKILL, cifsd_task); kthread_stop(cifsd_task); } rc = 0; @@ -3354,7 +3477,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) } else cFYI(1, ("No session or bad tcon")); } - + cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; @@ -3366,11 +3489,11 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) sesInfoFree(ses); FreeXid(xid); - return rc; /* BB check if we should always return zero here */ -} + return rc; /* BB check if we should always return zero here */ +} int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, - struct nls_table * nls_info) + struct nls_table *nls_info) { int rc = 0; char ntlm_session_key[CIFS_SESS_KEY_SIZE]; @@ -3378,16 +3501,16 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, int first_time = 0; /* what if server changes its buffer size after dropping the session? */ - if(pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ { + if (pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ { rc = CIFSSMBNegotiate(xid, pSesInfo); - if(rc == -EAGAIN) /* retry only once on 1st time connection */ { + if (rc == -EAGAIN) /* retry only once on 1st time connection */ { rc = CIFSSMBNegotiate(xid, pSesInfo); - if(rc == -EAGAIN) + if (rc == -EAGAIN) rc = -EHOSTDOWN; } - if(rc == 0) { + if (rc == 0) { spin_lock(&GlobalMid_Lock); - if(pSesInfo->server->tcpStatus != CifsExiting) + if (pSesInfo->server->tcpStatus != CifsExiting) pSesInfo->server->tcpStatus = CifsGood; else rc = -EHOSTDOWN; @@ -3399,18 +3522,19 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, if (!rc) { pSesInfo->flags = 0; pSesInfo->capabilities = pSesInfo->server->capabilities; - if(linuxExtEnabled == 0) + if (linuxExtEnabled == 0) pSesInfo->capabilities &= (~CAP_UNIX); /* pSesInfo->sequence_number = 0;*/ - cFYI(1,("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", + cFYI(1, + ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", pSesInfo->server->secMode, pSesInfo->server->capabilities, pSesInfo->server->timeAdj)); - if(experimEnabled < 2) + if (experimEnabled < 2) rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info); else if (extended_security - && (pSesInfo->capabilities + && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) && (pSesInfo->server->secType == NTLMSSP)) { rc = -EOPNOTSUPP; @@ -3423,21 +3547,22 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, &ntlmv2_flag, nls_info); if (!rc) { - if(ntlmv2_flag) { - char * v2_response; - cFYI(1,("more secure NTLM ver2 hash")); - if(CalcNTLMv2_partial_mac_key(pSesInfo, + if (ntlmv2_flag) { + char *v2_response; + cFYI(1, ("more secure NTLM ver2 hash")); + if (CalcNTLMv2_partial_mac_key(pSesInfo, nls_info)) { rc = -ENOMEM; goto ss_err_exit; } else v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL); - if(v2_response) { - CalcNTLMv2_response(pSesInfo,v2_response); - /* if(first_time) - cifs_calculate_ntlmv2_mac_key( - pSesInfo->server->mac_signing_key, - response, ntlm_session_key, */ + if (v2_response) { + CalcNTLMv2_response(pSesInfo, + v2_response); + /* if (first_time) + cifs_calculate_ntlmv2_mac_key( + pSesInfo->server->mac_signing_key, + response, ntlm_session_key,*/ kfree(v2_response); /* BB Put dummy sig in SessSetup PDU? */ } else { @@ -3450,9 +3575,9 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, pSesInfo->server->cryptKey, ntlm_session_key); - if(first_time) + if (first_time) cifs_calculate_mac_key( - pSesInfo->server->mac_signing_key, + &pSesInfo->server->mac_signing_key, ntlm_session_key, pSesInfo->password); } @@ -3470,18 +3595,18 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, pSesInfo->server->cryptKey, ntlm_session_key); - if(first_time) + if (first_time) cifs_calculate_mac_key( - pSesInfo->server->mac_signing_key, + &pSesInfo->server->mac_signing_key, ntlm_session_key, pSesInfo->password); rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); } if (rc) { - cERROR(1,("Send error in SessSetup = %d",rc)); + cERROR(1, ("Send error in SessSetup = %d", rc)); } else { - cFYI(1,("CIFS Session Established successfully")); + cFYI(1, ("CIFS Session Established successfully")); pSesInfo->status = CifsGood; } } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8e86aac..4830acc 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -135,10 +135,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; - FILE_ALL_INFO * buf = NULL; + FILE_ALL_INFO *buf = NULL; struct inode *newinode = NULL; - struct cifsFileInfo * pCifsFile = NULL; - struct cifsInodeInfo * pCifsInode; + struct cifsFileInfo *pCifsFile = NULL; + struct cifsInodeInfo *pCifsInode; int disposition = FILE_OVERWRITE_IF; int write_only = FALSE; @@ -207,8 +207,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, } else { /* If Open reported that we actually created a file then we now have to set the mode if possible */ - if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) && - (oplock & CIFS_CREATE_ACTION)) { + if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) { mode &= ~current->fs->umask; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, @@ -235,8 +234,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, /* Could set r/o dos attribute if mode & 0222 == 0 */ } - /* BB server might mask mode so we have to query for Unix case*/ - if (pTcon->ses->capabilities & CAP_UNIX) + /* server might mask mode so we have to query for it */ + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else { @@ -264,7 +263,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); } - if ((nd->flags & LOOKUP_OPEN) == FALSE) { + if ((nd == NULL /* nfsd case - nfs srv does not set nd */) || + ((nd->flags & LOOKUP_OPEN) == FALSE)) { /* mknod case - do not leave file open */ CIFSSMBClose(xid, pTcon, fileHandle); } else if (newinode) { @@ -323,7 +323,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; - struct inode * newinode = NULL; + struct inode *newinode = NULL; if (!old_valid_dev(device_number)) return -EINVAL; @@ -336,7 +336,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, full_path = build_path_from_dentry(direntry); if (full_path == NULL) rc = -ENOMEM; - else if (pTcon->ses->capabilities & CAP_UNIX) { + else if (pTcon->unix_ext) { mode &= ~current->fs->umask; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, @@ -490,7 +490,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, cFYI(1, (" Full path: %s inode = 0x%p", full_path, direntry->d_inode)); - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newInode, full_path, parent_dir_inode->i_sb, xid); else diff --git a/fs/cifs/export.c b/fs/cifs/export.c index 1d71639..893fd0a 100644 --- a/fs/cifs/export.c +++ b/fs/cifs/export.c @@ -5,7 +5,7 @@ * Author(s): Steve French (sfrench@us.ibm.com) * * Common Internet FileSystem (CIFS) client - * + * * Operations related to support for exporting files via NFSD * * This library is free software; you can redistribute it and/or modify @@ -22,31 +22,45 @@ * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - /* + + /* * See Documentation/filesystems/Exporting * and examples in fs/exportfs + * + * Since cifs is a network file system, an "fsid" must be included for + * any nfs exports file entries which refer to cifs paths. In addition + * the cifs mount must be mounted with the "serverino" option (ie use stable + * server inode numbers instead of locally generated temporary ones). + * Although cifs inodes do not use generation numbers (have generation number + * of zero) - the inode number alone should be good enough for simple cases + * in which users want to export cifs shares with NFS. The decode and encode + * could be improved by using a new routine which expects 64 bit inode numbers + * instead of the default 32 bit routines in fs/exportfs + * */ #include <linux/fs.h> - +#include <linux/exportfs.h> +#include "cifsglob.h" +#include "cifs_debug.h" + #ifdef CONFIG_CIFS_EXPERIMENTAL - static struct dentry *cifs_get_parent(struct dentry *dentry) { - /* BB need to add code here eventually to enable export via NFSD */ - return ERR_PTR(-EACCES); + /* BB need to add code here eventually to enable export via NFSD */ + cFYI(1, ("get parent for %p", dentry)); + return ERR_PTR(-EACCES); } - + struct export_operations cifs_export_ops = { - .get_parent = cifs_get_parent, -/* Following five export operations are unneeded so far and can default */ -/* .get_dentry = - .get_name = - .find_exported_dentry = - .decode_fh = - .encode_fs = */ - }; - + .get_parent = cifs_get_parent, +/* Following five export operations are unneeded so far and can default: + .get_dentry = + .get_name = + .find_exported_dentry = + .decode_fh = + .encode_fs = */ +}; + #endif /* EXPERIMENTAL */ - + diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c index 8e375bb..995474c 100644 --- a/fs/cifs/fcntl.c +++ b/fs/cifs/fcntl.c @@ -66,7 +66,7 @@ static __u32 convert_to_cifs_notify_flags(unsigned long fcntl_notify_flags) return cifs_ntfy_flags; } -int cifs_dir_notify(struct file * file, unsigned long arg) +int cifs_dir_notify(struct file *file, unsigned long arg) { int xid; int rc = -EINVAL; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 94d5b49..e13592a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2,8 +2,8 @@ * fs/cifs/file.c * * vfs operations that deal with files - * - * Copyright (C) International Business Machines Corp., 2002,2003 + * + * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * Jeremy Allison (jra@samba.org) * @@ -45,7 +45,7 @@ static inline struct cifsFileInfo *cifs_init_private( { memset(private_data, 0, sizeof(struct cifsFileInfo)); private_data->netfid = netfid; - private_data->pid = current->tgid; + private_data->pid = current->tgid; init_MUTEX(&private_data->fh_sem); mutex_init(&private_data->lock_mutex); INIT_LIST_HEAD(&private_data->llist); @@ -57,7 +57,7 @@ static inline struct cifsFileInfo *cifs_init_private( does not tell us which handle the write is for so there can be a close (overlapping with write) of the filehandle that cifs_writepages chose to use */ - atomic_set(&private_data->wrtPending,0); + atomic_set(&private_data->wrtPending, 0); return private_data; } @@ -105,7 +105,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, in the list so we do not have to walk the list to search for one in prepare_write */ if ((file->f_flags & O_ACCMODE) == O_WRONLY) { - list_add_tail(&pCifsFile->flist, + list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); } else { list_add(&pCifsFile->flist, @@ -138,7 +138,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, } client_can_cache: - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode, full_path, inode->i_sb, xid); else @@ -189,7 +189,7 @@ int cifs_open(struct inode *inode, struct file *file) /* needed for writepage */ pCifsFile->pfile = file; - + file->private_data = pCifsFile; break; } @@ -212,15 +212,15 @@ int cifs_open(struct inode *inode, struct file *file) return -ENOMEM; } - cFYI(1, (" inode = 0x%p file flags are 0x%x for %s", + cFYI(1, ("inode = 0x%p file flags are 0x%x for %s", inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->f_flags); /********************************************************************* * open flag mapping table: - * + * * POSIX Flag CIFS Disposition - * ---------- ---------------- + * ---------- ---------------- * O_CREAT FILE_OPEN_IF * O_CREAT | O_EXCL FILE_CREATE * O_CREAT | O_TRUNC FILE_OVERWRITE_IF @@ -228,12 +228,12 @@ int cifs_open(struct inode *inode, struct file *file) * none of the above FILE_OPEN * * Note that there is not a direct match between disposition - * FILE_SUPERSEDE (ie create whether or not file exists although + * FILE_SUPERSEDE (ie create whether or not file exists although * O_CREAT | O_TRUNC is similar but truncates the existing * file rather than creating a new file as FILE_SUPERSEDE does * (which uses the attributes / metadata passed in on open call) *? - *? O_SYNC is a reasonable match to CIFS writethrough flag + *? O_SYNC is a reasonable match to CIFS writethrough flag *? and the read write flags match reasonably. O_LARGEFILE *? is irrelevant because largefile support is always used *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, @@ -253,8 +253,8 @@ int cifs_open(struct inode *inode, struct file *file) and calling get_inode_info with returned buf (at least helps non-Unix server case) */ - /* BB we can not do this if this is the second open of a file - and the first handle has writebehind data, we might be + /* BB we can not do this if this is the second open of a file + and the first handle has writebehind data, we might be able to simply do a filemap_fdatawrite/filemap_fdatawait first */ buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (!buf) { @@ -263,7 +263,7 @@ int cifs_open(struct inode *inode, struct file *file) } if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) - rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, + rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -300,15 +300,15 @@ int cifs_open(struct inode *inode, struct file *file) write_unlock(&GlobalSMBSeslock); } - if (oplock & CIFS_CREATE_ACTION) { + if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) { + if (pTcon->unix_ext) { CIFSSMBUnixSetPerms(xid, pTcon, full_path, inode->i_mode, (__u64)-1, (__u64)-1, 0 /* dev */, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } else { /* BB implement via Windows security descriptors eg @@ -345,7 +345,7 @@ static int cifs_reopen_file(struct file *file, int can_flush) struct cifsTconInfo *pTcon; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; - struct inode * inode; + struct inode *inode; char *full_path = NULL; int desiredAccess; int disposition = FILE_OPEN; @@ -372,13 +372,13 @@ static int cifs_reopen_file(struct file *file, int can_flush) } inode = file->f_path.dentry->d_inode; - if(inode == NULL) { + if (inode == NULL) { cERROR(1, ("inode not valid")); dump_stack(); rc = -EBADF; goto reopen_error_exit; } - + cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; @@ -396,7 +396,7 @@ reopen_error_exit: } cFYI(1, ("inode = 0x%p file flags 0x%x for %s", - inode, file->f_flags,full_path)); + inode, file->f_flags, full_path)); desiredAccess = cifs_convert_flags(file->f_flags); if (oplockEnabled) @@ -405,14 +405,14 @@ reopen_error_exit: oplock = FALSE; /* Can not refresh inode by passing in file_info buf to be returned - by SMBOpen and then calling get_inode_info with returned buf - since file might have write behind data that needs to be flushed + by SMBOpen and then calling get_inode_info with returned buf + since file might have write behind data that needs to be flushed and server version of file size can be stale. If we knew for sure that inode was not dirty locally we could do this */ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, NULL, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { up(&pCifsFile->fh_sem); @@ -430,7 +430,7 @@ reopen_error_exit: go to server to get inode info */ pCifsInode->clientCanCacheAll = FALSE; pCifsInode->clientCanCacheRead = FALSE; - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, xid); else @@ -486,23 +486,24 @@ int cifs_close(struct inode *inode, struct file *file) already closed */ if (pTcon->tidStatus != CifsNeedReconnect) { int timeout = 2; - while((atomic_read(&pSMBFile->wrtPending) != 0) + while ((atomic_read(&pSMBFile->wrtPending) != 0) && (timeout < 1000) ) { /* Give write a better chance to get to server ahead of the close. We do not want to add a wait_q here as it would increase the memory utilization as the struct would be in each open file, - but this should give enough time to + but this should give enough time to clear the socket */ #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1,("close delay, write pending")); + cFYI(1, ("close delay, write pending")); #endif /* DEBUG2 */ msleep(timeout); timeout *= 4; } - if(atomic_read(&pSMBFile->wrtPending)) - cERROR(1,("close with pending writes")); + if (atomic_read(&pSMBFile->wrtPending)) + cERROR(1, + ("close with pending writes")); rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); } @@ -534,7 +535,7 @@ int cifs_close(struct inode *inode, struct file *file) CIFS_I(inode)->clientCanCacheRead = FALSE; CIFS_I(inode)->clientCanCacheAll = FALSE; } - if ((rc ==0) && CIFS_I(inode)->write_behind_rc) + if ((rc == 0) && CIFS_I(inode)->write_behind_rc) rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); return rc; @@ -554,7 +555,8 @@ int cifs_closedir(struct inode *inode, struct file *file) if (pCFileStruct) { struct cifsTconInfo *pTcon; - struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + struct cifs_sb_info *cifs_sb = + CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; @@ -572,7 +574,7 @@ int cifs_closedir(struct inode *inode, struct file *file) if (ptmp) { cFYI(1, ("closedir free smb buf in srch struct")); pCFileStruct->srch_inf.ntwrk_buf_start = NULL; - if(pCFileStruct->srch_inf.smallBuf) + if (pCFileStruct->srch_inf.smallBuf) cifs_small_buf_release(ptmp); else cifs_buf_release(ptmp); @@ -594,7 +596,8 @@ int cifs_closedir(struct inode *inode, struct file *file) static int store_file_lock(struct cifsFileInfo *fid, __u64 len, __u64 offset, __u8 lockType) { - struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); + struct cifsLockInfo *li = + kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); if (li == NULL) return -ENOMEM; li->offset = offset; @@ -625,8 +628,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) cFYI(1, ("Lock parm: 0x%x flockflags: " "0x%x flocktype: 0x%x start: %lld end: %lld", - cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start, - pfLock->fl_end)); + cmd, pfLock->fl_flags, pfLock->fl_type, pfLock->fl_start, + pfLock->fl_end)); if (pfLock->fl_flags & FL_POSIX) cFYI(1, ("Posix")); @@ -641,7 +644,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) "not implemented yet")); if (pfLock->fl_flags & FL_LEASE) cFYI(1, ("Lease on file - not implemented yet")); - if (pfLock->fl_flags & + if (pfLock->fl_flags & (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE))) cFYI(1, ("Unknown lock flags 0x%x", pfLock->fl_flags)); @@ -683,9 +686,9 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) account for negative length which we can not accept over the wire */ if (IS_GETLK(cmd)) { - if(posix_locking) { + if (posix_locking) { int posix_lock_type; - if(lockType & LOCKING_ANDX_SHARED_LOCK) + if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; @@ -700,7 +703,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, 0, 1, lockType, 0 /* wait flag */ ); if (rc == 0) { - rc = CIFSSMBLock(xid, pTcon, netfid, length, + rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, 0 /* wait flag */ ); @@ -729,22 +732,24 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) if (posix_locking) { int posix_lock_type; - if(lockType & LOCKING_ANDX_SHARED_LOCK) + if (lockType & LOCKING_ANDX_SHARED_LOCK) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; - - if(numUnlock == 1) + + if (numUnlock == 1) posix_lock_type = CIFS_UNLCK; rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */, length, pfLock, posix_lock_type, wait_flag); } else { - struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data; + struct cifsFileInfo *fid = + (struct cifsFileInfo *)file->private_data; if (numLock) { - rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start, + rc = CIFSSMBLock(xid, pTcon, netfid, length, + pfLock->fl_start, 0, numLock, lockType, wait_flag); if (rc == 0) { @@ -763,7 +768,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) list_for_each_entry_safe(li, tmp, &fid->llist, llist) { if (pfLock->fl_start <= li->offset && length >= li->length) { - stored_rc = CIFSSMBLock(xid, pTcon, netfid, + stored_rc = CIFSSMBLock(xid, pTcon, + netfid, li->length, li->offset, 1, 0, li->type, FALSE); if (stored_rc) @@ -805,7 +811,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *) file->private_data; - + xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) @@ -824,7 +830,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, and blocked, and the file has been freed on us while we blocked so return what we managed to write */ return total_written; - } + } if (open_file->closePend) { FreeXid(xid); if (total_written) @@ -867,8 +873,8 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, /* since the write may have blocked check these pointers again */ if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { struct inode *inode = file->f_path.dentry->d_inode; -/* Do not update local mtime - server will set its actual value on write - * inode->i_ctime = inode->i_mtime = +/* Do not update local mtime - server will set its actual value on write + * inode->i_ctime = inode->i_mtime = * current_fs_time(inode->i_sb);*/ if (total_written > 0) { spin_lock(&inode->i_lock); @@ -877,7 +883,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, *poffset); spin_unlock(&inode->i_lock); } - mark_inode_dirty_sync(file->f_path.dentry->d_inode); + mark_inode_dirty_sync(file->f_path.dentry->d_inode); } FreeXid(xid); return total_written; @@ -898,13 +904,13 @@ static ssize_t cifs_write(struct file *file, const char *write_data, pTcon = cifs_sb->tcon; - cFYI(1,("write %zd bytes to offset %lld of %s", write_size, + cFYI(1, ("write %zd bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name)); if (file->private_data == NULL) return -EBADF; open_file = (struct cifsFileInfo *)file->private_data; - + xid = GetXid(); if (*poffset > file->f_path.dentry->d_inode->i_size) @@ -921,10 +927,10 @@ static ssize_t cifs_write(struct file *file, const char *write_data, FreeXid(xid); /* if we have gotten here we have written some data and blocked, and the file has been freed on us - while we blocked so return what we managed to + while we blocked so return what we managed to write */ return total_written; - } + } if (open_file->closePend) { FreeXid(xid); if (total_written) @@ -935,14 +941,14 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (open_file->invalidHandle) { /* we could deadlock if we called filemap_fdatawait from here so tell - reopen_file not to flush data to + reopen_file not to flush data to server now */ rc = cifs_reopen_file(file, FALSE); if (rc != 0) break; } - if(experimEnabled || (pTcon->ses->server && - ((pTcon->ses->server->secMode & + if (experimEnabled || (pTcon->ses->server && + ((pTcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0))) { struct kvec iov[2]; @@ -976,7 +982,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, } } else *poffset += bytes_written; - long_op = FALSE; /* subsequent writes fast - + long_op = FALSE; /* subsequent writes fast - 15 seconds is plenty */ } @@ -1009,8 +1015,8 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) the VFS or MM) should not happen but we had reports of on oops (due to it being zero) during stress testcases so we need to check for it */ - if(cifs_inode == NULL) { - cERROR(1,("Null inode passed to cifs_writeable_file")); + if (cifs_inode == NULL) { + cERROR(1, ("Null inode passed to cifs_writeable_file")); dump_stack(); return NULL; } @@ -1024,13 +1030,14 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) (open_file->pfile->f_flags & O_WRONLY))) { atomic_inc(&open_file->wrtPending); read_unlock(&GlobalSMBSeslock); - if((open_file->invalidHandle) && + if ((open_file->invalidHandle) && (!open_file->closePend) /* BB fixme -since the second clause can not be true remove it BB */) { rc = cifs_reopen_file(open_file->pfile, FALSE); /* if it fails, try another handle - might be */ /* dangerous to hold up writepages with retry */ - if(rc) { - cFYI(1,("failed on reopen file in wp")); + if (rc) { + cFYI(1, + ("failed on reopen file in wp")); read_lock(&GlobalSMBSeslock); /* can not use this handle, no write pending on this one after all */ @@ -1082,7 +1089,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) /* check to make sure that we are not extending the file */ if (mapping->host->i_size - offset < (loff_t)to) - to = (unsigned)(mapping->host->i_size - offset); + to = (unsigned)(mapping->host->i_size - offset); open_file = find_writable_file(CIFS_I(mapping->host)); if (open_file) { @@ -1116,8 +1123,8 @@ static int cifs_writepages(struct address_space *mapping, int done = 0; pgoff_t end; pgoff_t index; - int range_whole = 0; - struct kvec * iov; + int range_whole = 0; + struct kvec *iov; int len; int n_iov = 0; pgoff_t next; @@ -1131,7 +1138,7 @@ static int cifs_writepages(struct address_space *mapping, int xid; cifs_sb = CIFS_SB(mapping->host->i_sb); - + /* * If wsize is smaller that the page cache size, default to writing * one page at a time via cifs_writepage @@ -1139,14 +1146,14 @@ static int cifs_writepages(struct address_space *mapping, if (cifs_sb->wsize < PAGE_CACHE_SIZE) return generic_writepages(mapping, wbc); - if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) - if(cifs_sb->tcon->ses->server->secMode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - if(!experimEnabled) + if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) + if (cifs_sb->tcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (!experimEnabled) return generic_writepages(mapping, wbc); iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); - if(iov == NULL) + if (iov == NULL) return generic_writepages(mapping, wbc); @@ -1279,7 +1286,7 @@ retry: 1); atomic_dec(&open_file->wrtPending); if (rc || bytes_written < bytes_to_write) { - cERROR(1,("Write2 ret %d, written = %d", + cERROR(1, ("Write2 ret %d, wrote %d", rc, bytes_written)); /* BB what if continued retry is requested via mount flags? */ @@ -1295,8 +1302,8 @@ retry: success rc but too little data written? */ /* BB investigate retry logic on temporary server crash cases and how recovery works - when page marked as error */ - if(rc) + when page marked as error */ + if (rc) SetPageError(page); kunmap(page); unlock_page(page); @@ -1326,7 +1333,7 @@ retry: return rc; } -static int cifs_writepage(struct page* page, struct writeback_control *wbc) +static int cifs_writepage(struct page *page, struct writeback_control *wbc) { int rc = -EFAULT; int xid; @@ -1334,7 +1341,7 @@ static int cifs_writepage(struct page* page, struct writeback_control *wbc) xid = GetXid(); /* BB add check for wbc flags */ page_cache_get(page); - if (!PageUptodate(page)) { + if (!PageUptodate(page)) { cFYI(1, ("ppw - page not up to date")); } @@ -1348,7 +1355,7 @@ static int cifs_writepage(struct page* page, struct writeback_control *wbc) * Just unlocking the page will cause the radix tree tag-bits * to fail to update with the state of the page correctly. */ - set_page_writeback(page); + set_page_writeback(page); rc = cifs_partialpagewrite(page, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); /* BB add check for error and Clearuptodate? */ unlock_page(page); @@ -1368,7 +1375,7 @@ static int cifs_commit_write(struct file *file, struct page *page, char *page_data; xid = GetXid(); - cFYI(1, ("commit write for page %p up to position %lld for %d", + cFYI(1, ("commit write for page %p up to position %lld for %d", page, position, to)); spin_lock(&inode->i_lock); if (position > inode->i_size) { @@ -1396,7 +1403,7 @@ static int cifs_commit_write(struct file *file, struct page *page, rc = 0; /* else if (rc < 0) should we set writebehind rc? */ kunmap(page); - } else { + } else { set_page_dirty(page); } @@ -1412,9 +1419,9 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) xid = GetXid(); - cFYI(1, ("Sync file - name: %s datasync: 0x%x", + cFYI(1, ("Sync file - name: %s datasync: 0x%x", dentry->d_name.name, datasync)); - + rc = filemap_fdatawrite(inode->i_mapping); if (rc == 0) CIFS_I(inode)->write_behind_rc = 0; @@ -1438,7 +1445,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) if (!inode) return; */ -/* fill in rpages then +/* fill in rpages then result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */ /* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index)); @@ -1456,7 +1463,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync) */ int cifs_flush(struct file *file, fl_owner_t id) { - struct inode * inode = file->f_path.dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; int rc = 0; /* Rather than do the steps manually: @@ -1471,8 +1478,8 @@ int cifs_flush(struct file *file, fl_owner_t id) rc = filemap_fdatawrite(inode->i_mapping); if (!rc) /* reset wb rc if we were able to write out dirty pages */ CIFS_I(inode)->write_behind_rc = 0; - - cFYI(1, ("Flush inode %p file %p rc %d",inode,file,rc)); + + cFYI(1, ("Flush inode %p file %p rc %d", inode, file, rc)); return rc; } @@ -1508,13 +1515,13 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { - current_read_size = min_t(const int, read_size - total_read, + current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); rc = -EAGAIN; smb_read_data = NULL; while (rc == -EAGAIN) { int buf_type = CIFS_NO_BUFFER; - if ((open_file->invalidHandle) && + if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, TRUE); if (rc != 0) @@ -1535,9 +1542,9 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, rc = -EFAULT; } - if(buf_type == CIFS_SMALL_BUFFER) + if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); - else if(buf_type == CIFS_LARGE_BUFFER) + else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } @@ -1586,21 +1593,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, ("attempting read on write only file instance")); - for (total_read = 0, current_offset = read_data; + for (total_read = 0, current_offset = read_data; read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(const int, read_size - total_read, cifs_sb->rsize); /* For windows me and 9x we do not want to request more than it negotiated since it will refuse the read then */ - if((pTcon->ses) && + if ((pTcon->ses) && !(pTcon->ses->capabilities & CAP_LARGE_FILES)) { current_read_size = min_t(const int, current_read_size, pTcon->ses->server->maxBuf - 128); } rc = -EAGAIN; while (rc == -EAGAIN) { - if ((open_file->invalidHandle) && + if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, TRUE); if (rc != 0) @@ -1646,7 +1653,7 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) } -static void cifs_copy_cache_pages(struct address_space *mapping, +static void cifs_copy_cache_pages(struct address_space *mapping, struct list_head *pages, int bytes_read, char *data, struct pagevec *plru_pvec) { @@ -1669,12 +1676,12 @@ static void cifs_copy_cache_pages(struct address_space *mapping, continue; } - target = kmap_atomic(page,KM_USER0); + target = kmap_atomic(page, KM_USER0); if (PAGE_CACHE_SIZE > bytes_read) { memcpy(target, data, bytes_read); /* zero the tail end of this partial page */ - memset(target + bytes_read, 0, + memset(target + bytes_read, 0, PAGE_CACHE_SIZE - bytes_read); bytes_read = 0; } else { @@ -1703,7 +1710,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int bytes_read = 0; - unsigned int read_size,i; + unsigned int read_size, i; char *smb_read_data = NULL; struct smb_com_read_rsp *pSMBr; struct pagevec lru_pvec; @@ -1720,7 +1727,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, pTcon = cifs_sb->tcon; pagevec_init(&lru_pvec, 0); - +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("rpages: num pages %d", num_pages)); +#endif for (i = 0; i < num_pages; ) { unsigned contig_pages; struct page *tmp_page; @@ -1734,14 +1743,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, /* count adjacent pages that we will read into */ contig_pages = 0; - expected_index = + expected_index = list_entry(page_list->prev, struct page, lru)->index; - list_for_each_entry_reverse(tmp_page,page_list,lru) { + list_for_each_entry_reverse(tmp_page, page_list, lru) { if (tmp_page->index == expected_index) { contig_pages++; expected_index++; } else - break; + break; } if (contig_pages + i > num_pages) contig_pages = num_pages - i; @@ -1753,10 +1762,13 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, /* Read size needs to be in multiples of one page */ read_size = min_t(const unsigned int, read_size, cifs_sb->rsize & PAGE_CACHE_MASK); - +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("rpages: read size 0x%x contiguous pages %d", + read_size, contig_pages)); +#endif rc = -EAGAIN; while (rc == -EAGAIN) { - if ((open_file->invalidHandle) && + if ((open_file->invalidHandle) && (!open_file->closePend)) { rc = cifs_reopen_file(file, TRUE); if (rc != 0) @@ -1769,11 +1781,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, &bytes_read, &smb_read_data, &buf_type); /* BB more RC checks ? */ - if (rc== -EAGAIN) { + if (rc == -EAGAIN) { if (smb_read_data) { - if(buf_type == CIFS_SMALL_BUFFER) + if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); - else if(buf_type == CIFS_LARGE_BUFFER) + else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } @@ -1794,10 +1806,10 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, if ((int)(bytes_read & PAGE_CACHE_MASK) != bytes_read) { i++; /* account for partial page */ - /* server copy of file can have smaller size + /* server copy of file can have smaller size than client */ - /* BB do we need to verify this common case ? - this case is ok - if we are at server EOF + /* BB do we need to verify this common case ? + this case is ok - if we are at server EOF we will hit it on next read */ /* break; */ @@ -1806,14 +1818,14 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, cFYI(1, ("No bytes read (%d) at offset %lld . " "Cleaning remaining pages from readahead list", bytes_read, offset)); - /* BB turn off caching and do new lookup on + /* BB turn off caching and do new lookup on file size at server? */ break; } if (smb_read_data) { - if(buf_type == CIFS_SMALL_BUFFER) + if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); - else if(buf_type == CIFS_LARGE_BUFFER) + else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; } @@ -1824,12 +1836,12 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, /* need to free smb_read_data buf before exit */ if (smb_read_data) { - if(buf_type == CIFS_SMALL_BUFFER) + if (buf_type == CIFS_SMALL_BUFFER) cifs_small_buf_release(smb_read_data); - else if(buf_type == CIFS_LARGE_BUFFER) + else if (buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(smb_read_data); smb_read_data = NULL; - } + } FreeXid(xid); return rc; @@ -1844,26 +1856,26 @@ static int cifs_readpage_worker(struct file *file, struct page *page, page_cache_get(page); read_data = kmap(page); /* for reads over a certain size could initiate async read ahead */ - + rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset); - + if (rc < 0) goto io_error; else - cFYI(1, ("Bytes read %d",rc)); - + cFYI(1, ("Bytes read %d", rc)); + file->f_path.dentry->d_inode->i_atime = current_fs_time(file->f_path.dentry->d_inode->i_sb); - + if (PAGE_CACHE_SIZE > rc) memset(read_data + rc, 0, PAGE_CACHE_SIZE - rc); flush_dcache_page(page); SetPageUptodate(page); rc = 0; - + io_error: - kunmap(page); + kunmap(page); page_cache_release(page); return rc; } @@ -1881,7 +1893,7 @@ static int cifs_readpage(struct file *file, struct page *page) return -EBADF; } - cFYI(1, ("readpage %p at offset %d 0x%x\n", + cFYI(1, ("readpage %p at offset %d 0x%x\n", page, (int)offset, (int)offset)); rc = cifs_readpage_worker(file, page, &offset); @@ -1895,7 +1907,7 @@ static int cifs_readpage(struct file *file, struct page *page) /* We do not want to update the file size from server for inodes open for write - to avoid races with writepage extending the file - in the future we could consider allowing - refreshing the inode only on increases in the file size + refreshing the inode only on increases in the file size but this is tricky to do without racing with writebehind page caching in the current Linux kernel design */ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) @@ -1904,8 +1916,8 @@ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) if (cifsInode) open_file = find_writable_file(cifsInode); - - if(open_file) { + + if (open_file) { struct cifs_sb_info *cifs_sb; /* there is not actually a write pending so let @@ -1915,12 +1927,12 @@ int is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) cifs_sb = CIFS_SB(cifsInode->vfs_inode.i_sb); if ( cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO ) { - /* since no page cache to corrupt on directio + /* since no page cache to corrupt on directio we can change size safely */ return 1; } - if(i_size_read(&cifsInode->vfs_inode) < end_of_file) + if (i_size_read(&cifsInode->vfs_inode) < end_of_file) return 1; return 0; @@ -1935,7 +1947,7 @@ static int cifs_prepare_write(struct file *file, struct page *page, loff_t i_size; loff_t offset; - cFYI(1, ("prepare write for page %p from %d to %d",page,from,to)); + cFYI(1, ("prepare write for page %p from %d to %d", page, from, to)); if (PageUptodate(page)) return 0; @@ -1955,14 +1967,7 @@ static int cifs_prepare_write(struct file *file, struct page *page, * We don't need to read data beyond the end of the file. * zero it, and set the page uptodate */ - void *kaddr = kmap_atomic(page, KM_USER0); - - if (from) - memset(kaddr, 0, from); - if (to < PAGE_CACHE_SIZE) - memset(kaddr + to, 0, PAGE_CACHE_SIZE - to); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + simple_prepare_write(file, page, from, to); SetPageUptodate(page); } else if ((file->f_flags & O_ACCMODE) != O_WRONLY) { /* might as well read a page, it is fast enough */ @@ -1974,8 +1979,8 @@ static int cifs_prepare_write(struct file *file, struct page *page, this will be written out by commit_write so is fine */ } - /* we do not need to pass errors back - e.g. if we do not have read access to the file + /* we do not need to pass errors back + e.g. if we do not have read access to the file because cifs_commit_write will do the right thing. -- shaggy */ return 0; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f0ff12b..dd41677 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -57,14 +57,14 @@ int cifs_get_inode_info_unix(struct inode **pinode, if (tmp_path == NULL) { return -ENOMEM; } - /* have to skip first of the double backslash of + /* have to skip first of the double backslash of UNC name */ strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); strncat(tmp_path, search_path, MAX_PATHCONF); rc = connect_to_dfs_path(xid, pTcon->ses, /* treename + */ tmp_path, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(tmp_path); @@ -81,7 +81,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, /* get new inode */ if (*pinode == NULL) { *pinode = new_inode(sb); - if (*pinode == NULL) + if (*pinode == NULL) return -ENOMEM; /* Is an i_ino of zero legal? */ /* Are there sanity checks we can use to ensure that @@ -92,7 +92,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, } /* note 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); } @@ -103,7 +103,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, cifsInfo->time = jiffies; cFYI(1, ("New time %ld", cifsInfo->time)); /* this is ok to set on every inode revalidate */ - atomic_set(&cifsInfo->inUse,1); + atomic_set(&cifsInfo->inUse, 1); inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime)); @@ -114,8 +114,8 @@ int cifs_get_inode_info_unix(struct inode **pinode, cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange)); inode->i_mode = le64_to_cpu(findData.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; + to avoid strange results if bits set above */ + inode->i_mode &= ~S_IFMT; if (type == UNIX_FILE) { inode->i_mode |= S_IFREG; } else if (type == UNIX_SYMLINK) { @@ -137,9 +137,9 @@ int cifs_get_inode_info_unix(struct inode **pinode, } else { /* safest to call it a file if we do not know */ inode->i_mode |= S_IFREG; - cFYI(1,("unknown type %d",type)); + cFYI(1, ("unknown type %d", type)); } - + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) inode->i_uid = cifs_sb->mnt_uid; else @@ -149,7 +149,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, inode->i_gid = cifs_sb->mnt_gid; else inode->i_gid = le64_to_cpu(findData.Gid); - + inode->i_nlink = le64_to_cpu(findData.Nlinks); spin_lock(&inode->i_lock); @@ -183,17 +183,17 @@ int cifs_get_inode_info_unix(struct inode **pinode, inode->i_op = &cifs_file_inode_ops; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = + inode->i_fop = &cifs_file_direct_nobrl_ops; else inode->i_fop = &cifs_file_direct_ops; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop = &cifs_file_nobrl_ops; - else /* not direct, send byte range locks */ + else /* not direct, send byte range locks */ inode->i_fop = &cifs_file_ops; /* check if server can support readpages */ - if (pTcon->ses->server->maxBuf < + if (pTcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else @@ -215,7 +215,7 @@ int cifs_get_inode_info_unix(struct inode **pinode, return rc; } -static int decode_sfu_inode(struct inode * inode, __u64 size, +static int decode_sfu_inode(struct inode *inode, __u64 size, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { @@ -225,7 +225,7 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, struct cifsTconInfo *pTcon = cifs_sb->tcon; char buf[24]; unsigned int bytes_read; - char * pbuf; + char *pbuf; pbuf = buf; @@ -235,22 +235,22 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, } else if (size < 8) { return -EINVAL; /* EOPNOTSUPP? */ } - + rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { int buf_type = CIFS_NO_BUFFER; /* Read header */ rc = CIFSSMBRead(xid, pTcon, - netfid, + 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")); + cFYI(1, ("Block device")); inode->i_mode |= S_IFBLK; if (bytes_read == 24) { /* we have enough to decode dev num */ @@ -261,7 +261,7 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, inode->i_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxCHR", pbuf, 8) == 0) { - cFYI(1,("Char device")); + cFYI(1, ("Char device")); inode->i_mode |= S_IFCHR; if (bytes_read == 24) { /* we have enough to decode dev num */ @@ -270,27 +270,26 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); inode->i_rdev = MKDEV(mjr, mnr); - } + } } else if (memcmp("IntxLNK", pbuf, 7) == 0) { - cFYI(1,("Symlink")); + cFYI(1, ("Symlink")); inode->i_mode |= S_IFLNK; } else { inode->i_mode |= S_IFREG; /* file? */ - rc = -EOPNOTSUPP; + rc = -EOPNOTSUPP; } } else { inode->i_mode |= S_IFREG; /* then it is a file */ - rc = -EOPNOTSUPP; /* or some unknown SFU type */ - } + rc = -EOPNOTSUPP; /* or some unknown SFU type */ + } CIFSSMBClose(xid, pTcon, netfid); } return rc; - } #define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ -static int get_sfu_uid_mode(struct inode * inode, +static int get_sfu_uid_mode(struct inode *inode, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { @@ -301,15 +300,15 @@ static int get_sfu_uid_mode(struct inode * inode, 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); + 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 &= ~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)); + cFYI(1, ("special mode bits 0%o", mode)); return 0; } else { return 0; @@ -317,8 +316,6 @@ static int get_sfu_uid_mode(struct inode * inode, #else return -EOPNOTSUPP; #endif - - } int cifs_get_inode_info(struct inode **pinode, @@ -334,11 +331,11 @@ int cifs_get_inode_info(struct inode **pinode, int adjustTZ = FALSE; pTcon = cifs_sb->tcon; - cFYI(1,("Getting info on %s", search_path)); + cFYI(1, ("Getting info on %s", search_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; } } @@ -359,12 +356,11 @@ int cifs_get_inode_info(struct inode **pinode, failed at least once - set flag in tcon or mount */ if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { rc = SMBQueryInformation(xid, pTcon, search_path, - pfindData, cifs_sb->local_nls, + pfindData, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); adjustTZ = TRUE; } - } /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ if (rc) { @@ -384,8 +380,8 @@ int cifs_get_inode_info(struct inode **pinode, strncat(tmp_path, search_path, MAX_PATHCONF); rc = connect_to_dfs_path(xid, pTcon->ses, /* treename + */ tmp_path, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(tmp_path); /* BB fix up inode etc. */ @@ -419,17 +415,17 @@ int cifs_get_inode_info(struct inode **pinode, there Windows server or network appliances for which IndexNumber field is not guaranteed unique? */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM){ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { int rc1 = 0; __u64 inode_num; - rc1 = CIFSGetSrvInodeNumber(xid, pTcon, - search_path, &inode_num, + rc1 = CIFSGetSrvInodeNumber(xid, pTcon, + search_path, &inode_num, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc1) { - cFYI(1,("GetSrvInodeNum rc %d", 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; @@ -463,7 +459,7 @@ int cifs_get_inode_info(struct inode **pinode, cFYI(0, ("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; + inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; } /* set default mode. will override for dirs below */ @@ -471,8 +467,9 @@ int cifs_get_inode_info(struct inode **pinode, /* new inode, can safely set these fields */ inode->i_mode = cifs_sb->mnt_file_mode; else /* since we set the inode type below we need to mask off - to avoid strange results if type changes and both get orred in */ - inode->i_mode &= ~S_IFMT; + to avoid strange results if type changes and both + get orred in */ + inode->i_mode &= ~S_IFMT; /* if (attr & ATTR_REPARSE) */ /* We no longer handle these as symlinks because we could not follow them due to the absolute path with drive letter */ @@ -490,13 +487,13 @@ int cifs_get_inode_info(struct inode **pinode, /* BB Finish for SFU style symlinks and devices */ } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && (cifsInfo->cifsAttrs & ATTR_SYSTEM)) { - if (decode_sfu_inode(inode, + if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile), search_path, cifs_sb, xid)) { - cFYI(1,("Unrecognized sfu inode type")); + cFYI(1, ("Unrecognized sfu inode type")); } - cFYI(1,("sfu mode 0%o",inode->i_mode)); + cFYI(1, ("sfu mode 0%o", inode->i_mode)); } else { inode->i_mode |= S_IFREG; /* treat the dos attribute of read-only as read-only @@ -512,12 +509,12 @@ int cifs_get_inode_info(struct inode **pinode, /* BB add code here - validate if device or weird share or device type? */ } - + 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)); + i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); /* 512 bytes (2**9) is the fake blocksize that must be used for this calculation */ @@ -528,7 +525,7 @@ int cifs_get_inode_info(struct inode **pinode, inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); - /* BB fill in uid and gid here? with help from winbind? + /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { /* fill in uid, gid, mode from server ACL */ @@ -540,7 +537,7 @@ int cifs_get_inode_info(struct inode **pinode, 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); + atomic_set(&cifsInfo->inUse, 1); } if (S_ISREG(inode->i_mode)) { @@ -557,7 +554,7 @@ int cifs_get_inode_info(struct inode **pinode, else /* not direct, send byte range locks */ inode->i_fop = &cifs_file_ops; - if (pTcon->ses->server->maxBuf < + if (pTcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else @@ -586,10 +583,11 @@ void cifs_read_inode(struct inode *inode) cifs_sb = CIFS_SB(inode->i_sb); xid = GetXid(); - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) - cifs_get_inode_info_unix(&inode, "", inode->i_sb,xid); + + if (cifs_sb->tcon->unix_ext) + cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid); else - cifs_get_inode_info(&inode, "", NULL, inode->i_sb,xid); + cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid); /* can not call macro FreeXid here since in a void func */ _FreeXid(xid); } @@ -623,9 +621,21 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) FreeXid(xid); return -ENOMEM; } - rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls, + + if ((pTcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(pTcon->fsUnixInfo.Capability))) { + rc = CIFSPOSIXDelFile(xid, pTcon, 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)); + if ((rc == 0) || (rc == -ENOENT)) + goto psx_del_no_retry; + } + rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +psx_del_no_retry: if (!rc) { if (direntry->d_inode) drop_nlink(direntry->d_inode); @@ -638,12 +648,12 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE, CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE, &netfid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); CIFSSMBClose(xid, pTcon, netfid); if (direntry->d_inode) @@ -659,7 +669,7 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) rc = CIFSSMBSetTimes(xid, pTcon, full_path, pinfo_buf, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else rc = -EOPNOTSUPP; @@ -670,7 +680,7 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) /* rc = CIFSSMBSetAttrLegacy(xid, pTcon, full_path, (__u16)ATTR_NORMAL, - cifs_sb->local_nls); + cifs_sb->local_nls); For some strange reason it seems that NT4 eats the old setattr call without actually setting the attributes so on to the third attempted workaround @@ -683,9 +693,9 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) FILE_WRITE_ATTRIBUTES, 0, &netfid, &oplock, NULL, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { rc = CIFSSMBSetFileTimes(xid, pTcon, pinfo_buf, netfid); @@ -694,10 +704,10 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) } kfree(pinfo_buf); } - if (rc==0) { - rc = CIFSSMBDelFile(xid, pTcon, full_path, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + if (rc == 0) { + rc = CIFSSMBDelFile(xid, pTcon, full_path, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (!rc) { if (direntry->d_inode) @@ -711,10 +721,10 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE, &netfid, &oplock, NULL, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL, cifs_sb->local_nls, @@ -773,8 +783,8 @@ static void posix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_mode = le64_to_cpu(pData->Permissions); /* since we set the inode type below we need to mask off type - to avoid strange results if bits above were corrupt */ - tmp_inode->i_mode &= ~S_IFMT; + to avoid strange results if bits above were corrupt */ + tmp_inode->i_mode &= ~S_IFMT; if (type == UNIX_FILE) { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; @@ -804,11 +814,11 @@ static void posix_fill_in_inode(struct inode *tmp_inode, /* safest to just call it a file */ *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; - cFYI(1,("unknown inode type %d",type)); + cFYI(1, ("unknown inode type %d", type)); } #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1,("object type: %d", type)); + cFYI(1, ("object type: %d", type)); #endif tmp_inode->i_uid = le64_to_cpu(pData->Uid); tmp_inode->i_gid = le64_to_cpu(pData->Gid); @@ -816,7 +826,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode, spin_lock(&tmp_inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { - /* can not safely change the file size here if the + /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); @@ -830,27 +840,28 @@ static void posix_fill_in_inode(struct inode *tmp_inode, cFYI(1, ("File inode")); tmp_inode->i_op = &cifs_file_inode_ops; - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop = &cifs_file_direct_nobrl_ops; else tmp_inode->i_fop = &cifs_file_direct_ops; - - } else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop = &cifs_file_nobrl_ops; else tmp_inode->i_fop = &cifs_file_ops; - if((cifs_sb->tcon) && (cifs_sb->tcon->ses) && - (cifs_sb->tcon->ses->server->maxBuf < + if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) && + (cifs_sb->tcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)) tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else tmp_inode->i_data.a_ops = &cifs_addr_ops; - if(isNewInode) - return; /* No sense invalidating pages for new inode since we - have not started caching readahead file data yet */ + if (isNewInode) + return; /* No sense invalidating pages for new inode + since we we have not started caching + readahead file data yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { @@ -869,10 +880,10 @@ static void posix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_op = &cifs_symlink_inode_ops; /* tmp_inode->i_fop = *//* do not need to set to anything */ } else { - cFYI(1, ("Special inode")); + cFYI(1, ("Special inode")); init_special_inode(tmp_inode, tmp_inode->i_mode, tmp_inode->i_rdev); - } + } } int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) @@ -896,22 +907,22 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) FreeXid(xid); return -ENOMEM; } - - if((pTcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & + + if ((pTcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(pTcon->fsUnixInfo.Capability))) { u32 oplock = 0; - FILE_UNIX_BASIC_INFO * pInfo = + FILE_UNIX_BASIC_INFO * pInfo = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if(pInfo == NULL) { + if (pInfo == NULL) { rc = -ENOMEM; goto mkdir_out; } - + rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, NULL /* netfid */, pInfo, &oplock, - full_path, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + full_path, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { cFYI(1, ("posix mkdir returned 0x%x", rc)); @@ -919,8 +930,9 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) } else { int obj_type; if (pInfo->Type == -1) /* no return info - go query */ - goto mkdir_get_info; -/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need to set uid/gid */ + goto mkdir_get_info; +/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need + to set uid/gid */ inc_nlink(inode); if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; @@ -937,7 +949,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) newinode->i_ino = (unsigned long)pInfo->UniqueId; } /* note ino incremented to unique num in new_inode */ - if(inode->i_sb->s_flags & MS_NOATIME) + if (inode->i_sb->s_flags & MS_NOATIME) newinode->i_flags |= S_NOATIME | S_NOCMTIME; newinode->i_nlink = 2; @@ -949,18 +961,18 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) posix_fill_in_inode(direntry->d_inode, pInfo, &obj_type, 1 /* NewInode */); #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1,("instantiated dentry %p %s to inode %p", + 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", + if (newinode->i_nlink != 2) + cFYI(1, ("unexpected number of links %d", newinode->i_nlink)); #endif } kfree(pInfo); goto mkdir_out; - } - + } + /* BB add setting the equivalent of mode via CreateX w/ACLs */ rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -968,14 +980,14 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) cFYI(1, ("cifs_mkdir returned 0x%x", rc)); d_drop(direntry); } else { -mkdir_get_info: +mkdir_get_info: inc_nlink(inode); - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb,xid); + inode->i_sb, xid); else rc = cifs_get_inode_info(&newinode, full_path, NULL, - inode->i_sb,xid); + inode->i_sb, xid); if (pTcon->nocase) direntry->d_op = &cifs_ci_dentry_ops; @@ -983,10 +995,10 @@ mkdir_get_info: direntry->d_op = &cifs_dentry_ops; d_instantiate(direntry, newinode); /* setting nlink not necessary except in cases where we - * failed to get it from the server or was set bogus */ + * failed to get it from the server or was set bogus */ if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2)) - direntry->d_inode->i_nlink = 2; - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) { + direntry->d_inode->i_nlink = 2; + if (pTcon->unix_ext) { mode &= ~current->fs->umask; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { CIFSSMBUnixSetPerms(xid, pTcon, full_path, @@ -1002,27 +1014,27 @@ mkdir_get_info: mode, (__u64)-1, (__u64)-1, 0 /* dev_t */, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } } else { /* BB to be implemented via Windows secrty descriptors eg CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, -1, -1, local_nls); */ - if(direntry->d_inode) { + if (direntry->d_inode) { direntry->d_inode->i_mode = mode; direntry->d_inode->i_mode |= S_IFDIR; - if(cifs_sb->mnt_cifs_flags & + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - direntry->d_inode->i_uid = + direntry->d_inode->i_uid = current->fsuid; - direntry->d_inode->i_gid = + direntry->d_inode->i_gid = current->fsgid; } } } } -mkdir_out: +mkdir_out: kfree(full_path); FreeXid(xid); return rc; @@ -1056,7 +1068,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) if (!rc) { drop_nlink(inode); spin_lock(&direntry->d_inode->i_lock); - i_size_write(direntry->d_inode,0); + i_size_write(direntry->d_inode, 0); clear_nlink(direntry->d_inode); spin_unlock(&direntry->d_inode->i_lock); } @@ -1119,9 +1131,9 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); if (info_buf_source != NULL) { info_buf_target = info_buf_source + 1; - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, - info_buf_source, + info_buf_source, cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -1171,12 +1183,12 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, might not right be right access to request */ rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, NULL, - cifs_sb_source->local_nls, - cifs_sb_source->mnt_cifs_flags & + cifs_sb_source->local_nls, + cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName, - cifs_sb_source->local_nls, + cifs_sb_source->local_nls, cifs_sb_source->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); CIFSSMBClose(xid, pTcon, netfid); @@ -1247,9 +1259,9 @@ int cifs_revalidate(struct dentry *direntry) local_mtime = direntry->d_inode->i_mtime; local_size = direntry->d_inode->i_size; - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) { + if (cifs_sb->tcon->unix_ext) { rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path, - direntry->d_sb,xid); + direntry->d_sb, xid); if (rc) { cFYI(1, ("error on getting revalidate info %d", rc)); /* if (rc != -ENOENT) @@ -1258,7 +1270,7 @@ int cifs_revalidate(struct dentry *direntry) } } else { rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, - direntry->d_sb,xid); + direntry->d_sb, xid); if (rc) { cFYI(1, ("error on getting revalidate info %d", rc)); /* if (rc != -ENOENT) @@ -1271,7 +1283,7 @@ int cifs_revalidate(struct dentry *direntry) /* if not oplocked, we invalidate inode pages if mtime or file size had changed on server */ - if (timespec_equal(&local_mtime,&direntry->d_inode->i_mtime) && + if (timespec_equal(&local_mtime, &direntry->d_inode->i_mtime) && (local_size == direntry->d_inode->i_size)) { cFYI(1, ("cifs_revalidate - inode unchanged")); } else { @@ -1298,7 +1310,7 @@ int cifs_revalidate(struct dentry *direntry) 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)) +/* 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) @@ -1313,7 +1325,7 @@ int cifs_revalidate(struct dentry *direntry) } } /* mutex_unlock(&direntry->d_inode->i_mutex); */ - + kfree(full_path); FreeXid(xid); return rc; @@ -1335,23 +1347,19 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from) pgoff_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE - 1); struct page *page; - char *kaddr; int rc = 0; page = grab_cache_page(mapping, index); if (!page) return -ENOMEM; - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, PAGE_CACHE_SIZE - offset, KM_USER0); unlock_page(page); page_cache_release(page); return rc; } -static int cifs_vmtruncate(struct inode * inode, loff_t offset) +static int cifs_vmtruncate(struct inode *inode, loff_t offset) { struct address_space *mapping = inode->i_mapping; unsigned long limit; @@ -1424,13 +1432,13 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { /* check if we have permission to change attrs */ rc = inode_change_ok(direntry->d_inode, attrs); - if(rc < 0) { + if (rc < 0) { FreeXid(xid); return rc; } else rc = 0; } - + full_path = build_path_from_dentry(direntry); if (full_path == NULL) { FreeXid(xid); @@ -1459,16 +1467,16 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, FALSE); atomic_dec(&open_file->wrtPending); - cFYI(1,("SetFSize for attrs rc = %d", rc)); - if((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { + cFYI(1, ("SetFSize for attrs rc = %d", rc)); + if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { int bytes_written; rc = CIFSSMBWrite(xid, pTcon, nfid, 0, attrs->ia_size, &bytes_written, NULL, NULL, 1 /* 45 seconds */); - cFYI(1,("Wrt seteof rc %d", rc)); + cFYI(1, ("Wrt seteof rc %d", rc)); } - } else + } else rc = -EINVAL; if (rc != 0) { @@ -1478,11 +1486,11 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) it by handle */ rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, FALSE, - cifs_sb->local_nls, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc)); - if((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { + if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { __u16 netfid; int oplock = FALSE; @@ -1493,14 +1501,14 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { int bytes_written; rc = CIFSSMBWrite(xid, pTcon, netfid, 0, attrs->ia_size, &bytes_written, NULL, NULL, 1 /* 45 sec */); - cFYI(1,("wrt seteof rc %d",rc)); + cFYI(1, ("wrt seteof rc %d", rc)); CIFSSMBClose(xid, pTcon, netfid); } @@ -1517,7 +1525,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) rc = cifs_vmtruncate(direntry->d_inode, attrs->ia_size); cifs_truncate_page(direntry->d_inode->i_mapping, direntry->d_inode->i_size); - } else + } else goto cifs_setattr_exit; } if (attrs->ia_valid & ATTR_UID) { @@ -1535,11 +1543,11 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) mode = attrs->ia_mode; } - if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) + if ((pTcon->unix_ext) && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID))) rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid, 0 /* dev_t */, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else if (attrs->ia_valid & ATTR_MODE) { rc = 0; @@ -1559,7 +1567,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs & (~ATTR_READONLY)); /* Windows ignores set to zero */ - if(time_buf.Attributes == 0) + if (time_buf.Attributes == 0) time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); } /* BB to be implemented - @@ -1585,7 +1593,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) stamps are changed explicitly (i.e. by utime() since we would then have a mix of client and server times */ - + if (set_time && (attrs->ia_valid & ATTR_CTIME)) { set_time = TRUE; /* Although Samba throws this field away @@ -1624,7 +1632,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc==0) { + if (rc == 0) { rc = CIFSSMBSetFileTimes(xid, pTcon, &time_buf, netfid); CIFSSMBClose(xid, pTcon, netfid); @@ -1634,7 +1642,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) granularity */ /* rc = CIFSSMBSetTimesLegacy(xid, pTcon, full_path, - &time_buf, cifs_sb->local_nls); */ + &time_buf, cifs_sb->local_nls); */ } } /* Even if error on time set, no sense failing the call if @@ -1642,7 +1650,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) and this check ensures that we are not being called from sys_utimes in which case we ought to fail the call back to the user when the server rejects the call */ - if((rc) && (attrs->ia_valid & + if ((rc) && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) rc = 0; } diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index a414f17..d24fe68 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -3,7 +3,7 @@ * * vfs operations that deal with io control * - * Copyright (C) International Business Machines Corp., 2005 + * Copyright (C) International Business Machines Corp., 2005,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ #define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2) -int cifs_ioctl (struct inode * inode, struct file * filep, +int cifs_ioctl (struct inode *inode, struct file *filep, unsigned int command, unsigned long arg) { int rc = -ENOTTY; /* strange error - but the precedent */ diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 6baea85..6a85ef7 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -50,32 +50,33 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); - if((fromName == NULL) || (toName == NULL)) { + if ((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_hl_exit; } - if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX) +/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ + if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, + cifs_sb_target->local_nls, cifs_sb_target->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else { rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, + cifs_sb_target->local_nls, cifs_sb_target->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if((rc == -EIO) || (rc == -EINVAL)) - rc = -EOPNOTSUPP; + if ((rc == -EIO) || (rc == -EINVAL)) + rc = -EOPNOTSUPP; } d_drop(direntry); /* force new lookup from server of target */ /* if source file is cached (oplocked) revalidate will not go to server until the file is closed or oplock broken so update nlinks locally */ - if(old_file->d_inode) { + if (old_file->d_inode) { cifsInode = CIFS_I(old_file->d_inode); - if(rc == 0) { + if (rc == 0) { old_file->d_inode->i_nlink++; /* BB should we make this contingent on superblock flag NOATIME? */ /* old_file->d_inode->i_ctime = CURRENT_TIME;*/ @@ -84,14 +85,14 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, to set the parent dir cifs inode time to zero to force revalidate (faster) for it too? */ } - /* if not oplocked will force revalidate to get info + /* if not oplocked will force revalidate to get info on source file from srv */ cifsInode->time = 0; - /* Will update parent dir timestamps from srv within a second. + /* Will update parent dir timestamps from srv within a second. Would it really be worth it to set the parent dir (cifs inode) time field to zero to force revalidate on parent - directory faster ie + directory faster ie CIFS_I(inode)->time = 0; */ } @@ -109,7 +110,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) int rc = -EACCES; int xid; char *full_path = NULL; - char * target_path = ERR_PTR(-ENOMEM); + char *target_path = ERR_PTR(-ENOMEM); struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -129,13 +130,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) goto out; } -/* BB add read reparse point symlink code and Unix extensions symlink code here BB */ + /* We could change this to: + if (pTcon->unix_ext) + but there does not seem any point in refusing to + get symlink info if we can, even if unix extensions + turned off for this mount */ + if (pTcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, target_path, PATH_MAX-1, cifs_sb->local_nls); else { + /* BB add read reparse point symlink code here */ /* rc = CIFSSMBQueryReparseLinkInfo */ /* BB Add code to Query ReparsePoint info */ /* BB Add MAC style xsymlink check here if enabled */ @@ -176,7 +183,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) full_path = build_path_from_dentry(direntry); - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } @@ -185,19 +192,20 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) cFYI(1, ("symname is %s", symname)); /* BB what if DFS and this volume is on different share? BB */ - if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, cifs_sb->local_nls); /* else - rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,cifs_sb_target->local_nls); */ + rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, + cifs_sb_target->local_nls); */ if (rc == 0) { - if (pTcon->ses->capabilities & CAP_UNIX) + if (pTcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb,xid); + inode->i_sb, xid); else rc = cifs_get_inode_info(&newinode, full_path, NULL, - inode->i_sb,xid); + inode->i_sb, xid); if (rc != 0) { cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d", @@ -226,9 +234,9 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; char *full_path = NULL; - char *tmp_path = NULL; - char * tmpbuffer; - unsigned char * referrals = NULL; + char *tmp_path = NULL; + char *tmpbuffer; + unsigned char *referrals = NULL; int num_referrals = 0; int len; __u16 fid; @@ -237,13 +245,13 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; -/* BB would it be safe against deadlock to grab this sem +/* BB would it be safe against deadlock to grab this sem even though rename itself grabs the sem and calls lookup? */ /* mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/ full_path = build_path_from_dentry(direntry); /* mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/ - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } @@ -251,70 +259,80 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) cFYI(1, ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d", full_path, inode, pBuffer, buflen)); - if(buflen > PATH_MAX) + if (buflen > PATH_MAX) len = PATH_MAX; else len = buflen; - tmpbuffer = kmalloc(len,GFP_KERNEL); - if(tmpbuffer == NULL) { + tmpbuffer = kmalloc(len, GFP_KERNEL); + if (tmpbuffer == NULL) { kfree(full_path); FreeXid(xid); return -ENOMEM; } -/* BB add read reparse point symlink code and Unix extensions symlink code here BB */ +/* BB add read reparse point symlink code and + Unix extensions symlink code here BB */ +/* We could disable this based on pTcon->unix_ext flag instead ... but why? */ if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, tmpbuffer, len - 1, cifs_sb->local_nls); else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - cERROR(1,("SFU style symlinks not implemented yet")); + cERROR(1, ("SFU style symlinks not implemented yet")); /* add open and read as in fs/cifs/inode.c */ - } else { rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, - OPEN_REPARSE_POINT,&fid, &oplock, NULL, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + OPEN_REPARSE_POINT, &fid, &oplock, NULL, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if(!rc) { + if (!rc) { rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path, tmpbuffer, - len - 1, + len - 1, fid, cifs_sb->local_nls); - if(CIFSSMBClose(xid, pTcon, fid)) { - cFYI(1,("Error closing junction point (open for ioctl)")); + if (CIFSSMBClose(xid, pTcon, fid)) { + cFYI(1, ("Error closing junction point " + "(open for ioctl)")); } - if(rc == -EIO) { + if (rc == -EIO) { /* Query if DFS Junction */ tmp_path = kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1, GFP_KERNEL); if (tmp_path) { - strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); - strncat(tmp_path, full_path, MAX_PATHCONF); - rc = get_dfs_path(xid, pTcon->ses, tmp_path, + strncpy(tmp_path, pTcon->treeName, + MAX_TREE_SIZE); + strncat(tmp_path, full_path, + MAX_PATHCONF); + rc = get_dfs_path(xid, pTcon->ses, + tmp_path, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1,("Get DFS for %s rc = %d ",tmp_path, rc)); - if((num_referrals == 0) && (rc == 0)) + cFYI(1, ("Get DFS for %s rc = %d ", + tmp_path, rc)); + if ((num_referrals == 0) && (rc == 0)) rc = -EACCES; else { - cFYI(1,("num referral: %d",num_referrals)); - if(referrals) { - cFYI(1,("referral string: %s",referrals)); - strncpy(tmpbuffer, referrals, len-1); + cFYI(1, ("num referral: %d", + num_referrals)); + if (referrals) { + cFYI(1,("referral string: %s", referrals)); + strncpy(tmpbuffer, + referrals, + len-1); } } kfree(referrals); kfree(tmp_path); } - /* BB add code like else decode referrals then memcpy to - tmpbuffer and free referrals string array BB */ + /* BB add code like else decode referrals + then memcpy to tmpbuffer and free referrals + string array BB */ } } } diff --git a/fs/cifs/md4.c b/fs/cifs/md4.c index 46d62c9..a2415c1 100644 --- a/fs/cifs/md4.c +++ b/fs/cifs/md4.c @@ -1,20 +1,20 @@ -/* +/* Unix SMB/Netbios implementation. Version 1.9. a implementation of MD4 designed for use in the SMB authentication protocol Copyright (C) Andrew Tridgell 1997-1998. Modified by Steve French (sfrench@us.ibm.com) 2002-2003 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -170,7 +170,7 @@ mdfour(unsigned char *out, unsigned char *in, int n) while (n > 64) { copy64(M, in); - mdfour64(M,&A,&B, &C, &D); + mdfour64(M, &A, &B, &C, &D); in += 64; n -= 64; } diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c index ccebf9b..e5c3e12 100644 --- a/fs/cifs/md5.c +++ b/fs/cifs/md5.c @@ -15,9 +15,9 @@ * will fill a supplied 16-byte array with the digest. */ -/* This code slightly modified to fit into Samba by - abartlet@samba.org Jun 2001 - and to fit the cifs vfs by +/* This code slightly modified to fit into Samba by + abartlet@samba.org Jun 2001 + and to fit the cifs vfs by Steve French sfrench@us.ibm.com */ #include <linux/string.h> @@ -106,7 +106,7 @@ MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) } /* - * Final wrapup - pad to 64-byte boundary with the bit pattern + * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 19cc294..0bcec08 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1,7 +1,7 @@ /* * fs/cifs/misc.c * - * Copyright (C) International Business Machines Corp., 2002,2005 + * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/slab.h> @@ -32,12 +32,12 @@ extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; -extern struct task_struct * oplockThread; +extern struct task_struct *oplockThread; -/* The xid serves as a useful identifier for each incoming vfs request, - in a similar way to the mid which is useful to track each sent smb, - and CurrentXid can also provide a running counter (although it - will eventually wrap past zero) of the total vfs operations handled +/* The xid serves as a useful identifier for each incoming vfs request, + in a similar way to the mid which is useful to track each sent smb, + and CurrentXid can also provide a running counter (although it + will eventually wrap past zero) of the total vfs operations handled since the cifs fs was mounted */ unsigned int @@ -47,10 +47,12 @@ _GetXid(void) spin_lock(&GlobalMid_Lock); GlobalTotalActiveXid++; + + /* keep high water mark for number of simultaneous ops in filesystem */ if (GlobalTotalActiveXid > GlobalMaxActiveXid) - GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ - if(GlobalTotalActiveXid > 65000) - cFYI(1,("warning: more than 65000 requests active")); + GlobalMaxActiveXid = GlobalTotalActiveXid; + if (GlobalTotalActiveXid > 65000) + cFYI(1, ("warning: more than 65000 requests active")); xid = GlobalCurrentXid++; spin_unlock(&GlobalMid_Lock); return xid; @@ -60,7 +62,7 @@ void _FreeXid(unsigned int xid) { spin_lock(&GlobalMid_Lock); - /* if(GlobalTotalActiveXid == 0) + /* if (GlobalTotalActiveXid == 0) BUG(); */ GlobalTotalActiveXid--; spin_unlock(&GlobalMid_Lock); @@ -144,12 +146,12 @@ cifs_buf_get(void) { struct smb_hdr *ret_buf = NULL; -/* We could use negotiated size instead of max_msgsize - - but it may be more efficient to always alloc same size - albeit slightly larger than necessary and maxbuffersize +/* We could use negotiated size instead of max_msgsize - + but it may be more efficient to always alloc same size + albeit slightly larger than necessary and maxbuffersize defaults to this and can not be bigger */ - ret_buf = - (struct smb_hdr *) mempool_alloc(cifs_req_poolp, GFP_KERNEL | GFP_NOFS); + ret_buf = (struct smb_hdr *) mempool_alloc(cifs_req_poolp, + GFP_KERNEL | GFP_NOFS); /* clear the first few header bytes */ /* for most paths, more is cleared in header_assemble */ @@ -172,7 +174,7 @@ cifs_buf_release(void *buf_to_free) /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/ return; } - mempool_free(buf_to_free,cifs_req_poolp); + mempool_free(buf_to_free, cifs_req_poolp); atomic_dec(&bufAllocCount); return; @@ -183,12 +185,12 @@ cifs_small_buf_get(void) { struct smb_hdr *ret_buf = NULL; -/* We could use negotiated size instead of max_msgsize - - but it may be more efficient to always alloc same size - albeit slightly larger than necessary and maxbuffersize +/* We could use negotiated size instead of max_msgsize - + but it may be more efficient to always alloc same size + albeit slightly larger than necessary and maxbuffersize defaults to this and can not be bigger */ - ret_buf = - (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, GFP_KERNEL | GFP_NOFS); + ret_buf = (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, + GFP_KERNEL | GFP_NOFS); if (ret_buf) { /* No need to clear memory here, cleared in header assemble */ /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ @@ -209,30 +211,30 @@ cifs_small_buf_release(void *buf_to_free) cFYI(1, ("Null buffer passed to cifs_small_buf_release")); return; } - mempool_free(buf_to_free,cifs_sm_req_poolp); + mempool_free(buf_to_free, cifs_sm_req_poolp); atomic_dec(&smBufAllocCount); return; } -/* +/* Find a free multiplex id (SMB mid). Otherwise there could be mid collisions which might cause problems, demultiplexing the wrong response to this request. Multiplex ids could collide if one of a series requests takes much longer than the others, or if a very large number of long lived requests (byte range locks or FindNotify requests) are pending. No more than - 64K-1 requests can be outstanding at one time. If no + 64K-1 requests can be outstanding at one time. If no mids are available, return zero. A future optimization could make the combination of mids and uid the key we use - to demultiplex on (rather than mid alone). + to demultiplex on (rather than mid alone). In addition to the above check, the cifs demultiplex code already used the command code as a secondary check of the frame and if signing is negotiated the response would be discarded if the mid were the same but the signature was wrong. Since the mid is not put in the pending queue until later (when it is about to be dispatched) - we do have to limit the number of outstanding requests + we do have to limit the number of outstanding requests to somewhat less than 64K-1 although it is hard to imagine so many threads being in the vfs at one time. */ @@ -240,27 +242,27 @@ __u16 GetNextMid(struct TCP_Server_Info *server) { __u16 mid = 0; __u16 last_mid; - int collision; + int collision; - if(server == NULL) + if (server == NULL) return mid; spin_lock(&GlobalMid_Lock); last_mid = server->CurrentMid; /* we do not want to loop forever */ server->CurrentMid++; /* This nested loop looks more expensive than it is. - In practice the list of pending requests is short, + In practice the list of pending requests is short, fewer than 50, and the mids are likely to be unique on the first pass through the loop unless some request takes longer than the 64 thousand requests before it (and it would also have to have been a request that did not time out) */ - while(server->CurrentMid != last_mid) { + while (server->CurrentMid != last_mid) { struct list_head *tmp; struct mid_q_entry *mid_entry; collision = 0; - if(server->CurrentMid == 0) + if (server->CurrentMid == 0) server->CurrentMid++; list_for_each(tmp, &server->pending_mid_q) { @@ -273,7 +275,7 @@ __u16 GetNextMid(struct TCP_Server_Info *server) break; } } - if(collision == 0) { + if (collision == 0) { mid = server->CurrentMid; break; } @@ -290,11 +292,11 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , const struct cifsTconInfo *treeCon, int word_count /* length of fixed section (word count) in two byte units */) { - struct list_head* temp_item; - struct cifsSesInfo * ses; + struct list_head *temp_item; + struct cifsSesInfo *ses; char *temp = (char *) buffer; - memset(temp,0,256); /* bigger than MAX_CIFS_HDR_SIZE */ + memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ buffer->smb_buf_length = (2 * word_count) + sizeof (struct smb_hdr) - @@ -325,7 +327,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , /* Uid is not converted */ buffer->Uid = treeCon->ses->Suid; buffer->Mid = GetNextMid(treeCon->ses->server); - if(multiuser_mount != 0) { + if (multiuser_mount != 0) { /* For the multiuser case, there are few obvious technically */ /* possible mechanisms to match the local linux user (uid) */ /* to a valid remote smb user (smb_uid): */ @@ -348,21 +350,22 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , /* flag were disabled. */ /* BB Add support for establishing new tCon and SMB Session */ - /* with userid/password pairs found on the smb session */ + /* with userid/password pairs found on the smb session */ /* for other target tcp/ip addresses BB */ - if(current->fsuid != treeCon->ses->linux_uid) { - cFYI(1,("Multiuser mode and UID did not match tcon uid")); + if (current->fsuid != treeCon->ses->linux_uid) { + cFYI(1, ("Multiuser mode and UID " + "did not match tcon uid")); read_lock(&GlobalSMBSeslock); list_for_each(temp_item, &GlobalSMBSessionList) { ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); - if(ses->linux_uid == current->fsuid) { - if(ses->server == treeCon->ses->server) { - cFYI(1,("found matching uid substitute right smb_uid")); + if (ses->linux_uid == current->fsuid) { + if (ses->server == treeCon->ses->server) { + cFYI(1, ("found matching uid substitute right smb_uid")); buffer->Uid = ses->Suid; break; } else { - /* BB eventually call cifs_setup_session here */ - cFYI(1,("local UID found but smb sess with this server does not exist")); + /* BB eventually call cifs_setup_session here */ + cFYI(1, ("local UID found but no smb sess with this server exists")); } } } @@ -374,8 +377,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , buffer->Flags2 |= SMBFLG2_DFS; if (treeCon->nocase) buffer->Flags |= SMBFLG_CASELESS; - if((treeCon->ses) && (treeCon->ses->server)) - if(treeCon->ses->server->secMode & + if ((treeCon->ses) && (treeCon->ses->server)) + if (treeCon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } @@ -388,18 +391,18 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , static int checkSMBhdr(struct smb_hdr *smb, __u16 mid) { - /* Make sure that this really is an SMB, that it is a response, + /* Make sure that this really is an SMB, that it is a response, and that the message ids match */ - if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) && - (mid == smb->Mid)) { - if(smb->Flags & SMBFLG_RESPONSE) - return 0; - else { + if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) && + (mid == smb->Mid)) { + if (smb->Flags & SMBFLG_RESPONSE) + return 0; + else { /* only one valid case where server sends us request */ - if(smb->Command == SMB_COM_LOCKING_ANDX) + if (smb->Command == SMB_COM_LOCKING_ANDX) return 0; else - cERROR(1, ("Rcvd Request not response")); + cERROR(1, ("Received Request not response")); } } else { /* bad signature or mid */ if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) @@ -426,9 +429,9 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) smb->WordCount = 0; /* some error cases do not return wct and bcc */ return 0; - } else if ((length == sizeof(struct smb_hdr) + 1) && + } else if ((length == sizeof(struct smb_hdr) + 1) && (smb->WordCount == 0)) { - char * tmp = (char *)smb; + char *tmp = (char *)smb; /* Need to work around a bug in two servers here */ /* First, check if the part of bcc they sent was zero */ if (tmp[sizeof(struct smb_hdr)] == 0) { @@ -442,7 +445,7 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) tmp[sizeof(struct smb_hdr)+1] = 0; return 0; } - cERROR(1,("rcvd invalid byte count (bcc)")); + cERROR(1, ("rcvd invalid byte count (bcc)")); } else { cERROR(1, ("Length less than smb header size")); } @@ -458,32 +461,33 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) return 1; clc_len = smbCalcSize_LE(smb); - if(4 + len != length) { - cERROR(1, ("Length read does not match RFC1001 length %d",len)); + if (4 + len != length) { + cERROR(1, ("Length read does not match RFC1001 length %d", + len)); return 1; } if (4 + len != clc_len) { /* check if bcc wrapped around for large read responses */ - if((len > 64 * 1024) && (len > clc_len)) { + if ((len > 64 * 1024) && (len > clc_len)) { /* check if lengths match mod 64K */ - if(((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) - return 0; /* bcc wrapped */ + if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) + return 0; /* bcc wrapped */ } cFYI(1, ("Calculated size %d vs length %d mismatch for mid %d", clc_len, 4 + len, smb->Mid)); /* Windows XP can return a few bytes too much, presumably - an illegal pad, at the end of byte range lock responses + an illegal pad, at the end of byte range lock responses so we allow for that three byte pad, as long as actual received length is as long or longer than calculated length */ - /* We have now had to extend this more, since there is a + /* We have now had to extend this more, since there is a case in which it needs to be bigger still to handle a malformed response to transact2 findfirst from WinXP when access denied is returned and thus bcc and wct are zero but server says length is 0x21 bytes too long as if the server forget to reset the smb rfc1001 length when it reset the wct and bcc to minimum size and drop the t2 parms and data */ - if((4+len > clc_len) && (len <= clc_len + 512)) + if ((4+len > clc_len) && (len <= clc_len + 512)) return 0; else { cERROR(1, ("RFC1001 size %d bigger than SMB for Mid=%d", @@ -495,61 +499,64 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) } int is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) -{ - struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf; +{ + struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct list_head *tmp; struct list_head *tmp1; struct cifsTconInfo *tcon; struct cifsFileInfo *netfile; - cFYI(1,("Checking for oplock break or dnotify response")); - if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && + cFYI(1, ("Checking for oplock break or dnotify response")); + if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { - struct smb_com_transaction_change_notify_rsp * pSMBr = + struct smb_com_transaction_change_notify_rsp *pSMBr = (struct smb_com_transaction_change_notify_rsp *)buf; - struct file_notify_information * pnotify; + struct file_notify_information *pnotify; __u32 data_offset = 0; - if(pSMBr->ByteCount > sizeof(struct file_notify_information)) { + if (pSMBr->ByteCount > sizeof(struct file_notify_information)) { data_offset = le32_to_cpu(pSMBr->DataOffset); pnotify = (struct file_notify_information *) ((char *)&pSMBr->hdr.Protocol + data_offset); - cFYI(1,("dnotify on %s Action: 0x%x",pnotify->FileName, + cFYI(1, ("dnotify on %s Action: 0x%x", + pnotify->FileName, pnotify->Action)); /* BB removeme BB */ - /* cifs_dump_mem("Rcvd notify Data: ",buf, + /* cifs_dump_mem("Rcvd notify Data: ",buf, sizeof(struct smb_hdr)+60); */ return TRUE; } - if(pSMBr->hdr.Status.CifsError) { - cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError)); + if (pSMBr->hdr.Status.CifsError) { + cFYI(1, ("notify err 0x%d", + pSMBr->hdr.Status.CifsError)); return TRUE; } return FALSE; - } - if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) + } + if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) return FALSE; - if(pSMB->hdr.Flags & SMBFLG_RESPONSE) { + if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { /* no sense logging error on invalid handle on oplock break - harmless race between close request and oplock break response is expected from time to time writing out large dirty files cached on the client */ - if ((NT_STATUS_INVALID_HANDLE) == - le32_to_cpu(pSMB->hdr.Status.CifsError)) { - cFYI(1,("invalid handle on oplock break")); + if ((NT_STATUS_INVALID_HANDLE) == + le32_to_cpu(pSMB->hdr.Status.CifsError)) { + cFYI(1, ("invalid handle on oplock break")); return TRUE; - } else if (ERRbadfid == + } else if (ERRbadfid == le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { - return TRUE; + return TRUE; } else { return FALSE; /* on valid oplock brk we get "request" */ } } - if(pSMB->hdr.WordCount != 8) + if (pSMB->hdr.WordCount != 8) return FALSE; - cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel)); - if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) - return FALSE; + cFYI(1, ("oplock type 0x%d level 0x%d", + pSMB->LockType, pSMB->OplockLevel)); + if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) + return FALSE; /* look up tcon based on tid & uid */ read_lock(&GlobalSMBSeslock); @@ -557,36 +564,38 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { cifs_stats_inc(&tcon->num_oplock_brks); - list_for_each(tmp1,&tcon->openFileList){ - netfile = list_entry(tmp1,struct cifsFileInfo, + list_for_each(tmp1, &tcon->openFileList) { + netfile = list_entry(tmp1, struct cifsFileInfo, tlist); - if(pSMB->Fid == netfile->netfid) { + if (pSMB->Fid == netfile->netfid) { struct cifsInodeInfo *pCifsInode; read_unlock(&GlobalSMBSeslock); - cFYI(1,("file id match, oplock break")); - pCifsInode = + cFYI(1, + ("file id match, oplock break")); + pCifsInode = CIFS_I(netfile->pInode); pCifsInode->clientCanCacheAll = FALSE; - if(pSMB->OplockLevel == 0) + if (pSMB->OplockLevel == 0) pCifsInode->clientCanCacheRead = FALSE; pCifsInode->oplockPending = TRUE; AllocOplockQEntry(netfile->pInode, netfile->netfid, tcon); - cFYI(1,("about to wake up oplock thd")); - if(oplockThread) + cFYI(1, + ("about to wake up oplock thread")); + if (oplockThread) wake_up_process(oplockThread); return TRUE; } } read_unlock(&GlobalSMBSeslock); - cFYI(1,("No matching file for oplock break")); + cFYI(1, ("No matching file for oplock break")); return TRUE; } } read_unlock(&GlobalSMBSeslock); - cFYI(1,("Can not process oplock break for non-existent connection")); + cFYI(1, ("Can not process oplock break for non-existent connection")); return TRUE; } @@ -643,13 +652,13 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) only legal in POSIX-like OS (if they are present in the string). Path names are little endian 16 bit Unicode on the wire */ int -cifs_convertUCSpath(char *target, const __le16 * source, int maxlen, - const struct nls_table * cp) +cifs_convertUCSpath(char *target, const __le16 *source, int maxlen, + const struct nls_table *cp) { - int i,j,len; + int i, j, len; __u16 src_char; - for(i = 0, j = 0; i < maxlen; i++) { + for (i = 0, j = 0; i < maxlen; i++) { src_char = le16_to_cpu(source[i]); switch (src_char) { case 0: @@ -678,10 +687,10 @@ cifs_convertUCSpath(char *target, const __le16 * source, int maxlen, case UNI_LESSTHAN: target[j] = '<'; break; - default: - len = cp->uni2char(src_char, &target[j], + default: + len = cp->uni2char(src_char, &target[j], NLS_MAX_CHARSET_SIZE); - if(len > 0) { + if (len > 0) { j += len; continue; } else { @@ -690,7 +699,7 @@ cifs_convertUCSpath(char *target, const __le16 * source, int maxlen, } j++; /* make sure we do not overrun callers allocated temp buffer */ - if(j >= (2 * NAME_MAX)) + if (j >= (2 * NAME_MAX)) break; } cUCS_out: @@ -703,18 +712,18 @@ cUCS_out: only legal in POSIX-like OS (if they are present in the string). Path names are little endian 16 bit Unicode on the wire */ int -cifsConvertToUCS(__le16 * target, const char *source, int maxlen, - const struct nls_table * cp, int mapChars) +cifsConvertToUCS(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars) { - int i,j,charlen; + int i, j, charlen; int len_remaining = maxlen; char src_char; __u16 temp; - if(!mapChars) + if (!mapChars) return cifs_strtoUCS(target, source, PATH_MAX, cp); - for(i = 0, j = 0; i < maxlen; j++) { + for (i = 0, j = 0; i < maxlen; j++) { src_char = source[i]; switch (src_char) { case 0: @@ -737,7 +746,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, break; case '|': target[j] = cpu_to_le16(UNI_PIPE); - break; + break; /* BB We can not handle remapping slash until all the calls to build_path_from_dentry are modified, as they use slash as separator BB */ @@ -749,7 +758,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, len_remaining, &temp); /* if no match, use question mark, which at least in some cases servers as wild card */ - if(charlen < 1) { + if (charlen < 1) { target[j] = cpu_to_le16(0x003f); charlen = 1; } else @@ -758,7 +767,7 @@ cifsConvertToUCS(__le16 * target, const char *source, int maxlen, /* character may take more than one byte in the the source string, but will take exactly two bytes in the target string */ - i+= charlen; + i += charlen; continue; } i++; /* move to next char in source string */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 53e304d..2bfed3f 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -3,23 +3,22 @@ * * Copyright (c) International Business Machines Corp., 2002 * Author(s): Steve French (sfrench@us.ibm.com) - * + * * Error mapping routines from Samba libsmb/errormap.c * Copyright (C) Andrew Tridgell 2001 * - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -30,9 +29,7 @@ #include <linux/fs.h> #include <asm/div64.h> #include <asm/byteorder.h> -#ifdef CONFIG_CIFS_EXPERIMENTAL #include <linux/inet.h> -#endif #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -67,22 +64,22 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { {ERRbadshare, -ETXTBSY}, {ERRlock, -EACCES}, {ERRunsup, -EINVAL}, - {ERRnosuchshare,-ENXIO}, + {ERRnosuchshare, -ENXIO}, {ERRfilexists, -EEXIST}, {ERRinvparm, -EINVAL}, {ERRdiskfull, -ENOSPC}, {ERRinvname, -ENOENT}, - {ERRinvlevel,-EOPNOTSUPP}, + {ERRinvlevel, -EOPNOTSUPP}, {ERRdirnotempty, -ENOTEMPTY}, {ERRnotlocked, -ENOLCK}, {ERRcancelviolation, -ENOLCK}, {ERRalreadyexists, -EEXIST}, {ERRmoredata, -EOVERFLOW}, - {ERReasnotsupported,-EOPNOTSUPP}, + {ERReasnotsupported, -EOPNOTSUPP}, {ErrQuota, -EDQUOT}, {ErrNotALink, -ENOLINK}, - {ERRnetlogonNotStarted,-ENOPROTOOPT}, - {ErrTooManyLinks,-EMLINK}, + {ERRnetlogonNotStarted, -ENOPROTOOPT}, + {ErrTooManyLinks, -EMLINK}, {0, 0} }; @@ -133,85 +130,24 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { /* returns 0 if invalid address */ int -cifs_inet_pton(int address_family, char *cp,void *dst) +cifs_inet_pton(int address_family, char *cp, void *dst) { -#ifdef CONFIG_CIFS_EXPERIMENTAL int ret = 0; /* calculate length by finding first slash or NULL */ - /* BB Should we convert '/' slash to '\' here since it seems already done - before this */ - if( address_family == AF_INET ){ - ret = in4_pton(cp, -1 /* len */, dst , '\\', NULL); - } else if( address_family == AF_INET6 ){ + /* BB Should we convert '/' slash to '\' here since it seems already + * done before this */ + if ( address_family == AF_INET ) { + ret = in4_pton(cp, -1 /* len */, dst , '\\', NULL); + } else if ( address_family == AF_INET6 ) { ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); } #ifdef CONFIG_CIFS_DEBUG2 - cFYI(1,("address conversion returned %d for %s", ret, cp)); + cFYI(1, ("address conversion returned %d for %s", ret, cp)); #endif if (ret > 0) ret = 1; return ret; -#else - int value; - int digit; - int i; - char temp; - char bytes[4]; - char *end = bytes; - static const int addr_class_max[4] = - { 0xffffffff, 0xffffff, 0xffff, 0xff }; - - if(address_family != AF_INET) - return -EAFNOSUPPORT; - - for (i = 0; i < 4; i++) { - bytes[i] = 0; - } - - temp = *cp; - - while (TRUE) { - if (!isdigit(temp)) - return 0; - - value = 0; - digit = 0; - for (;;) { - if (isascii(temp) && isdigit(temp)) { - value = (value * 10) + temp - '0'; - temp = *++cp; - digit = 1; - } else - break; - } - - if (temp == '.') { - if ((end > bytes + 2) || (value > 255)) - return 0; - *end++ = value; - temp = *++cp; - } else if (temp == ':') { - cFYI(1,("IPv6 addresses not supported for CIFS mounts yet")); - return -1; - } else - break; - } - - /* check for last characters */ - if (temp != '\0' && (!isascii(temp) || !isspace(temp))) - if (temp != '\\') { - if (temp != '/') - return 0; - else - (*cp = '\\'); /* switch the slash the expected way */ - } - if (value > addr_class_max[end - bytes]) - return 0; - - *((__be32 *)dst) = *((__be32 *) bytes) | htonl(value); - return 1; /* success */ -#endif /* EXPERIMENTAL */ } /***************************************************************************** @@ -246,7 +182,7 @@ static const struct { ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, /* { This NT error code was 'sqashed' - from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK during the session setup } */ { ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { @@ -261,7 +197,7 @@ static const struct { ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, /* { This NT error code was 'sqashed' - from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ { ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { @@ -331,7 +267,7 @@ static const struct { ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, /* { This NT error code was 'sqashed' - from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE during the session setup } */ { ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { @@ -341,7 +277,7 @@ static const struct { ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, /* { This NT error code was 'sqashed' - from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE during the session setup } */ { ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { @@ -393,8 +329,8 @@ static const struct { ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, /* { This NT error code was 'sqashed' - from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES - during the session setup } */ + from NT_STATUS_INSUFFICIENT_RESOURCES to + NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ { ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, { ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { @@ -638,8 +574,8 @@ static const struct { ERRDOS, 19, NT_STATUS_TOO_LATE}, { ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, /* { This NT error code was 'sqashed' - from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE - during the session setup } */ + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ { ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { @@ -658,7 +594,7 @@ static const struct { ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, /* { This NT error code was 'sqashed' - from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE during the session setup } */ { ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { @@ -789,7 +725,7 @@ cifs_print_status(__u32 status_code) if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == (status_code & 0xFFFFFF)) { printk(KERN_NOTICE "Status code returned 0x%08x %s\n", - status_code,nt_errs[idx].nt_errstr); + status_code, nt_errs[idx].nt_errstr); } idx++; } @@ -821,7 +757,7 @@ int map_smb_to_linux_error(struct smb_hdr *smb) { unsigned int i; - int rc = -EIO; /* if transport error smb error may not be set */ + int rc = -EIO; /* if transport error smb error may not be set */ __u8 smberrclass; __u16 smberrcode; @@ -832,9 +768,10 @@ map_smb_to_linux_error(struct smb_hdr *smb) return 0; if (smb->Flags2 & SMBFLG2_ERR_STATUS) { - /* translate the newer STATUS codes to old style errors and then to POSIX errors */ + /* translate the newer STATUS codes to old style SMB errors + * and then to POSIX errors */ __u32 err = le32_to_cpu(smb->Status.CifsError); - if(cifsFYI & CIFS_RC) + if (cifsFYI & CIFS_RC) cifs_print_status(err); ntstatus_to_dos(err, &smberrclass, &smberrcode); } else { @@ -845,38 +782,42 @@ map_smb_to_linux_error(struct smb_hdr *smb) /* old style errors */ /* DOS class smb error codes - map DOS */ - if (smberrclass == ERRDOS) { /* one byte field no need to byte reverse */ + if (smberrclass == ERRDOS) { /* 1 byte field no need to byte reverse */ for (i = 0; i < sizeof (mapping_table_ERRDOS) / sizeof (struct smb_to_posix_error); i++) { if (mapping_table_ERRDOS[i].smb_err == 0) break; - else if (mapping_table_ERRDOS[i].smb_err == smberrcode) { + else if (mapping_table_ERRDOS[i].smb_err == + smberrcode) { rc = mapping_table_ERRDOS[i].posix_code; break; } - /* else try the next error mapping one to see if it will match */ + /* else try next error mapping one to see if match */ } - } else if (smberrclass == ERRSRV) { /* server class of error codes */ + } else if (smberrclass == ERRSRV) { /* server class of error codes */ for (i = 0; i < sizeof (mapping_table_ERRSRV) / sizeof (struct smb_to_posix_error); i++) { if (mapping_table_ERRSRV[i].smb_err == 0) break; - else if (mapping_table_ERRSRV[i].smb_err == smberrcode) { + else if (mapping_table_ERRSRV[i].smb_err == + smberrcode) { rc = mapping_table_ERRSRV[i].posix_code; break; } - /* else try the next error mapping one to see if it will match */ + /* else try next error mapping to see if match */ } } /* else ERRHRD class errors or junk - return EIO */ - cFYI(1, (" !!Mapping smb error code %d to POSIX err %d !!", smberrcode,rc)); + cFYI(1, (" !!Mapping smb error code %d to POSIX err %d !!", + smberrcode, rc)); - /* generic corrective action e.g. reconnect SMB session on ERRbaduid could be added */ + /* generic corrective action e.g. reconnect SMB session on + * ERRbaduid could be added */ return rc; } @@ -910,7 +851,7 @@ smbCalcSize_LE(struct smb_hdr *ptr) struct timespec cifs_NTtimeToUnix(u64 ntutc) { - struct timespec ts; + struct timespec ts; /* BB what about the timezone? BB */ /* Subtract the NTFS time offset, then convert to 1s intervals. */ @@ -918,7 +859,7 @@ cifs_NTtimeToUnix(u64 ntutc) t = ntutc - NTFS_TIME_OFFSET; ts.tv_nsec = do_div(t, 10000000) * 100; - ts.tv_sec = t; + ts.tv_sec = t; return ts; } @@ -946,20 +887,20 @@ struct timespec cnvrtDosUnixTm(__u16 date, __u16 time) SMB_TIME * st = (SMB_TIME *)&time; SMB_DATE * sd = (SMB_DATE *)&date; - cFYI(1,("date %d time %d",date, time)); + cFYI(1, ("date %d time %d", date, time)); sec = 2 * st->TwoSeconds; min = st->Minutes; - if((sec > 59) || (min > 59)) - cERROR(1,("illegal time min %d sec %d", min, sec)); + if ((sec > 59) || (min > 59)) + cERROR(1, ("illegal time min %d sec %d", min, sec)); sec += (min * 60); sec += 60 * 60 * st->Hours; - if(st->Hours > 24) - cERROR(1,("illegal hours %d",st->Hours)); + if (st->Hours > 24) + cERROR(1, ("illegal hours %d", st->Hours)); days = sd->Day; month = sd->Month; - if((days > 31) || (month > 12)) - cERROR(1,("illegal date, month %d day: %d", month, days)); + if ((days > 31) || (month > 12)) + cERROR(1, ("illegal date, month %d day: %d", month, days)); month -= 1; days += total_days_of_prev_months[month]; days += 3652; /* account for difference in days between 1980 and 1970 */ @@ -970,15 +911,15 @@ struct timespec cnvrtDosUnixTm(__u16 date, __u16 time) 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 + 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 */ + if (year >= 120) /* the year 2100 */ days = days - 1; /* do not count leap year for the year 2100 */ /* adjust for leap year where we are still before leap day */ - if(year != 120) + if (year != 120) days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0); - sec += 24 * 60 * 60 * days; + sec += 24 * 60 * 60 * days; ts.tv_sec = sec; @@ -986,4 +927,4 @@ struct timespec cnvrtDosUnixTm(__u16 date, __u16 time) ts.tv_nsec = 0; return ts; -} +} diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c index 4da50cd..819fd99 100644 --- a/fs/cifs/nterr.c +++ b/fs/cifs/nterr.c @@ -1,19 +1,19 @@ -/* +/* * Unix SMB/Netbios implementation. * Version 1.9. * RPC Pipe client / server routines * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h index d2fb06c..588abbb 100644 --- a/fs/cifs/nterr.h +++ b/fs/cifs/nterr.h @@ -1,4 +1,4 @@ -/* +/* Unix SMB/Netbios implementation. Version 1.9. NT error code constants @@ -6,17 +6,17 @@ Copyright (C) John H Terpstra 1996-2000 Copyright (C) Luke Kenneth Casson Leighton 1996-2000 Copyright (C) Paul Ashton 1998-2000 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index d39b712..7170a9b 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -1,7 +1,7 @@ /* * fs/cifs/ntlmssp.h * - * Copyright (c) International Business Machines Corp., 2002,2006 + * Copyright (c) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define NTLMSSP_SIGNATURE "NTLMSSP" @@ -27,18 +27,18 @@ #define UnknownMessage cpu_to_le32(8) /* Negotiate Flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x01 // Text strings are in unicode -#define NTLMSSP_NEGOTIATE_OEM 0x02 // Text strings are in OEM -#define NTLMSSP_REQUEST_TARGET 0x04 // Server return its auth realm -#define NTLMSSP_NEGOTIATE_SIGN 0x0010 // Request signature capability -#define NTLMSSP_NEGOTIATE_SEAL 0x0020 // Request confidentiality +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are in unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Server return its auth realm */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signature capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ #define NTLMSSP_NEGOTIATE_DGRAM 0x0040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 // Use LM session key for sign/seal -#define NTLMSSP_NEGOTIATE_NTLM 0x0200 // NTLM authentication +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Sign/seal use LM session key */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ #define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 #define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 -#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 // client/server on same machine -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 // Sign for all security levels +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server on same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign for all security levels */ #define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 #define NTLMSSP_TARGET_TYPE_SERVER 0x20000 #define NTLMSSP_TARGET_TYPE_SHARE 0x40000 diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index c08bda9..916df94 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -2,7 +2,7 @@ * fs/cifs/readdir.c * * Directory search handling - * + * * Copyright (C) International Business Machines Corp., 2004, 2007 * Author(s): Steve French (sfrench@us.ibm.com) * @@ -34,24 +34,23 @@ #ifdef CONFIG_CIFS_DEBUG2 static void dump_cifs_file_struct(struct file *file, char *label) { - struct cifsFileInfo * cf; + struct cifsFileInfo *cf; if (file) { cf = file->private_data; if (cf == NULL) { - cFYI(1,("empty cifs private file data")); + cFYI(1, ("empty cifs private file data")); return; } if (cf->invalidHandle) { - cFYI(1,("invalid handle")); + cFYI(1, ("invalid handle")); } if (cf->srch_inf.endOfSearch) { - cFYI(1,("end of search")); + cFYI(1, ("end of search")); } if (cf->srch_inf.emptyDir) { - cFYI(1,("empty dir")); + cFYI(1, ("empty dir")); } - } } #endif /* DEBUG2 */ @@ -73,7 +72,8 @@ static int construct_dentry(struct qstr *qstring, struct file *file, qstring->hash = full_name_hash(qstring->name, qstring->len); tmp_dentry = d_lookup(file->f_path.dentry, qstring); if (tmp_dentry) { - cFYI(0, ("existing dentry with inode 0x%p", tmp_dentry->d_inode)); + cFYI(0, ("existing dentry with inode 0x%p", + tmp_dentry->d_inode)); *ptmp_inode = tmp_dentry->d_inode; /* BB overwrite old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len??*/ if (*ptmp_inode == NULL) { @@ -87,7 +87,7 @@ static int construct_dentry(struct qstr *qstring, struct file *file, } else { tmp_dentry = d_alloc(file->f_path.dentry, qstring); if (tmp_dentry == NULL) { - cERROR(1,("Failed allocating dentry")); + cERROR(1, ("Failed allocating dentry")); *ptmp_inode = NULL; return rc; } @@ -100,7 +100,7 @@ static int construct_dentry(struct qstr *qstring, struct file *file, if (*ptmp_inode == NULL) return rc; if (file->f_path.dentry->d_sb->s_flags & MS_NOATIME) - (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; + (*ptmp_inode)->i_flags |= S_NOATIME | S_NOCMTIME; rc = 2; } @@ -109,7 +109,7 @@ static int construct_dentry(struct qstr *qstring, struct file *file, return rc; } -static void AdjustForTZ(struct cifsTconInfo * tcon, struct inode * inode) +static void AdjustForTZ(struct cifsTconInfo *tcon, struct inode *inode) { if ((tcon) && (tcon->ses) && (tcon->ses->server)) { inode->i_ctime.tv_sec += tcon->ses->server->timeAdj; @@ -121,7 +121,7 @@ static void AdjustForTZ(struct cifsTconInfo * tcon, struct inode * inode) static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, - char * buf, int *pobject_type, int isNewInode) + char *buf, int *pobject_type, int isNewInode) { loff_t local_size; struct timespec local_mtime; @@ -150,7 +150,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); } else { /* legacy, OS2 and DOS style */ /* struct timespec ts;*/ - FIND_FILE_STANDARD_INFO * pfindData = + FIND_FILE_STANDARD_INFO * pfindData = (FIND_FILE_STANDARD_INFO *)buf; tmp_inode->i_mtime = cnvrtDosUnixTm( @@ -175,7 +175,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, /* treat dos attribute of read-only as read-only mode bit e.g. 555? */ /* 2767 perms - indicate mandatory locking */ - /* BB fill in uid and gid here? with help from winbind? + /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ if (atomic_read(&cifsInfo->inUse) == 0) { tmp_inode->i_uid = cifs_sb->mnt_uid; @@ -196,7 +196,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, tmp_inode->i_mode = cifs_sb->mnt_dir_mode; } tmp_inode->i_mode |= S_IFDIR; - } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && + } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && (attr & ATTR_SYSTEM)) { if (end_of_file == 0) { *pobject_type = DT_FIFO; @@ -206,13 +206,13 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, inode as needing revalidate and get the real type (blk vs chr vs. symlink) later ie in lookup */ *pobject_type = DT_REG; - tmp_inode->i_mode |= S_IFREG; - cifsInfo->time = 0; + tmp_inode->i_mode |= S_IFREG; + cifsInfo->time = 0; } /* we no longer mark these because we could not follow them */ /* } else if (attr & ATTR_REPARSE) { - *pobject_type = DT_LNK; - tmp_inode->i_mode |= S_IFLNK; */ + *pobject_type = DT_LNK; + tmp_inode->i_mode |= S_IFLNK; */ } else { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; @@ -220,7 +220,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, tmp_inode->i_mode &= ~(S_IWUGO); else if ((tmp_inode->i_mode & S_IWUGO) == 0) /* the ATTR_READONLY flag may have been changed on */ - /* server -- set any w bits allowed by mnt_file_mode */ + /* server -- set any w bits allowed by mnt_file_mode */ tmp_inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode); } /* could add code here - to validate if device or weird share type? */ @@ -231,7 +231,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, spin_lock(&tmp_inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { - /* can not safely change the file size here if the + /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); @@ -254,7 +254,6 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, tmp_inode->i_fop = &cifs_file_direct_nobrl_ops; else tmp_inode->i_fop = &cifs_file_direct_ops; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) tmp_inode->i_fop = &cifs_file_nobrl_ops; else @@ -322,8 +321,8 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions); /* since we set the inode type below we need to mask off type - to avoid strange results if bits above were corrupt */ - tmp_inode->i_mode &= ~S_IFMT; + to avoid strange results if bits above were corrupt */ + tmp_inode->i_mode &= ~S_IFMT; if (type == UNIX_FILE) { *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; @@ -353,7 +352,7 @@ static void unix_fill_in_inode(struct inode *tmp_inode, /* safest to just call it a file */ *pobject_type = DT_REG; tmp_inode->i_mode |= S_IFREG; - cFYI(1,("unknown inode type %d",type)); + cFYI(1, ("unknown inode type %d", type)); } if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) @@ -368,7 +367,7 @@ static void unix_fill_in_inode(struct inode *tmp_inode, spin_lock(&tmp_inode->i_lock); if (is_size_safe_to_change(cifsInfo, end_of_file)) { - /* can not safely change the file size here if the + /* can not safely change the file size here if the client is writing to it due to potential races */ i_size_write(tmp_inode, end_of_file); @@ -393,15 +392,16 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_fop = &cifs_file_ops; if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) && - (cifs_sb->tcon->ses->server->maxBuf < + (cifs_sb->tcon->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)) tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else tmp_inode->i_data.a_ops = &cifs_addr_ops; if (isNewInode) - return; /* No sense invalidating pages for new inode since we - have not started caching readahead file data yet */ + return; /* No sense invalidating pages for new inode + since we have not started caching readahead + file data for it yet */ if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) && (local_size == tmp_inode->i_size)) { @@ -420,7 +420,7 @@ static void unix_fill_in_inode(struct inode *tmp_inode, tmp_inode->i_op = &cifs_symlink_inode_ops; /* tmp_inode->i_fop = *//* do not need to set to anything */ } else { - cFYI(1, ("Special inode")); + cFYI(1, ("Special inode")); init_special_inode(tmp_inode, tmp_inode->i_mode, tmp_inode->i_rdev); } @@ -429,14 +429,14 @@ static void unix_fill_in_inode(struct inode *tmp_inode, static int initiate_cifs_search(const int xid, struct file *file) { int rc = 0; - char * full_path; - struct cifsFileInfo * cifsFile; + char *full_path; + struct cifsFileInfo *cifsFile; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; if (file->private_data == NULL) { - file->private_data = - kzalloc(sizeof(struct cifsFileInfo),GFP_KERNEL); + file->private_data = + kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); } if (file->private_data == NULL) @@ -463,9 +463,11 @@ static int initiate_cifs_search(const int xid, struct file *file) ffirst_retry: /* test for Unix extensions */ - if (pTcon->ses->capabilities & CAP_UNIX) { + /* but now check for them on the share/mount not on the SMB session */ +/* if (pTcon->ses->capabilities & CAP_UNIX) { */ + if (pTcon->unix_ext) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; - } else if ((pTcon->ses->capabilities & + } else if ((pTcon->ses->capabilities & (CAP_NT_SMBS | CAP_NT_FIND)) == 0) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { @@ -474,13 +476,13 @@ ffirst_retry: cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; } - rc = CIFSFindFirst(xid, pTcon,full_path,cifs_sb->local_nls, + rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls, &cifsFile->netfid, &cifsFile->srch_inf, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if (rc == 0) cifsFile->invalidHandle = FALSE; - if ((rc == -EOPNOTSUPP) && + if ((rc == -EOPNOTSUPP) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; goto ffirst_retry; @@ -495,17 +497,17 @@ static int cifs_unicode_bytelen(char *str) int len; __le16 * ustr = (__le16 *)str; - for(len=0;len <= PATH_MAX;len++) { + for (len = 0; len <= PATH_MAX; len++) { if (ustr[len] == 0) return len << 1; } - cFYI(1,("Unicode string longer than PATH_MAX found")); + cFYI(1, ("Unicode string longer than PATH_MAX found")); return len << 1; } static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) { - char * new_entry; + char *new_entry; FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; if (level == SMB_FIND_FILE_INFO_STANDARD) { @@ -516,21 +518,21 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) pfData->FileNameLength; } else new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); - cFYI(1,("new entry %p old entry %p",new_entry,old_entry)); + cFYI(1, ("new entry %p old entry %p", new_entry, old_entry)); /* validate that new_entry is not past end of SMB */ if (new_entry >= end_of_smb) { cERROR(1, ("search entry %p began after end of SMB %p old entry %p", - new_entry, end_of_smb, old_entry)); + new_entry, end_of_smb, old_entry)); return NULL; } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && - (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) || - ((level != SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) + || ((level != SMB_FIND_FILE_INFO_STANDARD) && (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { - cERROR(1,("search entry %p extends after end of SMB %p", + cERROR(1, ("search entry %p extends after end of SMB %p", new_entry, end_of_smb)); return NULL; - } else + } else return new_entry; } @@ -541,8 +543,8 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) { int rc = 0; - char * filename = NULL; - int len = 0; + char *filename = NULL; + int len = 0; if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; @@ -554,25 +556,25 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) len = strnlen(filename, 5); } } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) { - FILE_DIRECTORY_INFO * pFindData = + FILE_DIRECTORY_INFO * pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if (cfile->srch_inf.info_level == + } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { - FILE_FULL_DIRECTORY_INFO * pFindData = + FILE_FULL_DIRECTORY_INFO * pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { - SEARCH_ID_FULL_DIR_INFO * pFindData = + SEARCH_ID_FULL_DIR_INFO * pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if (cfile->srch_inf.info_level == + } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { - FILE_BOTH_DIRECTORY_INFO * pFindData = + FILE_BOTH_DIRECTORY_INFO * pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); @@ -582,7 +584,8 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) filename = &pFindData->FileName[0]; len = pFindData->FileNameLength; } else { - cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level)); + cFYI(1, ("Unknown findfirst level %d", + cfile->srch_inf.info_level)); } if (filename) { @@ -595,15 +598,15 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) } else if (len == 4) { /* check for .. */ if ((ufilename[0] == UNICODE_DOT) - &&(ufilename[1] == UNICODE_DOT)) + && (ufilename[1] == UNICODE_DOT)) rc = 2; } } else /* ASCII */ { if (len == 1) { - if (filename[0] == '.') + if (filename[0] == '.') rc = 1; } else if (len == 2) { - if((filename[0] == '.') && (filename[1] == '.')) + if ((filename[0] == '.') && (filename[1] == '.')) rc = 2; } } @@ -614,7 +617,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) /* Check if directory that we are searching has changed so we can decide whether we can use the cached search results from the previous search */ -static int is_dir_changed(struct file * file) +static int is_dir_changed(struct file *file) { struct inode *inode = file->f_path.dentry->d_inode; struct cifsInodeInfo *cifsInfo = CIFS_I(inode); @@ -633,22 +636,22 @@ static int is_dir_changed(struct file * file) /* We start counting in the buffer with entry 2 and increment for every entry (do not increment for . or .. entry) */ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, - struct file *file, char **ppCurrentEntry, int *num_to_ret) + struct file *file, char **ppCurrentEntry, int *num_to_ret) { int rc = 0; int pos_in_buf = 0; loff_t first_entry_in_buffer; loff_t index_to_find = file->f_pos; - struct cifsFileInfo * cifsFile = file->private_data; + struct cifsFileInfo *cifsFile = file->private_data; /* check if index in the buffer */ - - if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || + + if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL)) return -ENOENT; - + *ppCurrentEntry = NULL; - first_entry_in_buffer = - cifsFile->srch_inf.index_of_last_entry - + first_entry_in_buffer = + cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; /* if first entry in buf is zero then is first buffer @@ -660,17 +663,17 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, #ifdef CONFIG_CIFS_DEBUG2 dump_cifs_file_struct(file, "In fce "); #endif - if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) && - is_dir_changed(file)) || + if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) && + is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { /* close and restart search */ - cFYI(1,("search backing up - close and restart search")); + cFYI(1, ("search backing up - close and restart search")); cifsFile->invalidHandle = TRUE; CIFSFindClose(xid, pTcon, cifsFile->netfid); kfree(cifsFile->search_resume_name); cifsFile->search_resume_name = NULL; if (cifsFile->srch_inf.ntwrk_buf_start) { - cFYI(1,("freeing SMB ff cache buf on search rewind")); + cFYI(1, ("freeing SMB ff cache buf on search rewind")); if (cifsFile->srch_inf.smallBuf) cifs_small_buf_release(cifsFile->srch_inf. ntwrk_buf_start); @@ -678,17 +681,18 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, cifs_buf_release(cifsFile->srch_inf. ntwrk_buf_start); } - rc = initiate_cifs_search(xid,file); + rc = initiate_cifs_search(xid, file); if (rc) { - cFYI(1,("error %d reinitiating a search on rewind",rc)); + cFYI(1, ("error %d reinitiating a search on rewind", + rc)); return rc; } } - while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && - (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){ - cFYI(1,("calling findnext2")); - rc = CIFSFindNext(xid,pTcon,cifsFile->netfid, + while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && + (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)) { + cFYI(1, ("calling findnext2")); + rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, &cifsFile->srch_inf); if (rc) return -ENOENT; @@ -697,8 +701,8 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, /* we found the buffer that contains the entry */ /* scan and find it */ int i; - char * current_entry; - char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + + char *current_entry; + char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); @@ -706,28 +710,28 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry - cifsFile->srch_inf.entries_in_buffer; pos_in_buf = index_to_find - first_entry_in_buffer; - cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); + cFYI(1, ("found entry - pos_in_buf %d", pos_in_buf)); - for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) { + for (i=0; (i < (pos_in_buf)) && (current_entry != NULL); i++) { /* go entry by entry figuring out which is first */ - current_entry = nxt_dir_entry(current_entry,end_of_smb, + current_entry = nxt_dir_entry(current_entry, end_of_smb, cifsFile->srch_inf.info_level); } - if((current_entry == NULL) && (i < pos_in_buf)) { + if ((current_entry == NULL) && (i < pos_in_buf)) { /* BB fixme - check if we should flag this error */ - cERROR(1,("reached end of buf searching for pos in buf" + cERROR(1, ("reached end of buf searching for pos in buf" " %d index to find %lld rc %d", - pos_in_buf,index_to_find,rc)); + pos_in_buf, index_to_find, rc)); } rc = 0; *ppCurrentEntry = current_entry; } else { - cFYI(1,("index not in buffer - could not findnext into it")); + cFYI(1, ("index not in buffer - could not findnext into it")); return 0; } - if(pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { - cFYI(1,("can not return entries pos_in_buf beyond last entry")); + if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { + cFYI(1, ("can not return entries pos_in_buf beyond last")); *num_to_ret = 0; } else *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; @@ -738,81 +742,81 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, /* inode num, inode type and filename returned */ static int cifs_get_name_from_search_buf(struct qstr *pqst, char *current_entry, __u16 level, unsigned int unicode, - struct cifs_sb_info * cifs_sb, int max_len, ino_t *pinum) + struct cifs_sb_info *cifs_sb, int max_len, ino_t *pinum) { int rc = 0; unsigned int len = 0; - char * filename; - struct nls_table * nlt = cifs_sb->local_nls; + char *filename; + struct nls_table *nlt = cifs_sb->local_nls; *pinum = 0; - if(level == SMB_FIND_FILE_UNIX) { - FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; + if (level == SMB_FIND_FILE_UNIX) { + FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; - if(unicode) { + if (unicode) { len = cifs_unicode_bytelen(filename); } else { /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, PATH_MAX); } - /* BB fixme - hash low and high 32 bits if not 64 bit arch BB fixme */ - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + /* BB fixme - hash low and high 32 bits if not 64 bit arch BB */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) *pinum = pFindData->UniqueId; - } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) { - FILE_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { + FILE_DIRECTORY_INFO *pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { - FILE_FULL_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { + FILE_FULL_DIRECTORY_INFO *pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { - SEARCH_ID_FULL_DIR_INFO * pFindData = + } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { + SEARCH_ID_FULL_DIR_INFO *pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); *pinum = pFindData->UniqueId; - } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { - FILE_BOTH_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { + FILE_BOTH_DIRECTORY_INFO *pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); - } else if(level == SMB_FIND_FILE_INFO_STANDARD) { + } else if (level == SMB_FIND_FILE_INFO_STANDARD) { FIND_FILE_STANDARD_INFO * pFindData = (FIND_FILE_STANDARD_INFO *)current_entry; filename = &pFindData->FileName[0]; /* one byte length, no name conversion */ len = (unsigned int)pFindData->FileNameLength; } else { - cFYI(1,("Unknown findfirst level %d",level)); + cFYI(1, ("Unknown findfirst level %d", level)); return -EINVAL; } - if(len > max_len) { - cERROR(1,("bad search response length %d past smb end", len)); + if (len > max_len) { + cERROR(1, ("bad search response length %d past smb end", len)); return -EINVAL; } - if(unicode) { + if (unicode) { /* BB fixme - test with long names */ /* Note converted filename can be longer than in unicode */ - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) pqst->len = cifs_convertUCSpath((char *)pqst->name, (__le16 *)filename, len/2, nlt); else pqst->len = cifs_strfromUCS_le((char *)pqst->name, - (__le16 *)filename,len/2,nlt); + (__le16 *)filename, len/2, nlt); } else { pqst->name = filename; pqst->len = len; } - pqst->hash = full_name_hash(pqst->name,pqst->len); -/* cFYI(1,("filldir on %s",pqst->name)); */ + pqst->hash = full_name_hash(pqst->name, pqst->len); +/* cFYI(1, ("filldir on %s",pqst->name)); */ return rc; } @@ -821,49 +825,50 @@ static int cifs_filldir(char *pfindEntry, struct file *file, { int rc = 0; struct qstr qstring; - struct cifsFileInfo * pCifsF; + struct cifsFileInfo *pCifsF; unsigned obj_type; ino_t inum; - struct cifs_sb_info * cifs_sb; + struct cifs_sb_info *cifs_sb; struct inode *tmp_inode; struct dentry *tmp_dentry; /* get filename and len into qstring */ /* get dentry */ /* decide whether to create and populate ionde */ - if((direntry == NULL) || (file == NULL)) + if ((direntry == NULL) || (file == NULL)) return -EINVAL; pCifsF = file->private_data; - - if((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL)) + + if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL)) return -ENOENT; - rc = cifs_entry_is_dot(pfindEntry,pCifsF); + rc = cifs_entry_is_dot(pfindEntry, pCifsF); /* skip . and .. since we added them first */ - if(rc != 0) + if (rc != 0) return 0; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); qstring.name = scratch_buf; - rc = cifs_get_name_from_search_buf(&qstring,pfindEntry, + rc = cifs_get_name_from_search_buf(&qstring, pfindEntry, pCifsF->srch_inf.info_level, - pCifsF->srch_inf.unicode,cifs_sb, + pCifsF->srch_inf.unicode, cifs_sb, max_len, &inum /* returned */); - if(rc) + if (rc) return rc; - rc = construct_dentry(&qstring,file,&tmp_inode, &tmp_dentry); - if((tmp_inode == NULL) || (tmp_dentry == NULL)) + rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry); + if ((tmp_inode == NULL) || (tmp_dentry == NULL)) return -ENOMEM; - if(rc) { + if (rc) { /* inode created, we need to hash it with right inode number */ - if(inum != 0) { - /* BB fixme - hash the 2 32 quantities bits together if necessary BB */ + if (inum != 0) { + /* BB fixme - hash the 2 32 quantities bits together if + * necessary BB */ tmp_inode->i_ino = inum; } insert_inode_hash(tmp_inode); @@ -872,27 +877,27 @@ static int cifs_filldir(char *pfindEntry, struct file *file, /* we pass in rc below, indicating whether it is a new inode, so we can figure out whether to invalidate the inode cached data if the file has changed */ - if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) + if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) unix_fill_in_inode(tmp_inode, (FILE_UNIX_INFO *)pfindEntry, &obj_type, rc); - else if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) + else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */, pfindEntry, &obj_type, rc); else fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc); - if(rc) /* new inode - needs to be tied to dentry */ { + if (rc) /* new inode - needs to be tied to dentry */ { d_instantiate(tmp_dentry, tmp_inode); - if(rc == 2) + if (rc == 2) d_rehash(tmp_dentry); } - - - rc = filldir(direntry,qstring.name,qstring.len,file->f_pos, - tmp_inode->i_ino,obj_type); - if(rc) { - cFYI(1,("filldir rc = %d",rc)); + + + rc = filldir(direntry, qstring.name, qstring.len, file->f_pos, + tmp_inode->i_ino, obj_type); + if (rc) { + cFYI(1, ("filldir rc = %d", rc)); /* we can not return filldir errors to the caller since they are "normal" when the stat blocksize is too small - we return remapped error instead */ @@ -909,57 +914,57 @@ static int cifs_save_resume_key(const char *current_entry, int rc = 0; unsigned int len = 0; __u16 level; - char * filename; + char *filename; - if((cifsFile == NULL) || (current_entry == NULL)) + if ((cifsFile == NULL) || (current_entry == NULL)) return -EINVAL; level = cifsFile->srch_inf.info_level; - if(level == SMB_FIND_FILE_UNIX) { + if (level == SMB_FIND_FILE_UNIX) { FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry; filename = &pFindData->FileName[0]; - if(cifsFile->srch_inf.unicode) { + if (cifsFile->srch_inf.unicode) { len = cifs_unicode_bytelen(filename); } else { /* BB should we make this strnlen of PATH_MAX? */ len = strnlen(filename, PATH_MAX); } cifsFile->srch_inf.resume_key = pFindData->ResumeKey; - } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) { - FILE_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { + FILE_DIRECTORY_INFO *pFindData = (FILE_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; - } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { - FILE_FULL_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { + FILE_FULL_DIRECTORY_INFO *pFindData = (FILE_FULL_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; - } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { - SEARCH_ID_FULL_DIR_INFO * pFindData = + } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { + SEARCH_ID_FULL_DIR_INFO *pFindData = (SEARCH_ID_FULL_DIR_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; - } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { - FILE_BOTH_DIRECTORY_INFO * pFindData = + } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { + FILE_BOTH_DIRECTORY_INFO *pFindData = (FILE_BOTH_DIRECTORY_INFO *)current_entry; filename = &pFindData->FileName[0]; len = le32_to_cpu(pFindData->FileNameLength); cifsFile->srch_inf.resume_key = pFindData->FileIndex; - } else if(level == SMB_FIND_FILE_INFO_STANDARD) { - FIND_FILE_STANDARD_INFO * pFindData = + } else if (level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO *pFindData = (FIND_FILE_STANDARD_INFO *)current_entry; filename = &pFindData->FileName[0]; /* one byte length, no name conversion */ len = (unsigned int)pFindData->FileNameLength; cifsFile->srch_inf.resume_key = pFindData->ResumeKey; } else { - cFYI(1,("Unknown findfirst level %d",level)); + cFYI(1, ("Unknown findfirst level %d", level)); return -EINVAL; } cifsFile->srch_inf.resume_name_len = len; @@ -970,21 +975,21 @@ static int cifs_save_resume_key(const char *current_entry, int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) { int rc = 0; - int xid,i; + int xid, i; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; struct cifsFileInfo *cifsFile = NULL; - char * current_entry; + char *current_entry; int num_to_fill = 0; - char * tmp_buf = NULL; - char * end_of_smb; + char *tmp_buf = NULL; + char *end_of_smb; int max_len; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; - if(pTcon == NULL) + if (pTcon == NULL) return -EINVAL; switch ((int) file->f_pos) { @@ -1005,27 +1010,27 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) } file->f_pos++; default: - /* 1) If search is active, - is in current search buffer? + /* 1) If search is active, + is in current search buffer? if it before then restart search if after then keep searching till find it */ - if(file->private_data == NULL) { - rc = initiate_cifs_search(xid,file); - cFYI(1,("initiate cifs search rc %d",rc)); - if(rc) { + if (file->private_data == NULL) { + rc = initiate_cifs_search(xid, file); + cFYI(1, ("initiate cifs search rc %d", rc)); + if (rc) { FreeXid(xid); return rc; } } - if(file->private_data == NULL) { + if (file->private_data == NULL) { rc = -EINVAL; FreeXid(xid); return rc; } cifsFile = file->private_data; if (cifsFile->srch_inf.endOfSearch) { - if(cifsFile->srch_inf.emptyDir) { + if (cifsFile->srch_inf.emptyDir) { cFYI(1, ("End of search, empty dir")); rc = 0; break; @@ -1033,23 +1038,23 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) } /* else { cifsFile->invalidHandle = TRUE; CIFSFindClose(xid, pTcon, cifsFile->netfid); - } + } kfree(cifsFile->search_resume_name); cifsFile->search_resume_name = NULL; */ - rc = find_cifs_entry(xid,pTcon, file, - ¤t_entry,&num_to_fill); - if(rc) { - cFYI(1,("fce error %d",rc)); + rc = find_cifs_entry(xid, pTcon, file, + ¤t_entry, &num_to_fill); + if (rc) { + cFYI(1, ("fce error %d", rc)); goto rddir2_exit; } else if (current_entry != NULL) { - cFYI(1,("entry %lld found",file->f_pos)); + cFYI(1, ("entry %lld found", file->f_pos)); } else { - cFYI(1,("could not find entry")); + cFYI(1, ("could not find entry")); goto rddir2_exit; } - cFYI(1,("loop through %d times filling dir for net buf %p", - num_to_fill,cifsFile->srch_inf.ntwrk_buf_start)); + cFYI(1, ("loop through %d times filling dir for net buf %p", + num_to_fill, cifsFile->srch_inf.ntwrk_buf_start)); max_len = smbCalcSize((struct smb_hdr *) cifsFile->srch_inf.ntwrk_buf_start); end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; @@ -1059,8 +1064,8 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) such multibyte target UTF-8 characters. cifs_unicode.c, which actually does the conversion, has the same limit */ tmp_buf = kmalloc((2 * NAME_MAX) + 4, GFP_KERNEL); - for(i=0;(i<num_to_fill) && (rc == 0);i++) { - if(current_entry == NULL) { + for (i = 0; (i < num_to_fill) && (rc == 0); i++) { + if (current_entry == NULL) { /* evaluate whether this case is an error */ cERROR(1,("past end of SMB num to fill %d i %d", num_to_fill, i)); @@ -1070,20 +1075,20 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) we want to check for that here? */ rc = cifs_filldir(current_entry, file, filldir, direntry, tmp_buf, max_len); - if(rc == -EOVERFLOW) { + if (rc == -EOVERFLOW) { rc = 0; break; } file->f_pos++; - if(file->f_pos == + if (file->f_pos == cifsFile->srch_inf.index_of_last_entry) { - cFYI(1,("last entry in buf at pos %lld %s", - file->f_pos,tmp_buf)); - cifs_save_resume_key(current_entry,cifsFile); + cFYI(1, ("last entry in buf at pos %lld %s", + file->f_pos, tmp_buf)); + cifs_save_resume_key(current_entry, cifsFile); break; - } else - current_entry = + } else + current_entry = nxt_dir_entry(current_entry, end_of_smb, cifsFile->srch_inf.info_level); } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 7584646..2ea027d 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -3,7 +3,7 @@ * * SMB/CIFS session setup handling routines * - * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) International Business Machines Corp., 2006, 2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ #include <linux/utsname.h> extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, - unsigned char *p24); + unsigned char *p24); static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) { @@ -45,13 +45,14 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ - /* BB verify whether signing required on neg or just on auth frame + /* BB verify whether signing required on neg or just on auth frame (and NTLM case) */ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_UNICODE) { @@ -74,10 +75,10 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB) return capabilities; } -static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, - const struct nls_table * nls_cp) +static void unicode_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { - char * bcc_ptr = *pbcc_area; + char *bcc_ptr = *pbcc_area; int bytes_ret = 0; /* BB FIXME add check that strings total less @@ -89,7 +90,7 @@ static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, bcc_ptr++; } */ /* copy user */ - if(ses->userName == NULL) { + if (ses->userName == NULL) { /* null user mount */ *bcc_ptr = 0; *(bcc_ptr+1) = 0; @@ -100,14 +101,14 @@ static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null termination */ /* copy domain */ - if(ses->domainName == NULL) { + if (ses->domainName == NULL) { /* Sending null domain better than using a bogus domain name (as we did briefly in 2.6.18) since server will use its default */ *bcc_ptr = 0; *(bcc_ptr+1) = 0; bytes_ret = 0; } else - bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, + bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName, 256, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null terminator */ @@ -122,37 +123,37 @@ static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, bcc_ptr += 2; /* trailing null */ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); + 32, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* trailing null */ *pbcc_area = bcc_ptr; } -static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, - const struct nls_table * nls_cp) +static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { - char * bcc_ptr = *pbcc_area; + char *bcc_ptr = *pbcc_area; /* copy user */ /* BB what about null user mounts - check that we do this BB */ - /* copy user */ - if(ses->userName == NULL) { - /* BB what about null user mounts - check that we do this BB */ - } else { /* 300 should be long enough for any conceivable user name */ - strncpy(bcc_ptr, ses->userName, 300); - } + /* copy user */ + if (ses->userName == NULL) { + /* BB what about null user mounts - check that we do this BB */ + } else { /* 300 should be long enough for any conceivable user name */ + strncpy(bcc_ptr, ses->userName, 300); + } /* BB improve check for overflow */ - bcc_ptr += strnlen(ses->userName, 300); + bcc_ptr += strnlen(ses->userName, 300); *bcc_ptr = 0; - bcc_ptr++; /* account for null termination */ + bcc_ptr++; /* account for null termination */ - /* copy domain */ - - if(ses->domainName != NULL) { - strncpy(bcc_ptr, ses->domainName, 256); + /* copy domain */ + + if (ses->domainName != NULL) { + strncpy(bcc_ptr, ses->domainName, 256); bcc_ptr += strnlen(ses->domainName, 256); - } /* else we will send a null domain name + } /* else we will send a null domain name so the server will default to its own domain */ *bcc_ptr = 0; bcc_ptr++; @@ -167,19 +168,20 @@ static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses, strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - *pbcc_area = bcc_ptr; + *pbcc_area = bcc_ptr; } -static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, - const struct nls_table * nls_cp) +static int decode_unicode_ssetup(char **pbcc_area, int bleft, + struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { int rc = 0; int words_left, len; - char * data = *pbcc_area; + char *data = *pbcc_area; - cFYI(1,("bleft %d",bleft)); + cFYI(1, ("bleft %d", bleft)); /* SMB header is unaligned, so cifs servers word align start of @@ -189,7 +191,7 @@ static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInf their final Unicode string - in which case we now will not attempt to decode the byte of junk which follows it */ - + words_left = bleft / 2; /* save off server operating system */ @@ -198,14 +200,14 @@ static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInf /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ - if(len >= words_left) + if (len >= words_left) return rc; - if(ses->serverOS) + if (ses->serverOS) kfree(ses->serverOS); /* UTF-8 string will not grow more than four times as big as UCS-16 */ ses->serverOS = kzalloc(4 * len, GFP_KERNEL); - if(ses->serverOS != NULL) { + if (ses->serverOS != NULL) { cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len, nls_cp); } @@ -215,67 +217,68 @@ static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInf /* save off server network operating system */ len = UniStrnlen((wchar_t *) data, words_left); - if(len >= words_left) + if (len >= words_left) return rc; - if(ses->serverNOS) + if (ses->serverNOS) kfree(ses->serverNOS); ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */ - if(ses->serverNOS != NULL) { + if (ses->serverNOS != NULL) { cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len, nls_cp); - if(strncmp(ses->serverNOS, "NT LAN Manager 4",16) == 0) { - cFYI(1,("NT4 server")); + if (strncmp(ses->serverNOS, "NT LAN Manager 4", 16) == 0) { + cFYI(1, ("NT4 server")); ses->flags |= CIFS_SES_NT4; } } data += 2 * (len + 1); words_left -= len + 1; - /* save off server domain */ - len = UniStrnlen((wchar_t *) data, words_left); - - if(len > words_left) - return rc; - - if(ses->serverDomain) - kfree(ses->serverDomain); - ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */ - if(ses->serverDomain != NULL) { - cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, - nls_cp); - ses->serverDomain[2*len] = 0; - ses->serverDomain[(2*len) + 1] = 0; - } - data += 2 * (len + 1); - words_left -= len + 1; - - cFYI(1,("words left: %d",words_left)); + /* save off server domain */ + len = UniStrnlen((wchar_t *) data, words_left); + + if (len > words_left) + return rc; + + if (ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */ + if (ses->serverDomain != NULL) { + cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len, + nls_cp); + ses->serverDomain[2*len] = 0; + ses->serverDomain[(2*len) + 1] = 0; + } + data += 2 * (len + 1); + words_left -= len + 1; + + cFYI(1, ("words left: %d", words_left)); return rc; } -static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses, - const struct nls_table * nls_cp) +static int decode_ascii_ssetup(char **pbcc_area, int bleft, + struct cifsSesInfo *ses, + const struct nls_table *nls_cp) { int rc = 0; int len; - char * bcc_ptr = *pbcc_area; + char *bcc_ptr = *pbcc_area; + + cFYI(1, ("decode sessetup ascii. bleft %d", bleft)); - cFYI(1,("decode sessetup ascii. bleft %d", bleft)); - len = strnlen(bcc_ptr, bleft); - if(len >= bleft) + if (len >= bleft) return rc; - - if(ses->serverOS) + + if (ses->serverOS) kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); - if(ses->serverOS) + if (ses->serverOS) strncpy(ses->serverOS, bcc_ptr, len); - if(strncmp(ses->serverOS, "OS/2",4) == 0) { - cFYI(1,("OS/2 server")); + if (strncmp(ses->serverOS, "OS/2", 4) == 0) { + cFYI(1, ("OS/2 server")); ses->flags |= CIFS_SES_OS2; } @@ -283,34 +286,34 @@ static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo bleft -= len + 1; len = strnlen(bcc_ptr, bleft); - if(len >= bleft) + if (len >= bleft) return rc; - if(ses->serverNOS) + if (ses->serverNOS) kfree(ses->serverNOS); ses->serverNOS = kzalloc(len + 1, GFP_KERNEL); - if(ses->serverNOS) + if (ses->serverNOS) strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len + 1; bleft -= len + 1; - len = strnlen(bcc_ptr, bleft); - if(len > bleft) - return rc; + len = strnlen(bcc_ptr, bleft); + if (len > bleft) + return rc; /* No domain field in LANMAN case. Domain is returned by old servers in the SMB negprot response */ /* BB For newer servers which do not support Unicode, but thus do return domain here we could add parsing for it later, but it is not very important */ - cFYI(1,("ascii: bytes left %d",bleft)); + cFYI(1, ("ascii: bytes left %d", bleft)); return rc; } -int +int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, const struct nls_table *nls_cp) { @@ -328,13 +331,13 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, __u16 action; int bytes_remaining; - if(ses == NULL) + if (ses == NULL) return -EINVAL; type = ses->server->secType; - cFYI(1,("sess setup type %d",type)); - if(type == LANMAN) { + cFYI(1, ("sess setup type %d", type)); + if (type == LANMAN) { #ifndef CONFIG_CIFS_WEAK_PW_HASH /* LANMAN and plaintext are less secure and off by default. So we make this explicitly be turned on in kconfig (in the @@ -344,15 +347,15 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, return -EOPNOTSUPP; #endif wct = 10; /* lanman 2 style sessionsetup */ - } else if((type == NTLM) || (type == NTLMv2)) { + } else if ((type == NTLM) || (type == NTLMv2)) { /* For NTLMv2 failures eventually may need to retry NTLM */ wct = 13; /* old style NTLM sessionsetup */ - } else /* same size for negotiate or auth, NTLMSSP or extended security */ + } else /* same size: negotiate or auth, NTLMSSP or extended security */ wct = 12; rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, (void **)&smb_buf); - if(rc) + if (rc) return rc; pSMB = (SESSION_SETUP_ANDX *)smb_buf; @@ -364,8 +367,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, second part which will include the strings and rest of bcc area, in order to avoid having to do a large buffer 17K allocation */ - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = smb_buf->smb_buf_length + 4; + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = smb_buf->smb_buf_length + 4; /* 2000 big enough to fit max user, domain, NOS name etc. */ str_area = kmalloc(2000, GFP_KERNEL); @@ -373,18 +376,18 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, ses->flags &= ~CIFS_SES_LANMAN; - if(type == LANMAN) { + if (type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH char lnm_session_key[CIFS_SESS_KEY_SIZE]; /* no capabilities flags in old lanman negotiation */ - pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); + pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); /* BB calculate hash with password */ /* and copy into bcc */ calc_lanman_hash(ses, lnm_session_key); - ses->flags |= CIFS_SES_LANMAN; + ses->flags |= CIFS_SES_LANMAN; /* #ifdef CONFIG_CIFS_DEBUG2 cifs_dump_mem("cryptkey: ",ses->server->cryptKey, CIFS_SESS_KEY_SIZE); @@ -397,10 +400,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, changed to do higher than lanman dialect and we reconnected would we ever calc signing_key? */ - cFYI(1,("Negotiating LANMAN setting up strings")); + cFYI(1, ("Negotiating LANMAN setting up strings")); /* Unicode not allowed for LANMAN dialects */ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); -#endif +#endif } else if (type == NTLM) { char ntlm_session_key[CIFS_SESS_KEY_SIZE]; @@ -409,38 +412,38 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->req_no_secext.CaseSensitivePasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); - + /* calculate session key */ SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); - if(first_time) /* should this be moved into common code + if (first_time) /* should this be moved into common code with similar ntlmv2 path? */ - cifs_calculate_mac_key(ses->server->mac_signing_key, + cifs_calculate_mac_key(&ses->server->mac_signing_key, ntlm_session_key, ses->password); /* copy session key */ - memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE); + memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; - memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE); + memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; - if(ses->capabilities & CAP_UNICODE) { + if (ses->capabilities & CAP_UNICODE) { /* unicode strings must be word aligned */ if (iov[0].iov_len % 2) { *bcc_ptr = 0; - bcc_ptr++; - } + bcc_ptr++; + } unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); } else ascii_ssetup_strings(&bcc_ptr, ses, nls_cp); } else if (type == NTLMv2) { - char * v2_sess_key = + char *v2_sess_key = kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL); /* BB FIXME change all users of v2_sess_key to struct ntlmv2_resp */ - if(v2_sess_key == NULL) { + if (v2_sess_key == NULL) { cifs_small_buf_release(smb_buf); return -ENOMEM; } @@ -456,8 +459,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, /* calculate session key */ setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); - if(first_time) /* should this be moved into common code - with similar ntlmv2 path? */ + if (first_time) /* should this be moved into common code + with similar ntlmv2 path? */ /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key, response BB FIXME, v2_sess_key); */ @@ -465,11 +468,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE); bcc_ptr += LM2_SESS_KEY_SIZE; */ - memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp)); + memcpy(bcc_ptr, (char *)v2_sess_key, + sizeof(struct ntlmv2_resp)); bcc_ptr += sizeof(struct ntlmv2_resp); kfree(v2_sess_key); - if(ses->capabilities & CAP_UNICODE) { - if(iov[0].iov_len % 2) { + if (ses->capabilities & CAP_UNICODE) { + if (iov[0].iov_len % 2) { *bcc_ptr = 0; } bcc_ptr++; unicode_ssetup_strings(&bcc_ptr, ses, nls_cp); @@ -488,20 +492,20 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, BCC_LE(smb_buf) = cpu_to_le16(count); iov[1].iov_base = str_area; - iov[1].iov_len = count; + iov[1].iov_len = count; rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, 0); /* SMB request buf freed in SendReceive2 */ - cFYI(1,("ssetup rc from sendrecv2 is %d",rc)); - if(rc) + cFYI(1, ("ssetup rc from sendrecv2 is %d", rc)); + if (rc) goto ssetup_exit; pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; smb_buf = (struct smb_hdr *)iov[0].iov_base; - if((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) { + if ((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) { rc = -EIO; - cERROR(1,("bad word count %d", smb_buf->WordCount)); + cERROR(1, ("bad word count %d", smb_buf->WordCount)); goto ssetup_exit; } action = le16_to_cpu(pSMB->resp.Action); @@ -514,31 +518,32 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, bytes_remaining = BCC(smb_buf); bcc_ptr = pByteArea(smb_buf); - if(smb_buf->WordCount == 4) { + if (smb_buf->WordCount == 4) { __u16 blob_len; blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); bcc_ptr += blob_len; - if(blob_len > bytes_remaining) { - cERROR(1,("bad security blob length %d", blob_len)); + if (blob_len > bytes_remaining) { + cERROR(1, ("bad security blob length %d", blob_len)); rc = -EINVAL; goto ssetup_exit; } bytes_remaining -= blob_len; - } + } /* BB check if Unicode and decode strings */ - if(smb_buf->Flags2 & SMBFLG2_UNICODE) + if (smb_buf->Flags2 & SMBFLG2_UNICODE) rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); else - rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,nls_cp); - + rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, + ses, nls_cp); + ssetup_exit: kfree(str_area); - if(resp_buf_type == CIFS_SMALL_BUFFER) { - cFYI(1,("ssetup freeing small buf %p", iov[0].iov_base)); + if (resp_buf_type == CIFS_SMALL_BUFFER) { + cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base)); cifs_small_buf_release(iov[0].iov_base); - } else if(resp_buf_type == CIFS_LARGE_BUFFER) + } else if (resp_buf_type == CIFS_LARGE_BUFFER) cifs_buf_release(iov[0].iov_base); return rc; diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c index 1b1daf6..cfa6d21 100644 --- a/fs/cifs/smbdes.c +++ b/fs/cifs/smbdes.c @@ -1,32 +1,32 @@ -/* +/* Unix SMB/Netbios implementation. Version 1.9. - a partial implementation of DES designed for use in the + a partial implementation of DES designed for use in the SMB authentication protocol Copyright (C) Andrew Tridgell 1998 Modified by Steve French (sfrench@us.ibm.com) 2002,2004 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* NOTES: +/* NOTES: This code makes no attempt to be fast! In fact, it is a very - slow implementation + slow implementation This code is NOT a complete DES implementation. It implements only the minimum necessary for SMB authentication, as used by all SMB @@ -153,7 +153,7 @@ static uchar sbox[8][4][16] = { }; static void -permute(char *out, char *in, uchar * p, int n) +permute(char *out, char *in, uchar *p, int n) { int i; for (i = 0; i < n; i++) @@ -202,18 +202,18 @@ dohash(char *out, char *in, char *key, int forw) char *rl; /* Have to reduce stack usage */ - pk1 = kmalloc(56+56+64+64,GFP_KERNEL); - if(pk1 == NULL) + pk1 = kmalloc(56+56+64+64, GFP_KERNEL); + if (pk1 == NULL) return; ki = kmalloc(16*48, GFP_KERNEL); - if(ki == NULL) { + if (ki == NULL) { kfree(pk1); return; } cd = pk1 + 56; - pd1= cd + 56; + pd1 = cd + 56; rl = pd1 + 64; permute(pk1, key, perm1, 56); @@ -247,7 +247,7 @@ dohash(char *out, char *in, char *key, int forw) char *r2; /* r2[32] */ er = kmalloc(48+48+32+32+32, GFP_KERNEL); - if(er == NULL) { + if (er == NULL) { kfree(pk1); kfree(ki); return; @@ -327,8 +327,8 @@ smbhash(unsigned char *out, unsigned char *in, unsigned char *key, int forw) char *keyb; /* keyb[64] */ unsigned char key2[8]; - outb = kmalloc(64 * 3,GFP_KERNEL); - if(outb == NULL) + outb = kmalloc(64 * 3, GFP_KERNEL); + if (outb == NULL) return; inb = outb + 64; diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 4b25ba9..90542a3 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -1,4 +1,4 @@ -/* +/* Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup @@ -7,17 +7,17 @@ Modified by Jeremy Allison 1995. Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003 Modified by Steve French (sfrench@us.ibm.com) 2002-2003 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -57,7 +57,7 @@ void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); /* This implements the X/Open SMB password encryption - It takes a password, a 8 byte "crypt key" and puts 24 bytes of + It takes a password, a 8 byte "crypt key" and puts 24 bytes of encrypted password into p24 */ /* Note that password must be uppercased and null terminated */ void @@ -73,9 +73,9 @@ SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) E_P16(p14, p21); SMBOWFencrypt(p21, c8, p24); - - memset(p14,0,15); - memset(p21,0,21); + + memset(p14, 0, 15); + memset(p21, 0, 21); } /* Routines for Windows NT MD4 Hash functions. */ @@ -90,14 +90,14 @@ _my_wcslen(__u16 * str) /* * Convert a string into an NT UNICODE string. - * Note that regardless of processor type + * Note that regardless of processor type * this must be in intel (little-endian) * format. */ static int _my_mbstowcs(__u16 * dst, const unsigned char *src, int len) -{ /* not a very good conversion routine - change/fix */ +{ /* BB not a very good conversion routine - change/fix */ int i; __u16 val; @@ -112,7 +112,7 @@ _my_mbstowcs(__u16 * dst, const unsigned char *src, int len) return i; } -/* +/* * Creates the MD4 Hash of the users password in NT UNICODE. */ @@ -123,7 +123,7 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) __u16 wpwd[129]; /* Password cannot be longer than 128 characters */ - if(passwd) { + if (passwd) { len = strlen((char *) passwd); if (len > 128) { len = 128; @@ -138,7 +138,7 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) len = _my_wcslen(wpwd) * sizeof (__u16); mdfour(p16, (unsigned char *) wpwd, len); - memset(wpwd,0,129 * 2); + memset(wpwd, 0, 129 * 2); } #if 0 /* currently unused */ @@ -178,17 +178,17 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n, const char *domain_n, unsigned char kr_buf[16], const struct nls_table *nls_codepage) { - wchar_t * user_u; - wchar_t * dom_u; + wchar_t *user_u; + wchar_t *dom_u; int user_l, domain_l; struct HMACMD5Context ctx; /* might as well do one alloc to hold both (user_u and dom_u) */ - user_u = kmalloc(2048 * sizeof(wchar_t),GFP_KERNEL); - if(user_u == NULL) + user_u = kmalloc(2048 * sizeof(wchar_t), GFP_KERNEL); + if (user_u == NULL) return; dom_u = user_u + 1024; - + /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ @@ -206,7 +206,7 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n, kfree(user_u); } -#endif +#endif /* Does the des encryption from the NT or LM MD4 hash. */ static void @@ -256,15 +256,15 @@ SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) #if 0 static void SMBOWFencrypt_ntv2(const unsigned char kr[16], - const struct data_blob * srv_chal, - const struct data_blob * cli_chal, unsigned char resp_buf[16]) + const struct data_blob *srv_chal, + const struct data_blob *cli_chal, unsigned char resp_buf[16]) { - struct HMACMD5Context ctx; + struct HMACMD5Context ctx; - hmac_md5_init_limK_to_64(kr, 16, &ctx); - hmac_md5_update(srv_chal->data, srv_chal->length, &ctx); - hmac_md5_update(cli_chal->data, cli_chal->length, &ctx); - hmac_md5_final(resp_buf, &ctx); + hmac_md5_init_limK_to_64(kr, 16, &ctx); + hmac_md5_update(srv_chal->data, srv_chal->length, &ctx); + hmac_md5_update(cli_chal->data, cli_chal->length, &ctx); + hmac_md5_final(resp_buf, &ctx); } static void diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h index 212c3c2..2ef0be2 100644 --- a/fs/cifs/smberr.h +++ b/fs/cifs/smberr.h @@ -4,8 +4,8 @@ * Copyright (c) International Business Machines Corp., 2002,2004 * Author(s): Steve French (sfrench@us.ibm.com) * - * See Error Codes section of the SNIA CIFS Specification - * for more information + * See Error Codes section of the SNIA CIFS Specification + * for more information * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -19,7 +19,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SUCCESS 0x00 /* The request was successful. */ @@ -110,7 +110,7 @@ /* Below errors are used internally (do not come over the wire) for passthrough from STATUS codes to POSIX only */ -#define ErrTooManyLinks 0xFFFE +#define ErrTooManyLinks 0xFFFE /* Following error codes may be generated with the ERRSRV error class.*/ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 5f46845..746bc94 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -1,10 +1,10 @@ /* * fs/cifs/transport.c * - * Copyright (C) International Business Machines Corp., 2002,2005 + * Copyright (C) International Business Machines Corp., 2002,2007 * Author(s): Steve French (sfrench@us.ibm.com) * Jeremy Allison (jra@samba.org) 2006. - * + * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or @@ -17,7 +17,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/fs.h> @@ -32,7 +32,7 @@ #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" - + extern mempool_t *cifs_mid_poolp; extern struct kmem_cache *cifs_oplock_cachep; @@ -49,7 +49,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) cERROR(1, ("Null TCP session in AllocMidQEntry")); return NULL; } - + temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, GFP_KERNEL | GFP_NOFS); if (temp == NULL) @@ -86,7 +86,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) list_del(&midEntry->qhead); atomic_dec(&midCount); spin_unlock(&GlobalMid_Lock); - if(midEntry->largeBuf) + if (midEntry->largeBuf) cifs_buf_release(midEntry->resp_buf); else cifs_small_buf_release(midEntry->resp_buf); @@ -94,8 +94,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) now = jiffies; /* commands taking longer than one second are indications that something is wrong, unless it is quite a slow link or server */ - if((now - midEntry->when_alloc) > HZ) { - if((cifsFYI & CIFS_TIMER) && + if ((now - midEntry->when_alloc) > HZ) { + if ((cifsFYI & CIFS_TIMER) && (midEntry->command != SMB_COM_LOCKING_ANDX)) { printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d", midEntry->command, midEntry->mid); @@ -110,10 +110,10 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) } struct oplock_q_entry * -AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon) +AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon) { struct oplock_q_entry *temp; - if ((pinode== NULL) || (tcon == NULL)) { + if ((pinode == NULL) || (tcon == NULL)) { cERROR(1, ("Null parms passed to AllocOplockQEntry")); return NULL; } @@ -133,9 +133,9 @@ AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon) } -void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry) +void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry) { - spin_lock(&GlobalMid_Lock); + spin_lock(&GlobalMid_Lock); /* should we check if list empty first? */ list_del(&oplockEntry->qhead); spin_unlock(&GlobalMid_Lock); @@ -152,7 +152,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, struct kvec iov; unsigned len = smb_buf_length + 4; - if(ssocket == NULL) + if (ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ iov.iov_base = smb_buffer; iov.iov_len = len; @@ -164,8 +164,8 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ /* smb header is converted in header_assemble. bcc and rest of SMB word - area, and byte area if necessary, is converted to littleendian in - cifssmb.c and RFC1001 len is converted to bigendian in smb_send + area, and byte area if necessary, is converted to littleendian in + cifssmb.c and RFC1001 len is converted to bigendian in smb_send Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); @@ -177,9 +177,9 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; /* smaller timeout here than send2 since smaller size */ - /* Although it may not be required, this also is smaller - oplock break time */ - if(i > 12) { + /* Although it may not be required, this also is smaller + oplock break time */ + if (i > 12) { cERROR(1, ("sends on sock %p stuck for 7 seconds", ssocket)); @@ -189,7 +189,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, msleep(1 << i); continue; } - if (rc < 0) + if (rc < 0) break; else i = 0; /* reset i after each successful send */ @@ -199,7 +199,7 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, } if (rc < 0) { - cERROR(1,("Error %d sending data on socket to server", rc)); + cERROR(1, ("Error %d sending data on socket to server", rc)); } else { rc = 0; } @@ -223,8 +223,8 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, unsigned int total_len; int first_vec = 0; unsigned int smb_buf_length = smb_buffer->smb_buf_length; - - if(ssocket == NULL) + + if (ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ smb_msg.msg_name = sin; @@ -234,8 +234,8 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ /* smb header is converted in header_assemble. bcc and rest of SMB word - area, and byte area if necessary, is converted to littleendian in - cifssmb.c and RFC1001 len is converted to bigendian in smb_send + area, and byte area if necessary, is converted to littleendian in + cifssmb.c and RFC1001 len is converted to bigendian in smb_send Flags2 is converted in SendReceive */ @@ -252,7 +252,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, n_vec - first_vec, total_len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; - if(i >= 14) { + if (i >= 14) { cERROR(1, ("sends on sock %p stuck for 15 seconds", ssocket)); @@ -262,17 +262,17 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, msleep(1 << i); continue; } - if (rc < 0) + if (rc < 0) break; if (rc >= total_len) { WARN_ON(rc > total_len); break; } - if(rc == 0) { + if (rc == 0) { /* should never happen, letting socket clear before retrying is our only obvious option here */ - cERROR(1,("tcp sent no data")); + cERROR(1, ("tcp sent no data")); msleep(500); continue; } @@ -295,7 +295,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, } if (rc < 0) { - cERROR(1,("Error %d sending data on socket to server", rc)); + cERROR(1, ("Error %d sending data on socket to server", rc)); } else rc = 0; @@ -308,13 +308,13 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) { - if(long_op == -1) { + if (long_op == -1) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { - spin_lock(&GlobalMid_Lock); - while(1) { - if(atomic_read(&ses->server->inFlight) >= + spin_lock(&GlobalMid_Lock); + while (1) { + if (atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_STATS2 @@ -328,14 +328,14 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) #endif spin_lock(&GlobalMid_Lock); } else { - if(ses->server->tcpStatus == CifsExiting) { + if (ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } - /* can not count locking commands against total since - they are allowed to block on server */ - + /* can not count locking commands against total + as they are allowed to block on server */ + /* update # of requests on the wire to server */ if (long_op < 3) atomic_inc(&ses->server->inFlight); @@ -353,11 +353,11 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, if (ses->server->tcpStatus == CifsExiting) { return -ENOENT; } else if (ses->server->tcpStatus == CifsNeedReconnect) { - cFYI(1,("tcp session dead - return to caller to retry")); + cFYI(1, ("tcp session dead - return to caller to retry")); return -EAGAIN; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ - if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && + if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { return -EAGAIN; } /* else ok - we are setting up session */ @@ -369,7 +369,7 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, return 0; } -static int wait_for_response(struct cifsSesInfo *ses, +static int wait_for_response(struct cifsSesInfo *ses, struct mid_q_entry *midQ, unsigned long timeout, unsigned long time_to_wait) @@ -379,8 +379,8 @@ static int wait_for_response(struct cifsSesInfo *ses, for (;;) { curr_timeout = timeout + jiffies; wait_event(ses->server->response_q, - (!(midQ->midState == MID_REQUEST_SUBMITTED)) || - time_after(jiffies, curr_timeout) || + (!(midQ->midState == MID_REQUEST_SUBMITTED)) || + time_after(jiffies, curr_timeout) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); @@ -398,16 +398,16 @@ static int wait_for_response(struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); /* Calculate time_to_wait past last receive time. - Although we prefer not to time out if the + Although we prefer not to time out if the server is still responding - we will time - out if the server takes more than 15 (or 45 + out if the server takes more than 15 (or 45 or 180) seconds to respond to this request - and has not responded to any request from + and has not responded to any request from other threads on the client within 10 seconds */ lrt += time_to_wait; if (time_after(jiffies, lrt)) { /* No replies for time_to_wait. */ - cERROR(1,("server not responding")); + cERROR(1, ("server not responding")); return -1; } } else { @@ -417,8 +417,8 @@ static int wait_for_response(struct cifsSesInfo *ses, } int -SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, - struct kvec *iov, int n_vec, int * pRespBufType /* ret */, +SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, + struct kvec *iov, int n_vec, int *pRespBufType /* ret */, const int long_op) { int rc = 0; @@ -426,21 +426,21 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; - + *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); - cERROR(1,("Null session")); + cERROR(1, ("Null session")); return -EIO; } - if(ses->server->tcpStatus == CifsExiting) { + if (ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(in_buf); return -ENOENT; } - /* Ensure that we do not send more than 50 overlapping requests + /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ @@ -450,23 +450,23 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, return rc; } - /* make sure that we sign in the same order that we send on this socket + /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + down(&ses->server->tcpSem); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { up(&ses->server->tcpSem); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); + rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 @@ -482,7 +482,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, up(&ses->server->tcpSem); cifs_small_buf_release(in_buf); - if(rc < 0) + if (rc < 0) goto out; if (long_op == -1) @@ -490,18 +490,18 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, else if (long_op == 2) /* writes past end of file can take loong time */ timeout = 180 * HZ; else if (long_op == 1) - timeout = 45 * HZ; /* should be greater than + timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else timeout = 15 * HZ; - /* wait for 15 seconds or until woken up due to response arriving or + /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; - } + } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); @@ -511,10 +511,10 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response to cmd %d mid %d", + cERROR(1, ("No response to cmd %d mid %d", midQ->command, midQ->mid)); - if(midQ->midState == MID_REQUEST_SUBMITTED) { - if(ses->server->tcpStatus == CifsExiting) + if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; @@ -523,9 +523,9 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, } if (rc != -EHOSTDOWN) { - if(midQ->midState == MID_RETRY_NEEDED) { + if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; - cFYI(1,("marking request for retry")); + cFYI(1, ("marking request for retry")); } else { rc = -EIO; } @@ -533,21 +533,21 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ - if (midQ->resp_buf && + if (midQ->resp_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { iov[0].iov_base = (char *)midQ->resp_buf; - if(midQ->largeBuf) + if (midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; @@ -555,14 +555,14 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, dump_smb(midQ->resp_buf, 80); /* convert the length into a more usable form */ - if((receive_len > 24) && + if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, - ses->server->mac_signing_key, + &ses->server->mac_signing_key, midQ->sequence_number+1); - if(rc) { - cERROR(1,("Unexpected SMB signature")); + if (rc) { + cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } @@ -576,19 +576,19 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) - BCC(midQ->resp_buf) = + BCC(midQ->resp_buf) = le16_to_cpu(BCC_LE(midQ->resp_buf)); midQ->resp_buf = NULL; /* mark it so will not be freed by DeleteMidQEntry */ } else { rc = -EIO; - cFYI(1,("Bad MID state?")); + cFYI(1, ("Bad MID state?")); } } out: DeleteMidQEntry(midQ); - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; @@ -605,18 +605,18 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct mid_q_entry *midQ; if (ses == NULL) { - cERROR(1,("Null smb session")); + cERROR(1, ("Null smb session")); return -EIO; } - if(ses->server == NULL) { - cERROR(1,("Null tcp session")); + if (ses->server == NULL) { + cERROR(1, ("Null tcp session")); return -EIO; } - if(ses->server->tcpStatus == CifsExiting) + if (ses->server->tcpStatus == CifsExiting) return -ENOENT; - /* Ensure that we do not send more than 50 overlapping requests + /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ @@ -624,17 +624,17 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (rc) return rc; - /* make sure that we sign in the same order that we send on this socket + /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + down(&ses->server->tcpSem); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { up(&ses->server->tcpSem); /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } @@ -645,7 +645,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, DeleteMidQEntry(midQ); up(&ses->server->tcpSem); /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return -EIO; } @@ -664,7 +664,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, #endif up(&ses->server->tcpSem); - if(rc < 0) + if (rc < 0) goto out; if (long_op == -1) @@ -672,17 +672,17 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, else if (long_op == 2) /* writes past end of file can take loong time */ timeout = 180 * HZ; else if (long_op == 1) - timeout = 45 * HZ; /* should be greater than + timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else timeout = 15 * HZ; - /* wait for 15 seconds or until woken up due to response arriving or + /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; - } + } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); @@ -692,10 +692,10 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response for cmd %d mid %d", + cERROR(1, ("No response for cmd %d mid %d", midQ->command, midQ->mid)); - if(midQ->midState == MID_REQUEST_SUBMITTED) { - if(ses->server->tcpStatus == CifsExiting) + if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; @@ -704,9 +704,9 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, } if (rc != -EHOSTDOWN) { - if(midQ->midState == MID_RETRY_NEEDED) { + if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; - cFYI(1,("marking request for retry")); + cFYI(1, ("marking request for retry")); } else { rc = -EIO; } @@ -714,11 +714,11 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); @@ -734,14 +734,14 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, dump_smb(out_buf, 92); /* convert the length into a more usable form */ - if((receive_len > 24) && + if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - ses->server->mac_signing_key, + &ses->server->mac_signing_key, midQ->sequence_number+1); - if(rc) { - cERROR(1,("Unexpected SMB signature")); + if (rc) { + cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } @@ -759,13 +759,13 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; - cERROR(1,("Bad MID state?")); + cERROR(1, ("Bad MID state?")); } } out: DeleteMidQEntry(midQ); - atomic_dec(&ses->server->inFlight); + atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; @@ -783,7 +783,7 @@ send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; - down(&ses->server->tcpSem); + down(&ses->server->tcpSem); rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { up(&ses->server->tcpSem); @@ -832,20 +832,20 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, struct cifsSesInfo *ses; if (tcon == NULL || tcon->ses == NULL) { - cERROR(1,("Null smb session")); + cERROR(1, ("Null smb session")); return -EIO; } ses = tcon->ses; - if(ses->server == NULL) { - cERROR(1,("Null tcp session")); + if (ses->server == NULL) { + cERROR(1, ("Null tcp session")); return -EIO; } - if(ses->server->tcpStatus == CifsExiting) + if (ses->server->tcpStatus == CifsExiting) return -ENOENT; - /* Ensure that we do not send more than 50 overlapping requests + /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ @@ -853,11 +853,11 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, if (rc) return rc; - /* make sure that we sign in the same order that we send on this socket + /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ - down(&ses->server->tcpSem); + down(&ses->server->tcpSem); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { @@ -887,14 +887,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, #endif up(&ses->server->tcpSem); - if(rc < 0) { + if (rc < 0) { DeleteMidQEntry(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, - (!(midQ->midState == MID_REQUEST_SUBMITTED)) || + (!(midQ->midState == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); @@ -928,7 +928,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, } /* Wait 5 seconds for the response. */ - if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ)==0) { + if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { /* We got the response - restart system call. */ rstart = 1; } @@ -939,10 +939,10 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { - cERROR(1,("No response for cmd %d mid %d", + cERROR(1, ("No response for cmd %d mid %d", midQ->command, midQ->mid)); - if(midQ->midState == MID_REQUEST_SUBMITTED) { - if(ses->server->tcpStatus == CifsExiting) + if (midQ->midState == MID_REQUEST_SUBMITTED) { + if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; @@ -951,9 +951,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, } if (rc != -EHOSTDOWN) { - if(midQ->midState == MID_RETRY_NEEDED) { + if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; - cFYI(1,("marking request for retry")); + cFYI(1, ("marking request for retry")); } else { rc = -EIO; } @@ -962,7 +962,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, DeleteMidQEntry(midQ); return rc; } - + if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); @@ -978,14 +978,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, dump_smb(out_buf, 92); /* convert the length into a more usable form */ - if((receive_len > 24) && + if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - ses->server->mac_signing_key, + &ses->server->mac_signing_key, midQ->sequence_number+1); - if(rc) { - cERROR(1,("Unexpected SMB signature")); + if (rc) { + cERROR(1, ("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } @@ -1003,7 +1003,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; - cERROR(1,("Bad MID state?")); + cERROR(1, ("Bad MID state?")); } } DeleteMidQEntry(midQ); diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 18fcec1..f61e433 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -1,7 +1,7 @@ /* * fs/cifs/xattr.c * - * Copyright (c) International Business Machines Corp., 2003 + * Copyright (c) International Business Machines Corp., 2003, 2007 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -37,50 +37,52 @@ #define XATTR_TRUSTED_PREFIX_LEN 8 #define XATTR_SECURITY_PREFIX_LEN 9 /* BB need to add server (Samba e.g) support for security and trusted prefix */ - -int cifs_removexattr(struct dentry * direntry, const char * ea_name) + +int cifs_removexattr(struct dentry *direntry, const char *ea_name) { int rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct super_block * sb; - char * full_path; - - if(direntry == NULL) + struct super_block *sb; + char *full_path; + + if (direntry == NULL) return -EIO; - if(direntry->d_inode == NULL) + if (direntry->d_inode == NULL) return -EIO; sb = direntry->d_inode->i_sb; - if(sb == NULL) + if (sb == NULL) return -EIO; xid = GetXid(); - + cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - + full_path = build_path_from_dentry(direntry); - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } - if(ea_name == NULL) { - cFYI(1,("Null xattr names not supported")); - } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5) - && (strncmp(ea_name,CIFS_XATTR_OS2_PREFIX,4))) { - cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name)); + if (ea_name == NULL) { + cFYI(1, ("Null xattr names not supported")); + } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) + && (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4))) { + cFYI(1, + ("illegal xattr request %s (only user namespace supported)", + ea_name)); /* BB what if no namespace prefix? */ /* Should we just pass them to server, except for system and perhaps security prefixes? */ } else { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto remove_ea_exit; - ea_name+=5; /* skip past user. prefix */ - rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,NULL, + ea_name += 5; /* skip past user. prefix */ + rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL, (__u16)0, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } @@ -91,23 +93,23 @@ remove_ea_exit: return rc; } -int cifs_setxattr(struct dentry * direntry, const char * ea_name, - const void * ea_value, size_t value_size, int flags) +int cifs_setxattr(struct dentry *direntry, const char *ea_name, + const void *ea_value, size_t value_size, int flags) { int rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct super_block * sb; - char * full_path; + struct super_block *sb; + char *full_path; - if(direntry == NULL) + if (direntry == NULL) return -EIO; - if(direntry->d_inode == NULL) + if (direntry->d_inode == NULL) return -EIO; sb = direntry->d_inode->i_sb; - if(sb == NULL) + if (sb == NULL) return -EIO; xid = GetXid(); @@ -115,7 +117,7 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } @@ -123,67 +125,69 @@ int cifs_setxattr(struct dentry * direntry, const char * ea_name, /* return alt name if available as pseudo attr */ /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to + search server for EAs or streams to returns as xattrs */ - if(value_size > MAX_EA_VALUE_SIZE) { - cFYI(1,("size of EA value too large")); + if (value_size > MAX_EA_VALUE_SIZE) { + cFYI(1, ("size of EA value too large")); kfree(full_path); FreeXid(xid); return -EOPNOTSUPP; } - if(ea_name == NULL) { - cFYI(1,("Null xattr names not supported")); - } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5) == 0) { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + if (ea_name == NULL) { + cFYI(1, ("Null xattr names not supported")); + } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto set_ea_exit; - if(strncmp(ea_name,CIFS_XATTR_DOS_ATTRIB,14) == 0) { - cFYI(1,("attempt to set cifs inode metadata")); + if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) { + cFYI(1, ("attempt to set cifs inode metadata")); } ea_name += 5; /* skip past user. prefix */ - rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value, + rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, (__u16)value_size, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - } else if(strncmp(ea_name, CIFS_XATTR_OS2_PREFIX,4) == 0) { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto set_ea_exit; ea_name += 4; /* skip past os2. prefix */ - rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value, + rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, (__u16)value_size, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); } else { - int temp; - temp = strncmp(ea_name,POSIX_ACL_XATTR_ACCESS, + int temp; + temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS, strlen(POSIX_ACL_XATTR_ACCESS)); if (temp == 0) { #ifdef CONFIG_CIFS_POSIX - if(sb->s_flags & MS_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon,full_path, - ea_value, (const int)value_size, - ACL_TYPE_ACCESS,cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + if (sb->s_flags & MS_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + ea_value, (const int)value_size, + ACL_TYPE_ACCESS, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1,("set POSIX ACL rc %d",rc)); + cFYI(1, ("set POSIX ACL rc %d", rc)); #else - cFYI(1,("set POSIX ACL not supported")); + cFYI(1, ("set POSIX ACL not supported")); #endif - } else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT,strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { + } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT, + strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { #ifdef CONFIG_CIFS_POSIX - if(sb->s_flags & MS_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon,full_path, - ea_value, (const int)value_size, + if (sb->s_flags & MS_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + ea_value, (const int)value_size, ACL_TYPE_DEFAULT, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - cFYI(1,("set POSIX default ACL rc %d",rc)); + cFYI(1, ("set POSIX default ACL rc %d", rc)); #else - cFYI(1,("set default POSIX ACL not supported")); + cFYI(1, ("set default POSIX ACL not supported")); #endif } else { - cFYI(1,("illegal xattr request %s (only user namespace supported)",ea_name)); + cFYI(1, ("illegal xattr request %s (only user namespace" + " supported)", ea_name)); /* BB what if no namespace prefix? */ - /* Should we just pass them to server, except for + /* Should we just pass them to server, except for system and perhaps security prefixes? */ } } @@ -195,23 +199,23 @@ set_ea_exit: return rc; } -ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, - void * ea_value, size_t buf_size) +ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, + void *ea_value, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct super_block * sb; - char * full_path; + struct super_block *sb; + char *full_path; - if(direntry == NULL) + if (direntry == NULL) return -EIO; - if(direntry->d_inode == NULL) + if (direntry->d_inode == NULL) return -EIO; sb = direntry->d_inode->i_sb; - if(sb == NULL) + if (sb == NULL) return -EIO; xid = GetXid(); @@ -220,42 +224,42 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, pTcon = cifs_sb->tcon; full_path = build_path_from_dentry(direntry); - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ - if(ea_name == NULL) { - cFYI(1,("Null xattr names not supported")); - } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5) == 0) { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + if (ea_name == NULL) { + cFYI(1, ("Null xattr names not supported")); + } else if (strncmp(ea_name, CIFS_XATTR_USER_PREFIX, 5) == 0) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto get_ea_exit; - if(strncmp(ea_name,CIFS_XATTR_DOS_ATTRIB,14) == 0) { - cFYI(1,("attempt to query cifs inode metadata")); + if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) { + cFYI(1, ("attempt to query cifs inode metadata")); /* revalidate/getattr then populate from inode */ } /* BB add else when above is implemented */ ea_name += 5; /* skip past user. prefix */ - rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, + rc = CIFSSMBQueryEA(xid, pTcon, full_path, ea_name, ea_value, buf_size, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - } else if(strncmp(ea_name, CIFS_XATTR_OS2_PREFIX,4) == 0) { - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + } else if (strncmp(ea_name, CIFS_XATTR_OS2_PREFIX, 4) == 0) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) goto get_ea_exit; ea_name += 4; /* skip past os2. prefix */ - rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, + rc = CIFSSMBQueryEA(xid, pTcon, full_path, ea_name, ea_value, buf_size, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - } else if(strncmp(ea_name,POSIX_ACL_XATTR_ACCESS, + } else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS, strlen(POSIX_ACL_XATTR_ACCESS)) == 0) { #ifdef CONFIG_CIFS_POSIX - if(sb->s_flags & MS_POSIXACL) + if (sb->s_flags & MS_POSIXACL) rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - ea_value, buf_size, ACL_TYPE_ACCESS, + ea_value, buf_size, ACL_TYPE_ACCESS, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); /* else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { __u16 fid; @@ -272,39 +276,40 @@ ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, CIFSSMBClose(xid, pTcon, fid); } } */ /* BB enable after fixing up return data */ - -#else - cFYI(1,("query POSIX ACL not supported yet")); +#else + cFYI(1, ("query POSIX ACL not supported yet")); #endif /* CONFIG_CIFS_POSIX */ - } else if(strncmp(ea_name,POSIX_ACL_XATTR_DEFAULT, + } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT, strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) { #ifdef CONFIG_CIFS_POSIX - if(sb->s_flags & MS_POSIXACL) + if (sb->s_flags & MS_POSIXACL) rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - ea_value, buf_size, ACL_TYPE_DEFAULT, + ea_value, buf_size, ACL_TYPE_DEFAULT, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); -#else - cFYI(1,("query POSIX default ACL not supported yet")); +#else + cFYI(1, ("query POSIX default ACL not supported yet")); #endif - } else if(strncmp(ea_name, - CIFS_XATTR_TRUSTED_PREFIX,XATTR_TRUSTED_PREFIX_LEN) == 0) { - cFYI(1,("Trusted xattr namespace not supported yet")); - } else if(strncmp(ea_name, - CIFS_XATTR_SECURITY_PREFIX,XATTR_SECURITY_PREFIX_LEN) == 0) { - cFYI(1,("Security xattr namespace not supported yet")); + } else if (strncmp(ea_name, + CIFS_XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { + cFYI(1, ("Trusted xattr namespace not supported yet")); + } else if (strncmp(ea_name, + CIFS_XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { + cFYI(1, ("Security xattr namespace not supported yet")); } else { - cFYI(1,("illegal xattr name request %s (only user namespace supported)",ea_name)); + cFYI(1, + ("illegal xattr request %s (only user namespace supported)", + ea_name)); } - /* We could add an additional check for streams ie + /* We could add an additional check for streams ie if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to + search server for EAs or streams to returns as xattrs */ - if(rc == -EINVAL) - rc = -EOPNOTSUPP; + if (rc == -EINVAL) + rc = -EOPNOTSUPP; get_ea_exit: kfree(full_path); @@ -313,34 +318,34 @@ get_ea_exit: return rc; } -ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) +ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct super_block * sb; - char * full_path; + struct super_block *sb; + char *full_path; - if(direntry == NULL) + if (direntry == NULL) return -EIO; - if(direntry->d_inode == NULL) + if (direntry->d_inode == NULL) return -EIO; sb = direntry->d_inode->i_sb; - if(sb == NULL) + if (sb == NULL) return -EIO; cifs_sb = CIFS_SB(sb); pTcon = cifs_sb->tcon; - if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) return -EOPNOTSUPP; xid = GetXid(); full_path = build_path_from_dentry(direntry); - if(full_path == NULL) { + if (full_path == NULL) { FreeXid(xid); return -ENOMEM; } @@ -348,11 +353,11 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) /* return alt name if available as pseudo attr */ /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to + search server for EAs or streams to returns as xattrs */ - rc = CIFSSMBQAllEAs(xid,pTcon,full_path,data,buf_size, + rc = CIFSSMBQAllEAs(xid, pTcon, full_path, data, buf_size, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(full_path); diff --git a/fs/coda/cache.c b/fs/coda/cache.c index fcb88fa..8a23703 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -43,17 +43,12 @@ void coda_cache_enter(struct inode *inode, int mask) void coda_cache_clear_inode(struct inode *inode) { struct coda_inode_info *cii = ITOC(inode); - cii->c_cached_perm = 0; + cii->c_cached_epoch = atomic_read(&permission_epoch) - 1; } /* remove all acl caches */ void coda_cache_clear_all(struct super_block *sb) { - struct coda_sb_info *sbi; - - sbi = coda_sbp(sb); - BUG_ON(!sbi); - atomic_inc(&permission_epoch); } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 28c8727..a7a7809 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -55,11 +55,6 @@ static int coda_set_inode(struct inode *inode, void *data) return 0; } -static int coda_fail_inode(struct inode *inode, void *data) -{ - return -1; -} - struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, struct coda_vattr * attr) { @@ -141,7 +136,7 @@ struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) return NULL; } - inode = iget5_locked(sb, hash, coda_test_inode, coda_fail_inode, fid); + inode = ilookup5(sb, hash, coda_test_inode, fid); if ( !inode ) return NULL; diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h index 9e6338f..8ccd5ed 100644 --- a/fs/coda/coda_int.h +++ b/fs/coda/coda_int.h @@ -1,12 +1,19 @@ #ifndef _CODA_INT_ #define _CODA_INT_ +struct dentry; + extern struct file_system_type coda_fs_type; +extern unsigned long coda_timeout; +extern int coda_hard; +extern int coda_fake_statfs; void coda_destroy_inodecache(void); int coda_init_inodecache(void); int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync); +void coda_sysctl_init(void); +void coda_sysctl_clean(void); #endif /* _CODA_INT_ */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 9ddf5ed..f89ff08 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -25,7 +25,6 @@ #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> #include <linux/coda_cache.h> -#include <linux/coda_proc.h> #include "coda_int.h" @@ -43,15 +42,15 @@ static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry); /* dir file-ops */ -static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); +static int coda_readdir(struct file *file, void *buf, filldir_t filldir); /* dentry ops */ static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd); static int coda_dentry_delete(struct dentry *); /* support routines */ -static int coda_venus_readdir(struct file *filp, filldir_t filldir, - void *dirent, struct dentry *dir); +static int coda_venus_readdir(struct file *coda_file, void *buf, + filldir_t filldir); /* same as fs/bad_inode.c */ static int coda_return_EIO(void) @@ -87,7 +86,6 @@ const struct file_operations coda_dir_operations = { .read = generic_read_dir, .readdir = coda_readdir, .open = coda_open, - .flush = coda_flush, .release = coda_release, .fsync = coda_fsync, }; @@ -97,58 +95,45 @@ const struct file_operations coda_dir_operations = { /* access routines: lookup, readlink, permission */ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd) { - struct inode *res_inode = NULL; + struct inode *inode = NULL; struct CodaFid resfid = { { 0, } }; - int dropme = 0; /* to indicate entry should not be cached */ int type = 0; int error = 0; const char *name = entry->d_name.name; size_t length = entry->d_name.len; - - if ( length > CODA_MAXNAMLEN ) { - printk("name too long: lookup, %s (%*s)\n", + + if (length > CODA_MAXNAMLEN) { + printk(KERN_ERR "name too long: lookup, %s (%*s)\n", coda_i2s(dir), (int)length, name); return ERR_PTR(-ENAMETOOLONG); } + /* control object, create inode on the fly */ + if (coda_isroot(dir) && coda_iscontrol(name, length)) { + error = coda_cnode_makectl(&inode, dir->i_sb); + type = CODA_NOCACHE; + goto exit; + } + lock_kernel(); - /* control object, create inode on the fly */ - if (coda_isroot(dir) && coda_iscontrol(name, length)) { - error = coda_cnode_makectl(&res_inode, dir->i_sb); - dropme = 1; - goto exit; - } - error = venus_lookup(dir->i_sb, coda_i2f(dir), - (const char *)name, length, &type, &resfid); + error = venus_lookup(dir->i_sb, coda_i2f(dir), name, length, + &type, &resfid); + if (!error) + error = coda_cnode_make(&inode, &resfid, dir->i_sb); - res_inode = NULL; - if (!error) { - if (type & CODA_NOCACHE) { - type &= (~CODA_NOCACHE); - dropme = 1; - } + unlock_kernel(); - error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); - if (error) { - unlock_kernel(); - return ERR_PTR(error); - } - } else if (error != -ENOENT) { - unlock_kernel(); + if (error && error != -ENOENT) return ERR_PTR(error); - } exit: - entry->d_time = 0; entry->d_op = &coda_dentry_operations; - d_add(entry, res_inode); - if ( dropme ) { - d_drop(entry); - coda_flag_inode(res_inode, C_VATTR); - } - unlock_kernel(); - return NULL; + + if (inode && (type & CODA_NOCACHE)) + coda_flag_inode(inode, C_VATTR | C_PURGE); + + return d_splice_alias(inode, entry); } @@ -161,8 +146,6 @@ int coda_permission(struct inode *inode, int mask, struct nameidata *nd) lock_kernel(); - coda_vfs_stat.permission++; - if (coda_cache_check(inode, mask)) goto out; @@ -173,12 +156,11 @@ int coda_permission(struct inode *inode, int mask, struct nameidata *nd) out: unlock_kernel(); - - return error; + return error; } -static inline void coda_dir_changed(struct inode *dir, int link) +static inline void coda_dir_update_mtime(struct inode *dir) { #ifdef REQUERY_VENUS_FOR_MTIME /* invalidate the directory cnode's attributes so we refetch the @@ -186,12 +168,27 @@ static inline void coda_dir_changed(struct inode *dir, int link) coda_flag_inode(dir, C_VATTR); #else /* optimistically we can also act as if our nose bleeds. The - * granularity of the mtime is coarse anyways so we might actually be - * right most of the time. Note: we only do this for directories. */ + * granularity of the mtime is coarse anyways so we might actually be + * right most of the time. Note: we only do this for directories. */ dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; #endif - if (link) - dir->i_nlink += link; +} + +/* we have to wrap inc_nlink/drop_nlink because sometimes userspace uses a + * trick to fool GNU find's optimizations. If we can't be sure of the link + * (because of volume mount points) we set i_nlink to 1 which forces find + * to consider every child as a possible directory. We should also never + * see an increment or decrement for deleted directories where i_nlink == 0 */ +static inline void coda_dir_inc_nlink(struct inode *dir) +{ + if (dir->i_nlink >= 2) + inc_nlink(dir); +} + +static inline void coda_dir_drop_nlink(struct inode *dir) +{ + if (dir->i_nlink > 2) + drop_nlink(dir); } /* creation routines: create, mknod, mkdir, link, symlink */ @@ -205,7 +202,6 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode, struct na struct coda_vattr attrs; lock_kernel(); - coda_vfs_stat.create++; if (coda_isroot(dir) && coda_iscontrol(name, length)) { unlock_kernel(); @@ -229,10 +225,10 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode, struct na } /* invalidate the directory cnode's attributes */ - coda_dir_changed(dir, 0); + coda_dir_update_mtime(dir); unlock_kernel(); d_instantiate(de, inode); - return 0; + return 0; } static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) @@ -245,7 +241,6 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) struct CodaFid newfid; lock_kernel(); - coda_vfs_stat.mkdir++; if (coda_isroot(dir) && coda_iscontrol(name, len)) { unlock_kernel(); @@ -268,12 +263,13 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) d_drop(de); return PTR_ERR(inode); } - + /* invalidate the directory cnode's attributes */ - coda_dir_changed(dir, 1); + coda_dir_inc_nlink(dir); + coda_dir_update_mtime(dir); unlock_kernel(); d_instantiate(de, inode); - return 0; + return 0; } /* try to make de an entry in dir_inodde linked to source_de */ @@ -286,7 +282,6 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, int error; lock_kernel(); - coda_vfs_stat.link++; if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { unlock_kernel(); @@ -296,16 +291,16 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, error = venus_link(dir_inode->i_sb, coda_i2f(inode), coda_i2f(dir_inode), (const char *)name, len); - if (error) { + if (error) { d_drop(de); goto out; } - coda_dir_changed(dir_inode, 0); + coda_dir_update_mtime(dir_inode); atomic_inc(&inode->i_count); d_instantiate(de, inode); inc_nlink(inode); - + out: unlock_kernel(); return(error); @@ -318,10 +313,9 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, const char *name = de->d_name.name; int len = de->d_name.len; int symlen; - int error=0; - + int error = 0; + lock_kernel(); - coda_vfs_stat.symlink++; if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { unlock_kernel(); @@ -336,18 +330,18 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, /* * This entry is now negative. Since we do not create - * an inode for the entry we have to drop it. + * an inode for the entry we have to drop it. */ d_drop(de); - error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, + error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, symname, symlen); /* mtime is no good anymore */ if ( !error ) - coda_dir_changed(dir_inode, 0); + coda_dir_update_mtime(dir_inode); unlock_kernel(); - return error; + return error; } /* destruction routines: unlink, rmdir */ @@ -358,79 +352,70 @@ int coda_unlink(struct inode *dir, struct dentry *de) int len = de->d_name.len; lock_kernel(); - coda_vfs_stat.unlink++; - error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); - if ( error ) { + error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); + if ( error ) { unlock_kernel(); - return error; - } + return error; + } - coda_dir_changed(dir, 0); + coda_dir_update_mtime(dir); drop_nlink(de->d_inode); unlock_kernel(); - - return 0; + return 0; } int coda_rmdir(struct inode *dir, struct dentry *de) { const char *name = de->d_name.name; int len = de->d_name.len; - int error; + int error; lock_kernel(); - coda_vfs_stat.rmdir++; - if (!d_unhashed(de)) { - unlock_kernel(); - return -EBUSY; - } error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); + if (!error) { + /* VFS may delete the child */ + if (de->d_inode) + de->d_inode->i_nlink = 0; - if ( error ) { - unlock_kernel(); - return error; - } - - coda_dir_changed(dir, -1); - drop_nlink(de->d_inode); - d_delete(de); + /* fix the link count of the parent */ + coda_dir_drop_nlink(dir); + coda_dir_update_mtime(dir); + } unlock_kernel(); - - return 0; + return error; } /* rename */ -static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, +static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - const char *old_name = old_dentry->d_name.name; - const char *new_name = new_dentry->d_name.name; + const char *old_name = old_dentry->d_name.name; + const char *new_name = new_dentry->d_name.name; int old_length = old_dentry->d_name.len; int new_length = new_dentry->d_name.len; - int link_adjust = 0; - int error; + int error; lock_kernel(); - coda_vfs_stat.rename++; - error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), - coda_i2f(new_dir), old_length, new_length, + error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), + coda_i2f(new_dir), old_length, new_length, (const char *) old_name, (const char *)new_name); - if ( !error ) { + if ( !error ) { if ( new_dentry->d_inode ) { - if ( S_ISDIR(new_dentry->d_inode->i_mode) ) - link_adjust = 1; - - coda_dir_changed(old_dir, -link_adjust); - coda_dir_changed(new_dir, link_adjust); + if ( S_ISDIR(new_dentry->d_inode->i_mode) ) { + coda_dir_drop_nlink(old_dir); + coda_dir_inc_nlink(new_dir); + } + coda_dir_update_mtime(old_dir); + coda_dir_update_mtime(new_dir); coda_flag_inode(new_dentry->d_inode, C_VATTR); } else { coda_flag_inode(old_dir, C_VATTR); coda_flag_inode(new_dir, C_VATTR); - } + } } unlock_kernel(); @@ -439,44 +424,41 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, /* file operations for directories */ -int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) +int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) { - struct dentry *coda_dentry = coda_file->f_path.dentry; struct coda_file_info *cfi; struct file *host_file; - struct inode *host_inode; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - coda_vfs_stat.readdir++; - - host_inode = host_file->f_path.dentry->d_inode; - mutex_lock(&host_inode->i_mutex); - host_file->f_pos = coda_file->f_pos; - - if (!host_file->f_op->readdir) { - /* Venus: we must read Venus dirents from the file */ - ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry); - } else { - /* potemkin case: we were handed a directory inode. */ - /* Yuk, we can't call vfs_readdir because we are already - * holding the inode semaphore. */ - ret = -ENOTDIR; - if (!host_file->f_op || !host_file->f_op->readdir) - goto out; + if (!host_file->f_op) + return -ENOTDIR; + + if (host_file->f_op->readdir) + { + /* potemkin case: we were handed a directory inode. + * We can't use vfs_readdir because we have to keep the file + * position in sync between the coda_file and the host_file. + * and as such we need grab the inode mutex. */ + struct inode *host_inode = host_file->f_path.dentry->d_inode; + + mutex_lock(&host_inode->i_mutex); + host_file->f_pos = coda_file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { - ret = host_file->f_op->readdir(host_file, filldir, dirent); + ret = host_file->f_op->readdir(host_file, buf, filldir); file_accessed(host_file); } + + coda_file->f_pos = host_file->f_pos; + mutex_unlock(&host_inode->i_mutex); } -out: - coda_file->f_pos = host_file->f_pos; - mutex_unlock(&host_inode->i_mutex); + else /* Venus: we must read Venus dirents from a file */ + ret = coda_venus_readdir(coda_file, buf, filldir); return ret; } @@ -501,57 +483,68 @@ static inline unsigned int CDT2DT(unsigned char cdt) } /* support routines */ -static int coda_venus_readdir(struct file *filp, filldir_t filldir, - void *dirent, struct dentry *dir) +static int coda_venus_readdir(struct file *coda_file, void *buf, + filldir_t filldir) { int result = 0; /* # of entries returned */ + struct coda_file_info *cfi; + struct coda_inode_info *cii; + struct file *host_file; + struct dentry *de; struct venus_dirent *vdir; unsigned long vdir_size = (unsigned long)(&((struct venus_dirent *)0)->d_name); unsigned int type; struct qstr name; ino_t ino; - int ret, i; + int ret; + + cfi = CODA_FTOC(coda_file); + BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); + host_file = cfi->cfi_container; + + de = coda_file->f_path.dentry; + cii = ITOC(de->d_inode); vdir = kmalloc(sizeof(*vdir), GFP_KERNEL); if (!vdir) return -ENOMEM; - i = filp->f_pos; - switch(i) { - case 0: - ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR); - if (ret < 0) break; + if (coda_file->f_pos == 0) { + ret = filldir(buf, ".", 1, 0, de->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto out; result++; - filp->f_pos++; - /* fallthrough */ - case 1: - ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR); - if (ret < 0) break; + coda_file->f_pos++; + } + if (coda_file->f_pos == 1) { + ret = filldir(buf, "..", 2, 1, de->d_parent->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto out; result++; - filp->f_pos++; - /* fallthrough */ - default: + coda_file->f_pos++; + } while (1) { /* read entries from the directory file */ - ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir, + ret = kernel_read(host_file, coda_file->f_pos - 2, (char *)vdir, sizeof(*vdir)); if (ret < 0) { - printk("coda_venus_readdir: read dir failed %d\n", ret); + printk(KERN_ERR "coda readdir: read dir %s failed %d\n", + coda_f2s(&cii->c_fid), ret); break; } if (ret == 0) break; /* end of directory file reached */ /* catch truncated reads */ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { - printk("coda_venus_readdir: short read: %ld\n", - filp->f_path.dentry->d_inode->i_ino); + printk(KERN_ERR "coda readdir: short read on %s\n", + coda_f2s(&cii->c_fid)); ret = -EBADF; break; } /* validate whether the directory file actually makes sense */ if (vdir->d_reclen < vdir_size + vdir->d_namlen) { - printk("coda_venus_readdir: Invalid dir: %ld\n", - filp->f_path.dentry->d_inode->i_ino); + printk(KERN_ERR "coda readdir: invalid dir %s\n", + coda_f2s(&cii->c_fid)); ret = -EBADF; break; } @@ -570,21 +563,21 @@ static int coda_venus_readdir(struct file *filp, filldir_t filldir, * userspace doesn't have to worry about breaking * getcwd by having mismatched inode numbers for * internal volume mountpoints. */ - ino = find_inode_number(dir, &name); + ino = find_inode_number(de, &name); if (!ino) ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); - ret = filldir(dirent, name.name, name.len, filp->f_pos, - ino, type); + ret = filldir(buf, name.name, name.len, + coda_file->f_pos, ino, type); /* failure means no space for filling in this round */ if (ret < 0) break; result++; } /* we'll always have progress because d_reclen is unsigned and * we've already established it is non-zero. */ - filp->f_pos += vdir->d_reclen; + coda_file->f_pos += vdir->d_reclen; } - } +out: kfree(vdir); return result ? result : ret; } diff --git a/fs/coda/file.c b/fs/coda/file.c index 99dbe86..29137ff 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -22,14 +22,9 @@ #include <linux/coda_linux.h> #include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> -#include <linux/coda_proc.h> #include "coda_int.h" -/* if CODA_STORE fails with EOPNOTSUPP, venus clearly doesn't support - * CODA_STORE/CODA_RELEASE and we fall back on using the CODA_CLOSE upcall */ -static int use_coda_close; - static ssize_t coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos) { @@ -134,8 +129,6 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_file_info *cfi; - coda_vfs_stat.open++; - cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL); if (!cfi) return -ENOMEM; @@ -143,8 +136,11 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) lock_kernel(); error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, - &host_file); - if (error || !host_file) { + &host_file); + if (!host_file) + error = -EIO; + + if (error) { kfree(cfi); unlock_kernel(); return error; @@ -163,49 +159,6 @@ int coda_open(struct inode *coda_inode, struct file *coda_file) return 0; } -int coda_flush(struct file *coda_file, fl_owner_t id) -{ - unsigned short flags = coda_file->f_flags & ~O_EXCL; - unsigned short coda_flags = coda_flags_to_cflags(flags); - struct coda_file_info *cfi; - struct inode *coda_inode; - int err = 0, fcnt; - - lock_kernel(); - - coda_vfs_stat.flush++; - - /* last close semantics */ - fcnt = file_count(coda_file); - if (fcnt > 1) - goto out; - - /* No need to make an upcall when we have not made any modifications - * to the file */ - if ((coda_file->f_flags & O_ACCMODE) == O_RDONLY) - goto out; - - if (use_coda_close) - goto out; - - cfi = CODA_FTOC(coda_file); - BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - - coda_inode = coda_file->f_path.dentry->d_inode; - - err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, - coda_file->f_uid); - - if (err == -EOPNOTSUPP) { - use_coda_close = 1; - err = 0; - } - -out: - unlock_kernel(); - return err; -} - int coda_release(struct inode *coda_inode, struct file *coda_file) { unsigned short flags = (coda_file->f_flags) & (~O_EXCL); @@ -216,23 +169,12 @@ int coda_release(struct inode *coda_inode, struct file *coda_file) int err = 0; lock_kernel(); - coda_vfs_stat.release++; - - if (!use_coda_close) { - err = venus_release(coda_inode->i_sb, coda_i2f(coda_inode), - coda_flags); - if (err == -EOPNOTSUPP) { - use_coda_close = 1; - err = 0; - } - } cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - if (use_coda_close) - err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), - coda_flags, coda_file->f_uid); + err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), + coda_flags, coda_file->f_uid); host_inode = cfi->cfi_container->f_path.dentry->d_inode; cii = ITOC(coda_inode); @@ -249,7 +191,10 @@ int coda_release(struct inode *coda_inode, struct file *coda_file) coda_file->private_data = NULL; unlock_kernel(); - return err; + + /* VFS fput ignores the return value from file_operations->release, so + * there is no use returning an error here */ + return 0; } int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) @@ -268,8 +213,6 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; - coda_vfs_stat.fsync++; - if (host_file->f_op && host_file->f_op->fsync) { host_dentry = host_file->f_path.dentry; host_inode = host_dentry->d_inode; @@ -293,7 +236,6 @@ const struct file_operations coda_file_operations = { .write = coda_file_write, .mmap = coda_file_mmap, .open = coda_open, - .flush = coda_flush, .release = coda_release, .fsync = coda_fsync, .splice_read = coda_file_splice_read, diff --git a/fs/coda/inode.c b/fs/coda/inode.c index dbff1bd..342f4e0 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -64,13 +64,13 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + int coda_init_inodecache(void) { coda_inode_cachep = kmem_cache_create("coda_inode_cache", sizeof(struct coda_inode_info), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - init_once, NULL); + init_once); if (coda_inode_cachep == NULL) return -ENOMEM; return 0; @@ -83,7 +83,7 @@ void coda_destroy_inodecache(void) static int coda_remount(struct super_block *sb, int *flags, char *data) { - *flags |= MS_NODIRATIME; + *flags |= MS_NOATIME; return 0; } @@ -141,11 +141,10 @@ static int get_device_index(struct coda_mount_data *data) static int coda_fill_super(struct super_block *sb, void *data, int silent) { - struct inode *root = NULL; - struct coda_sb_info *sbi = NULL; + struct inode *root = NULL; struct venus_comm *vc = NULL; struct CodaFid fid; - int error; + int error; int idx; idx = get_device_index((struct coda_mount_data *) data); @@ -167,21 +166,14 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) return -EBUSY; } - sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL); - if(!sbi) { - return -ENOMEM; - } - vc->vc_sb = sb; - sbi->sbi_vcomm = vc; - - sb->s_fs_info = sbi; - sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ - sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ - sb->s_blocksize_bits = 10; - sb->s_magic = CODA_SUPER_MAGIC; - sb->s_op = &coda_super_operations; + sb->s_fs_info = vc; + sb->s_flags |= MS_NOATIME; + sb->s_blocksize = 4096; /* XXXXX what do we put here?? */ + sb->s_blocksize_bits = 12; + sb->s_magic = CODA_SUPER_MAGIC; + sb->s_op = &coda_super_operations; /* get root fid from Venus: this needs the root inode */ error = venus_rootfid(sb, &fid); @@ -207,26 +199,20 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) return 0; error: - if (sbi) { - kfree(sbi); - if(vc) - vc->vc_sb = NULL; - } if (root) - iput(root); + iput(root); + if (vc) + vc->vc_sb = NULL; - return -EINVAL; + return -EINVAL; } static void coda_put_super(struct super_block *sb) { - struct coda_sb_info *sbi; - - sbi = coda_sbp(sb); - sbi->sbi_vcomm->vc_sb = NULL; + coda_vcp(sb)->vc_sb = NULL; + sb->s_fs_info = NULL; printk("Coda: Bye bye.\n"); - kfree(sbi); } static void coda_clear_inode(struct inode *inode) @@ -296,7 +282,7 @@ static int coda_statfs(struct dentry *dentry, struct kstatfs *buf) /* and fill in the rest */ buf->f_type = CODA_SUPER_MAGIC; - buf->f_bsize = 1024; + buf->f_bsize = 4096; buf->f_namelen = CODA_MAXNAMLEN; return 0; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 803aacf..dcc6aea 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -45,12 +45,9 @@ #include <linux/coda_linux.h> #include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> -#include <linux/coda_proc.h> #include "coda_int.h" -#define upc_free(r) kfree(r) - /* statistics */ int coda_hard; /* allows signals during upcalls */ unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ @@ -195,7 +192,8 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, if (req->uc_opcode == CODA_OPEN_BY_FD) { struct coda_open_by_fd_out *outp = (struct coda_open_by_fd_out *)req->uc_data; - outp->fh = fget(outp->fd); + if (!outp->oh.result) + outp->fh = fget(outp->fd); } wake_up(&req->uc_sleep); @@ -263,7 +261,7 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, } CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); - upc_free(req); + kfree(req); out: unlock_kernel(); return (count ? count : retval); @@ -271,71 +269,70 @@ out: static int coda_psdev_open(struct inode * inode, struct file * file) { - struct venus_comm *vcp; - int idx; + struct venus_comm *vcp; + int idx, err; - lock_kernel(); idx = iminor(inode); - if(idx >= MAX_CODADEVS) { - unlock_kernel(); + if (idx < 0 || idx >= MAX_CODADEVS) return -ENODEV; - } + lock_kernel(); + + err = -EBUSY; vcp = &coda_comms[idx]; - if(vcp->vc_inuse) { - unlock_kernel(); - return -EBUSY; - } - - if (!vcp->vc_inuse++) { + if (!vcp->vc_inuse) { + vcp->vc_inuse++; + INIT_LIST_HEAD(&vcp->vc_pending); INIT_LIST_HEAD(&vcp->vc_processing); init_waitqueue_head(&vcp->vc_waitq); vcp->vc_sb = NULL; vcp->vc_seq = 0; + + file->private_data = vcp; + err = 0; } - - file->private_data = vcp; unlock_kernel(); - return 0; + return err; } static int coda_psdev_release(struct inode * inode, struct file * file) { - struct venus_comm *vcp = (struct venus_comm *) file->private_data; - struct upc_req *req, *tmp; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; + struct upc_req *req, *tmp; - lock_kernel(); - if ( !vcp->vc_inuse ) { - unlock_kernel(); + if (!vcp || !vcp->vc_inuse ) { printk("psdev_release: Not open.\n"); return -1; } - if (--vcp->vc_inuse) { - unlock_kernel(); - return 0; - } - - /* Wakeup clients so they can return. */ + lock_kernel(); + + /* Wakeup clients so they can return. */ list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) { + list_del(&req->uc_chain); + /* Async requests need to be freed here */ if (req->uc_flags & REQ_ASYNC) { CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); - upc_free(req); + kfree(req); continue; } req->uc_flags |= REQ_ABORT; wake_up(&req->uc_sleep); - } - - list_for_each_entry(req, &vcp->vc_processing, uc_chain) { + } + + list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) { + list_del(&req->uc_chain); + req->uc_flags |= REQ_ABORT; - wake_up(&req->uc_sleep); - } + wake_up(&req->uc_sleep); + } + file->private_data = NULL; + vcp->vc_inuse--; unlock_kernel(); return 0; } @@ -376,21 +373,20 @@ out: return err; } - -MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); +MODULE_AUTHOR("Jan Harkes, Peter J. Braam"); +MODULE_DESCRIPTION("Coda Distributed File System VFS interface"); +MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR); MODULE_LICENSE("GPL"); +#ifdef CONFIG_CODA_FS_OLD_API +MODULE_VERSION("5.3.21"); +#else +MODULE_VERSION("6.6"); +#endif static int __init init_coda(void) { int status; int i; - printk(KERN_INFO "Coda Kernel/Venus communications, " -#ifdef CONFIG_CODA_FS_OLD_API - "v5.3.20" -#else - "v6.0.0" -#endif - ", coda@cs.cmu.edu\n"); status = coda_init_inodecache(); if (status) diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 76e00a6..4513b7258 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -20,7 +20,6 @@ #include <linux/coda_linux.h> #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> -#include <linux/coda_proc.h> static int coda_symlink_filler(struct file *file, struct page *page) { @@ -32,7 +31,6 @@ static int coda_symlink_filler(struct file *file, struct page *page) lock_kernel(); cii = ITOC(inode); - coda_vfs_stat.follow_link++; error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len); unlock_kernel(); diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index c57a1fa..81b7771 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -5,181 +5,14 @@ * * Carnegie Mellon encourages users to contribute improvements to * the Coda project. Contact Peter Braam (coda@cs.cmu.edu). - * - * CODA operation statistics - * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu> - * */ -#include <linux/time.h> -#include <linux/mm.h> #include <linux/sysctl.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/stat.h> -#include <linux/ctype.h> -#include <linux/bitops.h> -#include <asm/uaccess.h> -#include <linux/utsname.h> -#include <linux/module.h> -#include <linux/coda.h> -#include <linux/coda_linux.h> -#include <linux/coda_fs_i.h> -#include <linux/coda_psdev.h> -#include <linux/coda_cache.h> -#include <linux/coda_proc.h> +#include "coda_int.h" static struct ctl_table_header *fs_table_header; -#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */ -#define CODA_HARD 5 /* mount type "hard" or "soft" */ -#define CODA_VFS 6 /* vfs statistics */ -#define CODA_CACHE_INV 9 /* cache invalidation statistics */ -#define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */ - -struct coda_vfs_stats coda_vfs_stat; -static struct coda_cache_inv_stats coda_cache_inv_stat; - -static void reset_coda_vfs_stats( void ) -{ - memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) ); -} - -static void reset_coda_cache_inv_stats( void ) -{ - memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) ); -} - -static int do_reset_coda_vfs_stats( ctl_table * table, int write, - struct file * filp, void __user * buffer, - size_t * lenp, loff_t * ppos ) -{ - if ( write ) { - reset_coda_vfs_stats(); - - *ppos += *lenp; - } else { - *lenp = 0; - } - - return 0; -} - -static int do_reset_coda_cache_inv_stats( ctl_table * table, int write, - struct file * filp, - void __user * buffer, - size_t * lenp, loff_t * ppos ) -{ - if ( write ) { - reset_coda_cache_inv_stats(); - - *ppos += *lenp; - } else { - *lenp = 0; - } - - return 0; -} - -static int proc_vfs_stats_show(struct seq_file *m, void *v) -{ - struct coda_vfs_stats * ps = & coda_vfs_stat; - - seq_printf(m, - "Coda VFS statistics\n" - "===================\n\n" - "File Operations:\n" - "\topen\t\t%9d\n" - "\tflush\t\t%9d\n" - "\trelease\t\t%9d\n" - "\tfsync\t\t%9d\n\n" - "Dir Operations:\n" - "\treaddir\t\t%9d\n\n" - "Inode Operations\n" - "\tcreate\t\t%9d\n" - "\tlookup\t\t%9d\n" - "\tlink\t\t%9d\n" - "\tunlink\t\t%9d\n" - "\tsymlink\t\t%9d\n" - "\tmkdir\t\t%9d\n" - "\trmdir\t\t%9d\n" - "\trename\t\t%9d\n" - "\tpermission\t%9d\n", - - /* file operations */ - ps->open, - ps->flush, - ps->release, - ps->fsync, - - /* dir operations */ - ps->readdir, - - /* inode operations */ - ps->create, - ps->lookup, - ps->link, - ps->unlink, - ps->symlink, - ps->mkdir, - ps->rmdir, - ps->rename, - ps->permission); - return 0; -} - -static int proc_cache_inv_stats_show(struct seq_file *m, void *v) -{ - struct coda_cache_inv_stats * ps = & coda_cache_inv_stat; - - seq_printf(m, - "Coda cache invalidation statistics\n" - "==================================\n\n" - "flush\t\t%9d\n" - "purge user\t%9d\n" - "zap_dir\t\t%9d\n" - "zap_file\t%9d\n" - "zap_vnode\t%9d\n" - "purge_fid\t%9d\n" - "replace\t\t%9d\n", - ps->flush, - ps->purge_user, - ps->zap_dir, - ps->zap_file, - ps->zap_vnode, - ps->purge_fid, - ps->replace ); - return 0; -} - -static int proc_vfs_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_vfs_stats_show, NULL); -} - -static int proc_cache_inv_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_cache_inv_stats_show, NULL); -} - -static const struct file_operations proc_vfs_stats_fops = { - .owner = THIS_MODULE, - .open = proc_vfs_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations proc_cache_inv_stats_fops = { - .owner = THIS_MODULE, - .open = proc_cache_inv_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static ctl_table coda_table[] = { { .ctl_name = CTL_UNNUMBERED, @@ -199,22 +32,6 @@ static ctl_table coda_table[] = { }, { .ctl_name = CTL_UNNUMBERED, - .procname = "vfs_stats", - .data = NULL, - .maxlen = 0, - .mode = 0644, - .proc_handler = &do_reset_coda_vfs_stats - }, - { - .ctl_name = CTL_UNNUMBERED, - .procname = "cache_inv_stats", - .data = NULL, - .maxlen = 0, - .mode = 0644, - .proc_handler = &do_reset_coda_cache_inv_stats - }, - { - .ctl_name = CTL_UNNUMBERED, .procname = "fake_statfs", .data = &coda_fake_statfs, .maxlen = sizeof(int), @@ -235,59 +52,20 @@ static ctl_table fs_table[] = { }; -#ifdef CONFIG_PROC_FS - -/* - target directory structure: - /proc/fs (see linux/fs/proc/root.c) - /proc/fs/coda - /proc/fs/coda/{vfs_stats, - -*/ - -static struct proc_dir_entry* proc_fs_coda; - -#endif - void coda_sysctl_init(void) { - reset_coda_vfs_stats(); - reset_coda_cache_inv_stats(); - -#ifdef CONFIG_PROC_FS - proc_fs_coda = proc_mkdir("coda", proc_root_fs); - if (proc_fs_coda) { - struct proc_dir_entry *pde; - - proc_fs_coda->owner = THIS_MODULE; - pde = create_proc_entry("vfs_stats", 0, proc_fs_coda); - if (pde) - pde->proc_fops = &proc_vfs_stats_fops; - pde = create_proc_entry("cache_inv_stats", 0, proc_fs_coda); - if (pde) - pde->proc_fops = &proc_cache_inv_stats_fops; - } -#endif - #ifdef CONFIG_SYSCTL if ( !fs_table_header ) fs_table_header = register_sysctl_table(fs_table); -#endif +#endif } -void coda_sysctl_clean(void) +void coda_sysctl_clean(void) { - #ifdef CONFIG_SYSCTL if ( fs_table_header ) { unregister_sysctl_table(fs_table_header); fs_table_header = NULL; } #endif - -#ifdef CONFIG_PROC_FS - remove_proc_entry("cache_inv_stats", proc_fs_coda); - remove_proc_entry("vfs_stats", proc_fs_coda); - remove_proc_entry("coda", proc_root_fs); -#endif } diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 5faacdb..cdb4c07 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -35,12 +35,10 @@ #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> #include <linux/coda_cache.h> -#include <linux/coda_proc.h> -#define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL) -#define upc_free(r) kfree(r) +#include "coda_int.h" -static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, +static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize, union inputArgs *buffer); static void *alloc_upcall(int opcode, int size) @@ -86,13 +84,9 @@ int venus_rootfid(struct super_block *sb, struct CodaFid *fidp) insize = SIZE(root); UPARG(CODA_ROOT); - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - if (error) { - printk("coda_get_rootfid: error %d\n", error); - } else { + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) *fidp = outp->coda_root.VFid; - } CODA_FREE(inp, insize); return error; @@ -109,9 +103,9 @@ int venus_getattr(struct super_block *sb, struct CodaFid *fid, UPARG(CODA_GETATTR); inp->coda_getattr.VFid = *fid; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - *attr = outp->coda_getattr.attr; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *attr = outp->coda_getattr.attr; CODA_FREE(inp, insize); return error; @@ -130,7 +124,7 @@ int venus_setattr(struct super_block *sb, struct CodaFid *fid, inp->coda_setattr.VFid = *fid; inp->coda_setattr.attr = *vattr; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -156,64 +150,18 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - *resfid = outp->coda_lookup.VFid; - *type = outp->coda_lookup.vtype; - - CODA_FREE(inp, insize); - return error; -} - -int venus_store(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) -{ - union inputArgs *inp; - union outputArgs *outp; - int insize, outsize, error; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred = { 0, }; - cred.cr_fsuid = uid; -#endif - - insize = SIZE(store); - UPARG(CODA_STORE); - -#ifdef CONFIG_CODA_FS_OLD_API - memcpy(&(inp->ih.cred), &cred, sizeof(cred)); -#else - inp->ih.uid = uid; -#endif - - inp->coda_store.VFid = *fid; - inp->coda_store.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - CODA_FREE(inp, insize); - return error; -} - -int venus_release(struct super_block *sb, struct CodaFid *fid, int flags) -{ - union inputArgs *inp; - union outputArgs *outp; - int insize, outsize, error; - - insize = SIZE(release); - UPARG(CODA_RELEASE); - - inp->coda_release.VFid = *fid; - inp->coda_release.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *resfid = outp->coda_lookup.VFid; + *type = outp->coda_lookup.vtype; + } CODA_FREE(inp, insize); return error; } int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) + vuid_t uid) { union inputArgs *inp; union outputArgs *outp; @@ -235,7 +183,7 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -251,12 +199,12 @@ int venus_open(struct super_block *sb, struct CodaFid *fid, insize = SIZE(open_by_fd); UPARG(CODA_OPEN_BY_FD); - inp->coda_open.VFid = *fid; - inp->coda_open.flags = flags; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + inp->coda_open_by_fd.VFid = *fid; + inp->coda_open_by_fd.flags = flags; - *fh = outp->coda_open_by_fd.fh; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) + *fh = outp->coda_open_by_fd.fh; CODA_FREE(inp, insize); return error; @@ -281,11 +229,12 @@ int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, /* Venus must get null terminated string */ memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->coda_mkdir.attr; - *newfid = outp->coda_mkdir.VFid; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_mkdir.attr; + *newfid = outp->coda_mkdir.VFid; + } CODA_FREE(inp, insize); return error; @@ -323,7 +272,7 @@ int venus_rename(struct super_block *sb, struct CodaFid *old_fid, memcpy((char *)(inp) + offset, new_name, new_length); *((char *)inp + offset + new_length) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -351,11 +300,12 @@ int venus_create(struct super_block *sb, struct CodaFid *dirfid, /* Venus must get null terminated string */ memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->coda_create.attr; - *newfid = outp->coda_create.VFid; + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + *attrs = outp->coda_create.attr; + *newfid = outp->coda_create.VFid; + } CODA_FREE(inp, insize); return error; @@ -377,8 +327,8 @@ int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, inp->coda_rmdir.name = offset; memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -399,8 +349,8 @@ int venus_remove(struct super_block *sb, struct CodaFid *dirfid, inp->coda_remove.name = offset; memcpy((char *)(inp) + offset, name, length); *((char *)inp + offset + length) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -420,19 +370,18 @@ int venus_readlink(struct super_block *sb, struct CodaFid *fid, UPARG(CODA_READLINK); inp->coda_readlink.VFid = *fid; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - - if (! error) { - retlen = outp->coda_readlink.count; + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); + if (!error) { + retlen = outp->coda_readlink.count; if ( retlen > *length ) - retlen = *length; + retlen = *length; *length = retlen; result = (char *)outp + (long)outp->coda_readlink.data; memcpy(buffer, result, retlen); *(buffer + retlen) = '\0'; } - + CODA_FREE(inp, insize); return error; } @@ -458,8 +407,8 @@ int venus_link(struct super_block *sb, struct CodaFid *fid, /* make sure strings are null terminated */ memcpy((char *)(inp) + offset, name, len); *((char *)inp + offset + len) = '\0'; - - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -494,7 +443,7 @@ int venus_symlink(struct super_block *sb, struct CodaFid *fid, memcpy((char *)(inp) + offset, name, len); *((char *)inp + offset + len) = '\0'; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -509,9 +458,9 @@ int venus_fsync(struct super_block *sb, struct CodaFid *fid) insize=SIZE(fsync); UPARG(CODA_FSYNC); - inp->coda_fsync.VFid = *fid; - error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), - &outsize, inp); + inp->coda_fsync.VFid = *fid; + error = coda_upcall(coda_vcp(sb), sizeof(union inputArgs), + &outsize, inp); CODA_FREE(inp, insize); return error; @@ -529,7 +478,7 @@ int venus_access(struct super_block *sb, struct CodaFid *fid, int mask) inp->coda_access.VFid = *fid; inp->coda_access.flags = mask; - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_vcp(sb), insize, &outsize, inp); CODA_FREE(inp, insize); return error; @@ -578,9 +527,9 @@ int venus_pioctl(struct super_block *sb, struct CodaFid *fid, goto exit; } - error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, - &outsize, inp); - + error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size, + &outsize, inp); + if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", error, coda_f2s(fid)); @@ -620,16 +569,13 @@ int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs)); UPARG(CODA_STATFS); - error = coda_upcall(coda_sbp(dentry->d_sb), insize, &outsize, inp); - - if (!error) { + error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp); + if (!error) { sfs->f_blocks = outp->coda_statfs.stat.f_blocks; sfs->f_bfree = outp->coda_statfs.stat.f_bfree; sfs->f_bavail = outp->coda_statfs.stat.f_bavail; sfs->f_files = outp->coda_statfs.stat.f_files; sfs->f_ffree = outp->coda_statfs.stat.f_ffree; - } else { - printk("coda_statfs: Venus returns: %d\n", error); } CODA_FREE(inp, insize); @@ -638,96 +584,129 @@ int venus_statfs(struct dentry *dentry, struct kstatfs *sfs) /* * coda_upcall and coda_downcall routines. - * */ +static void coda_block_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + *old = current->blocked; + + sigfillset(¤t->blocked); + sigdelset(¤t->blocked, SIGKILL); + sigdelset(¤t->blocked, SIGSTOP); + sigdelset(¤t->blocked, SIGINT); + + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +static void coda_unblock_signals(sigset_t *old) +{ + spin_lock_irq(¤t->sighand->siglock); + current->blocked = *old; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +/* Don't allow signals to interrupt the following upcalls before venus + * has seen them, + * - CODA_CLOSE or CODA_RELEASE upcall (to avoid reference count problems) + * - CODA_STORE (to avoid data loss) + */ +#define CODA_INTERRUPTIBLE(r) (!coda_hard && \ + (((r)->uc_opcode != CODA_CLOSE && \ + (r)->uc_opcode != CODA_STORE && \ + (r)->uc_opcode != CODA_RELEASE) || \ + (r)->uc_flags & REQ_READ)) -static inline void coda_waitfor_upcall(struct upc_req *vmp, - struct venus_comm *vcommp) +static inline void coda_waitfor_upcall(struct upc_req *req) { DECLARE_WAITQUEUE(wait, current); + unsigned long timeout = jiffies + coda_timeout * HZ; + sigset_t old; + int blocked; - vmp->uc_posttime = jiffies; + coda_block_signals(&old); + blocked = 1; - add_wait_queue(&vmp->uc_sleep, &wait); + add_wait_queue(&req->uc_sleep, &wait); for (;;) { - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) + if (CODA_INTERRUPTIBLE(req)) set_current_state(TASK_INTERRUPTIBLE); else set_current_state(TASK_UNINTERRUPTIBLE); - /* venus died */ - if ( !vcommp->vc_inuse ) - break; - /* got a reply */ - if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) + if (req->uc_flags & (REQ_WRITE | REQ_ABORT)) break; - if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { - /* if this process really wants to die, let it go */ - if ( sigismember(&(current->pending.signal), SIGKILL) || - sigismember(&(current->pending.signal), SIGINT) ) - break; - /* signal is present: after timeout always return - really smart idea, probably useless ... */ - if ( jiffies - vmp->uc_posttime > coda_timeout * HZ ) - break; + if (blocked && time_after(jiffies, timeout) && + CODA_INTERRUPTIBLE(req)) + { + coda_unblock_signals(&old); + blocked = 0; + } + + if (signal_pending(current)) { + list_del(&req->uc_chain); + break; } - schedule(); + + if (blocked) + schedule_timeout(HZ); + else + schedule(); } - remove_wait_queue(&vmp->uc_sleep, &wait); - set_current_state(TASK_RUNNING); + if (blocked) + coda_unblock_signals(&old); - return; + remove_wait_queue(&req->uc_sleep, &wait); + set_current_state(TASK_RUNNING); } -/* - * coda_upcall will return an error in the case of +/* + * coda_upcall will return an error in the case of * failed communication with Venus _or_ will peek at Venus * reply and return Venus' error. * * As venus has 2 types of errors, normal errors (positive) and internal * errors (negative), normal errors are negated, while internal errors * are all mapped to -EINTR, while showing a nice warning message. (jh) - * */ -static int coda_upcall(struct coda_sb_info *sbi, - int inSize, int *outSize, - union inputArgs *buffer) +static int coda_upcall(struct venus_comm *vcp, + int inSize, int *outSize, + union inputArgs *buffer) { - struct venus_comm *vcommp; union outputArgs *out; - struct upc_req *req; + union inputArgs *sig_inputArgs; + struct upc_req *req, *sig_req; int error = 0; - vcommp = sbi->sbi_vcomm; - if ( !vcommp->vc_inuse ) { - printk("No pseudo device in upcall comms at %p\n", vcommp); - return -ENXIO; + if (!vcp->vc_inuse) { + printk(KERN_NOTICE "coda: Venus dead, not sending upcall\n"); + return -ENXIO; } /* Format the request message. */ - req = upc_alloc(); - if (!req) { - printk("Failed to allocate upc_req structure\n"); + req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!req) return -ENOMEM; - } + req->uc_data = (void *)buffer; req->uc_flags = 0; req->uc_inSize = inSize; req->uc_outSize = *outSize ? *outSize : inSize; req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode; - req->uc_unique = ++vcommp->vc_seq; + req->uc_unique = ++vcp->vc_seq; init_waitqueue_head(&req->uc_sleep); - + /* Fill in the common input args. */ ((union inputArgs *)buffer)->ih.unique = req->uc_unique; /* Append msg to pending queue and poke Venus. */ - list_add_tail(&(req->uc_chain), &vcommp->vc_pending); - - wake_up_interruptible(&vcommp->vc_waitq); + list_add_tail(&req->uc_chain, &vcp->vc_pending); + + wake_up_interruptible(&vcp->vc_waitq); /* We can be interrupted while we wait for Venus to process * our request. If the interrupt occurs before Venus has read * the request, we dequeue and return. If it occurs after the @@ -738,67 +717,60 @@ static int coda_upcall(struct coda_sb_info *sbi, * ENODEV. */ /* Go to sleep. Wake up on signals only after the timeout. */ - coda_waitfor_upcall(req, vcommp); + coda_waitfor_upcall(req); - if (vcommp->vc_inuse) { /* i.e. Venus is still alive */ - /* Op went through, interrupt or not... */ - if (req->uc_flags & REQ_WRITE) { + /* Op went through, interrupt or not... */ + if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ error = -out->oh.result; *outSize = req->uc_outSize; goto exit; - } - if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { - /* Interrupted before venus read it. */ - list_del(&(req->uc_chain)); - /* perhaps the best way to convince the app to - give up? */ - error = -EINTR; + } + + error = -EINTR; + if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) { + printk(KERN_WARNING "coda: Unexpected interruption.\n"); goto exit; - } - if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) { - /* interrupted after Venus did its read, send signal */ - union inputArgs *sig_inputArgs; - struct upc_req *sig_req; - - list_del(&(req->uc_chain)); - error = -ENOMEM; - sig_req = upc_alloc(); - if (!sig_req) goto exit; - - CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); - if (!sig_req->uc_data) { - upc_free(sig_req); - goto exit; - } - - error = -EINTR; - sig_inputArgs = (union inputArgs *)sig_req->uc_data; - sig_inputArgs->ih.opcode = CODA_SIGNAL; - sig_inputArgs->ih.unique = req->uc_unique; - - sig_req->uc_flags = REQ_ASYNC; - sig_req->uc_opcode = sig_inputArgs->ih.opcode; - sig_req->uc_unique = sig_inputArgs->ih.unique; - sig_req->uc_inSize = sizeof(struct coda_in_hdr); - sig_req->uc_outSize = sizeof(struct coda_in_hdr); - - /* insert at head of queue! */ - list_add(&(sig_req->uc_chain), &vcommp->vc_pending); - wake_up_interruptible(&vcommp->vc_waitq); - } else { - printk("Coda: Strange interruption..\n"); - error = -EINTR; - } - } else { /* If venus died i.e. !VC_OPEN(vcommp) */ - printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", - req->uc_opcode, req->uc_unique, req->uc_flags); - error = -ENODEV; } - exit: - upc_free(req); + /* Interrupted before venus read it. */ + if (!(req->uc_flags & REQ_READ)) + goto exit; + + /* Venus saw the upcall, make sure we can send interrupt signal */ + if (!vcp->vc_inuse) { + printk(KERN_INFO "coda: Venus dead, not sending signal.\n"); + goto exit; + } + + error = -ENOMEM; + sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL); + if (!sig_req) goto exit; + + CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); + if (!sig_req->uc_data) { + kfree(sig_req); + goto exit; + } + + error = -EINTR; + sig_inputArgs = (union inputArgs *)sig_req->uc_data; + sig_inputArgs->ih.opcode = CODA_SIGNAL; + sig_inputArgs->ih.unique = req->uc_unique; + + sig_req->uc_flags = REQ_ASYNC; + sig_req->uc_opcode = sig_inputArgs->ih.opcode; + sig_req->uc_unique = sig_inputArgs->ih.unique; + sig_req->uc_inSize = sizeof(struct coda_in_hdr); + sig_req->uc_outSize = sizeof(struct coda_in_hdr); + + /* insert at head of queue! */ + list_add(&(sig_req->uc_chain), &vcp->vc_pending); + wake_up_interruptible(&vcp->vc_waitq); + +exit: + kfree(req); return error; } @@ -838,77 +810,66 @@ static int coda_upcall(struct coda_sb_info *sbi, int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { + struct inode *inode = NULL; + struct CodaFid *fid, *newfid; + /* Handle invalidation requests. */ - if ( !sb || !sb->s_root || !sb->s_root->d_inode) - return 0; - - switch (opcode) { - - case CODA_FLUSH : { - coda_cache_clear_all(sb); - shrink_dcache_sb(sb); - coda_flag_inode(sb->s_root->d_inode, C_FLUSH); - return(0); - } - - case CODA_PURGEUSER : { - coda_cache_clear_all(sb); - return(0); - } - - case CODA_ZAPDIR : { - struct inode *inode; - struct CodaFid *fid = &out->coda_zapdir.CodaFid; - - inode = coda_fid_to_inode(fid, sb); - if (inode) { - coda_flag_inode_children(inode, C_PURGE); - coda_flag_inode(inode, C_VATTR); - iput(inode); - } - - return(0); - } - - case CODA_ZAPFILE : { - struct inode *inode; - struct CodaFid *fid = &out->coda_zapfile.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if ( inode ) { - coda_flag_inode(inode, C_VATTR); - iput(inode); - } - return 0; - } - - case CODA_PURGEFID : { - struct inode *inode; - struct CodaFid *fid = &out->coda_purgefid.CodaFid; - inode = coda_fid_to_inode(fid, sb); - if ( inode ) { + if ( !sb || !sb->s_root) + return 0; + + switch (opcode) { + case CODA_FLUSH: + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + if (sb->s_root->d_inode) + coda_flag_inode(sb->s_root->d_inode, C_FLUSH); + break; + + case CODA_PURGEUSER: + coda_cache_clear_all(sb); + break; + + case CODA_ZAPDIR: + fid = &out->coda_zapdir.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) { + coda_flag_inode_children(inode, C_PURGE); + coda_flag_inode(inode, C_VATTR); + } + break; + + case CODA_ZAPFILE: + fid = &out->coda_zapfile.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) + coda_flag_inode(inode, C_VATTR); + break; + + case CODA_PURGEFID: + fid = &out->coda_purgefid.CodaFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) { coda_flag_inode_children(inode, C_PURGE); /* catch the dentries later if some are still busy */ coda_flag_inode(inode, C_PURGE); d_prune_aliases(inode); - iput(inode); - } - return 0; - } - - case CODA_REPLACE : { - struct inode *inode; - struct CodaFid *oldfid = &out->coda_replace.OldFid; - struct CodaFid *newfid = &out->coda_replace.NewFid; - inode = coda_fid_to_inode(oldfid, sb); - if ( inode ) { - coda_replace_fid(inode, oldfid, newfid); - iput(inode); - } - return 0; - } - } - return 0; + } + break; + + case CODA_REPLACE: + fid = &out->coda_replace.OldFid; + newfid = &out->coda_replace.NewFid; + inode = coda_fid_to_inode(fid, sb); + if (inode) + coda_replace_fid(inode, fid, newfid); + break; + } + + if (inode) + iput(inode); + + return 0; } diff --git a/fs/compat.c b/fs/compat.c index 4db6216..15078ce 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1257,6 +1257,7 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv, { struct page *kmapped_page = NULL; char *kaddr = NULL; + unsigned long kpos = 0; int ret; while (argc-- > 0) { @@ -1265,92 +1266,84 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv, unsigned long pos; if (get_user(str, argv+argc) || - !(len = strnlen_user(compat_ptr(str), bprm->p))) { + !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) { ret = -EFAULT; goto out; } - if (bprm->p < len) { + if (len > MAX_ARG_STRLEN) { ret = -E2BIG; goto out; } - bprm->p -= len; - /* XXX: add architecture specific overflow check here. */ + /* We're going to work our way backwords. */ pos = bprm->p; + str += len; + bprm->p -= len; while (len > 0) { - int i, new, err; int offset, bytes_to_copy; - struct page *page; offset = pos % PAGE_SIZE; - i = pos/PAGE_SIZE; - page = bprm->page[i]; - new = 0; - if (!page) { - page = alloc_page(GFP_HIGHUSER); - bprm->page[i] = page; - if (!page) { - ret = -ENOMEM; + if (offset == 0) + offset = PAGE_SIZE; + + bytes_to_copy = offset; + if (bytes_to_copy > len) + bytes_to_copy = len; + + offset -= bytes_to_copy; + pos -= bytes_to_copy; + str -= bytes_to_copy; + len -= bytes_to_copy; + + if (!kmapped_page || kpos != (pos & PAGE_MASK)) { + struct page *page; + +#ifdef CONFIG_STACK_GROWSUP + ret = expand_stack_downwards(bprm->vma, pos); + if (ret < 0) { + /* We've exceed the stack rlimit. */ + ret = -E2BIG; + goto out; + } +#endif + ret = get_user_pages(current, bprm->mm, pos, + 1, 1, 1, &page, NULL); + if (ret <= 0) { + /* We've exceed the stack rlimit. */ + ret = -E2BIG; goto out; } - new = 1; - } - if (page != kmapped_page) { - if (kmapped_page) + if (kmapped_page) { + flush_kernel_dcache_page(kmapped_page); kunmap(kmapped_page); + put_page(kmapped_page); + } kmapped_page = page; kaddr = kmap(kmapped_page); + kpos = pos & PAGE_MASK; + flush_cache_page(bprm->vma, kpos, + page_to_pfn(kmapped_page)); } - if (new && offset) - memset(kaddr, 0, offset); - bytes_to_copy = PAGE_SIZE - offset; - if (bytes_to_copy > len) { - bytes_to_copy = len; - if (new) - memset(kaddr+offset+len, 0, - PAGE_SIZE-offset-len); - } - err = copy_from_user(kaddr+offset, compat_ptr(str), - bytes_to_copy); - if (err) { + if (copy_from_user(kaddr+offset, compat_ptr(str), + bytes_to_copy)) { ret = -EFAULT; goto out; } - - pos += bytes_to_copy; - str += bytes_to_copy; - len -= bytes_to_copy; } } ret = 0; out: - if (kmapped_page) + if (kmapped_page) { + flush_kernel_dcache_page(kmapped_page); kunmap(kmapped_page); - return ret; -} - -#ifdef CONFIG_MMU - -#define free_arg_pages(bprm) do { } while (0) - -#else - -static inline void free_arg_pages(struct linux_binprm *bprm) -{ - int i; - - for (i = 0; i < MAX_ARG_PAGES; i++) { - if (bprm->page[i]) - __free_page(bprm->page[i]); - bprm->page[i] = NULL; + put_page(kmapped_page); } + return ret; } -#endif /* CONFIG_MMU */ - /* * compat_do_execve() is mostly a copy of do_execve(), with the exception * that it processes 32 bit argv and envp pointers. @@ -1363,7 +1356,6 @@ int compat_do_execve(char * filename, struct linux_binprm *bprm; struct file *file; int retval; - int i; retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); @@ -1377,24 +1369,19 @@ int compat_do_execve(char * filename, sched_exec(); - bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); bprm->file = file; bprm->filename = filename; bprm->interp = filename; - bprm->mm = mm_alloc(); - retval = -ENOMEM; - if (!bprm->mm) - goto out_file; - retval = init_new_context(current, bprm->mm); - if (retval < 0) - goto out_mm; + retval = bprm_mm_init(bprm); + if (retval) + goto out_file; - bprm->argc = compat_count(argv, bprm->p / sizeof(compat_uptr_t)); + bprm->argc = compat_count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0) goto out_mm; - bprm->envc = compat_count(envp, bprm->p / sizeof(compat_uptr_t)); + bprm->envc = compat_count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0) goto out_mm; @@ -1421,8 +1408,6 @@ int compat_do_execve(char * filename, retval = search_binary_handler(bprm, regs); if (retval >= 0) { - free_arg_pages(bprm); - /* execve success */ security_bprm_free(bprm); acct_update_integrals(current); @@ -1431,19 +1416,12 @@ int compat_do_execve(char * filename, } out: - /* Something went wrong, return the inode and free the argument pages*/ - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page * page = bprm->page[i]; - if (page) - __free_page(page); - } - if (bprm->security) security_bprm_free(bprm); out_mm: if (bprm->mm) - mmdrop(bprm->mm); + mmput(bprm->mm); out_file: if (bprm->file) { diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 6b44cdc..e440a7b 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -63,6 +63,7 @@ #include <linux/wireless.h> #include <linux/atalk.h> #include <linux/blktrace_api.h> +#include <linux/loop.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci.h> @@ -3489,6 +3490,9 @@ HANDLE_IOCTL(LPSETTIMEOUT, lp_timeout_trans) IGNORE_IOCTL(VFAT_IOCTL_READDIR_BOTH32) IGNORE_IOCTL(VFAT_IOCTL_READDIR_SHORT32) + +/* loop */ +IGNORE_IOCTL(LOOP_CLR_FD) }; #define IOCTL_HASHSIZE 256 diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index 7b48c03..3b0185f 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h @@ -29,10 +29,11 @@ struct configfs_dirent { atomic_t s_count; + int s_dependent_count; struct list_head s_sibling; struct list_head s_children; struct list_head s_links; - void * s_element; + void * s_element; int s_type; umode_t s_mode; struct dentry * s_dentry; @@ -41,8 +42,8 @@ struct configfs_dirent { #define CONFIGFS_ROOT 0x0001 #define CONFIGFS_DIR 0x0002 -#define CONFIGFS_ITEM_ATTR 0x0004 -#define CONFIGFS_ITEM_LINK 0x0020 +#define CONFIGFS_ITEM_ATTR 0x0004 +#define CONFIGFS_ITEM_LINK 0x0020 #define CONFIGFS_USET_DIR 0x0040 #define CONFIGFS_USET_DEFAULT 0x0080 #define CONFIGFS_USET_DROPPING 0x0100 diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 5e6e37e..2f436d4 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -355,6 +355,10 @@ static int configfs_detach_prep(struct dentry *dentry) /* Mark that we've taken i_mutex */ sd->s_type |= CONFIGFS_USET_DROPPING; + /* + * Yup, recursive. If there's a problem, blame + * deep nesting of default_groups + */ ret = configfs_detach_prep(sd->s_dentry); if (!ret) continue; @@ -562,7 +566,7 @@ static int populate_groups(struct config_group *group) /* * All of link_obj/unlink_obj/link_group/unlink_group require that - * subsys->su_sem is held. + * subsys->su_mutex is held. */ static void unlink_obj(struct config_item *item) @@ -714,6 +718,28 @@ static void configfs_detach_group(struct config_item *item) } /* + * After the item has been detached from the filesystem view, we are + * ready to tear it out of the hierarchy. Notify the client before + * we do that so they can perform any cleanup that requires + * navigating the hierarchy. A client does not need to provide this + * callback. The subsystem semaphore MUST be held by the caller, and + * references must be valid for both items. It also assumes the + * caller has validated ci_type. + */ +static void client_disconnect_notify(struct config_item *parent_item, + struct config_item *item) +{ + struct config_item_type *type; + + type = parent_item->ci_type; + BUG_ON(!type); + + if (type->ct_group_ops && type->ct_group_ops->disconnect_notify) + type->ct_group_ops->disconnect_notify(to_config_group(parent_item), + item); +} + +/* * Drop the initial reference from make_item()/make_group() * This function assumes that reference is held on item * and that item holds a valid reference to the parent. Also, it @@ -733,11 +759,244 @@ static void client_drop_item(struct config_item *parent_item, */ if (type->ct_group_ops && type->ct_group_ops->drop_item) type->ct_group_ops->drop_item(to_config_group(parent_item), - item); + item); else config_item_put(item); } +#ifdef DEBUG +static void configfs_dump_one(struct configfs_dirent *sd, int level) +{ + printk(KERN_INFO "%*s\"%s\":\n", level, " ", configfs_get_name(sd)); + +#define type_print(_type) if (sd->s_type & _type) printk(KERN_INFO "%*s %s\n", level, " ", #_type); + type_print(CONFIGFS_ROOT); + type_print(CONFIGFS_DIR); + type_print(CONFIGFS_ITEM_ATTR); + type_print(CONFIGFS_ITEM_LINK); + type_print(CONFIGFS_USET_DIR); + type_print(CONFIGFS_USET_DEFAULT); + type_print(CONFIGFS_USET_DROPPING); +#undef type_print +} + +static int configfs_dump(struct configfs_dirent *sd, int level) +{ + struct configfs_dirent *child_sd; + int ret = 0; + + configfs_dump_one(sd, level); + + if (!(sd->s_type & (CONFIGFS_DIR|CONFIGFS_ROOT))) + return 0; + + list_for_each_entry(child_sd, &sd->s_children, s_sibling) { + ret = configfs_dump(child_sd, level + 2); + if (ret) + break; + } + + return ret; +} +#endif + + +/* + * configfs_depend_item() and configfs_undepend_item() + * + * WARNING: Do not call these from a configfs callback! + * + * This describes these functions and their helpers. + * + * Allow another kernel system to depend on a config_item. If this + * happens, the item cannot go away until the dependant can live without + * it. The idea is to give client modules as simple an interface as + * possible. When a system asks them to depend on an item, they just + * call configfs_depend_item(). If the item is live and the client + * driver is in good shape, we'll happily do the work for them. + * + * Why is the locking complex? Because configfs uses the VFS to handle + * all locking, but this function is called outside the normal + * VFS->configfs path. So it must take VFS locks to prevent the + * VFS->configfs stuff (configfs_mkdir(), configfs_rmdir(), etc). This is + * why you can't call these functions underneath configfs callbacks. + * + * Note, btw, that this can be called at *any* time, even when a configfs + * subsystem isn't registered, or when configfs is loading or unloading. + * Just like configfs_register_subsystem(). So we take the same + * precautions. We pin the filesystem. We lock each i_mutex _in_order_ + * on our way down the tree. If we can find the target item in the + * configfs tree, it must be part of the subsystem tree as well, so we + * do not need the subsystem semaphore. Holding the i_mutex chain locks + * out mkdir() and rmdir(), who might be racing us. + */ + +/* + * configfs_depend_prep() + * + * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are + * attributes. This is similar but not the same to configfs_detach_prep(). + * Note that configfs_detach_prep() expects the parent to be locked when it + * is called, but we lock the parent *inside* configfs_depend_prep(). We + * do that so we can unlock it if we find nothing. + * + * Here we do a depth-first search of the dentry hierarchy looking for + * our object. We take i_mutex on each step of the way down. IT IS + * ESSENTIAL THAT i_mutex LOCKING IS ORDERED. If we come back up a branch, + * we'll drop the i_mutex. + * + * If the target is not found, -ENOENT is bubbled up and we have released + * all locks. If the target was found, the locks will be cleared by + * configfs_depend_rollback(). + * + * This adds a requirement that all config_items be unique! + * + * This is recursive because the locking traversal is tricky. There isn't + * much on the stack, though, so folks that need this function - be careful + * about your stack! Patches will be accepted to make it iterative. + */ +static int configfs_depend_prep(struct dentry *origin, + struct config_item *target) +{ + struct configfs_dirent *child_sd, *sd = origin->d_fsdata; + int ret = 0; + + BUG_ON(!origin || !sd); + + /* Lock this guy on the way down */ + mutex_lock(&sd->s_dentry->d_inode->i_mutex); + if (sd->s_element == target) /* Boo-yah */ + goto out; + + list_for_each_entry(child_sd, &sd->s_children, s_sibling) { + if (child_sd->s_type & CONFIGFS_DIR) { + ret = configfs_depend_prep(child_sd->s_dentry, + target); + if (!ret) + goto out; /* Child path boo-yah */ + } + } + + /* We looped all our children and didn't find target */ + mutex_unlock(&sd->s_dentry->d_inode->i_mutex); + ret = -ENOENT; + +out: + return ret; +} + +/* + * This is ONLY called if configfs_depend_prep() did its job. So we can + * trust the entire path from item back up to origin. + * + * We walk backwards from item, unlocking each i_mutex. We finish by + * unlocking origin. + */ +static void configfs_depend_rollback(struct dentry *origin, + struct config_item *item) +{ + struct dentry *dentry = item->ci_dentry; + + while (dentry != origin) { + mutex_unlock(&dentry->d_inode->i_mutex); + dentry = dentry->d_parent; + } + + mutex_unlock(&origin->d_inode->i_mutex); +} + +int configfs_depend_item(struct configfs_subsystem *subsys, + struct config_item *target) +{ + int ret; + struct configfs_dirent *p, *root_sd, *subsys_sd = NULL; + struct config_item *s_item = &subsys->su_group.cg_item; + + /* + * Pin the configfs filesystem. This means we can safely access + * the root of the configfs filesystem. + */ + ret = configfs_pin_fs(); + if (ret) + return ret; + + /* + * Next, lock the root directory. We're going to check that the + * subsystem is really registered, and so we need to lock out + * configfs_[un]register_subsystem(). + */ + mutex_lock(&configfs_sb->s_root->d_inode->i_mutex); + + root_sd = configfs_sb->s_root->d_fsdata; + + list_for_each_entry(p, &root_sd->s_children, s_sibling) { + if (p->s_type & CONFIGFS_DIR) { + if (p->s_element == s_item) { + subsys_sd = p; + break; + } + } + } + + if (!subsys_sd) { + ret = -ENOENT; + goto out_unlock_fs; + } + + /* Ok, now we can trust subsys/s_item */ + + /* Scan the tree, locking i_mutex recursively, return 0 if found */ + ret = configfs_depend_prep(subsys_sd->s_dentry, target); + if (ret) + goto out_unlock_fs; + + /* We hold all i_mutexes from the subsystem down to the target */ + p = target->ci_dentry->d_fsdata; + p->s_dependent_count += 1; + + configfs_depend_rollback(subsys_sd->s_dentry, target); + +out_unlock_fs: + mutex_unlock(&configfs_sb->s_root->d_inode->i_mutex); + + /* + * If we succeeded, the fs is pinned via other methods. If not, + * we're done with it anyway. So release_fs() is always right. + */ + configfs_release_fs(); + + return ret; +} +EXPORT_SYMBOL(configfs_depend_item); + +/* + * Release the dependent linkage. This is much simpler than + * configfs_depend_item() because we know that that the client driver is + * pinned, thus the subsystem is pinned, and therefore configfs is pinned. + */ +void configfs_undepend_item(struct configfs_subsystem *subsys, + struct config_item *target) +{ + struct configfs_dirent *sd; + + /* + * Since we can trust everything is pinned, we just need i_mutex + * on the item. + */ + mutex_lock(&target->ci_dentry->d_inode->i_mutex); + + sd = target->ci_dentry->d_fsdata; + BUG_ON(sd->s_dependent_count < 1); + + sd->s_dependent_count -= 1; + + /* + * After this unlock, we cannot trust the item to stay alive! + * DO NOT REFERENCE item after this unlock. + */ + mutex_unlock(&target->ci_dentry->d_inode->i_mutex); +} +EXPORT_SYMBOL(configfs_undepend_item); static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { @@ -783,7 +1042,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); - down(&subsys->su_sem); + mutex_lock(&subsys->su_mutex); group = NULL; item = NULL; if (type->ct_group_ops->make_group) { @@ -797,7 +1056,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (item) link_obj(parent_item, item); } - up(&subsys->su_sem); + mutex_unlock(&subsys->su_mutex); kfree(name); if (!item) { @@ -841,13 +1100,16 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) out_unlink: if (ret) { /* Tear down everything we built up */ - down(&subsys->su_sem); + mutex_lock(&subsys->su_mutex); + + client_disconnect_notify(parent_item, item); if (group) unlink_group(group); else unlink_obj(item); client_drop_item(parent_item, item); - up(&subsys->su_sem); + + mutex_unlock(&subsys->su_mutex); if (module_got) module_put(owner); @@ -881,6 +1143,13 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) if (sd->s_type & CONFIGFS_USET_DEFAULT) return -EPERM; + /* + * Here's where we check for dependents. We're protected by + * i_mutex. + */ + if (sd->s_dependent_count) + return -EBUSY; + /* Get a working ref until we have the child */ parent_item = configfs_get_config_item(dentry->d_parent); subsys = to_config_group(parent_item)->cg_subsys; @@ -910,17 +1179,19 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) if (sd->s_type & CONFIGFS_USET_DIR) { configfs_detach_group(item); - down(&subsys->su_sem); + mutex_lock(&subsys->su_mutex); + client_disconnect_notify(parent_item, item); unlink_group(to_config_group(item)); } else { configfs_detach_item(item); - down(&subsys->su_sem); + mutex_lock(&subsys->su_mutex); + client_disconnect_notify(parent_item, item); unlink_obj(item); } client_drop_item(parent_item, item); - up(&subsys->su_sem); + mutex_unlock(&subsys->su_mutex); /* Drop our reference from above */ config_item_put(item); diff --git a/fs/configfs/file.c b/fs/configfs/file.c index 3527c7c..a3658f9 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -27,19 +27,26 @@ #include <linux/fs.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/mutex.h> #include <asm/uaccess.h> -#include <asm/semaphore.h> #include <linux/configfs.h> #include "configfs_internal.h" +/* + * A simple attribute can only be 4096 characters. Why 4k? Because the + * original code limited it to PAGE_SIZE. That's a bad idea, though, + * because an attribute of 16k on ia64 won't work on x86. So we limit to + * 4k, our minimum common page size. + */ +#define SIMPLE_ATTR_SIZE 4096 struct configfs_buffer { size_t count; loff_t pos; char * page; struct configfs_item_operations * ops; - struct semaphore sem; + struct mutex mutex; int needs_read_fill; }; @@ -69,7 +76,7 @@ static int fill_read_buffer(struct dentry * dentry, struct configfs_buffer * buf count = ops->show_attribute(item,attr,buffer->page); buffer->needs_read_fill = 0; - BUG_ON(count > (ssize_t)PAGE_SIZE); + BUG_ON(count > (ssize_t)SIMPLE_ATTR_SIZE); if (count >= 0) buffer->count = count; else @@ -102,7 +109,7 @@ configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *pp struct configfs_buffer * buffer = file->private_data; ssize_t retval = 0; - down(&buffer->sem); + mutex_lock(&buffer->mutex); if (buffer->needs_read_fill) { if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) goto out; @@ -112,7 +119,7 @@ configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *pp retval = simple_read_from_buffer(buf, count, ppos, buffer->page, buffer->count); out: - up(&buffer->sem); + mutex_unlock(&buffer->mutex); return retval; } @@ -137,8 +144,8 @@ fill_write_buffer(struct configfs_buffer * buffer, const char __user * buf, size if (!buffer->page) return -ENOMEM; - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; + if (count >= SIMPLE_ATTR_SIZE) + count = SIMPLE_ATTR_SIZE - 1; error = copy_from_user(buffer->page,buf,count); buffer->needs_read_fill = 1; /* if buf is assumed to contain a string, terminate it by \0, @@ -193,13 +200,13 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof struct configfs_buffer * buffer = file->private_data; ssize_t len; - down(&buffer->sem); + mutex_lock(&buffer->mutex); len = fill_write_buffer(buffer, buf, count); if (len > 0) len = flush_write_buffer(file->f_path.dentry, buffer, count); if (len > 0) *ppos += len; - up(&buffer->sem); + mutex_unlock(&buffer->mutex); return len; } @@ -253,7 +260,7 @@ static int check_perm(struct inode * inode, struct file * file) error = -ENOMEM; goto Enomem; } - init_MUTEX(&buffer->sem); + mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; buffer->ops = ops; file->private_data = buffer; @@ -292,6 +299,7 @@ static int configfs_release(struct inode * inode, struct file * filp) if (buffer) { if (buffer->page) free_page((unsigned long)buffer->page); + mutex_destroy(&buffer->mutex); kfree(buffer); } return 0; diff --git a/fs/configfs/item.c b/fs/configfs/item.c index 2442120..76dc4c3 100644 --- a/fs/configfs/item.c +++ b/fs/configfs/item.c @@ -62,7 +62,6 @@ void config_item_init(struct config_item * item) * dynamically allocated string that @item->ci_name points to. * Otherwise, use the static @item->ci_namebuf array. */ - int config_item_set_name(struct config_item * item, const char * fmt, ...) { int error = 0; @@ -139,12 +138,7 @@ struct config_item * config_item_get(struct config_item * item) return item; } -/** - * config_item_cleanup - free config_item resources. - * @item: item. - */ - -void config_item_cleanup(struct config_item * item) +static void config_item_cleanup(struct config_item * item) { struct config_item_type * t = item->ci_type; struct config_group * s = item->ci_group; @@ -179,39 +173,35 @@ void config_item_put(struct config_item * item) kref_put(&item->ci_kref, config_item_release); } - /** * config_group_init - initialize a group for use * @k: group */ - void config_group_init(struct config_group *group) { config_item_init(&group->cg_item); INIT_LIST_HEAD(&group->cg_children); } - /** - * config_group_find_obj - search for item in group. + * config_group_find_item - search for item in group. * @group: group we're looking in. * @name: item's name. * - * Lock group via @group->cg_subsys, and iterate over @group->cg_list, - * looking for a matching config_item. If matching item is found - * take a reference and return the item. + * Iterate over @group->cg_list, looking for a matching config_item. + * If matching item is found take a reference and return the item. + * Caller must have locked group via @group->cg_subsys->su_mtx. */ - -struct config_item * config_group_find_obj(struct config_group * group, const char * name) +struct config_item *config_group_find_item(struct config_group *group, + const char *name) { struct list_head * entry; struct config_item * ret = NULL; - /* XXX LOCKING! */ list_for_each(entry,&group->cg_children) { struct config_item * item = to_item(entry); if (config_item_name(item) && - !strcmp(config_item_name(item), name)) { + !strcmp(config_item_name(item), name)) { ret = config_item_get(item); break; } @@ -219,9 +209,8 @@ struct config_item * config_group_find_obj(struct config_group * group, const ch return ret; } - EXPORT_SYMBOL(config_item_init); EXPORT_SYMBOL(config_group_init); EXPORT_SYMBOL(config_item_get); EXPORT_SYMBOL(config_item_put); -EXPORT_SYMBOL(config_group_find_obj); +EXPORT_SYMBOL(config_group_find_item); diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index b00d962..871b0cb6 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -136,7 +136,7 @@ static int __init configfs_init(void) configfs_dir_cachep = kmem_cache_create("configfs_dir_cache", sizeof(struct configfs_dirent), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!configfs_dir_cachep) goto out; diff --git a/fs/dcache.c b/fs/dcache.c index 0e73aa0..678d39de 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -883,6 +883,11 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask) return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; } +static struct shrinker dcache_shrinker = { + .shrink = shrink_dcache_memory, + .seeks = DEFAULT_SEEKS, +}; + /** * d_alloc - allocate a dcache entry * @parent: parent of entry to allocate @@ -2115,7 +2120,7 @@ static void __init dcache_init(unsigned long mempages) dentry_cache = KMEM_CACHE(dentry, SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD); - set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory); + register_shrinker(&dcache_shrinker); /* Hash may have been set up in dcache_init_early */ if (!hashdist) @@ -2160,10 +2165,10 @@ void __init vfs_caches_init(unsigned long mempages) mempages -= reserve; names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); + SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, - SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); + SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); dcache_init(mempages); inode_init(mempages); diff --git a/fs/dcookies.c b/fs/dcookies.c index 21af162..c1208f5 100644 --- a/fs/dcookies.c +++ b/fs/dcookies.c @@ -205,7 +205,7 @@ static int dcookie_init(void) dcookie_cache = kmem_cache_create("dcookie_cache", sizeof(struct dcookie_struct), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!dcookie_cache) goto out; diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index ec8896b..11be8a3 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -345,11 +345,6 @@ void debugfs_remove(struct dentry *dentry) switch (dentry->d_inode->i_mode & S_IFMT) { case S_IFDIR: ret = simple_rmdir(parent->d_inode, dentry); - if (ret) - printk(KERN_ERR - "DebugFS rmdir on %s failed : " - "directory not empty.\n", - dentry->d_name.name); break; case S_IFLNK: kfree(dentry->d_inode->i_private); @@ -368,6 +363,69 @@ void debugfs_remove(struct dentry *dentry) } EXPORT_SYMBOL_GPL(debugfs_remove); +/** + * debugfs_rename - rename a file/directory in the debugfs filesystem + * @old_dir: a pointer to the parent dentry for the renamed object. This + * should be a directory dentry. + * @old_dentry: dentry of an object to be renamed. + * @new_dir: a pointer to the parent dentry where the object should be + * moved. This should be a directory dentry. + * @new_name: a pointer to a string containing the target name. + * + * This function renames a file/directory in debugfs. The target must not + * exist for rename to succeed. + * + * This function will return a pointer to old_dentry (which is updated to + * reflect renaming) if it succeeds. If an error occurs, %NULL will be + * returned. + * + * If debugfs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, + struct dentry *new_dir, const char *new_name) +{ + int error; + struct dentry *dentry = NULL, *trap; + const char *old_name; + + trap = lock_rename(new_dir, old_dir); + /* Source or destination directories don't exist? */ + if (!old_dir->d_inode || !new_dir->d_inode) + goto exit; + /* Source does not exist, cyclic rename, or mountpoint? */ + if (!old_dentry->d_inode || old_dentry == trap || + d_mountpoint(old_dentry)) + goto exit; + dentry = lookup_one_len(new_name, new_dir, strlen(new_name)); + /* Lookup failed, cyclic rename or target exists? */ + if (IS_ERR(dentry) || dentry == trap || dentry->d_inode) + goto exit; + + old_name = fsnotify_oldname_init(old_dentry->d_name.name); + + error = simple_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, + dentry); + if (error) { + fsnotify_oldname_free(old_name); + goto exit; + } + d_move(old_dentry, dentry); + fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name, + old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode), + NULL, old_dentry->d_inode); + fsnotify_oldname_free(old_name); + unlock_rename(new_dir, old_dir); + dput(dentry); + return old_dentry; +exit: + if (dentry && !IS_ERR(dentry)) + dput(dentry); + unlock_rename(new_dir, old_dir); + return NULL; +} +EXPORT_SYMBOL_GPL(debugfs_rename); + static decl_subsys(debug, NULL, NULL); static int __init debugfs_init(void) diff --git a/fs/dlm/config.c b/fs/dlm/config.c index 5069b2c..2f8e3c8 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -133,14 +133,6 @@ static ssize_t cluster_set(struct cluster *cl, unsigned int *cl_field, return len; } -#define __CONFIGFS_ATTR(_name,_mode,_read,_write) { \ - .attr = { .ca_name = __stringify(_name), \ - .ca_mode = _mode, \ - .ca_owner = THIS_MODULE }, \ - .show = _read, \ - .store = _write, \ -} - #define CLUSTER_ATTR(name, check_zero) \ static ssize_t name##_write(struct cluster *cl, const char *buf, size_t len) \ { \ @@ -615,7 +607,7 @@ static struct clusters clusters_root = { int dlm_config_init(void) { config_group_init(&clusters_root.subsys.su_group); - init_MUTEX(&clusters_root.subsys.su_sem); + mutex_init(&clusters_root.subsys.su_mutex); return configfs_register_subsystem(&clusters_root.subsys); } @@ -759,9 +751,9 @@ static struct space *get_space(char *name) if (!space_list) return NULL; - down(&space_list->cg_subsys->su_sem); - i = config_group_find_obj(space_list, name); - up(&space_list->cg_subsys->su_sem); + mutex_lock(&space_list->cg_subsys->su_mutex); + i = config_group_find_item(space_list, name); + mutex_unlock(&space_list->cg_subsys->su_mutex); return to_space(i); } @@ -780,7 +772,7 @@ static struct comm *get_comm(int nodeid, struct sockaddr_storage *addr) if (!comm_list) return NULL; - down(&clusters_root.subsys.su_sem); + mutex_lock(&clusters_root.subsys.su_mutex); list_for_each_entry(i, &comm_list->cg_children, ci_entry) { cm = to_comm(i); @@ -800,7 +792,7 @@ static struct comm *get_comm(int nodeid, struct sockaddr_storage *addr) break; } } - up(&clusters_root.subsys.su_sem); + mutex_unlock(&clusters_root.subsys.su_mutex); if (!found) cm = NULL; diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index 0553a61..dd36273 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -1449,7 +1449,7 @@ int dlm_lowcomms_start(void) error = -ENOMEM; con_cache = kmem_cache_create("dlm_conn", sizeof(struct connection), __alignof__(struct connection), 0, - NULL, NULL); + NULL); if (!con_cache) goto out; diff --git a/fs/dlm/memory.c b/fs/dlm/memory.c index f858fef..ecf0e5c 100644 --- a/fs/dlm/memory.c +++ b/fs/dlm/memory.c @@ -23,7 +23,7 @@ int dlm_memory_init(void) int ret = 0; lkb_cache = kmem_cache_create("dlm_lkb", sizeof(struct dlm_lkb), - __alignof__(struct dlm_lkb), 0, NULL, NULL); + __alignof__(struct dlm_lkb), 0, NULL); if (!lkb_cache) ret = -ENOMEM; return ret; @@ -39,9 +39,7 @@ char *allocate_lvb(struct dlm_ls *ls) { char *p; - p = kmalloc(ls->ls_lvblen, GFP_KERNEL); - if (p) - memset(p, 0, ls->ls_lvblen); + p = kzalloc(ls->ls_lvblen, GFP_KERNEL); return p; } @@ -59,9 +57,7 @@ struct dlm_rsb *allocate_rsb(struct dlm_ls *ls, int namelen) DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN,); - r = kmalloc(sizeof(*r) + namelen, GFP_KERNEL); - if (r) - memset(r, 0, sizeof(*r) + namelen); + r = kzalloc(sizeof(*r) + namelen, GFP_KERNEL); return r; } @@ -101,9 +97,7 @@ struct dlm_direntry *allocate_direntry(struct dlm_ls *ls, int namelen) DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN, printk("namelen = %d\n", namelen);); - de = kmalloc(sizeof(*de) + namelen, GFP_KERNEL); - if (de) - memset(de, 0, sizeof(*de) + namelen); + de = kzalloc(sizeof(*de) + namelen, GFP_KERNEL); return de; } diff --git a/fs/dnotify.c b/fs/dnotify.c index 936409f..28d01ed 100644 --- a/fs/dnotify.c +++ b/fs/dnotify.c @@ -176,7 +176,7 @@ EXPORT_SYMBOL_GPL(dnotify_parent); static int __init dnotify_init(void) { dn_cache = kmem_cache_create("dnotify_cache", - sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL, NULL); + sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL); return 0; } @@ -538,6 +538,11 @@ static int shrink_dqcache_memory(int nr, gfp_t gfp_mask) return (dqstats.free_dquots / 100) * sysctl_vfs_cache_pressure; } +static struct shrinker dqcache_shrinker = { + .shrink = shrink_dqcache_memory, + .seeks = DEFAULT_SEEKS, +}; + /* * Put reference to dquot * NOTE: If you change this function please check whether dqput_blocks() works right... @@ -1843,11 +1848,11 @@ static int __init dquot_init(void) register_sysctl_table(sys_table); - dquot_cachep = kmem_cache_create("dquot", + dquot_cachep = kmem_cache_create("dquot", sizeof(struct dquot), sizeof(unsigned long) * 4, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD|SLAB_PANIC), - NULL, NULL); + NULL); order = 0; dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order); @@ -1870,7 +1875,7 @@ static int __init dquot_init(void) printk("Dquot-cache hash table entries: %ld (order %ld, %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order)); - set_shrinker(DEFAULT_SEEKS, shrink_dqcache_memory); + register_shrinker(&dqcache_shrinker); return 0; } diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 03ea769..59375ef 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -20,7 +20,7 @@ static void drop_pagecache_sb(struct super_block *sb) list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { if (inode->i_state & (I_FREEING|I_WILL_FREE)) continue; - invalidate_mapping_pages(inode->i_mapping, 0, -1); + __invalidate_mapping_pages(inode->i_mapping, 0, -1, true); } spin_unlock(&inode_lock); } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 83e94fe..0a50942 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -282,7 +282,7 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, struct dentry *lower_dentry; struct vfsmount *lower_mnt; char *encoded_name; - unsigned int encoded_namelen; + int encoded_namelen; struct ecryptfs_crypt_stat *crypt_stat = NULL; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; char *page_virt = NULL; @@ -473,7 +473,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, struct dentry *lower_dir_dentry; umode_t mode; char *encoded_symname; - unsigned int encoded_symlen; + int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; lower_dentry = ecryptfs_dentry_to_lower(dentry); @@ -902,8 +902,9 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) mutex_lock(&crypt_stat->cs_mutex); if (S_ISDIR(dentry->d_inode->i_mode)) crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); - else if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) - || !(crypt_stat->flags & ECRYPTFS_KEY_VALID)) { + else if (S_ISREG(dentry->d_inode->i_mode) + && (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) + || !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { struct vfsmount *lower_mnt; struct file *lower_file = NULL; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 606128f..e557a67 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -677,7 +677,7 @@ static int ecryptfs_init_kmem_caches(void) info = &ecryptfs_cache_infos[i]; *(info->cache) = kmem_cache_create(info->name, info->size, - 0, SLAB_HWCACHE_ALIGN, info->ctor, NULL); + 0, SLAB_HWCACHE_ALIGN, info->ctor); if (!*(info->cache)) { ecryptfs_free_kmem_caches(); ecryptfs_printk(KERN_WARNING, "%s: " @@ -840,8 +840,6 @@ static int __init ecryptfs_init(void) goto out; } kobj_set_kset_s(&ecryptfs_subsys, fs_subsys); - sysfs_attr_version.attr.owner = THIS_MODULE; - sysfs_attr_version_str.attr.owner = THIS_MODULE; rc = do_sysfs_registration(); if (rc) { printk(KERN_ERR "sysfs registration failed\n"); diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 7d5a43c..e4ab7bc 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -409,8 +409,7 @@ static int ecryptfs_prepare_write(struct file *file, struct page *page, if (!PageUptodate(page)) rc = ecryptfs_do_readpage(file, page, page->index); if (page->index != 0) { - loff_t end_of_prev_pg_pos = - (((loff_t)page->index << PAGE_CACHE_SHIFT) - 1); + loff_t end_of_prev_pg_pos = page_offset(page) - 1; if (end_of_prev_pg_pos > i_size_read(page->mapping->host)) { rc = ecryptfs_truncate(file->f_path.dentry, @@ -736,7 +735,7 @@ static int ecryptfs_commit_write(struct file *file, struct page *page, goto out; } inode->i_blocks = lower_inode->i_blocks; - pos = (page->index << PAGE_CACHE_SHIFT) + to; + pos = page_offset(page) + to; if (pos > i_size_read(inode)) { i_size_write(inode, pos); ecryptfs_printk(KERN_DEBUG, "Expanded file size to " diff --git a/fs/efs/namei.c b/fs/efs/namei.c index ed4a207..5276b19 100644 --- a/fs/efs/namei.c +++ b/fs/efs/namei.c @@ -75,6 +75,38 @@ struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry, struct namei return NULL; } +struct dentry *efs_get_dentry(struct super_block *sb, void *vobjp) +{ + __u32 *objp = vobjp; + unsigned long ino = objp[0]; + __u32 generation = objp[1]; + struct inode *inode; + struct dentry *result; + + if (ino == 0) + return ERR_PTR(-ESTALE); + inode = iget(sb, ino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + + if (is_bad_inode(inode) || + (generation && inode->i_generation != generation)) { + result = ERR_PTR(-ESTALE); + goto out_iput; + } + + result = d_alloc_anon(inode); + if (!result) { + result = ERR_PTR(-ENOMEM); + goto out_iput; + } + return result; + + out_iput: + iput(inode); + return result; +} + struct dentry *efs_get_parent(struct dentry *child) { struct dentry *parent; diff --git a/fs/efs/super.c b/fs/efs/super.c index e0a6839..ce4acb8 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -11,6 +11,7 @@ #include <linux/efs_fs.h> #include <linux/efs_vh.h> #include <linux/efs_fs_sb.h> +#include <linux/exportfs.h> #include <linux/slab.h> #include <linux/buffer_head.h> #include <linux/vfs.h> @@ -74,13 +75,13 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { efs_inode_cachep = kmem_cache_create("efs_inode_cache", sizeof(struct efs_inode_info), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - init_once, NULL); + init_once); if (efs_inode_cachep == NULL) return -ENOMEM; return 0; @@ -113,6 +114,7 @@ static const struct super_operations efs_superblock_operations = { }; static struct export_operations efs_export_ops = { + .get_dentry = efs_get_dentry, .get_parent = efs_get_parent, }; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 0b73cd4..77b9953 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1324,12 +1324,12 @@ static int __init eventpoll_init(void) /* Allocates slab cache used to allocate "struct epitem" items */ epi_cache = kmem_cache_create("eventpoll_epi", sizeof(struct epitem), 0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC, - NULL, NULL); + NULL); /* Allocates slab cache used to allocate "struct eppoll_entry" */ pwq_cache = kmem_cache_create("eventpoll_pwq", sizeof(struct eppoll_entry), 0, - EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL); + EPI_SLAB_DEBUG|SLAB_PANIC, NULL); return 0; } @@ -54,6 +54,7 @@ #include <asm/uaccess.h> #include <asm/mmu_context.h> +#include <asm/tlb.h> #ifdef CONFIG_KMOD #include <linux/kmod.h> @@ -178,6 +179,207 @@ exit: goto out; } +#ifdef CONFIG_MMU + +static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, + int write) +{ + struct page *page; + int ret; + +#ifdef CONFIG_STACK_GROWSUP + if (write) { + ret = expand_stack_downwards(bprm->vma, pos); + if (ret < 0) + return NULL; + } +#endif + ret = get_user_pages(current, bprm->mm, pos, + 1, write, 1, &page, NULL); + if (ret <= 0) + return NULL; + + if (write) { + struct rlimit *rlim = current->signal->rlim; + unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start; + + /* + * Limit to 1/4-th the stack size for the argv+env strings. + * This ensures that: + * - the remaining binfmt code will not run out of stack space, + * - the program will have a reasonable amount of stack left + * to work from. + */ + if (size > rlim[RLIMIT_STACK].rlim_cur / 4) { + put_page(page); + return NULL; + } + } + + return page; +} + +static void put_arg_page(struct page *page) +{ + put_page(page); +} + +static void free_arg_page(struct linux_binprm *bprm, int i) +{ +} + +static void free_arg_pages(struct linux_binprm *bprm) +{ +} + +static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos, + struct page *page) +{ + flush_cache_page(bprm->vma, pos, page_to_pfn(page)); +} + +static int __bprm_mm_init(struct linux_binprm *bprm) +{ + int err = -ENOMEM; + struct vm_area_struct *vma = NULL; + struct mm_struct *mm = bprm->mm; + + bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); + if (!vma) + goto err; + + down_write(&mm->mmap_sem); + vma->vm_mm = mm; + + /* + * Place the stack at the largest stack address the architecture + * supports. Later, we'll move this to an appropriate place. We don't + * use STACK_TOP because that can depend on attributes which aren't + * configured yet. + */ + vma->vm_end = STACK_TOP_MAX; + vma->vm_start = vma->vm_end - PAGE_SIZE; + + vma->vm_flags = VM_STACK_FLAGS; + vma->vm_page_prot = protection_map[vma->vm_flags & 0x7]; + err = insert_vm_struct(mm, vma); + if (err) { + up_write(&mm->mmap_sem); + goto err; + } + + mm->stack_vm = mm->total_vm = 1; + up_write(&mm->mmap_sem); + + bprm->p = vma->vm_end - sizeof(void *); + + return 0; + +err: + if (vma) { + bprm->vma = NULL; + kmem_cache_free(vm_area_cachep, vma); + } + + return err; +} + +static bool valid_arg_len(struct linux_binprm *bprm, long len) +{ + return len <= MAX_ARG_STRLEN; +} + +#else + +static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, + int write) +{ + struct page *page; + + page = bprm->page[pos / PAGE_SIZE]; + if (!page && write) { + page = alloc_page(GFP_HIGHUSER|__GFP_ZERO); + if (!page) + return NULL; + bprm->page[pos / PAGE_SIZE] = page; + } + + return page; +} + +static void put_arg_page(struct page *page) +{ +} + +static void free_arg_page(struct linux_binprm *bprm, int i) +{ + if (bprm->page[i]) { + __free_page(bprm->page[i]); + bprm->page[i] = NULL; + } +} + +static void free_arg_pages(struct linux_binprm *bprm) +{ + int i; + + for (i = 0; i < MAX_ARG_PAGES; i++) + free_arg_page(bprm, i); +} + +static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos, + struct page *page) +{ +} + +static int __bprm_mm_init(struct linux_binprm *bprm) +{ + bprm->p = PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *); + return 0; +} + +static bool valid_arg_len(struct linux_binprm *bprm, long len) +{ + return len <= bprm->p; +} + +#endif /* CONFIG_MMU */ + +/* + * Create a new mm_struct and populate it with a temporary stack + * vm_area_struct. We don't have enough context at this point to set the stack + * flags, permissions, and offset, so we use temporary values. We'll update + * them later in setup_arg_pages(). + */ +int bprm_mm_init(struct linux_binprm *bprm) +{ + int err; + struct mm_struct *mm = NULL; + + bprm->mm = mm = mm_alloc(); + err = -ENOMEM; + if (!mm) + goto err; + + err = init_new_context(current, mm); + if (err) + goto err; + + err = __bprm_mm_init(bprm); + if (err) + goto err; + + return 0; + +err: + if (mm) { + bprm->mm = NULL; + mmdrop(mm); + } + + return err; +} + /* * count() counts the number of strings in array ARGV. */ @@ -203,15 +405,16 @@ static int count(char __user * __user * argv, int max) } /* - * 'copy_strings()' copies argument/environment strings from user - * memory to free pages in kernel mem. These are in a format ready - * to be put directly into the top of new user memory. + * 'copy_strings()' copies argument/environment strings from the old + * processes's memory to the new process's stack. The call to get_user_pages() + * ensures the destination page is created and not swapped out. */ static int copy_strings(int argc, char __user * __user * argv, struct linux_binprm *bprm) { struct page *kmapped_page = NULL; char *kaddr = NULL; + unsigned long kpos = 0; int ret; while (argc-- > 0) { @@ -220,69 +423,69 @@ static int copy_strings(int argc, char __user * __user * argv, unsigned long pos; if (get_user(str, argv+argc) || - !(len = strnlen_user(str, bprm->p))) { + !(len = strnlen_user(str, MAX_ARG_STRLEN))) { ret = -EFAULT; goto out; } - if (bprm->p < len) { + if (!valid_arg_len(bprm, len)) { ret = -E2BIG; goto out; } - bprm->p -= len; - /* XXX: add architecture specific overflow check here. */ + /* We're going to work our way backwords. */ pos = bprm->p; + str += len; + bprm->p -= len; while (len > 0) { - int i, new, err; int offset, bytes_to_copy; - struct page *page; offset = pos % PAGE_SIZE; - i = pos/PAGE_SIZE; - page = bprm->page[i]; - new = 0; - if (!page) { - page = alloc_page(GFP_HIGHUSER); - bprm->page[i] = page; + if (offset == 0) + offset = PAGE_SIZE; + + bytes_to_copy = offset; + if (bytes_to_copy > len) + bytes_to_copy = len; + + offset -= bytes_to_copy; + pos -= bytes_to_copy; + str -= bytes_to_copy; + len -= bytes_to_copy; + + if (!kmapped_page || kpos != (pos & PAGE_MASK)) { + struct page *page; + + page = get_arg_page(bprm, pos, 1); if (!page) { - ret = -ENOMEM; + ret = -E2BIG; goto out; } - new = 1; - } - if (page != kmapped_page) { - if (kmapped_page) + if (kmapped_page) { + flush_kernel_dcache_page(kmapped_page); kunmap(kmapped_page); + put_arg_page(kmapped_page); + } kmapped_page = page; kaddr = kmap(kmapped_page); + kpos = pos & PAGE_MASK; + flush_arg_page(bprm, kpos, kmapped_page); } - if (new && offset) - memset(kaddr, 0, offset); - bytes_to_copy = PAGE_SIZE - offset; - if (bytes_to_copy > len) { - bytes_to_copy = len; - if (new) - memset(kaddr+offset+len, 0, - PAGE_SIZE-offset-len); - } - err = copy_from_user(kaddr+offset, str, bytes_to_copy); - if (err) { + if (copy_from_user(kaddr+offset, str, bytes_to_copy)) { ret = -EFAULT; goto out; } - - pos += bytes_to_copy; - str += bytes_to_copy; - len -= bytes_to_copy; } } ret = 0; out: - if (kmapped_page) + if (kmapped_page) { + flush_kernel_dcache_page(kmapped_page); kunmap(kmapped_page); + put_arg_page(kmapped_page); + } return ret; } @@ -298,181 +501,172 @@ int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm) set_fs(oldfs); return r; } - EXPORT_SYMBOL(copy_strings_kernel); #ifdef CONFIG_MMU + /* - * This routine is used to map in a page into an address space: needed by - * execve() for the initial stack and environment pages. + * During bprm_mm_init(), we create a temporary stack at STACK_TOP_MAX. Once + * the binfmt code determines where the new stack should reside, we shift it to + * its final location. The process proceeds as follows: * - * vma->vm_mm->mmap_sem is held for writing. + * 1) Use shift to calculate the new vma endpoints. + * 2) Extend vma to cover both the old and new ranges. This ensures the + * arguments passed to subsequent functions are consistent. + * 3) Move vma's page tables to the new range. + * 4) Free up any cleared pgd range. + * 5) Shrink the vma to cover only the new range. */ -void install_arg_page(struct vm_area_struct *vma, - struct page *page, unsigned long address) +static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) { struct mm_struct *mm = vma->vm_mm; - pte_t * pte; - spinlock_t *ptl; + unsigned long old_start = vma->vm_start; + unsigned long old_end = vma->vm_end; + unsigned long length = old_end - old_start; + unsigned long new_start = old_start - shift; + unsigned long new_end = old_end - shift; + struct mmu_gather *tlb; - if (unlikely(anon_vma_prepare(vma))) - goto out; + BUG_ON(new_start > new_end); - flush_dcache_page(page); - pte = get_locked_pte(mm, address, &ptl); - if (!pte) - goto out; - if (!pte_none(*pte)) { - pte_unmap_unlock(pte, ptl); - goto out; + /* + * ensure there are no vmas between where we want to go + * and where we are + */ + if (vma != find_vma(mm, new_start)) + return -EFAULT; + + /* + * cover the whole range: [new_start, old_end) + */ + vma_adjust(vma, new_start, old_end, vma->vm_pgoff, NULL); + + /* + * move the page tables downwards, on failure we rely on + * process cleanup to remove whatever mess we made. + */ + if (length != move_page_tables(vma, old_start, + vma, new_start, length)) + return -ENOMEM; + + lru_add_drain(); + tlb = tlb_gather_mmu(mm, 0); + if (new_end > old_start) { + /* + * when the old and new regions overlap clear from new_end. + */ + free_pgd_range(&tlb, new_end, old_end, new_end, + vma->vm_next ? vma->vm_next->vm_start : 0); + } else { + /* + * otherwise, clean from old_start; this is done to not touch + * the address space in [new_end, old_start) some architectures + * have constraints on va-space that make this illegal (IA64) - + * for the others its just a little faster. + */ + free_pgd_range(&tlb, old_start, old_end, new_end, + vma->vm_next ? vma->vm_next->vm_start : 0); } - inc_mm_counter(mm, anon_rss); - lru_cache_add_active(page); - set_pte_at(mm, address, pte, pte_mkdirty(pte_mkwrite(mk_pte( - page, vma->vm_page_prot)))); - page_add_new_anon_rmap(page, vma, address); - pte_unmap_unlock(pte, ptl); - - /* no need for flush_tlb */ - return; -out: - __free_page(page); - force_sig(SIGKILL, current); + tlb_finish_mmu(tlb, new_end, old_end); + + /* + * shrink the vma to just the new range. + */ + vma_adjust(vma, new_start, new_end, vma->vm_pgoff, NULL); + + return 0; } #define EXTRA_STACK_VM_PAGES 20 /* random */ +/* + * Finalizes the stack vm_area_struct. The flags and permissions are updated, + * the stack is optionally relocated, and some extra space is added. + */ int setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, int executable_stack) { - unsigned long stack_base; - struct vm_area_struct *mpnt; + unsigned long ret; + unsigned long stack_shift; struct mm_struct *mm = current->mm; - int i, ret; - long arg_size; + struct vm_area_struct *vma = bprm->vma; + struct vm_area_struct *prev = NULL; + unsigned long vm_flags; + unsigned long stack_base; #ifdef CONFIG_STACK_GROWSUP - /* Move the argument and environment strings to the bottom of the - * stack space. - */ - int offset, j; - char *to, *from; - - /* Start by shifting all the pages down */ - i = 0; - for (j = 0; j < MAX_ARG_PAGES; j++) { - struct page *page = bprm->page[j]; - if (!page) - continue; - bprm->page[i++] = page; - } - - /* Now move them within their pages */ - offset = bprm->p % PAGE_SIZE; - to = kmap(bprm->page[0]); - for (j = 1; j < i; j++) { - memmove(to, to + offset, PAGE_SIZE - offset); - from = kmap(bprm->page[j]); - memcpy(to + PAGE_SIZE - offset, from, offset); - kunmap(bprm->page[j - 1]); - to = from; - } - memmove(to, to + offset, PAGE_SIZE - offset); - kunmap(bprm->page[j - 1]); - /* Limit stack size to 1GB */ stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max; if (stack_base > (1 << 30)) stack_base = 1 << 30; - stack_base = PAGE_ALIGN(stack_top - stack_base); - /* Adjust bprm->p to point to the end of the strings. */ - bprm->p = stack_base + PAGE_SIZE * i - offset; + /* Make sure we didn't let the argument array grow too large. */ + if (vma->vm_end - vma->vm_start > stack_base) + return -ENOMEM; - mm->arg_start = stack_base; - arg_size = i << PAGE_SHIFT; + stack_base = PAGE_ALIGN(stack_top - stack_base); - /* zero pages that were copied above */ - while (i < MAX_ARG_PAGES) - bprm->page[i++] = NULL; + stack_shift = vma->vm_start - stack_base; + mm->arg_start = bprm->p - stack_shift; + bprm->p = vma->vm_end - stack_shift; #else - stack_base = arch_align_stack(stack_top - MAX_ARG_PAGES*PAGE_SIZE); - stack_base = PAGE_ALIGN(stack_base); - bprm->p += stack_base; + stack_top = arch_align_stack(stack_top); + stack_top = PAGE_ALIGN(stack_top); + stack_shift = vma->vm_end - stack_top; + + bprm->p -= stack_shift; mm->arg_start = bprm->p; - arg_size = stack_top - (PAGE_MASK & (unsigned long) mm->arg_start); #endif - arg_size += EXTRA_STACK_VM_PAGES * PAGE_SIZE; - if (bprm->loader) - bprm->loader += stack_base; - bprm->exec += stack_base; - - mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); - if (!mpnt) - return -ENOMEM; + bprm->loader -= stack_shift; + bprm->exec -= stack_shift; down_write(&mm->mmap_sem); - { - mpnt->vm_mm = mm; -#ifdef CONFIG_STACK_GROWSUP - mpnt->vm_start = stack_base; - mpnt->vm_end = stack_base + arg_size; -#else - mpnt->vm_end = stack_top; - mpnt->vm_start = mpnt->vm_end - arg_size; -#endif - /* Adjust stack execute permissions; explicitly enable - * for EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X - * and leave alone (arch default) otherwise. */ - if (unlikely(executable_stack == EXSTACK_ENABLE_X)) - mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC; - else if (executable_stack == EXSTACK_DISABLE_X) - mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC; - else - mpnt->vm_flags = VM_STACK_FLAGS; - mpnt->vm_flags |= mm->def_flags; - mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7]; - if ((ret = insert_vm_struct(mm, mpnt))) { + vm_flags = vma->vm_flags; + + /* + * Adjust stack execute permissions; explicitly enable for + * EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X and leave alone + * (arch default) otherwise. + */ + if (unlikely(executable_stack == EXSTACK_ENABLE_X)) + vm_flags |= VM_EXEC; + else if (executable_stack == EXSTACK_DISABLE_X) + vm_flags &= ~VM_EXEC; + vm_flags |= mm->def_flags; + + ret = mprotect_fixup(vma, &prev, vma->vm_start, vma->vm_end, + vm_flags); + if (ret) + goto out_unlock; + BUG_ON(prev != vma); + + /* Move stack pages down in memory. */ + if (stack_shift) { + ret = shift_arg_pages(vma, stack_shift); + if (ret) { up_write(&mm->mmap_sem); - kmem_cache_free(vm_area_cachep, mpnt); return ret; } - mm->stack_vm = mm->total_vm = vma_pages(mpnt); } - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page *page = bprm->page[i]; - if (page) { - bprm->page[i] = NULL; - install_arg_page(mpnt, page, stack_base); - } - stack_base += PAGE_SIZE; - } +#ifdef CONFIG_STACK_GROWSUP + stack_base = vma->vm_end + EXTRA_STACK_VM_PAGES * PAGE_SIZE; +#else + stack_base = vma->vm_start - EXTRA_STACK_VM_PAGES * PAGE_SIZE; +#endif + ret = expand_stack(vma, stack_base); + if (ret) + ret = -EFAULT; + +out_unlock: up_write(&mm->mmap_sem); - return 0; } - EXPORT_SYMBOL(setup_arg_pages); -#define free_arg_pages(bprm) do { } while (0) - -#else - -static inline void free_arg_pages(struct linux_binprm *bprm) -{ - int i; - - for (i = 0; i < MAX_ARG_PAGES; i++) { - if (bprm->page[i]) - __free_page(bprm->page[i]); - bprm->page[i] = NULL; - } -} - #endif /* CONFIG_MMU */ struct file *open_exec(const char *name) @@ -864,9 +1058,9 @@ int flush_old_exec(struct linux_binprm * bprm) current->sas_ss_sp = current->sas_ss_size = 0; if (current->euid == current->uid && current->egid == current->gid) - current->mm->dumpable = 1; + set_dumpable(current->mm, 1); else - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); name = bprm->filename; @@ -894,7 +1088,7 @@ int flush_old_exec(struct linux_binprm * bprm) file_permission(bprm->file, MAY_READ) || (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { suid_keys(current); - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); } /* An exec changes our domain. We are no longer part of the thread @@ -1000,43 +1194,42 @@ EXPORT_SYMBOL(compute_creds); * points to; chop off the first by relocating brpm->p to right after * the first '\0' encountered. */ -void remove_arg_zero(struct linux_binprm *bprm) +int remove_arg_zero(struct linux_binprm *bprm) { - if (bprm->argc) { - char ch; + int ret = 0; + unsigned long offset; + char *kaddr; + struct page *page; - do { - unsigned long offset; - unsigned long index; - char *kaddr; - struct page *page; - - offset = bprm->p & ~PAGE_MASK; - index = bprm->p >> PAGE_SHIFT; + if (!bprm->argc) + return 0; - page = bprm->page[index]; - kaddr = kmap_atomic(page, KM_USER0); + do { + offset = bprm->p & ~PAGE_MASK; + page = get_arg_page(bprm, bprm->p, 0); + if (!page) { + ret = -EFAULT; + goto out; + } + kaddr = kmap_atomic(page, KM_USER0); - /* run through page until we reach end or find NUL */ - do { - ch = *(kaddr + offset); + for (; offset < PAGE_SIZE && kaddr[offset]; + offset++, bprm->p++) + ; - /* discard that character... */ - bprm->p++; - offset++; - } while (offset < PAGE_SIZE && ch != '\0'); + kunmap_atomic(kaddr, KM_USER0); + put_arg_page(page); - kunmap_atomic(kaddr, KM_USER0); + if (offset == PAGE_SIZE) + free_arg_page(bprm, (bprm->p >> PAGE_SHIFT) - 1); + } while (offset == PAGE_SIZE); - /* free the old page */ - if (offset == PAGE_SIZE) { - __free_page(page); - bprm->page[index] = NULL; - } - } while (ch != '\0'); + bprm->p++; + bprm->argc--; + ret = 0; - bprm->argc--; - } +out: + return ret; } EXPORT_SYMBOL(remove_arg_zero); @@ -1062,7 +1255,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) fput(bprm->file); bprm->file = NULL; - loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + loader = bprm->vma->vm_end - sizeof(void *); file = open_exec("/sbin/loader"); retval = PTR_ERR(file); @@ -1154,8 +1347,8 @@ int do_execve(char * filename, { struct linux_binprm *bprm; struct file *file; + unsigned long env_p; int retval; - int i; retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); @@ -1169,25 +1362,19 @@ int do_execve(char * filename, sched_exec(); - bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - bprm->file = file; bprm->filename = filename; bprm->interp = filename; - bprm->mm = mm_alloc(); - retval = -ENOMEM; - if (!bprm->mm) - goto out_file; - retval = init_new_context(current, bprm->mm); - if (retval < 0) - goto out_mm; + retval = bprm_mm_init(bprm); + if (retval) + goto out_file; - bprm->argc = count(argv, bprm->p / sizeof(void *)); + bprm->argc = count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0) goto out_mm; - bprm->envc = count(envp, bprm->p / sizeof(void *)); + bprm->envc = count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0) goto out_mm; @@ -1208,15 +1395,16 @@ int do_execve(char * filename, if (retval < 0) goto out; + env_p = bprm->p; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out; + bprm->argv_len = env_p - bprm->p; retval = search_binary_handler(bprm,regs); if (retval >= 0) { - free_arg_pages(bprm); - /* execve success */ + free_arg_pages(bprm); security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); @@ -1224,26 +1412,19 @@ int do_execve(char * filename, } out: - /* Something went wrong, return the inode and free the argument pages*/ - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page * page = bprm->page[i]; - if (page) - __free_page(page); - } - + free_arg_pages(bprm); if (bprm->security) security_bprm_free(bprm); out_mm: if (bprm->mm) - mmdrop(bprm->mm); + mmput (bprm->mm); out_file: if (bprm->file) { allow_write_access(bprm->file); fput(bprm->file); } - out_kfree: kfree(bprm); @@ -1484,6 +1665,56 @@ fail: return core_waiters; } +/* + * set_dumpable converts traditional three-value dumpable to two flags and + * stores them into mm->flags. It modifies lower two bits of mm->flags, but + * these bits are not changed atomically. So get_dumpable can observe the + * intermediate state. To avoid doing unexpected behavior, get get_dumpable + * return either old dumpable or new one by paying attention to the order of + * modifying the bits. + * + * dumpable | mm->flags (binary) + * old new | initial interim final + * ---------+----------------------- + * 0 1 | 00 01 01 + * 0 2 | 00 10(*) 11 + * 1 0 | 01 00 00 + * 1 2 | 01 11 11 + * 2 0 | 11 10(*) 00 + * 2 1 | 11 11 01 + * + * (*) get_dumpable regards interim value of 10 as 11. + */ +void set_dumpable(struct mm_struct *mm, int value) +{ + switch (value) { + case 0: + clear_bit(MMF_DUMPABLE, &mm->flags); + smp_wmb(); + clear_bit(MMF_DUMP_SECURELY, &mm->flags); + break; + case 1: + set_bit(MMF_DUMPABLE, &mm->flags); + smp_wmb(); + clear_bit(MMF_DUMP_SECURELY, &mm->flags); + break; + case 2: + set_bit(MMF_DUMP_SECURELY, &mm->flags); + smp_wmb(); + set_bit(MMF_DUMPABLE, &mm->flags); + break; + } +} +EXPORT_SYMBOL_GPL(set_dumpable); + +int get_dumpable(struct mm_struct *mm) +{ + int ret; + + ret = mm->flags & 0x3; + return (ret >= 2) ? 2 : ret; +} + int do_coredump(long signr, int exit_code, struct pt_regs * regs) { char corename[CORENAME_MAX_SIZE + 1]; @@ -1502,7 +1733,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!binfmt || !binfmt->core_dump) goto fail; down_write(&mm->mmap_sem); - if (!mm->dumpable) { + if (!get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } @@ -1512,11 +1743,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) * process nor do we know its entire history. We only know it * was tainted so we dump it as root in mode 2. */ - if (mm->dumpable == 2) { /* Setuid core dump mode */ + if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ flag = O_EXCL; /* Stop rewrite attacks */ current->fsuid = 0; /* Dump root private */ } - mm->dumpable = 0; + set_dumpable(mm, 0); retval = coredump_wait(exit_code); if (retval < 0) diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index e98f6cd..8adb32a 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -1,15 +1,45 @@ +#include <linux/exportfs.h> #include <linux/fs.h> #include <linux/file.h> #include <linux/module.h> +#include <linux/mount.h> #include <linux/namei.h> -struct export_operations export_op_default; +#define dprintk(fmt, args...) do{}while(0) -#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) -#define dprintk(fmt, args...) do{}while(0) +static int get_name(struct dentry *dentry, char *name, + struct dentry *child); + + +static struct dentry *exportfs_get_dentry(struct super_block *sb, void *obj) +{ + struct dentry *result = ERR_PTR(-ESTALE); + + if (sb->s_export_op->get_dentry) { + result = sb->s_export_op->get_dentry(sb, obj); + if (!result) + result = ERR_PTR(-ESTALE); + } + + return result; +} + +static int exportfs_get_name(struct dentry *dir, char *name, + struct dentry *child) +{ + struct export_operations *nop = dir->d_sb->s_export_op; + if (nop->get_name) + return nop->get_name(dir, name, child); + else + return get_name(dir, name, child); +} + +/* + * Check if the dentry or any of it's aliases is acceptable. + */ static struct dentry * find_acceptable_alias(struct dentry *result, int (*acceptable)(void *context, struct dentry *dentry), @@ -17,6 +47,9 @@ find_acceptable_alias(struct dentry *result, { struct dentry *dentry, *toput = NULL; + if (acceptable(context, result)) + return result; + spin_lock(&dcache_lock); list_for_each_entry(dentry, &result->d_inode->i_dentry, d_alias) { dget_locked(dentry); @@ -37,130 +70,50 @@ find_acceptable_alias(struct dentry *result, return NULL; } -/** - * find_exported_dentry - helper routine to implement export_operations->decode_fh - * @sb: The &super_block identifying the filesystem - * @obj: An opaque identifier of the object to be found - passed to - * get_inode - * @parent: An optional opqaue identifier of the parent of the object. - * @acceptable: A function used to test possible &dentries to see if they are - * acceptable - * @context: A parameter to @acceptable so that it knows on what basis to - * judge. - * - * find_exported_dentry is the central helper routine to enable file systems - * to provide the decode_fh() export_operation. It's main task is to take - * an &inode, find or create an appropriate &dentry structure, and possibly - * splice this into the dcache in the correct place. - * - * The decode_fh() operation provided by the filesystem should call - * find_exported_dentry() with the same parameters that it received except - * that instead of the file handle fragment, pointers to opaque identifiers - * for the object and optionally its parent are passed. The default decode_fh - * routine passes one pointer to the start of the filehandle fragment, and - * one 8 bytes into the fragment. It is expected that most filesystems will - * take this approach, though the offset to the parent identifier may well be - * different. - * - * find_exported_dentry() will call get_dentry to get an dentry pointer from - * the file system. If any &dentry in the d_alias list is acceptable, it will - * be returned. Otherwise find_exported_dentry() will attempt to splice a new - * &dentry into the dcache using get_name() and get_parent() to find the - * appropriate place. +/* + * Find root of a disconnected subtree and return a reference to it. */ - -struct dentry * -find_exported_dentry(struct super_block *sb, void *obj, void *parent, - int (*acceptable)(void *context, struct dentry *de), - void *context) +static struct dentry * +find_disconnected_root(struct dentry *dentry) { - struct dentry *result = NULL; - struct dentry *target_dir; - int err; - struct export_operations *nops = sb->s_export_op; - struct dentry *alias; - int noprogress; - char nbuf[NAME_MAX+1]; - - /* - * Attempt to find the inode. - */ - result = CALL(sb->s_export_op,get_dentry)(sb,obj); - err = -ESTALE; - if (result == NULL) - goto err_out; - if (IS_ERR(result)) { - err = PTR_ERR(result); - goto err_out; + dget(dentry); + spin_lock(&dentry->d_lock); + while (!IS_ROOT(dentry) && + (dentry->d_parent->d_flags & DCACHE_DISCONNECTED)) { + struct dentry *parent = dentry->d_parent; + dget(parent); + spin_unlock(&dentry->d_lock); + dput(dentry); + dentry = parent; + spin_lock(&dentry->d_lock); } - if (S_ISDIR(result->d_inode->i_mode) && - (result->d_flags & DCACHE_DISCONNECTED)) { - /* it is an unconnected directory, we must connect it */ - ; - } else { - if (acceptable(context, result)) - return result; - if (S_ISDIR(result->d_inode->i_mode)) { - err = -EACCES; - goto err_result; - } + spin_unlock(&dentry->d_lock); + return dentry; +} - alias = find_acceptable_alias(result, acceptable, context); - if (alias) - return alias; - } - - /* It's a directory, or we are required to confirm the file's - * location in the tree based on the parent information - */ - dprintk("find_exported_dentry: need to look harder for %s/%d\n",sb->s_id,*(int*)obj); - if (S_ISDIR(result->d_inode->i_mode)) - target_dir = dget(result); - else { - if (parent == NULL) - goto err_result; - target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent); - if (IS_ERR(target_dir)) - err = PTR_ERR(target_dir); - if (target_dir == NULL || IS_ERR(target_dir)) - goto err_result; - } - /* - * Now we need to make sure that target_dir is properly connected. - * It may already be, as the flag isn't always updated when connection - * happens. - * So, we walk up parent links until we find a connected directory, - * or we run out of directories. Then we find the parent, find - * the name of the child in that parent, and do a lookup. - * This should connect the child into the parent - * We then repeat. - */ +/* + * Make sure target_dir is fully connected to the dentry tree. + * + * It may already be, as the flag isn't always updated when connection happens. + */ +static int +reconnect_path(struct super_block *sb, struct dentry *target_dir) +{ + char nbuf[NAME_MAX+1]; + int noprogress = 0; + int err = -ESTALE; - /* it is possible that a confused file system might not let us complete + /* + * It is possible that a confused file system might not let us complete * the path to the root. For example, if get_parent returns a directory * in which we cannot find a name for the child. While this implies a * very sick filesystem we don't want it to cause knfsd to spin. Hence * the noprogress counter. If we go through the loop 10 times (2 is * probably enough) without getting anywhere, we just give up */ - noprogress= 0; while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) { - struct dentry *pd = target_dir; - - dget(pd); - spin_lock(&pd->d_lock); - while (!IS_ROOT(pd) && - (pd->d_parent->d_flags&DCACHE_DISCONNECTED)) { - struct dentry *parent = pd->d_parent; - - dget(parent); - spin_unlock(&pd->d_lock); - dput(pd); - pd = parent; - spin_lock(&pd->d_lock); - } - spin_unlock(&pd->d_lock); + struct dentry *pd = find_disconnected_root(target_dir); if (!IS_ROOT(pd)) { /* must have found a connected parent - great */ @@ -175,29 +128,40 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, spin_unlock(&pd->d_lock); noprogress = 0; } else { - /* we have hit the top of a disconnected path. Try - * to find parent and connect - * note: racing with some other process renaming a - * directory isn't much of a problem here. If someone - * renames the directory, it will end up properly - * connected, which is what we want + /* + * We have hit the top of a disconnected path, try to + * find parent and connect. + * + * Racing with some other process renaming a directory + * isn't much of a problem here. If someone renames + * the directory, it will end up properly connected, + * which is what we want + * + * Getting the parent can't be supported generically, + * the locking is too icky. + * + * Instead we just return EACCES. If server reboots + * or inodes get flushed, you lose */ - struct dentry *ppd; + struct dentry *ppd = ERR_PTR(-EACCES); struct dentry *npd; mutex_lock(&pd->d_inode->i_mutex); - ppd = CALL(nops,get_parent)(pd); + if (sb->s_export_op->get_parent) + ppd = sb->s_export_op->get_parent(pd); mutex_unlock(&pd->d_inode->i_mutex); if (IS_ERR(ppd)) { err = PTR_ERR(ppd); - dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n", - pd->d_inode->i_ino, err); + dprintk("%s: get_parent of %ld failed, err %d\n", + __FUNCTION__, pd->d_inode->i_ino, err); dput(pd); break; } - dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino); - err = CALL(nops,get_name)(ppd, nbuf, pd); + + dprintk("%s: find name of %lu in %lu\n", __FUNCTION__, + pd->d_inode->i_ino, ppd->d_inode->i_ino); + err = exportfs_get_name(ppd, nbuf, pd); if (err) { dput(ppd); dput(pd); @@ -208,13 +172,14 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, continue; break; } - dprintk("find_exported_dentry: found name: %s\n", nbuf); + dprintk("%s: found name: %s\n", __FUNCTION__, nbuf); mutex_lock(&ppd->d_inode->i_mutex); npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); mutex_unlock(&ppd->d_inode->i_mutex); if (IS_ERR(npd)) { err = PTR_ERR(npd); - dprintk("find_exported_dentry: lookup failed: %d\n", err); + dprintk("%s: lookup failed: %d\n", + __FUNCTION__, err); dput(ppd); dput(pd); break; @@ -227,7 +192,7 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, if (npd == pd) noprogress = 0; else - printk("find_exported_dentry: npd != pd\n"); + printk("%s: npd != pd\n", __FUNCTION__); dput(npd); dput(ppd); if (IS_ROOT(pd)) { @@ -243,15 +208,101 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, /* something went wrong - oh-well */ if (!err) err = -ESTALE; - goto err_target; + return err; } - /* if we weren't after a directory, have one more step to go */ - if (result != target_dir) { - struct dentry *nresult; - err = CALL(nops,get_name)(target_dir, nbuf, result); + + return 0; +} + +/** + * find_exported_dentry - helper routine to implement export_operations->decode_fh + * @sb: The &super_block identifying the filesystem + * @obj: An opaque identifier of the object to be found - passed to + * get_inode + * @parent: An optional opqaue identifier of the parent of the object. + * @acceptable: A function used to test possible &dentries to see if they are + * acceptable + * @context: A parameter to @acceptable so that it knows on what basis to + * judge. + * + * find_exported_dentry is the central helper routine to enable file systems + * to provide the decode_fh() export_operation. It's main task is to take + * an &inode, find or create an appropriate &dentry structure, and possibly + * splice this into the dcache in the correct place. + * + * The decode_fh() operation provided by the filesystem should call + * find_exported_dentry() with the same parameters that it received except + * that instead of the file handle fragment, pointers to opaque identifiers + * for the object and optionally its parent are passed. The default decode_fh + * routine passes one pointer to the start of the filehandle fragment, and + * one 8 bytes into the fragment. It is expected that most filesystems will + * take this approach, though the offset to the parent identifier may well be + * different. + * + * find_exported_dentry() will call get_dentry to get an dentry pointer from + * the file system. If any &dentry in the d_alias list is acceptable, it will + * be returned. Otherwise find_exported_dentry() will attempt to splice a new + * &dentry into the dcache using get_name() and get_parent() to find the + * appropriate place. + */ + +struct dentry * +find_exported_dentry(struct super_block *sb, void *obj, void *parent, + int (*acceptable)(void *context, struct dentry *de), + void *context) +{ + struct dentry *result, *alias; + int err = -ESTALE; + + /* + * Attempt to find the inode. + */ + result = exportfs_get_dentry(sb, obj); + if (IS_ERR(result)) + return result; + + if (S_ISDIR(result->d_inode->i_mode)) { + if (!(result->d_flags & DCACHE_DISCONNECTED)) { + if (acceptable(context, result)) + return result; + err = -EACCES; + goto err_result; + } + + err = reconnect_path(sb, result); + if (err) + goto err_result; + } else { + struct dentry *target_dir, *nresult; + char nbuf[NAME_MAX+1]; + + alias = find_acceptable_alias(result, acceptable, context); + if (alias) + return alias; + + if (parent == NULL) + goto err_result; + + target_dir = exportfs_get_dentry(sb,parent); + if (IS_ERR(target_dir)) { + err = PTR_ERR(target_dir); + goto err_result; + } + + err = reconnect_path(sb, target_dir); + if (err) { + dput(target_dir); + goto err_result; + } + + /* + * As we weren't after a directory, have one more step to go. + */ + err = exportfs_get_name(target_dir, nbuf, result); if (!err) { mutex_lock(&target_dir->d_inode->i_mutex); - nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); + nresult = lookup_one_len(nbuf, target_dir, + strlen(nbuf)); mutex_unlock(&target_dir->d_inode->i_mutex); if (!IS_ERR(nresult)) { if (nresult->d_inode) { @@ -261,11 +312,8 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, dput(nresult); } } + dput(target_dir); } - dput(target_dir); - /* now result is properly connected, it is our best bet */ - if (acceptable(context, result)) - return result; alias = find_acceptable_alias(result, acceptable, context); if (alias) @@ -275,32 +323,16 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent, dput(result); /* It might be justifiable to return ESTALE here, * but the filehandle at-least looks reasonable good - * and it just be a permission problem, so returning + * and it may just be a permission problem, so returning * -EACCESS is safer */ return ERR_PTR(-EACCES); - err_target: - dput(target_dir); err_result: dput(result); - err_out: return ERR_PTR(err); } - - -static struct dentry *get_parent(struct dentry *child) -{ - /* get_parent cannot be supported generically, the locking - * is too icky. - * instead, we just return EACCES. If server reboots or inodes - * get flushed, you lose - */ - return ERR_PTR(-EACCES); -} - - struct getdents_callback { char *name; /* name that was found. It already points to a buffer NAME_MAX+1 is size */ @@ -390,61 +422,6 @@ out: return error; } - -static struct dentry *export_iget(struct super_block *sb, unsigned long ino, __u32 generation) -{ - - /* iget isn't really right if the inode is currently unallocated!! - * This should really all be done inside each filesystem - * - * ext2fs' read_inode has been strengthed to return a bad_inode if - * the inode had been deleted. - * - * Currently we don't know the generation for parent directory, so - * a generation of 0 means "accept any" - */ - struct inode *inode; - struct dentry *result; - if (ino == 0) - return ERR_PTR(-ESTALE); - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) - || (generation && inode->i_generation != generation) - ) { - /* we didn't find the right inode.. */ - dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n", - inode->i_ino, - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_generation, - generation); - - iput(inode); - return ERR_PTR(-ESTALE); - } - /* now to find a dentry. - * If possible, get a well-connected one - */ - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; -} - - -static struct dentry *get_object(struct super_block *sb, void *vobjp) -{ - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; - - return export_iget(sb, ino, generation); -} - - /** * export_encode_fh - default export_operations->encode_fh function * @dentry: the dentry to encode @@ -517,16 +494,40 @@ static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh acceptable, context); } -struct export_operations export_op_default = { - .decode_fh = export_decode_fh, - .encode_fh = export_encode_fh, +int exportfs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, + int connectable) +{ + struct export_operations *nop = dentry->d_sb->s_export_op; + int error; + + if (nop->encode_fh) + error = nop->encode_fh(dentry, fh, max_len, connectable); + else + error = export_encode_fh(dentry, fh, max_len, connectable); - .get_name = get_name, - .get_parent = get_parent, - .get_dentry = get_object, -}; + return error; +} +EXPORT_SYMBOL_GPL(exportfs_encode_fh); + +struct dentry *exportfs_decode_fh(struct vfsmount *mnt, __u32 *fh, int fh_len, + int fileid_type, int (*acceptable)(void *, struct dentry *), + void *context) +{ + struct export_operations *nop = mnt->mnt_sb->s_export_op; + struct dentry *result; + + if (nop->decode_fh) { + result = nop->decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type, + acceptable, context); + } else { + result = export_decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type, + acceptable, context); + } + + return result; +} +EXPORT_SYMBOL_GPL(exportfs_decode_fh); -EXPORT_SYMBOL(export_op_default); EXPORT_SYMBOL(find_exported_dentry); MODULE_LICENSE("GPL"); diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 7c420b8..e58669e 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -464,7 +464,7 @@ ext2_xattr_set_acl(struct inode *inode, int type, const void *value, if (!test_opt(inode->i_sb, POSIX_ACL)) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 04afeec..ab79612 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -24,9 +24,9 @@ #include "acl.h" /* - * Called when an inode is released. Note that this is different - * from ext2_open_file: open gets called at every open, but release - * gets called only when /all/ the files are closed. + * Called when filp is released. This happens when all file descriptors + * for a single struct file are closed. Note that different open() calls + * for the same file yield different struct file structures. */ static int ext2_release_file (struct inode * inode, struct file * filp) { diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index e85c482..3bcd254 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -36,7 +36,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) @@ -74,7 +74,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, case EXT2_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *) arg); case EXT2_IOC_SETVERSION: - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 5de5061..68579a0 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -25,6 +25,7 @@ #include <linux/parser.h> #include <linux/random.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/smp_lock.h> #include <linux/vfs.h> #include <linux/seq_file.h> @@ -166,14 +167,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag #endif inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { ext2_inode_cachep = kmem_cache_create("ext2_inode_cache", sizeof(struct ext2_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (ext2_inode_cachep == NULL) return -ENOMEM; return 0; @@ -882,13 +883,11 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } bgl_lock_init(&sbi->s_blockgroup_lock); - sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts), - GFP_KERNEL); + sbi->s_debts = kcalloc(sbi->s_groups_count, sizeof(*sbi->s_debts), GFP_KERNEL); if (!sbi->s_debts) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount_group_desc; } - memset(sbi->s_debts, 0, sbi->s_groups_count * sizeof(*sbi->s_debts)); for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logic_sb_block, i); sbi->s_group_desc[i] = sb_bread(sb, block); @@ -1099,15 +1098,18 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) struct super_block *sb = dentry->d_sb; struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = sbi->s_es; - unsigned long overhead; - int i; u64 fsid; if (test_opt (sb, MINIX_DF)) - overhead = 0; - else { + sbi->s_overhead_last = 0; + else if (sbi->s_blocks_last != le32_to_cpu(es->s_blocks_count)) { + unsigned long i, overhead = 0; + smp_rmb(); + /* - * Compute the overhead (FS structures) + * Compute the overhead (FS structures). This is constant + * for a given filesystem unless the number of block groups + * changes so we cache the previous value until it does. */ /* @@ -1131,17 +1133,22 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) */ overhead += (sbi->s_groups_count * (2 + sbi->s_itb_per_group)); + sbi->s_overhead_last = overhead; + smp_wmb(); + sbi->s_blocks_last = le32_to_cpu(es->s_blocks_count); } buf->f_type = EXT2_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = le32_to_cpu(es->s_blocks_count) - overhead; + buf->f_blocks = le32_to_cpu(es->s_blocks_count) - sbi->s_overhead_last; buf->f_bfree = ext2_count_free_blocks(sb); + es->s_free_blocks_count = cpu_to_le32(buf->f_bfree); buf->f_bavail = buf->f_bfree - le32_to_cpu(es->s_r_blocks_count); if (buf->f_bfree < le32_to_cpu(es->s_r_blocks_count)) buf->f_bavail = 0; buf->f_files = le32_to_cpu(es->s_inodes_count); buf->f_ffree = ext2_count_free_inodes(sb); + es->s_free_inodes_count = cpu_to_le32(buf->f_ffree); buf->f_namelen = EXT2_NAME_LEN; fsid = le64_to_cpup((void *)es->s_uuid) ^ le64_to_cpup((void *)es->s_uuid + sizeof(u64)); diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index 1e5038d..d34e996 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -489,7 +489,7 @@ ext3_xattr_set_acl(struct inode *inode, int type, const void *value, if (!test_opt(inode->i_sb, POSIX_ACL)) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 8528698..c00723a 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c @@ -136,12 +136,14 @@ static int ext3_readdir(struct file * filp, err = ext3_get_blocks_handle(NULL, inode, blk, 1, &map_bh, 0, 0); if (err > 0) { - page_cache_readahead(sb->s_bdev->bd_inode->i_mapping, - &filp->f_ra, - filp, - map_bh.b_blocknr >> - (PAGE_CACHE_SHIFT - inode->i_blkbits), - 1); + pgoff_t index = map_bh.b_blocknr >> + (PAGE_CACHE_SHIFT - inode->i_blkbits); + if (!ra_has_index(&filp->f_ra, index)) + page_cache_sync_readahead( + sb->s_bdev->bd_inode->i_mapping, + &filp->f_ra, filp, + index, 1); + filp->f_ra.prev_index = index; bh = ext3_bread(NULL, inode, blk, 0, &err); } diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2a85dde..de4e316 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -3195,7 +3195,7 @@ int ext3_change_inode_journal_flag(struct inode *inode, int val) */ journal = EXT3_JOURNAL(inode); - if (is_journal_aborted(journal) || IS_RDONLY(inode)) + if (is_journal_aborted(journal)) return -EROFS; journal_lock_updates(journal); diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c index 965006d..4a2a02c 100644 --- a/fs/ext3/ioctl.c +++ b/fs/ext3/ioctl.c @@ -41,7 +41,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) @@ -122,7 +122,7 @@ flags_err: __u32 generation; int err; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; @@ -181,7 +181,7 @@ flags_err: if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(rsv_window_size, (int __user *)arg)) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 9bb046d..1586807 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1019,6 +1019,11 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str if (!inode) return ERR_PTR(-EACCES); + + if (is_bad_inode(inode)) { + iput(inode); + return ERR_PTR(-ENOENT); + } } return d_splice_alias(inode, dentry); } @@ -1054,6 +1059,11 @@ struct dentry *ext3_get_parent(struct dentry *child) if (!inode) return ERR_PTR(-EACCES); + if (is_bad_inode(inode)) { + iput(inode); + return ERR_PTR(-ENOENT); + } + parent = d_alloc_anon(inode); if (!parent) { iput(inode); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6e30629..f0614e3 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -29,12 +29,14 @@ #include <linux/parser.h> #include <linux/smp_lock.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/vfs.h> #include <linux/random.h> #include <linux/mount.h> #include <linux/namei.h> #include <linux/quotaops.h> #include <linux/seq_file.h> +#include <linux/log2.h> #include <asm/uaccess.h> @@ -459,6 +461,14 @@ static struct inode *ext3_alloc_inode(struct super_block *sb) static void ext3_destroy_inode(struct inode *inode) { + if (!list_empty(&(EXT3_I(inode)->i_orphan))) { + printk("EXT3 Inode %p: orphan list check failed!\n", + EXT3_I(inode)); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 16, 4, + EXT3_I(inode), sizeof(struct ext3_inode_info), + false); + dump_stack(); + } kmem_cache_free(ext3_inode_cachep, EXT3_I(inode)); } @@ -480,7 +490,7 @@ static int init_inodecache(void) sizeof(struct ext3_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (ext3_inode_cachep == NULL) return -ENOMEM; return 0; @@ -1566,7 +1576,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT3_GOOD_OLD_INODE_SIZE) || - (sbi->s_inode_size & (sbi->s_inode_size - 1)) || + (!is_power_of_2(sbi->s_inode_size)) || (sbi->s_inode_size > blocksize)) { printk (KERN_ERR "EXT3-fs: unsupported inode size: %d\n", @@ -2075,6 +2085,7 @@ static int ext3_create_journal(struct super_block * sb, unsigned int journal_inum) { journal_t *journal; + int err; if (sb->s_flags & MS_RDONLY) { printk(KERN_ERR "EXT3-fs: readonly filesystem when trying to " @@ -2082,13 +2093,15 @@ static int ext3_create_journal(struct super_block * sb, return -EROFS; } - if (!(journal = ext3_get_journal(sb, journal_inum))) + journal = ext3_get_journal(sb, journal_inum); + if (!journal) return -EINVAL; printk(KERN_INFO "EXT3-fs: creating new journal on inode %u\n", journal_inum); - if (journal_create(journal)) { + err = journal_create(journal); + if (err) { printk(KERN_ERR "EXT3-fs: error creating journal.\n"); journal_destroy(journal); return -EIO; @@ -2139,12 +2152,14 @@ static void ext3_mark_recovery_complete(struct super_block * sb, journal_lock_updates(journal); journal_flush(journal); + lock_super(sb); if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER) && sb->s_flags & MS_RDONLY) { EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER); sb->s_dirt = 0; ext3_commit_super(sb, es, 1); } + unlock_super(sb); journal_unlock_updates(journal); } @@ -2333,7 +2348,13 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) (sbi->s_mount_state & EXT3_VALID_FS)) es->s_state = cpu_to_le16(sbi->s_mount_state); + /* + * We have to unlock super so that we can wait for + * transactions. + */ + unlock_super(sb); ext3_mark_recovery_complete(sb, es); + lock_super(sb); } else { __le32 ret; if ((ret = EXT3_HAS_RO_COMPAT_FEATURE(sb, @@ -2406,19 +2427,19 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf) struct super_block *sb = dentry->d_sb; struct ext3_sb_info *sbi = EXT3_SB(sb); struct ext3_super_block *es = sbi->s_es; - ext3_fsblk_t overhead; - int i; u64 fsid; - if (test_opt (sb, MINIX_DF)) - overhead = 0; - else { - unsigned long ngroups; - ngroups = EXT3_SB(sb)->s_groups_count; + if (test_opt(sb, MINIX_DF)) { + sbi->s_overhead_last = 0; + } else if (sbi->s_blocks_last != le32_to_cpu(es->s_blocks_count)) { + unsigned long ngroups = sbi->s_groups_count, i; + ext3_fsblk_t overhead = 0; smp_rmb(); /* - * Compute the overhead (FS structures) + * Compute the overhead (FS structures). This is constant + * for a given filesystem unless the number of block groups + * changes so we cache the previous value until it does. */ /* @@ -2442,18 +2463,23 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf) * Every block group has an inode bitmap, a block * bitmap, and an inode table. */ - overhead += (ngroups * (2 + EXT3_SB(sb)->s_itb_per_group)); + overhead += ngroups * (2 + sbi->s_itb_per_group); + sbi->s_overhead_last = overhead; + smp_wmb(); + sbi->s_blocks_last = le32_to_cpu(es->s_blocks_count); } buf->f_type = EXT3_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = le32_to_cpu(es->s_blocks_count) - overhead; + buf->f_blocks = le32_to_cpu(es->s_blocks_count) - sbi->s_overhead_last; buf->f_bfree = percpu_counter_sum(&sbi->s_freeblocks_counter); + es->s_free_blocks_count = cpu_to_le32(buf->f_bfree); buf->f_bavail = buf->f_bfree - le32_to_cpu(es->s_r_blocks_count); if (buf->f_bfree < le32_to_cpu(es->s_r_blocks_count)) buf->f_bavail = 0; buf->f_files = le32_to_cpu(es->s_inodes_count); buf->f_ffree = percpu_counter_sum(&sbi->s_freeinodes_counter); + es->s_free_inodes_count = cpu_to_le32(buf->f_ffree); buf->f_namelen = EXT3_NAME_LEN; fsid = le64_to_cpup((void *)es->s_uuid) ^ le64_to_cpup((void *)es->s_uuid + sizeof(u64)); diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 9e88254..a8bae8c 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -489,7 +489,7 @@ ext4_xattr_set_acl(struct inode *inode, int type, const void *value, if (!test_opt(inode->i_sb, POSIX_ACL)) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 3b64bb1..e53b4af 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -517,7 +517,7 @@ do_more: /* * An HJ special. This is expensive... */ -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG jbd_unlock_bh_state(bitmap_bh); { struct buffer_head *debug_bh; @@ -1585,7 +1585,7 @@ allocated: ret_block = grp_alloc_blk + ext4_group_first_block_no(sb, group_no); if (in_range(ext4_block_bitmap(sb, gdp), ret_block, num) || - in_range(ext4_block_bitmap(sb, gdp), ret_block, num) || + in_range(ext4_inode_bitmap(sb, gdp), ret_block, num) || in_range(ret_block, ext4_inode_table(sb, gdp), EXT4_SB(sb)->s_itb_per_group) || in_range(ret_block + num - 1, ext4_inode_table(sb, gdp), @@ -1597,7 +1597,7 @@ allocated: performed_allocation = 1; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG { struct buffer_head *debug_bh; diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index e8ad06e..3ab01c0 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -135,12 +135,14 @@ static int ext4_readdir(struct file * filp, map_bh.b_state = 0; err = ext4_get_blocks_wrap(NULL, inode, blk, 1, &map_bh, 0, 0); if (err > 0) { - page_cache_readahead(sb->s_bdev->bd_inode->i_mapping, - &filp->f_ra, - filp, - map_bh.b_blocknr >> - (PAGE_CACHE_SHIFT - inode->i_blkbits), - 1); + pgoff_t index = map_bh.b_blocknr >> + (PAGE_CACHE_SHIFT - inode->i_blkbits); + if (!ra_has_index(&filp->f_ra, index)) + page_cache_sync_readahead( + sb->s_bdev->bd_inode->i_mapping, + &filp->f_ra, filp, + index, 1); + filp->f_ra.prev_index = index; bh = ext4_bread(NULL, inode, blk, 0, &err); } diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index b9ce241..750c46f 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -39,6 +39,7 @@ #include <linux/quotaops.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/falloc.h> #include <linux/ext4_fs_extents.h> #include <asm/uaccess.h> @@ -91,36 +92,6 @@ static void ext4_idx_store_pblock(struct ext4_extent_idx *ix, ext4_fsblk_t pb) ix->ei_leaf_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) & 0xffff); } -static int ext4_ext_check_header(const char *function, struct inode *inode, - struct ext4_extent_header *eh) -{ - const char *error_msg = NULL; - - if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) { - error_msg = "invalid magic"; - goto corrupted; - } - if (unlikely(eh->eh_max == 0)) { - error_msg = "invalid eh_max"; - goto corrupted; - } - if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) { - error_msg = "invalid eh_entries"; - goto corrupted; - } - return 0; - -corrupted: - ext4_error(inode->i_sb, function, - "bad header in inode #%lu: %s - magic %x, " - "entries %u, max %u, depth %u", - inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), - le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), - le16_to_cpu(eh->eh_depth)); - - return -EIO; -} - static handle_t *ext4_ext_journal_restart(handle_t *handle, int needed) { int err; @@ -269,6 +240,70 @@ static int ext4_ext_space_root_idx(struct inode *inode) return size; } +static int +ext4_ext_max_entries(struct inode *inode, int depth) +{ + int max; + + if (depth == ext_depth(inode)) { + if (depth == 0) + max = ext4_ext_space_root(inode); + else + max = ext4_ext_space_root_idx(inode); + } else { + if (depth == 0) + max = ext4_ext_space_block(inode); + else + max = ext4_ext_space_block_idx(inode); + } + + return max; +} + +static int __ext4_ext_check_header(const char *function, struct inode *inode, + struct ext4_extent_header *eh, + int depth) +{ + const char *error_msg; + int max = 0; + + if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) { + error_msg = "invalid magic"; + goto corrupted; + } + if (unlikely(le16_to_cpu(eh->eh_depth) != depth)) { + error_msg = "unexpected eh_depth"; + goto corrupted; + } + if (unlikely(eh->eh_max == 0)) { + error_msg = "invalid eh_max"; + goto corrupted; + } + max = ext4_ext_max_entries(inode, depth); + if (unlikely(le16_to_cpu(eh->eh_max) > max)) { + error_msg = "too large eh_max"; + goto corrupted; + } + if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) { + error_msg = "invalid eh_entries"; + goto corrupted; + } + return 0; + +corrupted: + ext4_error(inode->i_sb, function, + "bad header in inode #%lu: %s - magic %x, " + "entries %u, max %u(%u), depth %u(%u)", + inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), + le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), + max, le16_to_cpu(eh->eh_depth), depth); + + return -EIO; +} + +#define ext4_ext_check_header(inode, eh, depth) \ + __ext4_ext_check_header(__FUNCTION__, inode, eh, depth) + #ifdef EXT_DEBUG static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) { @@ -282,7 +317,7 @@ static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) } else if (path->p_ext) { ext_debug(" %d:%d:%llu ", le32_to_cpu(path->p_ext->ee_block), - le16_to_cpu(path->p_ext->ee_len), + ext4_ext_get_actual_len(path->p_ext), ext_pblock(path->p_ext)); } else ext_debug(" []"); @@ -305,7 +340,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path) for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ex++) { ext_debug("%d:%d:%llu ", le32_to_cpu(ex->ee_block), - le16_to_cpu(ex->ee_len), ext_pblock(ex)); + ext4_ext_get_actual_len(ex), ext_pblock(ex)); } ext_debug("\n"); } @@ -329,6 +364,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path) /* * ext4_ext_binsearch_idx: * binary search for the closest index of the given block + * the header must be checked before calling this */ static void ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int block) @@ -336,27 +372,25 @@ ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int bloc struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent_idx *r, *l, *m; - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - BUG_ON(le16_to_cpu(eh->eh_entries) <= 0); ext_debug("binsearch for %d(idx): ", block); l = EXT_FIRST_INDEX(eh) + 1; - r = EXT_FIRST_INDEX(eh) + le16_to_cpu(eh->eh_entries) - 1; + r = EXT_LAST_INDEX(eh); while (l <= r) { m = l + (r - l) / 2; if (block < le32_to_cpu(m->ei_block)) r = m - 1; else l = m + 1; - ext_debug("%p(%u):%p(%u):%p(%u) ", l, l->ei_block, - m, m->ei_block, r, r->ei_block); + ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ei_block), + m, le32_to_cpu(m->ei_block), + r, le32_to_cpu(r->ei_block)); } path->p_idx = l - 1; ext_debug(" -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block), - idx_block(path->p_idx)); + idx_pblock(path->p_idx)); #ifdef CHECK_BINSEARCH { @@ -388,6 +422,7 @@ ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int bloc /* * ext4_ext_binsearch: * binary search for closest extent of the given block + * the header must be checked before calling this */ static void ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) @@ -395,9 +430,6 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent *r, *l, *m; - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - if (eh->eh_entries == 0) { /* * this leaf is empty: @@ -409,7 +441,7 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) ext_debug("binsearch for %d: ", block); l = EXT_FIRST_EXTENT(eh) + 1; - r = EXT_FIRST_EXTENT(eh) + le16_to_cpu(eh->eh_entries) - 1; + r = EXT_LAST_EXTENT(eh); while (l <= r) { m = l + (r - l) / 2; @@ -417,15 +449,16 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) r = m - 1; else l = m + 1; - ext_debug("%p(%u):%p(%u):%p(%u) ", l, l->ee_block, - m, m->ee_block, r, r->ee_block); + ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ee_block), + m, le32_to_cpu(m->ee_block), + r, le32_to_cpu(r->ee_block)); } path->p_ext = l - 1; ext_debug(" -> %d:%llu:%d ", le32_to_cpu(path->p_ext->ee_block), ext_pblock(path->p_ext), - le16_to_cpu(path->p_ext->ee_len)); + ext4_ext_get_actual_len(path->p_ext)); #ifdef CHECK_BINSEARCH { @@ -468,11 +501,10 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) short int depth, i, ppos = 0, alloc = 0; eh = ext_inode_hdr(inode); - BUG_ON(eh == NULL); - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) + depth = ext_depth(inode); + if (ext4_ext_check_header(inode, eh, depth)) return ERR_PTR(-EIO); - i = depth = ext_depth(inode); /* account possible depth increase */ if (!path) { @@ -484,10 +516,12 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) } path[0].p_hdr = eh; + i = depth; /* walk through the tree */ while (i) { ext_debug("depth %d: num %d, max %d\n", ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max)); + ext4_ext_binsearch_idx(inode, path + ppos, block); path[ppos].p_block = idx_pblock(path[ppos].p_idx); path[ppos].p_depth = i; @@ -504,7 +538,7 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) path[ppos].p_hdr = eh; i--; - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) + if (ext4_ext_check_header(inode, eh, i)) goto err; } @@ -513,9 +547,6 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) path[ppos].p_ext = NULL; path[ppos].p_idx = NULL; - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) - goto err; - /* find extent */ ext4_ext_binsearch(inode, path + ppos, block); @@ -553,7 +584,7 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode, if (curp->p_idx != EXT_LAST_INDEX(curp->p_hdr)) { len = (len - 1) * sizeof(struct ext4_extent_idx); len = len < 0 ? 0 : len; - ext_debug("insert new index %d after: %d. " + ext_debug("insert new index %d after: %llu. " "move %d from 0x%p to 0x%p\n", logical, ptr, len, (curp->p_idx + 1), (curp->p_idx + 2)); @@ -564,7 +595,7 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode, /* insert before */ len = len * sizeof(struct ext4_extent_idx); len = len < 0 ? 0 : len; - ext_debug("insert new index %d before: %d. " + ext_debug("insert new index %d before: %llu. " "move %d from 0x%p to 0x%p\n", logical, ptr, len, curp->p_idx, (curp->p_idx + 1)); @@ -686,7 +717,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, ext_debug("move %d:%llu:%d in new leaf %llu\n", le32_to_cpu(path[depth].p_ext->ee_block), ext_pblock(path[depth].p_ext), - le16_to_cpu(path[depth].p_ext->ee_len), + ext4_ext_get_actual_len(path[depth].p_ext), newblock); /*memmove(ex++, path[depth].p_ext++, sizeof(struct ext4_extent)); @@ -764,7 +795,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, BUG_ON(EXT_MAX_INDEX(path[i].p_hdr) != EXT_LAST_INDEX(path[i].p_hdr)); while (path[i].p_idx <= EXT_MAX_INDEX(path[i].p_hdr)) { - ext_debug("%d: move %d:%d in new index %llu\n", i, + ext_debug("%d: move %d:%llu in new index %llu\n", i, le32_to_cpu(path[i].p_idx->ei_block), idx_pblock(path[i].p_idx), newblock); @@ -893,8 +924,13 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, curp->p_hdr->eh_max = cpu_to_le16(ext4_ext_space_root_idx(inode)); curp->p_hdr->eh_entries = cpu_to_le16(1); curp->p_idx = EXT_FIRST_INDEX(curp->p_hdr); - /* FIXME: it works, but actually path[0] can be index */ - curp->p_idx->ei_block = EXT_FIRST_EXTENT(path[0].p_hdr)->ee_block; + + if (path[0].p_hdr->eh_depth) + curp->p_idx->ei_block = + EXT_FIRST_INDEX(path[0].p_hdr)->ei_block; + else + curp->p_idx->ei_block = + EXT_FIRST_EXTENT(path[0].p_hdr)->ee_block; ext4_idx_store_pblock(curp->p_idx, newblock); neh = ext_inode_hdr(inode); @@ -1106,7 +1142,24 @@ static int ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, struct ext4_extent *ex2) { - if (le32_to_cpu(ex1->ee_block) + le16_to_cpu(ex1->ee_len) != + unsigned short ext1_ee_len, ext2_ee_len, max_len; + + /* + * Make sure that either both extents are uninitialized, or + * both are _not_. + */ + if (ext4_ext_is_uninitialized(ex1) ^ ext4_ext_is_uninitialized(ex2)) + return 0; + + if (ext4_ext_is_uninitialized(ex1)) + max_len = EXT_UNINIT_MAX_LEN; + else + max_len = EXT_INIT_MAX_LEN; + + ext1_ee_len = ext4_ext_get_actual_len(ex1); + ext2_ee_len = ext4_ext_get_actual_len(ex2); + + if (le32_to_cpu(ex1->ee_block) + ext1_ee_len != le32_to_cpu(ex2->ee_block)) return 0; @@ -1115,19 +1168,66 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, * as an RO_COMPAT feature, refuse to merge to extents if * this can result in the top bit of ee_len being set. */ - if (le16_to_cpu(ex1->ee_len) + le16_to_cpu(ex2->ee_len) > EXT_MAX_LEN) + if (ext1_ee_len + ext2_ee_len > max_len) return 0; #ifdef AGGRESSIVE_TEST if (le16_to_cpu(ex1->ee_len) >= 4) return 0; #endif - if (ext_pblock(ex1) + le16_to_cpu(ex1->ee_len) == ext_pblock(ex2)) + if (ext_pblock(ex1) + ext1_ee_len == ext_pblock(ex2)) return 1; return 0; } /* + * This function tries to merge the "ex" extent to the next extent in the tree. + * It always tries to merge towards right. If you want to merge towards + * left, pass "ex - 1" as argument instead of "ex". + * Returns 0 if the extents (ex and ex+1) were _not_ merged and returns + * 1 if they got merged. + */ +int ext4_ext_try_to_merge(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_extent *ex) +{ + struct ext4_extent_header *eh; + unsigned int depth, len; + int merge_done = 0; + int uninitialized = 0; + + depth = ext_depth(inode); + BUG_ON(path[depth].p_hdr == NULL); + eh = path[depth].p_hdr; + + while (ex < EXT_LAST_EXTENT(eh)) { + if (!ext4_can_extents_be_merged(inode, ex, ex + 1)) + break; + /* merge with next extent! */ + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + + ext4_ext_get_actual_len(ex + 1)); + if (uninitialized) + ext4_ext_mark_uninitialized(ex); + + if (ex + 1 < EXT_LAST_EXTENT(eh)) { + len = (EXT_LAST_EXTENT(eh) - ex - 1) + * sizeof(struct ext4_extent); + memmove(ex + 1, ex + 2, len); + } + eh->eh_entries = cpu_to_le16(le16_to_cpu(eh->eh_entries) - 1); + merge_done = 1; + WARN_ON(eh->eh_entries == 0); + if (!eh->eh_entries) + ext4_error(inode->i_sb, "ext4_ext_try_to_merge", + "inode#%lu, eh->eh_entries = 0!", inode->i_ino); + } + + return merge_done; +} + +/* * check if a portion of the "newext" extent overlaps with an * existing extent. * @@ -1144,7 +1244,7 @@ unsigned int ext4_ext_check_overlap(struct inode *inode, unsigned int ret = 0; b1 = le32_to_cpu(newext->ee_block); - len1 = le16_to_cpu(newext->ee_len); + len1 = ext4_ext_get_actual_len(newext); depth = ext_depth(inode); if (!path[depth].p_ext) goto out; @@ -1191,8 +1291,9 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, struct ext4_extent *nearex; /* nearest extent */ struct ext4_ext_path *npath = NULL; int depth, len, err, next; + unsigned uninitialized = 0; - BUG_ON(newext->ee_len == 0); + BUG_ON(ext4_ext_get_actual_len(newext) == 0); depth = ext_depth(inode); ex = path[depth].p_ext; BUG_ON(path[depth].p_hdr == NULL); @@ -1200,14 +1301,24 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, /* try to insert block into found extent and return */ if (ex && ext4_can_extents_be_merged(inode, ex, newext)) { ext_debug("append %d block to %d:%d (from %llu)\n", - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), le32_to_cpu(ex->ee_block), - le16_to_cpu(ex->ee_len), ext_pblock(ex)); + ext4_ext_get_actual_len(ex), ext_pblock(ex)); err = ext4_ext_get_access(handle, inode, path + depth); if (err) return err; - ex->ee_len = cpu_to_le16(le16_to_cpu(ex->ee_len) - + le16_to_cpu(newext->ee_len)); + + /* + * ext4_can_extents_be_merged should have checked that either + * both extents are uninitialized, or both aren't. Thus we + * need to check only one of them here. + */ + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + + ext4_ext_get_actual_len(newext)); + if (uninitialized) + ext4_ext_mark_uninitialized(ex); eh = path[depth].p_hdr; nearex = ex; goto merge; @@ -1263,7 +1374,7 @@ has_space: ext_debug("first extent in the leaf: %d:%llu:%d\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len)); + ext4_ext_get_actual_len(newext)); path[depth].p_ext = EXT_FIRST_EXTENT(eh); } else if (le32_to_cpu(newext->ee_block) > le32_to_cpu(nearex->ee_block)) { @@ -1276,7 +1387,7 @@ has_space: "move %d from 0x%p to 0x%p\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), nearex, len, nearex + 1, nearex + 2); memmove(nearex + 2, nearex + 1, len); } @@ -1289,7 +1400,7 @@ has_space: "move %d from 0x%p to 0x%p\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), nearex, len, nearex + 1, nearex + 2); memmove(nearex + 1, nearex, len); path[depth].p_ext = nearex; @@ -1304,20 +1415,7 @@ has_space: merge: /* try to merge extents to the right */ - while (nearex < EXT_LAST_EXTENT(eh)) { - if (!ext4_can_extents_be_merged(inode, nearex, nearex + 1)) - break; - /* merge with next extent! */ - nearex->ee_len = cpu_to_le16(le16_to_cpu(nearex->ee_len) - + le16_to_cpu(nearex[1].ee_len)); - if (nearex + 1 < EXT_LAST_EXTENT(eh)) { - len = (EXT_LAST_EXTENT(eh) - nearex - 1) - * sizeof(struct ext4_extent); - memmove(nearex + 1, nearex + 2, len); - } - eh->eh_entries = cpu_to_le16(le16_to_cpu(eh->eh_entries)-1); - BUG_ON(eh->eh_entries == 0); - } + ext4_ext_try_to_merge(inode, path, nearex); /* try to merge extents to the left */ @@ -1379,8 +1477,8 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, end = le32_to_cpu(ex->ee_block); if (block + num < end) end = block + num; - } else if (block >= - le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len)) { + } else if (block >= le32_to_cpu(ex->ee_block) + + ext4_ext_get_actual_len(ex)) { /* need to allocate space after found extent */ start = block; end = block + num; @@ -1392,7 +1490,8 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, * by found extent */ start = block; - end = le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len); + end = le32_to_cpu(ex->ee_block) + + ext4_ext_get_actual_len(ex); if (block + num < end) end = block + num; exists = 1; @@ -1408,7 +1507,7 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, cbex.ec_type = EXT4_EXT_CACHE_GAP; } else { cbex.ec_block = le32_to_cpu(ex->ee_block); - cbex.ec_len = le16_to_cpu(ex->ee_len); + cbex.ec_len = ext4_ext_get_actual_len(ex); cbex.ec_start = ext_pblock(ex); cbex.ec_type = EXT4_EXT_CACHE_EXTENT; } @@ -1481,15 +1580,15 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, ext_debug("cache gap(before): %lu [%lu:%lu]", (unsigned long) block, (unsigned long) le32_to_cpu(ex->ee_block), - (unsigned long) le16_to_cpu(ex->ee_len)); + (unsigned long) ext4_ext_get_actual_len(ex)); } else if (block >= le32_to_cpu(ex->ee_block) - + le16_to_cpu(ex->ee_len)) { + + ext4_ext_get_actual_len(ex)) { lblock = le32_to_cpu(ex->ee_block) - + le16_to_cpu(ex->ee_len); + + ext4_ext_get_actual_len(ex); len = ext4_ext_next_allocated_block(path); ext_debug("cache gap(after): [%lu:%lu] %lu", (unsigned long) le32_to_cpu(ex->ee_block), - (unsigned long) le16_to_cpu(ex->ee_len), + (unsigned long) ext4_ext_get_actual_len(ex), (unsigned long) block); BUG_ON(len == lblock); len = len - lblock; @@ -1619,12 +1718,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, unsigned long from, unsigned long to) { struct buffer_head *bh; + unsigned short ee_len = ext4_ext_get_actual_len(ex); int i; #ifdef EXTENTS_STATS { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - unsigned short ee_len = le16_to_cpu(ex->ee_len); spin_lock(&sbi->s_ext_stats_lock); sbi->s_ext_blocks += ee_len; sbi->s_ext_extents++; @@ -1638,12 +1737,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, } #endif if (from >= le32_to_cpu(ex->ee_block) - && to == le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - 1) { + && to == le32_to_cpu(ex->ee_block) + ee_len - 1) { /* tail removal */ unsigned long num; ext4_fsblk_t start; - num = le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - from; - start = ext_pblock(ex) + le16_to_cpu(ex->ee_len) - num; + num = le32_to_cpu(ex->ee_block) + ee_len - from; + start = ext_pblock(ex) + ee_len - num; ext_debug("free last %lu blocks starting %llu\n", num, start); for (i = 0; i < num; i++) { bh = sb_find_get_block(inode->i_sb, start + i); @@ -1651,12 +1750,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, } ext4_free_blocks(handle, inode, start, num); } else if (from == le32_to_cpu(ex->ee_block) - && to <= le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - 1) { + && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { printk("strange request: removal %lu-%lu from %u:%u\n", - from, to, le32_to_cpu(ex->ee_block), le16_to_cpu(ex->ee_len)); + from, to, le32_to_cpu(ex->ee_block), ee_len); } else { printk("strange request: removal(2) %lu-%lu from %u:%u\n", - from, to, le32_to_cpu(ex->ee_block), le16_to_cpu(ex->ee_len)); + from, to, le32_to_cpu(ex->ee_block), ee_len); } return 0; } @@ -1671,21 +1770,23 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, unsigned a, b, block, num; unsigned long ex_ee_block; unsigned short ex_ee_len; + unsigned uninitialized = 0; struct ext4_extent *ex; + /* the header must be checked already in ext4_ext_remove_space() */ ext_debug("truncate since %lu in leaf\n", start); if (!path[depth].p_hdr) path[depth].p_hdr = ext_block_hdr(path[depth].p_bh); eh = path[depth].p_hdr; BUG_ON(eh == NULL); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); /* find where to start removing */ ex = EXT_LAST_EXTENT(eh); ex_ee_block = le32_to_cpu(ex->ee_block); - ex_ee_len = le16_to_cpu(ex->ee_len); + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex_ee_len = ext4_ext_get_actual_len(ex); while (ex >= EXT_FIRST_EXTENT(eh) && ex_ee_block + ex_ee_len > start) { @@ -1753,6 +1854,12 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ex->ee_block = cpu_to_le32(block); ex->ee_len = cpu_to_le16(num); + /* + * Do not mark uninitialized if all the blocks in the + * extent have been removed. + */ + if (uninitialized && num) + ext4_ext_mark_uninitialized(ex); err = ext4_ext_dirty(handle, inode, path + depth); if (err) @@ -1762,7 +1869,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ext_pblock(ex)); ex--; ex_ee_block = le32_to_cpu(ex->ee_block); - ex_ee_len = le16_to_cpu(ex->ee_len); + ex_ee_len = ext4_ext_get_actual_len(ex); } if (correct_index && eh->eh_entries) @@ -1825,7 +1932,7 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) return -ENOMEM; } path[0].p_hdr = ext_inode_hdr(inode); - if (ext4_ext_check_header(__FUNCTION__, inode, path[0].p_hdr)) { + if (ext4_ext_check_header(inode, path[0].p_hdr, depth)) { err = -EIO; goto out; } @@ -1846,17 +1953,8 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) if (!path[i].p_hdr) { ext_debug("initialize header\n"); path[i].p_hdr = ext_block_hdr(path[i].p_bh); - if (ext4_ext_check_header(__FUNCTION__, inode, - path[i].p_hdr)) { - err = -EIO; - goto out; - } } - BUG_ON(le16_to_cpu(path[i].p_hdr->eh_entries) - > le16_to_cpu(path[i].p_hdr->eh_max)); - BUG_ON(path[i].p_hdr->eh_magic != EXT4_EXT_MAGIC); - if (!path[i].p_idx) { /* this level hasn't been touched yet */ path[i].p_idx = EXT_LAST_INDEX(path[i].p_hdr); @@ -1873,17 +1971,27 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) i, EXT_FIRST_INDEX(path[i].p_hdr), path[i].p_idx); if (ext4_ext_more_to_rm(path + i)) { + struct buffer_head *bh; /* go to the next level */ ext_debug("move to level %d (block %llu)\n", i + 1, idx_pblock(path[i].p_idx)); memset(path + i + 1, 0, sizeof(*path)); - path[i+1].p_bh = - sb_bread(sb, idx_pblock(path[i].p_idx)); - if (!path[i+1].p_bh) { + bh = sb_bread(sb, idx_pblock(path[i].p_idx)); + if (!bh) { /* should we reset i_size? */ err = -EIO; break; } + if (WARN_ON(i + 1 > depth)) { + err = -EIO; + break; + } + if (ext4_ext_check_header(inode, ext_block_hdr(bh), + depth - i - 1)) { + err = -EIO; + break; + } + path[i + 1].p_bh = bh; /* save actual number of indexes since this * number is changed at the next iteration */ @@ -1977,15 +2085,158 @@ void ext4_ext_release(struct super_block *sb) #endif } +/* + * This function is called by ext4_ext_get_blocks() if someone tries to write + * to an uninitialized extent. It may result in splitting the uninitialized + * extent into multiple extents (upto three - one initialized and two + * uninitialized). + * There are three possibilities: + * a> There is no split required: Entire extent should be initialized + * b> Splits in two extents: Write is happening at either end of the extent + * c> Splits in three extents: Somone is writing in middle of the extent + */ +int ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode, + struct ext4_ext_path *path, + ext4_fsblk_t iblock, + unsigned long max_blocks) +{ + struct ext4_extent *ex, newex; + struct ext4_extent *ex1 = NULL; + struct ext4_extent *ex2 = NULL; + struct ext4_extent *ex3 = NULL; + struct ext4_extent_header *eh; + unsigned int allocated, ee_block, ee_len, depth; + ext4_fsblk_t newblock; + int err = 0; + int ret = 0; + + depth = ext_depth(inode); + eh = path[depth].p_hdr; + ex = path[depth].p_ext; + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + allocated = ee_len - (iblock - ee_block); + newblock = iblock - ee_block + ext_pblock(ex); + ex2 = ex; + + /* ex1: ee_block to iblock - 1 : uninitialized */ + if (iblock > ee_block) { + ex1 = ex; + ex1->ee_len = cpu_to_le16(iblock - ee_block); + ext4_ext_mark_uninitialized(ex1); + ex2 = &newex; + } + /* + * for sanity, update the length of the ex2 extent before + * we insert ex3, if ex1 is NULL. This is to avoid temporary + * overlap of blocks. + */ + if (!ex1 && allocated > max_blocks) + ex2->ee_len = cpu_to_le16(max_blocks); + /* ex3: to ee_block + ee_len : uninitialised */ + if (allocated > max_blocks) { + unsigned int newdepth; + ex3 = &newex; + ex3->ee_block = cpu_to_le32(iblock + max_blocks); + ext4_ext_store_pblock(ex3, newblock + max_blocks); + ex3->ee_len = cpu_to_le16(allocated - max_blocks); + ext4_ext_mark_uninitialized(ex3); + err = ext4_ext_insert_extent(handle, inode, path, ex3); + if (err) + goto out; + /* + * The depth, and hence eh & ex might change + * as part of the insert above. + */ + newdepth = ext_depth(inode); + if (newdepth != depth) { + depth = newdepth; + path = ext4_ext_find_extent(inode, iblock, NULL); + if (IS_ERR(path)) { + err = PTR_ERR(path); + path = NULL; + goto out; + } + eh = path[depth].p_hdr; + ex = path[depth].p_ext; + if (ex2 != &newex) + ex2 = ex; + } + allocated = max_blocks; + } + /* + * If there was a change of depth as part of the + * insertion of ex3 above, we need to update the length + * of the ex1 extent again here + */ + if (ex1 && ex1 != ex) { + ex1 = ex; + ex1->ee_len = cpu_to_le16(iblock - ee_block); + ext4_ext_mark_uninitialized(ex1); + ex2 = &newex; + } + /* ex2: iblock to iblock + maxblocks-1 : initialised */ + ex2->ee_block = cpu_to_le32(iblock); + ex2->ee_start = cpu_to_le32(newblock); + ext4_ext_store_pblock(ex2, newblock); + ex2->ee_len = cpu_to_le16(allocated); + if (ex2 != ex) + goto insert; + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) + goto out; + /* + * New (initialized) extent starts from the first block + * in the current extent. i.e., ex2 == ex + * We have to see if it can be merged with the extent + * on the left. + */ + if (ex2 > EXT_FIRST_EXTENT(eh)) { + /* + * To merge left, pass "ex2 - 1" to try_to_merge(), + * since it merges towards right _only_. + */ + ret = ext4_ext_try_to_merge(inode, path, ex2 - 1); + if (ret) { + err = ext4_ext_correct_indexes(handle, inode, path); + if (err) + goto out; + depth = ext_depth(inode); + ex2--; + } + } + /* + * Try to Merge towards right. This might be required + * only when the whole extent is being written to. + * i.e. ex2 == ex and ex3 == NULL. + */ + if (!ex3) { + ret = ext4_ext_try_to_merge(inode, path, ex2); + if (ret) { + err = ext4_ext_correct_indexes(handle, inode, path); + if (err) + goto out; + } + } + /* Mark modified extent as dirty */ + err = ext4_ext_dirty(handle, inode, path + depth); + goto out; +insert: + err = ext4_ext_insert_extent(handle, inode, path, &newex); +out: + return err ? err : allocated; +} + int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, ext4_fsblk_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create, int extend_disksize) { struct ext4_ext_path *path = NULL; + struct ext4_extent_header *eh; struct ext4_extent newex, *ex; ext4_fsblk_t goal, newblock; - int err = 0, depth; + int err = 0, depth, ret; unsigned long allocated = 0; __clear_bit(BH_New, &bh_result->b_state); @@ -1998,8 +2249,10 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, if (goal) { if (goal == EXT4_EXT_CACHE_GAP) { if (!create) { - /* block isn't allocated yet and - * user doesn't want to allocate it */ + /* + * block isn't allocated yet and + * user doesn't want to allocate it + */ goto out2; } /* we should allocate requested block */ @@ -2033,21 +2286,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, * this is why assert can't be put in ext4_ext_find_extent() */ BUG_ON(path[depth].p_ext == NULL && depth != 0); + eh = path[depth].p_hdr; ex = path[depth].p_ext; if (ex) { unsigned long ee_block = le32_to_cpu(ex->ee_block); ext4_fsblk_t ee_start = ext_pblock(ex); - unsigned short ee_len = le16_to_cpu(ex->ee_len); + unsigned short ee_len; /* - * Allow future support for preallocated extents to be added - * as an RO_COMPAT feature: * Uninitialized extents are treated as holes, except that - * we avoid (fail) allocating new blocks during a write. + * we split out initialized portions during a write. */ - if (ee_len > EXT_MAX_LEN) - goto out2; + ee_len = ext4_ext_get_actual_len(ex); /* if found extent covers block, simply return it */ if (iblock >= ee_block && iblock < ee_block + ee_len) { newblock = iblock - ee_block + ee_start; @@ -2055,9 +2306,27 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, allocated = ee_len - (iblock - ee_block); ext_debug("%d fit into %lu:%d -> %llu\n", (int) iblock, ee_block, ee_len, newblock); - ext4_ext_put_in_cache(inode, ee_block, ee_len, - ee_start, EXT4_EXT_CACHE_EXTENT); - goto out; + + /* Do not put uninitialized extent in the cache */ + if (!ext4_ext_is_uninitialized(ex)) { + ext4_ext_put_in_cache(inode, ee_block, + ee_len, ee_start, + EXT4_EXT_CACHE_EXTENT); + goto out; + } + if (create == EXT4_CREATE_UNINITIALIZED_EXT) + goto out; + if (!create) + goto out2; + + ret = ext4_ext_convert_to_initialized(handle, inode, + path, iblock, + max_blocks); + if (ret <= 0) + goto out2; + else + allocated = ret; + goto outnew; } } @@ -2066,8 +2335,10 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, * we couldn't try to create block if create flag is zero */ if (!create) { - /* put just found gap into cache to speed up - * subsequent requests */ + /* + * put just found gap into cache to speed up + * subsequent requests + */ ext4_ext_put_gap_in_cache(inode, path, iblock); goto out2; } @@ -2081,6 +2352,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* allocate new block */ goal = ext4_ext_find_goal(inode, path, iblock); + /* + * See if request is beyond maximum number of blocks we can have in + * a single extent. For an initialized extent this limit is + * EXT_INIT_MAX_LEN and for an uninitialized extent this limit is + * EXT_UNINIT_MAX_LEN. + */ + if (max_blocks > EXT_INIT_MAX_LEN && + create != EXT4_CREATE_UNINITIALIZED_EXT) + max_blocks = EXT_INIT_MAX_LEN; + else if (max_blocks > EXT_UNINIT_MAX_LEN && + create == EXT4_CREATE_UNINITIALIZED_EXT) + max_blocks = EXT_UNINIT_MAX_LEN; + /* Check if we can really insert (iblock)::(iblock+max_blocks) extent */ newex.ee_block = cpu_to_le32(iblock); newex.ee_len = cpu_to_le16(max_blocks); @@ -2098,6 +2382,8 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* try to insert new extent into found leaf and return */ ext4_ext_store_pblock(&newex, newblock); newex.ee_len = cpu_to_le16(allocated); + if (create == EXT4_CREATE_UNINITIALIZED_EXT) /* Mark uninitialized */ + ext4_ext_mark_uninitialized(&newex); err = ext4_ext_insert_extent(handle, inode, path, &newex); if (err) { /* free data blocks we just allocated */ @@ -2111,10 +2397,13 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* previous routine could use block we allocated */ newblock = ext_pblock(&newex); +outnew: __set_bit(BH_New, &bh_result->b_state); - ext4_ext_put_in_cache(inode, iblock, allocated, newblock, - EXT4_EXT_CACHE_EXTENT); + /* Cache only when it is _not_ an uninitialized extent */ + if (create != EXT4_CREATE_UNINITIALIZED_EXT) + ext4_ext_put_in_cache(inode, iblock, allocated, newblock, + EXT4_EXT_CACHE_EXTENT); out: if (allocated > max_blocks) allocated = max_blocks; @@ -2178,7 +2467,8 @@ void ext4_ext_truncate(struct inode * inode, struct page *page) err = ext4_ext_remove_space(inode, last_block); /* In a multi-transaction truncate, we only make the final - * transaction synchronous. */ + * transaction synchronous. + */ if (IS_SYNC(inode)) handle->h_sync = 1; @@ -2217,3 +2507,127 @@ int ext4_ext_writepage_trans_blocks(struct inode *inode, int num) return needed; } + +/* + * preallocate space for a file. This implements ext4's fallocate inode + * operation, which gets called from sys_fallocate system call. + * For block-mapped files, posix_fallocate should fall back to the method + * of writing zeroes to the required new blocks (the same behavior which is + * expected for file systems which do not support fallocate() system call). + */ +long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len) +{ + handle_t *handle; + ext4_fsblk_t block, max_blocks; + ext4_fsblk_t nblocks = 0; + int ret = 0; + int ret2 = 0; + int retries = 0; + struct buffer_head map_bh; + unsigned int credits, blkbits = inode->i_blkbits; + + /* + * currently supporting (pre)allocate mode for extent-based + * files _only_ + */ + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) + return -EOPNOTSUPP; + + /* preallocation to directories is currently not supported */ + if (S_ISDIR(inode->i_mode)) + return -ENODEV; + + block = offset >> blkbits; + max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) + - block; + + /* + * credits to insert 1 extent into extent tree + buffers to be able to + * modify 1 super block, 1 block bitmap and 1 group descriptor. + */ + credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + 3; +retry: + while (ret >= 0 && ret < max_blocks) { + block = block + ret; + max_blocks = max_blocks - ret; + handle = ext4_journal_start(inode, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + break; + } + + ret = ext4_ext_get_blocks(handle, inode, block, + max_blocks, &map_bh, + EXT4_CREATE_UNINITIALIZED_EXT, 0); + WARN_ON(!ret); + if (!ret) { + ext4_error(inode->i_sb, "ext4_fallocate", + "ext4_ext_get_blocks returned 0! inode#%lu" + ", block=%llu, max_blocks=%llu", + inode->i_ino, block, max_blocks); + ret = -EIO; + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + break; + } + if (ret > 0) { + /* check wrap through sign-bit/zero here */ + if ((block + ret) < 0 || (block + ret) < block) { + ret = -EIO; + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + break; + } + if (buffer_new(&map_bh) && ((block + ret) > + (EXT4_BLOCK_ALIGN(i_size_read(inode), blkbits) + >> blkbits))) + nblocks = nblocks + ret; + } + + /* Update ctime if new blocks get allocated */ + if (nblocks) { + struct timespec now; + + now = current_fs_time(inode->i_sb); + if (!timespec_equal(&inode->i_ctime, &now)) + inode->i_ctime = now; + } + + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + if (ret2) + break; + } + + if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry; + + /* + * Time to update the file size. + * Update only when preallocation was requested beyond the file size. + */ + if (!(mode & FALLOC_FL_KEEP_SIZE) && + (offset + len) > i_size_read(inode)) { + if (ret > 0) { + /* + * if no error, we assume preallocation succeeded + * completely + */ + mutex_lock(&inode->i_mutex); + i_size_write(inode, offset + len); + EXT4_I(inode)->i_disksize = i_size_read(inode); + mutex_unlock(&inode->i_mutex); + } else if (ret < 0 && nblocks) { + /* Handle partial allocation scenario */ + loff_t newsize; + + mutex_lock(&inode->i_mutex); + newsize = (nblocks << blkbits) + i_size_read(inode); + i_size_write(inode, EXT4_BLOCK_ALIGN(newsize, blkbits)); + EXT4_I(inode)->i_disksize = i_size_read(inode); + mutex_unlock(&inode->i_mutex); + } + } + + return ret > 0 ? ret2 : ret; +} diff --git a/fs/ext4/file.c b/fs/ext4/file.c index d4c8186..1a81cd6 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -134,5 +134,6 @@ const struct inode_operations ext4_file_inode_operations = { .removexattr = generic_removexattr, #endif .permission = ext4_permission, + .fallocate = ext4_fallocate, }; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index c88b439..427f830 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -563,7 +563,8 @@ got: inode->i_ino = ino; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; + inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime = + ext4_current_time(inode); memset(ei->i_data, 0, sizeof(ei->i_data)); ei->i_dir_start_lookup = 0; @@ -595,9 +596,8 @@ got: spin_unlock(&sbi->s_next_gen_lock); ei->i_state = EXT4_STATE_NEW; - ei->i_extra_isize = - (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) ? - sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE : 0; + + ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize; ret = inode; if(DQUOT_ALLOC_INODE(inode)) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8416fa2..a4848e0 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -726,7 +726,7 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode, /* We are done with atomic stuff, now do the rest of housekeeping */ - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); /* had we spliced it onto indirect block? */ @@ -1766,7 +1766,6 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, struct inode *inode = mapping->host; struct buffer_head *bh; int err = 0; - void *kaddr; blocksize = inode->i_sb->s_blocksize; length = blocksize - (offset & (blocksize - 1)); @@ -1778,10 +1777,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, */ if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode) && PageUptodate(page)) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); set_page_dirty(page); goto unlock; } @@ -1834,10 +1830,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, goto unlock; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); BUFFER_TRACE(bh, "zeroed end of block"); @@ -2375,7 +2368,7 @@ do_indirects: ext4_discard_reservation(inode); mutex_unlock(&ei->truncate_mutex); - inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + inode->i_mtime = inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); /* @@ -2583,6 +2576,25 @@ void ext4_set_inode_flags(struct inode *inode) inode->i_flags |= S_DIRSYNC; } +/* Propagate flags from i_flags to EXT4_I(inode)->i_flags */ +void ext4_get_inode_flags(struct ext4_inode_info *ei) +{ + unsigned int flags = ei->vfs_inode.i_flags; + + ei->i_flags &= ~(EXT4_SYNC_FL|EXT4_APPEND_FL| + EXT4_IMMUTABLE_FL|EXT4_NOATIME_FL|EXT4_DIRSYNC_FL); + if (flags & S_SYNC) + ei->i_flags |= EXT4_SYNC_FL; + if (flags & S_APPEND) + ei->i_flags |= EXT4_APPEND_FL; + if (flags & S_IMMUTABLE) + ei->i_flags |= EXT4_IMMUTABLE_FL; + if (flags & S_NOATIME) + ei->i_flags |= EXT4_NOATIME_FL; + if (flags & S_DIRSYNC) + ei->i_flags |= EXT4_DIRSYNC_FL; +} + void ext4_read_inode(struct inode * inode) { struct ext4_iloc iloc; @@ -2610,10 +2622,6 @@ void ext4_read_inode(struct inode * inode) } inode->i_nlink = le16_to_cpu(raw_inode->i_links_count); inode->i_size = le32_to_cpu(raw_inode->i_size); - inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime); - inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime); - inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime); - inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0; ei->i_state = 0; ei->i_dir_start_lookup = 0; @@ -2691,6 +2699,11 @@ void ext4_read_inode(struct inode * inode) } else ei->i_extra_isize = 0; + EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode); + EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode); + EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode); + EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode); + if (S_ISREG(inode->i_mode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; @@ -2744,6 +2757,7 @@ static int ext4_do_update_inode(handle_t *handle, if (ei->i_state & EXT4_STATE_NEW) memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size); + ext4_get_inode_flags(ei); raw_inode->i_mode = cpu_to_le16(inode->i_mode); if(!(test_opt(inode->i_sb, NO_UID32))) { raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); @@ -2771,9 +2785,12 @@ static int ext4_do_update_inode(handle_t *handle, } raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); raw_inode->i_size = cpu_to_le32(ei->i_disksize); - raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec); - raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); - raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); + + EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode); + EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode); + EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode); + EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode); + raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); raw_inode->i_flags = cpu_to_le32(ei->i_flags); @@ -2886,7 +2903,7 @@ int ext4_write_inode(struct inode *inode, int wait) return 0; if (ext4_journal_current_handle()) { - jbd_debug(0, "called recursively, non-PF_MEMALLOC!\n"); + jbd_debug(1, "called recursively, non-PF_MEMALLOC!\n"); dump_stack(); return -EIO; } @@ -3082,6 +3099,39 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, } /* + * Expand an inode by new_extra_isize bytes. + * Returns 0 on success or negative error number on failure. + */ +int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize, + struct ext4_iloc iloc, handle_t *handle) +{ + struct ext4_inode *raw_inode; + struct ext4_xattr_ibody_header *header; + struct ext4_xattr_entry *entry; + + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) + return 0; + + raw_inode = ext4_raw_inode(&iloc); + + header = IHDR(inode, raw_inode); + entry = IFIRST(header); + + /* No extended attributes present */ + if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) || + header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) { + memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0, + new_extra_isize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; + return 0; + } + + /* try to expand with EAs present */ + return ext4_expand_extra_isize_ea(inode, new_extra_isize, + raw_inode, handle); +} + +/* * What we do here is to mark the in-core inode as clean with respect to inode * dirtiness (it may still be data-dirty). * This means that the in-core inode may be reaped by prune_icache @@ -3105,10 +3155,38 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) { struct ext4_iloc iloc; - int err; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + static unsigned int mnt_count; + int err, ret; might_sleep(); err = ext4_reserve_inode_write(handle, inode, &iloc); + if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize && + !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) { + /* + * We need extra buffer credits since we may write into EA block + * with this same handle. If journal_extend fails, then it will + * only result in a minor loss of functionality for that inode. + * If this is felt to be critical, then e2fsck should be run to + * force a large enough s_min_extra_isize. + */ + if ((jbd2_journal_extend(handle, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) { + ret = ext4_expand_extra_isize(inode, + sbi->s_want_extra_isize, + iloc, handle); + if (ret) { + EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND; + if (mnt_count != sbi->s_es->s_mnt_count) { + ext4_warning(inode->i_sb, __FUNCTION__, + "Unable to expand inode %lu. Delete" + " some EAs or run e2fsck.", + inode->i_ino); + mnt_count = sbi->s_es->s_mnt_count; + } + } + } + } if (!err) err = ext4_mark_iloc_dirty(handle, inode, &iloc); return err; @@ -3197,7 +3275,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) */ journal = EXT4_JOURNAL(inode); - if (is_journal_aborted(journal) || IS_RDONLY(inode)) + if (is_journal_aborted(journal)) return -EROFS; jbd2_journal_lock_updates(journal); diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 500567d..c04c7cc 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -28,6 +28,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, switch (cmd) { case EXT4_IOC_GETFLAGS: + ext4_get_inode_flags(ei); flags = ei->i_flags & EXT4_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT4_IOC_SETFLAGS: { @@ -40,7 +41,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) @@ -96,7 +97,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ei->i_flags = flags; ext4_set_inode_flags(inode); - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); err = ext4_mark_iloc_dirty(handle, inode, &iloc); flags_err: @@ -121,7 +122,7 @@ flags_err: __u32 generation; int err; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; @@ -133,14 +134,14 @@ flags_err: return PTR_ERR(handle); err = ext4_reserve_inode_write(handle, inode, &iloc); if (err == 0) { - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); inode->i_generation = generation; err = ext4_mark_iloc_dirty(handle, inode, &iloc); } ext4_journal_stop(handle); return err; } -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG case EXT4_IOC_WAIT_FOR_READONLY: /* * This is racy - by the time we're woken up and running, @@ -180,7 +181,7 @@ flags_err: if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(rsv_window_size, (int __user *)arg)) @@ -282,7 +283,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC32_SETVERSION_OLD: cmd = EXT4_IOC_SETVERSION_OLD; break; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG case EXT4_IOC32_WAIT_FOR_READONLY: cmd = EXT4_IOC_WAIT_FOR_READONLY; break; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2811e57..da22497 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1017,6 +1017,11 @@ static struct dentry *ext4_lookup(struct inode * dir, struct dentry *dentry, str if (!inode) return ERR_PTR(-EACCES); + + if (is_bad_inode(inode)) { + iput(inode); + return ERR_PTR(-ENOENT); + } } return d_splice_alias(inode, dentry); } @@ -1052,6 +1057,11 @@ struct dentry *ext4_get_parent(struct dentry *child) if (!inode) return ERR_PTR(-EACCES); + if (is_bad_inode(inode)) { + iput(inode); + return ERR_PTR(-ENOENT); + } + parent = d_alloc_anon(inode); if (!parent) { iput(inode); @@ -1285,7 +1295,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, * happen is that the times are slightly out of date * and/or different from the directory change time. */ - dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_mtime = dir->i_ctime = ext4_current_time(dir); ext4_update_dx_flag(dir); dir->i_version++; ext4_mark_inode_dirty(handle, dir); @@ -1619,6 +1629,35 @@ static int ext4_delete_entry (handle_t *handle, return -ENOENT; } +/* + * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2, + * since this indicates that nlinks count was previously 1. + */ +static void ext4_inc_count(handle_t *handle, struct inode *inode) +{ + inc_nlink(inode); + if (is_dx(inode) && inode->i_nlink > 1) { + /* limit is 16-bit i_links_count */ + if (inode->i_nlink >= EXT4_LINK_MAX || inode->i_nlink == 2) { + inode->i_nlink = 1; + EXT4_SET_RO_COMPAT_FEATURE(inode->i_sb, + EXT4_FEATURE_RO_COMPAT_DIR_NLINK); + } + } +} + +/* + * If a directory had nlink == 1, then we should let it be 1. This indicates + * directory has >EXT4_LINK_MAX subdirs. + */ +static void ext4_dec_count(handle_t *handle, struct inode *inode) +{ + drop_nlink(inode); + if (S_ISDIR(inode->i_mode) && inode->i_nlink == 0) + inc_nlink(inode); +} + + static int ext4_add_nondir(handle_t *handle, struct dentry *dentry, struct inode *inode) { @@ -1715,7 +1754,7 @@ static int ext4_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct ext4_dir_entry_2 * de; int err, retries = 0; - if (dir->i_nlink >= EXT4_LINK_MAX) + if (EXT4_DIR_LINK_MAX(dir)) return -EMLINK; retry: @@ -1738,7 +1777,7 @@ retry: inode->i_size = EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize; dir_block = ext4_bread (handle, inode, 0, 1, &err); if (!dir_block) { - drop_nlink(inode); /* is this nlink == 0? */ + ext4_dec_count(handle, inode); /* is this nlink == 0? */ ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; @@ -1770,7 +1809,7 @@ retry: iput (inode); goto out_stop; } - inc_nlink(dir); + ext4_inc_count(handle, dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); d_instantiate(dentry, inode); @@ -2035,9 +2074,9 @@ static int ext4_rmdir (struct inode * dir, struct dentry *dentry) retval = ext4_delete_entry(handle, dir, de, bh); if (retval) goto end_rmdir; - if (inode->i_nlink != 2) + if (!EXT4_DIR_LINK_EMPTY(inode)) ext4_warning (inode->i_sb, "ext4_rmdir", - "empty directory has nlink!=2 (%d)", + "empty directory has too many links (%d)", inode->i_nlink); inode->i_version++; clear_nlink(inode); @@ -2046,9 +2085,9 @@ static int ext4_rmdir (struct inode * dir, struct dentry *dentry) * recovery. */ inode->i_size = 0; ext4_orphan_add(handle, inode); - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; + inode->i_ctime = dir->i_ctime = dir->i_mtime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); - drop_nlink(dir); + ext4_dec_count(handle, dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); @@ -2096,13 +2135,13 @@ static int ext4_unlink(struct inode * dir, struct dentry *dentry) retval = ext4_delete_entry(handle, dir, de, bh); if (retval) goto end_unlink; - dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; + dir->i_ctime = dir->i_mtime = ext4_current_time(dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); - drop_nlink(inode); + ext4_dec_count(handle, inode); if (!inode->i_nlink) ext4_orphan_add(handle, inode); - inode->i_ctime = dir->i_ctime; + inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); retval = 0; @@ -2149,7 +2188,7 @@ retry: err = __page_symlink(inode, symname, l, mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); if (err) { - drop_nlink(inode); + ext4_dec_count(handle, inode); ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; @@ -2175,8 +2214,9 @@ static int ext4_link (struct dentry * old_dentry, struct inode *inode = old_dentry->d_inode; int err, retries = 0; - if (inode->i_nlink >= EXT4_LINK_MAX) + if (EXT4_DIR_LINK_MAX(inode)) return -EMLINK; + /* * Return -ENOENT if we've raced with unlink and i_nlink is 0. Doing * otherwise has the potential to corrupt the orphan inode list. @@ -2193,8 +2233,8 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode->i_ctime = CURRENT_TIME_SEC; - inc_nlink(inode); + inode->i_ctime = ext4_current_time(inode); + ext4_inc_count(handle, inode); atomic_inc(&inode->i_count); err = ext4_add_nondir(handle, dentry, inode); @@ -2295,7 +2335,7 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, * Like most other Unix systems, set the ctime for inodes on a * rename. */ - old_inode->i_ctime = CURRENT_TIME_SEC; + old_inode->i_ctime = ext4_current_time(old_inode); ext4_mark_inode_dirty(handle, old_inode); /* @@ -2327,10 +2367,10 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, } if (new_inode) { - drop_nlink(new_inode); - new_inode->i_ctime = CURRENT_TIME_SEC; + ext4_dec_count(handle, new_inode); + new_inode->i_ctime = ext4_current_time(new_inode); } - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; + old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir); ext4_update_dx_flag(old_dir); if (dir_bh) { BUFFER_TRACE(dir_bh, "get_write_access"); @@ -2338,11 +2378,13 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino); BUFFER_TRACE(dir_bh, "call ext4_journal_dirty_metadata"); ext4_journal_dirty_metadata(handle, dir_bh); - drop_nlink(old_dir); + ext4_dec_count(handle, old_dir); if (new_inode) { - drop_nlink(new_inode); + /* checked empty_dir above, can't have another parent, + * ext3_dec_count() won't work for many-linked dirs */ + new_inode->i_nlink = 0; } else { - inc_nlink(new_dir); + ext4_inc_count(handle, new_dir); ext4_update_dx_flag(new_dir); ext4_mark_inode_dirty(handle, new_dir); } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 175b68c..75adbb6 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -29,12 +29,14 @@ #include <linux/parser.h> #include <linux/smp_lock.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/vfs.h> #include <linux/random.h> #include <linux/mount.h> #include <linux/namei.h> #include <linux/quotaops.h> #include <linux/seq_file.h> +#include <linux/log2.h> #include <asm/uaccess.h> @@ -510,6 +512,14 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) static void ext4_destroy_inode(struct inode *inode) { + if (!list_empty(&(EXT4_I(inode)->i_orphan))) { + printk("EXT4 Inode %p: orphan list check failed!\n", + EXT4_I(inode)); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 16, 4, + EXT4_I(inode), sizeof(struct ext4_inode_info), + true); + dump_stack(); + } kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); } @@ -531,7 +541,7 @@ static int init_inodecache(void) sizeof(struct ext4_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (ext4_inode_cachep == NULL) return -ENOMEM; return 0; @@ -725,7 +735,7 @@ enum { Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, - Opt_grpquota, Opt_extents, + Opt_grpquota, Opt_extents, Opt_noextents, }; static match_table_t tokens = { @@ -776,6 +786,7 @@ static match_table_t tokens = { {Opt_usrquota, "usrquota"}, {Opt_barrier, "barrier=%u"}, {Opt_extents, "extents"}, + {Opt_noextents, "noextents"}, {Opt_err, NULL}, {Opt_resize, "resize"}, }; @@ -1111,6 +1122,9 @@ clear_qf_name: case Opt_extents: set_opt (sbi->s_mount_opt, EXTENTS); break; + case Opt_noextents: + clear_opt (sbi->s_mount_opt, EXTENTS); + break; default: printk (KERN_ERR "EXT4-fs: Unrecognized mount option \"%s\" " @@ -1542,6 +1556,12 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) set_opt(sbi->s_mount_opt, RESERVATION); + /* + * turn on extents feature by default in ext4 filesystem + * User -o noextents to turn it off + */ + set_opt(sbi->s_mount_opt, EXTENTS); + if (!parse_options ((char *) data, sb, &journal_inum, &journal_devnum, NULL, 0)) goto failed_mount; @@ -1625,13 +1645,15 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || - (sbi->s_inode_size & (sbi->s_inode_size - 1)) || + (!is_power_of_2(sbi->s_inode_size)) || (sbi->s_inode_size > blocksize)) { printk (KERN_ERR "EXT4-fs: unsupported inode size: %d\n", sbi->s_inode_size); goto failed_mount; } + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) + sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2); } sbi->s_frag_size = EXT4_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); @@ -1794,6 +1816,13 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) goto failed_mount3; } + if (ext4_blocks_count(es) > 0xffffffffULL && + !jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0, + JBD2_FEATURE_INCOMPAT_64BIT)) { + printk(KERN_ERR "ext4: Failed to set 64-bit journal feature\n"); + goto failed_mount4; + } + /* We have now updated the journal if required, so we can * validate the data journaling mode. */ switch (test_opt(sb, DATA_FLAGS)) { @@ -1848,6 +1877,32 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) } ext4_setup_super (sb, es, sb->s_flags & MS_RDONLY); + + /* determine the minimum size of new large inodes, if present */ + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) { + if (sbi->s_want_extra_isize < + le16_to_cpu(es->s_want_extra_isize)) + sbi->s_want_extra_isize = + le16_to_cpu(es->s_want_extra_isize); + if (sbi->s_want_extra_isize < + le16_to_cpu(es->s_min_extra_isize)) + sbi->s_want_extra_isize = + le16_to_cpu(es->s_min_extra_isize); + } + } + /* Check if enough inode space is available */ + if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize > + sbi->s_inode_size) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + printk(KERN_INFO "EXT4-fs: required extra inode space not" + "available.\n"); + } + /* * akpm: core read_super() calls in here with the superblock locked. * That deadlocks, because orphan cleanup needs to lock the superblock @@ -2150,6 +2205,7 @@ static int ext4_create_journal(struct super_block * sb, unsigned int journal_inum) { journal_t *journal; + int err; if (sb->s_flags & MS_RDONLY) { printk(KERN_ERR "EXT4-fs: readonly filesystem when trying to " @@ -2157,13 +2213,15 @@ static int ext4_create_journal(struct super_block * sb, return -EROFS; } - if (!(journal = ext4_get_journal(sb, journal_inum))) + journal = ext4_get_journal(sb, journal_inum); + if (!journal) return -EINVAL; printk(KERN_INFO "EXT4-fs: creating new journal on inode %u\n", journal_inum); - if (jbd2_journal_create(journal)) { + err = jbd2_journal_create(journal); + if (err) { printk(KERN_ERR "EXT4-fs: error creating journal.\n"); jbd2_journal_destroy(journal); return -EIO; @@ -2214,12 +2272,14 @@ static void ext4_mark_recovery_complete(struct super_block * sb, jbd2_journal_lock_updates(journal); jbd2_journal_flush(journal); + lock_super(sb); if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER) && sb->s_flags & MS_RDONLY) { EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); sb->s_dirt = 0; ext4_commit_super(sb, es, 1); } + unlock_super(sb); jbd2_journal_unlock_updates(journal); } @@ -2408,7 +2468,13 @@ static int ext4_remount (struct super_block * sb, int * flags, char * data) (sbi->s_mount_state & EXT4_VALID_FS)) es->s_state = cpu_to_le16(sbi->s_mount_state); + /* + * We have to unlock super so that we can wait for + * transactions. + */ + unlock_super(sb); ext4_mark_recovery_complete(sb, es); + lock_super(sb); } else { __le32 ret; if ((ret = EXT4_HAS_RO_COMPAT_FEATURE(sb, @@ -2481,19 +2547,19 @@ static int ext4_statfs (struct dentry * dentry, struct kstatfs * buf) struct super_block *sb = dentry->d_sb; struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_super_block *es = sbi->s_es; - ext4_fsblk_t overhead; - int i; u64 fsid; - if (test_opt (sb, MINIX_DF)) - overhead = 0; - else { - unsigned long ngroups; - ngroups = EXT4_SB(sb)->s_groups_count; + if (test_opt(sb, MINIX_DF)) { + sbi->s_overhead_last = 0; + } else if (sbi->s_blocks_last != le32_to_cpu(es->s_blocks_count)) { + unsigned long ngroups = sbi->s_groups_count, i; + ext4_fsblk_t overhead = 0; smp_rmb(); /* - * Compute the overhead (FS structures) + * Compute the overhead (FS structures). This is constant + * for a given filesystem unless the number of block groups + * changes so we cache the previous value until it does. */ /* @@ -2517,18 +2583,23 @@ static int ext4_statfs (struct dentry * dentry, struct kstatfs * buf) * Every block group has an inode bitmap, a block * bitmap, and an inode table. */ - overhead += (ngroups * (2 + EXT4_SB(sb)->s_itb_per_group)); + overhead += ngroups * (2 + sbi->s_itb_per_group); + sbi->s_overhead_last = overhead; + smp_wmb(); + sbi->s_blocks_last = le32_to_cpu(es->s_blocks_count); } buf->f_type = EXT4_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = ext4_blocks_count(es) - overhead; + buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; buf->f_bfree = percpu_counter_sum(&sbi->s_freeblocks_counter); + es->s_free_blocks_count = cpu_to_le32(buf->f_bfree); buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es); if (buf->f_bfree < ext4_r_blocks_count(es)) buf->f_bavail = 0; buf->f_files = le32_to_cpu(es->s_inodes_count); buf->f_ffree = percpu_counter_sum(&sbi->s_freeinodes_counter); + es->s_free_inodes_count = cpu_to_le32(buf->f_ffree); buf->f_namelen = EXT4_NAME_LEN; fsid = le64_to_cpup((void *)es->s_uuid) ^ le64_to_cpup((void *)es->s_uuid + sizeof(u64)); diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index e832e96..b10d68f 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -66,13 +66,6 @@ #define BFIRST(bh) ENTRY(BHDR(bh)+1) #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) -#define IHDR(inode, raw_inode) \ - ((struct ext4_xattr_ibody_header *) \ - ((void *)raw_inode + \ - EXT4_GOOD_OLD_INODE_SIZE + \ - EXT4_I(inode)->i_extra_isize)) -#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) - #ifdef EXT4_XATTR_DEBUG # define ea_idebug(inode, f...) do { \ printk(KERN_DEBUG "inode %s:%lu: ", \ @@ -508,6 +501,24 @@ out: return; } +/* + * Find the available free space for EAs. This also returns the total number of + * bytes used by EA entries. + */ +static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last, + size_t *min_offs, void *base, int *total) +{ + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + *total += EXT4_XATTR_LEN(last->e_name_len); + if (!last->e_value_block && last->e_value_size) { + size_t offs = le16_to_cpu(last->e_value_offs); + if (offs < *min_offs) + *min_offs = offs; + } + } + return (*min_offs - ((void *)last - base) - sizeof(__u32)); +} + struct ext4_xattr_info { int name_index; const char *name; @@ -1013,7 +1024,9 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, } if (!error) { ext4_xattr_update_super_block(handle, inode->i_sb); - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); + if (!value) + EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND; error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); /* * The bh is consumed by ext4_mark_iloc_dirty, even with @@ -1067,6 +1080,253 @@ retry: } /* + * Shift the EA entries in the inode to create space for the increased + * i_extra_isize. + */ +static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry, + int value_offs_shift, void *to, + void *from, size_t n, int blocksize) +{ + struct ext4_xattr_entry *last = entry; + int new_offs; + + /* Adjust the value offsets of the entries */ + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + if (!last->e_value_block && last->e_value_size) { + new_offs = le16_to_cpu(last->e_value_offs) + + value_offs_shift; + BUG_ON(new_offs + le32_to_cpu(last->e_value_size) + > blocksize); + last->e_value_offs = cpu_to_le16(new_offs); + } + } + /* Shift the entries by n bytes */ + memmove(to, from, n); +} + +/* + * Expand an inode by new_extra_isize bytes when EAs are present. + * Returns 0 on success or negative error number on failure. + */ +int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle) +{ + struct ext4_xattr_ibody_header *header; + struct ext4_xattr_entry *entry, *last, *first; + struct buffer_head *bh = NULL; + struct ext4_xattr_ibody_find *is = NULL; + struct ext4_xattr_block_find *bs = NULL; + char *buffer = NULL, *b_entry_name = NULL; + size_t min_offs, free; + int total_ino, total_blk; + void *base, *start, *end; + int extra_isize = 0, error = 0, tried_min_extra_isize = 0; + int s_min_extra_isize = EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize; + + down_write(&EXT4_I(inode)->xattr_sem); +retry: + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { + up_write(&EXT4_I(inode)->xattr_sem); + return 0; + } + + header = IHDR(inode, raw_inode); + entry = IFIRST(header); + + /* + * Check if enough free space is available in the inode to shift the + * entries ahead by new_extra_isize. + */ + + base = start = entry; + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; + min_offs = end - base; + last = entry; + total_ino = sizeof(struct ext4_xattr_ibody_header); + + free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); + if (free >= new_extra_isize) { + entry = IFIRST(header); + ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize + - new_extra_isize, (void *)raw_inode + + EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize, + (void *)header, total_ino, + inode->i_sb->s_blocksize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; + error = 0; + goto cleanup; + } + + /* + * Enough free space isn't available in the inode, check if + * EA block can hold new_extra_isize bytes. + */ + if (EXT4_I(inode)->i_file_acl) { + bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); + error = -EIO; + if (!bh) + goto cleanup; + if (ext4_xattr_check_block(bh)) { + ext4_error(inode->i_sb, __FUNCTION__, + "inode %lu: bad block %llu", inode->i_ino, + EXT4_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + base = BHDR(bh); + first = BFIRST(bh); + end = bh->b_data + bh->b_size; + min_offs = end - base; + free = ext4_xattr_free_space(first, &min_offs, base, + &total_blk); + if (free < new_extra_isize) { + if (!tried_min_extra_isize && s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + brelse(bh); + goto retry; + } + error = -1; + goto cleanup; + } + } else { + free = inode->i_sb->s_blocksize; + } + + while (new_extra_isize > 0) { + size_t offs, size, entry_size; + struct ext4_xattr_entry *small_entry = NULL; + struct ext4_xattr_info i = { + .value = NULL, + .value_len = 0, + }; + unsigned int total_size; /* EA entry size + value size */ + unsigned int shift_bytes; /* No. of bytes to shift EAs by? */ + unsigned int min_total_size = ~0U; + + is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS); + bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS); + if (!is || !bs) { + error = -ENOMEM; + goto cleanup; + } + + is->s.not_found = -ENODATA; + bs->s.not_found = -ENODATA; + is->iloc.bh = NULL; + bs->bh = NULL; + + last = IFIRST(header); + /* Find the entry best suited to be pushed into EA block */ + entry = NULL; + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + total_size = + EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) + + EXT4_XATTR_LEN(last->e_name_len); + if (total_size <= free && total_size < min_total_size) { + if (total_size < new_extra_isize) { + small_entry = last; + } else { + entry = last; + min_total_size = total_size; + } + } + } + + if (entry == NULL) { + if (small_entry) { + entry = small_entry; + } else { + if (!tried_min_extra_isize && + s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + goto retry; + } + error = -1; + goto cleanup; + } + } + offs = le16_to_cpu(entry->e_value_offs); + size = le32_to_cpu(entry->e_value_size); + entry_size = EXT4_XATTR_LEN(entry->e_name_len); + i.name_index = entry->e_name_index, + buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS); + b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS); + if (!buffer || !b_entry_name) { + error = -ENOMEM; + goto cleanup; + } + /* Save the entry name and the entry value */ + memcpy(buffer, (void *)IFIRST(header) + offs, + EXT4_XATTR_SIZE(size)); + memcpy(b_entry_name, entry->e_name, entry->e_name_len); + b_entry_name[entry->e_name_len] = '\0'; + i.name = b_entry_name; + + error = ext4_get_inode_loc(inode, &is->iloc); + if (error) + goto cleanup; + + error = ext4_xattr_ibody_find(inode, &i, is); + if (error) + goto cleanup; + + /* Remove the chosen entry from the inode */ + error = ext4_xattr_ibody_set(handle, inode, &i, is); + + entry = IFIRST(header); + if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize) + shift_bytes = new_extra_isize; + else + shift_bytes = entry_size + size; + /* Adjust the offsets and shift the remaining entries ahead */ + ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize - + shift_bytes, (void *)raw_inode + + EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes, + (void *)header, total_ino - entry_size, + inode->i_sb->s_blocksize); + + extra_isize += shift_bytes; + new_extra_isize -= shift_bytes; + EXT4_I(inode)->i_extra_isize = extra_isize; + + i.name = b_entry_name; + i.value = buffer; + i.value_len = cpu_to_le32(size); + error = ext4_xattr_block_find(inode, &i, bs); + if (error) + goto cleanup; + + /* Add entry which was removed from the inode into the block */ + error = ext4_xattr_block_set(handle, inode, &i, bs); + if (error) + goto cleanup; + kfree(b_entry_name); + kfree(buffer); + brelse(is->iloc.bh); + kfree(is); + kfree(bs); + } + brelse(bh); + up_write(&EXT4_I(inode)->xattr_sem); + return 0; + +cleanup: + kfree(b_entry_name); + kfree(buffer); + if (is) + brelse(is->iloc.bh); + kfree(is); + kfree(bs); + brelse(bh); + up_write(&EXT4_I(inode)->xattr_sem); + return error; +} + + + +/* * ext4_xattr_delete_inode() * * Free extended attribute resources associated with this inode. This diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 79432b3..d7f5d6a 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -56,6 +56,13 @@ struct ext4_xattr_entry { #define EXT4_XATTR_SIZE(size) \ (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND) +#define IHDR(inode, raw_inode) \ + ((struct ext4_xattr_ibody_header *) \ + ((void *)raw_inode + \ + EXT4_GOOD_OLD_INODE_SIZE + \ + EXT4_I(inode)->i_extra_isize)) +#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) + # ifdef CONFIG_EXT4DEV_FS_XATTR extern struct xattr_handler ext4_xattr_user_handler; @@ -74,6 +81,9 @@ extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, extern void ext4_xattr_delete_inode(handle_t *, struct inode *); extern void ext4_xattr_put_super(struct super_block *); +extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle); + extern int init_ext4_xattr(void); extern void exit_ext4_xattr(void); @@ -129,6 +139,13 @@ exit_ext4_xattr(void) { } +static inline int +ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle) +{ + return -EOPNOTSUPP; +} + #define ext4_xattr_handlers NULL # endif /* CONFIG_EXT4DEV_FS_XATTR */ diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 3c9c8a1..be6f89b 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -48,7 +48,7 @@ int __init fat_cache_init(void) fat_cache_cachep = kmem_cache_create("fat_cache", sizeof(struct fat_cache), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - init_once, NULL); + init_once); if (fat_cache_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/fat/dir.c b/fs/fat/dir.c index ccf161d..72cbcd6 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -313,7 +313,7 @@ int fat_search_long(struct inode *inode, const unsigned char *name, wchar_t bufuname[14]; unsigned char xlate_len, nr_slots; wchar_t *unicode = NULL; - unsigned char work[8], bufname[260]; /* 256 + 4 */ + unsigned char work[MSDOS_NAME], bufname[260]; /* 256 + 4 */ int uni_xlate = sbi->options.unicode_xlate; int utf8 = sbi->options.utf8; int anycase = (sbi->options.name_check != 's'); @@ -351,7 +351,8 @@ parse_record: if (work[0] == 0x05) work[0] = 0xE5; for (i = 0, j = 0, last_u = 0; i < 8;) { - if (!work[i]) break; + if (!work[i]) + break; chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_BASE); @@ -365,13 +366,15 @@ parse_record: } j = last_u; fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); - for (i = 0; i < 3;) { - if (!de->ext[i]) break; - chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i, + for (i = 8; i < MSDOS_NAME;) { + if (!work[i]) + break; + chl = fat_shortname2uni(nls_disk, &work[i], + MSDOS_NAME - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { - if (de->ext[i] != ' ') + if (work[i] != ' ') last_u = j; } else { last_u = j; @@ -445,7 +448,7 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, int fill_len; wchar_t bufuname[14]; wchar_t *unicode = NULL; - unsigned char c, work[8], bufname[56], *ptname = bufname; + unsigned char c, work[MSDOS_NAME], bufname[56], *ptname = bufname; unsigned long lpos, dummy, *furrfu = &lpos; int uni_xlate = sbi->options.unicode_xlate; int isvfat = sbi->options.isvfat; @@ -527,7 +530,8 @@ parse_record: if (work[0] == 0x05) work[0] = 0xE5; for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) { - if (!(c = work[i])) break; + if (!(c = work[i])) + break; chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_BASE); @@ -549,9 +553,10 @@ parse_record: j = last_u; fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); ptname[i++] = '.'; - for (i2 = 0; i2 < 3;) { - if (!(c = de->ext[i2])) break; - chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2, + for (i2 = 8; i2 < MSDOS_NAME;) { + if (!(c = work[i2])) + break; + chl = fat_shortname2uni(nls_disk, &work[i2], MSDOS_NAME - i2, &bufuname[j++], opt_shortname, de->lcase & CASE_LOWER_EXT); if (chl <= 1) { @@ -563,8 +568,8 @@ parse_record: } } else { last_u = j; - for (chi = 0; chi < chl && i2 < 3; chi++) { - ptname[i++] = de->ext[i2++]; + for (chi = 0; chi < chl && i2 < MSDOS_NAME; chi++) { + ptname[i++] = work[i2++]; last = i; } } diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index ab171ea..2c1b73f 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -17,6 +17,8 @@ struct fatent_operations { int (*ent_next)(struct fat_entry *); }; +static DEFINE_SPINLOCK(fat12_entry_lock); + static void fat12_ent_blocknr(struct super_block *sb, int entry, int *offset, sector_t *blocknr) { @@ -116,10 +118,13 @@ static int fat12_ent_get(struct fat_entry *fatent) u8 **ent12_p = fatent->u.ent12_p; int next; + spin_lock(&fat12_entry_lock); if (fatent->entry & 1) next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4); else next = (*ent12_p[1] << 8) | *ent12_p[0]; + spin_unlock(&fat12_entry_lock); + next &= 0x0fff; if (next >= BAD_FAT12) next = FAT_ENT_EOF; @@ -151,6 +156,7 @@ static void fat12_ent_put(struct fat_entry *fatent, int new) if (new == FAT_ENT_EOF) new = EOF_FAT12; + spin_lock(&fat12_entry_lock); if (fatent->entry & 1) { *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f); *ent12_p[1] = new >> 4; @@ -158,6 +164,7 @@ static void fat12_ent_put(struct fat_entry *fatent, int new) *ent12_p[0] = new & 0xff; *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8); } + spin_unlock(&fat12_entry_lock); mark_buffer_dirty(fatent->bhs[0]); if (fatent->nr_bhs == 2) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 479722d..4baa5f2 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -20,6 +20,7 @@ #include <linux/pagemap.h> #include <linux/mpage.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/mount.h> #include <linux/vfs.h> #include <linux/parser.h> @@ -354,8 +355,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) } else { /* not a directory */ inode->i_generation |= 1; inode->i_mode = MSDOS_MKMODE(de->attr, - ((sbi->options.showexec && - !is_exec(de->ext)) + ((sbi->options.showexec && !is_exec(de->name + 8)) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~sbi->options.fs_fmask) | S_IFREG; MSDOS_I(inode)->i_start = le16_to_cpu(de->start); @@ -514,7 +514,7 @@ static int __init fat_init_inodecache(void) sizeof(struct msdos_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (fat_inode_cachep == NULL) return -ENOMEM; return 0; @@ -215,7 +215,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg) /* O_NOATIME can only be set by the owner or superuser */ if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME)) - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; /* required for strict SunOS emulation */ @@ -638,7 +638,7 @@ EXPORT_SYMBOL(kill_fasync); static int __init fasync_init(void) { fasync_cache = kmem_cache_create("fasync_cache", - sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL, NULL); + sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL); return 0; } diff --git a/fs/freevxfs/vxfs_dir.h b/fs/freevxfs/vxfs_dir.h index 8a4dfef..3c96d6e 100644 --- a/fs/freevxfs/vxfs_dir.h +++ b/fs/freevxfs/vxfs_dir.h @@ -80,7 +80,7 @@ struct vxfs_direct { * a d_name with size len. */ #define VXFS_DIRPAD 4 -#define VXFS_NAMEMIN ((int)((struct vxfs_direct *)0)->d_name) +#define VXFS_NAMEMIN offsetof(struct vxfs_direct, d_name) #define VXFS_DIRROUND(len) ((VXFS_DIRPAD + (len) - 1) & ~(VXFS_DIRPAD -1)) #define VXFS_DIRLEN(len) (VXFS_DIRROUND(VXFS_NAMEMIN + (len))) diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index 647d600..4f95572 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -263,8 +263,8 @@ vxfs_init(void) int rv; vxfs_inode_cachep = kmem_cache_create("vxfs_inode", - sizeof(struct vxfs_inode_info), 0, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL, NULL); + sizeof(struct vxfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); if (!vxfs_inode_cachep) return -ENOMEM; rv = register_filesystem(&vxfs_fs_type); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 357764d..3ad22be 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1044,7 +1044,7 @@ int __init fuse_dev_init(void) int err = -ENOMEM; fuse_req_cachep = kmem_cache_create("fuse_request", sizeof(struct fuse_req), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!fuse_req_cachep) goto out; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index cc5efc1..5448f62 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -706,7 +706,7 @@ static int __init fuse_fs_init(void) fuse_inode_cachep = kmem_cache_create("fuse_inode", sizeof(struct fuse_inode), 0, SLAB_HWCACHE_ALIGN, - fuse_inode_init_once, NULL); + fuse_inode_init_once); err = -ENOMEM; if (!fuse_inode_cachep) goto out_unreg2; diff --git a/fs/generic_acl.c b/fs/generic_acl.c index 9ccb789..995d63b 100644 --- a/fs/generic_acl.c +++ b/fs/generic_acl.c @@ -78,7 +78,7 @@ generic_acl_set(struct inode *inode, struct generic_acl_operations *ops, if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { acl = posix_acl_from_xattr(value, size); diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 6e80844..1047a8c 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -74,7 +74,7 @@ int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access) { if (!GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl) return -EOPNOTSUPP; - if (current->fsuid != ip->i_inode.i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(&ip->i_inode)) return -EPERM; if (S_ISLNK(ip->i_inode.i_mode)) return -EOPNOTSUPP; diff --git a/fs/gfs2/eaops.c b/fs/gfs2/eaops.c index c1f4400..1ab3e9d 100644 --- a/fs/gfs2/eaops.c +++ b/fs/gfs2/eaops.c @@ -11,6 +11,7 @@ #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/buffer_head.h> +#include <linux/capability.h> #include <linux/xattr.h> #include <linux/gfs2_ondisk.h> #include <linux/lm_interface.h> diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 787a0ed..d5d4e68 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -72,7 +72,7 @@ static int __init init_gfs2_fs(void) gfs2_glock_cachep = kmem_cache_create("gfs2_glock", sizeof(struct gfs2_glock), 0, 0, - gfs2_init_glock_once, NULL); + gfs2_init_glock_once); if (!gfs2_glock_cachep) goto fail; @@ -80,13 +80,13 @@ static int __init init_gfs2_fs(void) sizeof(struct gfs2_inode), 0, SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD, - gfs2_init_inode_once, NULL); + gfs2_init_inode_once); if (!gfs2_inode_cachep) goto fail; gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata", sizeof(struct gfs2_bufdata), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!gfs2_bufdata_cachep) goto fail; diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 26c8888..ce90032 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -251,7 +251,7 @@ static int gfs2_readpage(struct file *file, struct page *page) if (file) { gf = file->private_data; if (test_bit(GFF_EXLOCK, &gf->f_flags)) - /* gfs2_sharewrite_nopage has grabbed the ip->i_gl already */ + /* gfs2_sharewrite_fault has grabbed the ip->i_gl already */ goto skip_lock; } gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|LM_FLAG_TRY_1CB, &gh); diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index 99ea565..b8312ed 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -11,6 +11,7 @@ #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/gfs2_ondisk.h> #include <linux/crc32.h> #include <linux/lm_interface.h> diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 196d832..1a5e8e8 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -489,6 +489,29 @@ static int gfs2_fsync(struct file *file, struct dentry *dentry, int datasync) } /** + * gfs2_setlease - acquire/release a file lease + * @file: the file pointer + * @arg: lease type + * @fl: file lock + * + * Returns: errno + */ + +static int gfs2_setlease(struct file *file, long arg, struct file_lock **fl) +{ + struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host); + + /* + * We don't currently have a way to enforce a lease across the whole + * cluster; until we do, disable leases (by just returning -EINVAL), + * unless the administrator has requested purely local locking. + */ + if (!sdp->sd_args.ar_localflocks) + return -EINVAL; + return setlease(file, arg, fl); +} + +/** * gfs2_lock - acquire/release a posix lock on a file * @file: the file pointer * @cmd: either modify or retrieve lock state, possibly wait @@ -638,6 +661,7 @@ const struct file_operations gfs2_file_fops = { .flock = gfs2_flock, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, + .setlease = gfs2_setlease, }; const struct file_operations gfs2_dir_fops = { diff --git a/fs/gfs2/ops_vm.c b/fs/gfs2/ops_vm.c index 404b7cc..927d739 100644 --- a/fs/gfs2/ops_vm.c +++ b/fs/gfs2/ops_vm.c @@ -27,13 +27,12 @@ #include "trans.h" #include "util.h" -static struct page *gfs2_private_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int gfs2_private_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - struct gfs2_inode *ip = GFS2_I(area->vm_file->f_mapping->host); + struct gfs2_inode *ip = GFS2_I(vma->vm_file->f_mapping->host); set_bit(GIF_PAGED, &ip->i_flags); - return filemap_nopage(area, address, type); + return filemap_fault(vma, vmf); } static int alloc_page_backing(struct gfs2_inode *ip, struct page *page) @@ -104,58 +103,67 @@ out: return error; } -static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int gfs2_sharewrite_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) { - struct file *file = area->vm_file; + struct file *file = vma->vm_file; struct gfs2_file *gf = file->private_data; struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); struct gfs2_holder i_gh; - struct page *result = NULL; - unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + - area->vm_pgoff; int alloc_required; int error; + int ret = 0; error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); if (error) - return NULL; + goto out; set_bit(GIF_PAGED, &ip->i_flags); set_bit(GIF_SW_PAGED, &ip->i_flags); - error = gfs2_write_alloc_required(ip, (u64)index << PAGE_CACHE_SHIFT, - PAGE_CACHE_SIZE, &alloc_required); - if (error) - goto out; + error = gfs2_write_alloc_required(ip, + (u64)vmf->pgoff << PAGE_CACHE_SHIFT, + PAGE_CACHE_SIZE, &alloc_required); + if (error) { + ret = VM_FAULT_OOM; /* XXX: are these right? */ + goto out_unlock; + } set_bit(GFF_EXLOCK, &gf->f_flags); - result = filemap_nopage(area, address, type); + ret = filemap_fault(vma, vmf); clear_bit(GFF_EXLOCK, &gf->f_flags); - if (!result || result == NOPAGE_OOM) - goto out; + if (ret & VM_FAULT_ERROR) + goto out_unlock; if (alloc_required) { - error = alloc_page_backing(ip, result); + /* XXX: do we need to drop page lock around alloc_page_backing?*/ + error = alloc_page_backing(ip, vmf->page); if (error) { - page_cache_release(result); - result = NULL; - goto out; + /* + * VM_FAULT_LOCKED should always be the case for + * filemap_fault, but it may not be in a future + * implementation. + */ + if (ret & VM_FAULT_LOCKED) + unlock_page(vmf->page); + page_cache_release(vmf->page); + ret = VM_FAULT_OOM; + goto out_unlock; } - set_page_dirty(result); + set_page_dirty(vmf->page); } -out: +out_unlock: gfs2_glock_dq_uninit(&i_gh); - - return result; +out: + return ret; } struct vm_operations_struct gfs2_vm_ops_private = { - .nopage = gfs2_private_nopage, + .fault = gfs2_private_fault, }; struct vm_operations_struct gfs2_vm_ops_sharewrite = { - .nopage = gfs2_sharewrite_nopage, + .fault = gfs2_sharewrite_fault, }; diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 92cf875..6c5f92d 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -443,7 +443,7 @@ static int __init init_hfs_fs(void) hfs_inode_cachep = kmem_cache_create("hfs_inode_cache", sizeof(struct hfs_inode_info), 0, SLAB_HWCACHE_ALIGN, - hfs_init_once, NULL); + hfs_init_once); if (!hfs_inode_cachep) return -ENOMEM; err = register_filesystem(&hfs_fs_type); diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 90ebab7..050d29c 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -62,8 +62,10 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) && (head->key_type == HFSPLUS_KEY_BINARY)) tree->keycmp = hfsplus_cat_bin_cmp_key; - else + else { tree->keycmp = hfsplus_cat_case_cmp_key; + HFSPLUS_SB(sb).flags |= HFSPLUS_SB_CASEFOLD; + } } else { printk(KERN_ERR "hfs: unknown B*Tree requested\n"); goto fail_page; diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 80b5682..1955ee6 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -36,6 +36,8 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, u16 type; sb = dir->i_sb; + + dentry->d_op = &hfsplus_dentry_operations; dentry->d_fsdata = NULL; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 3915635..d9f5eda 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -150,6 +150,7 @@ struct hfsplus_sb_info { #define HFSPLUS_SB_NODECOMPOSE 0x0002 #define HFSPLUS_SB_FORCE 0x0004 #define HFSPLUS_SB_HFSX 0x0008 +#define HFSPLUS_SB_CASEFOLD 0x0010 struct hfsplus_inode_info { @@ -321,6 +322,7 @@ void hfsplus_file_truncate(struct inode *); /* inode.c */ extern const struct address_space_operations hfsplus_aops; extern const struct address_space_operations hfsplus_btree_aops; +extern struct dentry_operations hfsplus_dentry_operations; void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); @@ -353,6 +355,8 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *, const struct hfsplus_unist int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *); int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *); int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int); +int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str); +int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2); /* wrapper.c */ int hfsplus_read_wrapper(struct super_block *); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 409ce54..6f7c662 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -131,6 +131,11 @@ const struct address_space_operations hfsplus_aops = { .writepages = hfsplus_writepages, }; +struct dentry_operations hfsplus_dentry_operations = { + .d_hash = hfsplus_hash_dentry, + .d_compare = hfsplus_compare_dentry, +}; + static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 79fd104..b60c0af 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -38,7 +38,7 @@ int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *)arg)) diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index ebd1b38..7b0f2e5 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -283,11 +283,10 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) struct nls_table *nls = NULL; int err = -EINVAL; - sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL); + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) return -ENOMEM; - memset(sbi, 0, sizeof(HFSPLUS_SB(sb))); sb->s_fs_info = sbi; INIT_HLIST_HEAD(&sbi->rsrc_inodes); hfsplus_fill_defaults(sbi); @@ -381,6 +380,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) iput(root); goto cleanup; } + sb->s_root->d_op = &hfsplus_dentry_operations; str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; str.name = HFSP_HIDDENDIR_NAME; @@ -479,7 +479,7 @@ static int __init init_hfsplus_fs(void) hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache", HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN, - hfsplus_init_once, NULL); + hfsplus_init_once); if (!hfsplus_inode_cachep) return -ENOMEM; err = register_filesystem(&hfsplus_fs_type); diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 689c8bd..9e10f94 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -239,61 +239,201 @@ out: return res; } -int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) +/* + * Convert one or more ASCII characters into a single unicode character. + * Returns the number of ASCII characters corresponding to the unicode char. + */ +static inline int asc2unichar(struct super_block *sb, const char *astr, int len, + wchar_t *uc) { - struct nls_table *nls = HFSPLUS_SB(sb).nls; - int size, off, decompose; + int size = HFSPLUS_SB(sb).nls->char2uni(astr, len, uc); + if (size <= 0) { + *uc = '?'; + size = 1; + } + switch (*uc) { + case 0x2400: + *uc = 0; + break; + case ':': + *uc = '/'; + break; + } + return size; +} + +/* Decomposes a single unicode character. */ +static inline u16 *decompose_unichar(wchar_t uc, int *size) +{ + int off; + + off = hfsplus_decompose_table[(uc >> 12) & 0xf]; + if (off == 0 || off == 0xffff) + return NULL; + + off = hfsplus_decompose_table[off + ((uc >> 8) & 0xf)]; + if (!off) + return NULL; + + off = hfsplus_decompose_table[off + ((uc >> 4) & 0xf)]; + if (!off) + return NULL; + + off = hfsplus_decompose_table[off + (uc & 0xf)]; + *size = off & 3; + if (*size == 0) + return NULL; + return hfsplus_decompose_table + (off / 4); +} + +int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, + const char *astr, int len) +{ + int size, dsize, decompose; + u16 *dstr, outlen = 0; wchar_t c; - u16 outlen = 0; decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); - while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { - size = nls->char2uni(astr, len, &c); - if (size <= 0) { - c = '?'; - size = 1; - } - astr += size; - len -= size; - switch (c) { - case 0x2400: - c = 0; - break; - case ':': - c = '/'; - break; - } - if (c >= 0xc0 && decompose) { - off = hfsplus_decompose_table[(c >> 12) & 0xf]; - if (!off) - goto done; - if (off == 0xffff) { - goto done; - } - off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)]; - if (!off) - goto done; - off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)]; - if (!off) - goto done; - off = hfsplus_decompose_table[off + (c & 0xf)]; - size = off & 3; - if (!size) - goto done; - off /= 4; - if (outlen + size > HFSPLUS_MAX_STRLEN) + size = asc2unichar(sb, astr, len, &c); + + if (decompose && (dstr = decompose_unichar(c, &dsize))) { + if (outlen + dsize > HFSPLUS_MAX_STRLEN) break; do { - ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]); - } while (--size > 0); - continue; - } - done: - ustr->unicode[outlen++] = cpu_to_be16(c); + ustr->unicode[outlen++] = cpu_to_be16(*dstr++); + } while (--dsize > 0); + } else + ustr->unicode[outlen++] = cpu_to_be16(c); + + astr += size; + len -= size; } ustr->length = cpu_to_be16(outlen); if (len > 0) return -ENAMETOOLONG; return 0; } + +/* + * Hash a string to an integer as appropriate for the HFS+ filesystem. + * Composed unicode characters are decomposed and case-folding is performed + * if the appropriate bits are (un)set on the superblock. + */ +int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str) +{ + struct super_block *sb = dentry->d_sb; + const char *astr; + const u16 *dstr; + int casefold, decompose, size, dsize, len; + unsigned long hash; + wchar_t c; + u16 c2; + + casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD); + decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + hash = init_name_hash(); + astr = str->name; + len = str->len; + while (len > 0) { + size = asc2unichar(sb, astr, len, &c); + astr += size; + len -= size; + + if (decompose && (dstr = decompose_unichar(c, &dsize))) { + do { + c2 = *dstr++; + if (!casefold || (c2 = case_fold(c2))) + hash = partial_name_hash(c2, hash); + } while (--dsize > 0); + } else { + c2 = c; + if (!casefold || (c2 = case_fold(c2))) + hash = partial_name_hash(c2, hash); + } + } + str->hash = end_name_hash(hash); + + return 0; +} + +/* + * Compare strings with HFS+ filename ordering. + * Composed unicode characters are decomposed and case-folding is performed + * if the appropriate bits are (un)set on the superblock. + */ +int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2) +{ + struct super_block *sb = dentry->d_sb; + int casefold, decompose, size; + int dsize1, dsize2, len1, len2; + const u16 *dstr1, *dstr2; + const char *astr1, *astr2; + u16 c1, c2; + wchar_t c; + + casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD); + decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + astr1 = s1->name; + len1 = s1->len; + astr2 = s2->name; + len2 = s2->len; + dsize1 = dsize2 = 0; + dstr1 = dstr2 = NULL; + + while (len1 > 0 && len2 > 0) { + if (!dsize1) { + size = asc2unichar(sb, astr1, len1, &c); + astr1 += size; + len1 -= size; + + if (!decompose || !(dstr1 = decompose_unichar(c, &dsize1))) { + c1 = c; + dstr1 = &c1; + dsize1 = 1; + } + } + + if (!dsize2) { + size = asc2unichar(sb, astr2, len2, &c); + astr2 += size; + len2 -= size; + + if (!decompose || !(dstr2 = decompose_unichar(c, &dsize2))) { + c2 = c; + dstr2 = &c2; + dsize2 = 1; + } + } + + c1 = *dstr1; + c2 = *dstr2; + if (casefold) { + if (!(c1 = case_fold(c1))) { + dstr1++; + dsize1--; + continue; + } + if (!(c2 = case_fold(c2))) { + dstr2++; + dsize2--; + continue; + } + } + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + + dstr1++; + dsize1--; + dstr2++; + dsize2--; + } + + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + return 0; +} diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 29cc34a..89612ee 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -181,14 +181,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag mutex_init(&ei->i_parent_mutex); inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache", sizeof(struct hpfs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (hpfs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index e6b46b3..c848a19 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -13,15 +13,18 @@ #include <linux/fs.h> #include <linux/mount.h> #include <linux/file.h> +#include <linux/kernel.h> #include <linux/writeback.h> #include <linux/pagemap.h> #include <linux/highmem.h> #include <linux/init.h> #include <linux/string.h> #include <linux/capability.h> +#include <linux/ctype.h> #include <linux/backing-dev.h> #include <linux/hugetlb.h> #include <linux/pagevec.h> +#include <linux/parser.h> #include <linux/mman.h> #include <linux/quotaops.h> #include <linux/slab.h> @@ -47,6 +50,21 @@ static struct backing_dev_info hugetlbfs_backing_dev_info = { int sysctl_hugetlb_shm_group; +enum { + Opt_size, Opt_nr_inodes, + Opt_mode, Opt_uid, Opt_gid, + Opt_err, +}; + +static match_table_t tokens = { + {Opt_size, "size=%s"}, + {Opt_nr_inodes, "nr_inodes=%s"}, + {Opt_mode, "mode=%o"}, + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_err, NULL}, +}; + static void huge_pagevec_release(struct pagevec *pvec) { int i; @@ -594,46 +612,73 @@ static const struct super_operations hugetlbfs_ops = { static int hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) { - char *opt, *value, *rest; + char *p, *rest; + substring_t args[MAX_OPT_ARGS]; + int option; if (!options) return 0; - while ((opt = strsep(&options, ",")) != NULL) { - if (!*opt) + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) continue; - value = strchr(opt, '='); - if (!value || !*value) - return -EINVAL; - else - *value++ = '\0'; - - if (!strcmp(opt, "uid")) - pconfig->uid = simple_strtoul(value, &value, 0); - else if (!strcmp(opt, "gid")) - pconfig->gid = simple_strtoul(value, &value, 0); - else if (!strcmp(opt, "mode")) - pconfig->mode = simple_strtoul(value,&value,0) & 0777U; - else if (!strcmp(opt, "size")) { - unsigned long long size = memparse(value, &rest); + token = match_token(p, tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + goto bad_val; + pconfig->uid = option; + break; + + case Opt_gid: + if (match_int(&args[0], &option)) + goto bad_val; + pconfig->gid = option; + break; + + case Opt_mode: + if (match_octal(&args[0], &option)) + goto bad_val; + pconfig->mode = option & 0777U; + break; + + case Opt_size: { + unsigned long long size; + /* memparse() will accept a K/M/G without a digit */ + if (!isdigit(*args[0].from)) + goto bad_val; + size = memparse(args[0].from, &rest); if (*rest == '%') { size <<= HPAGE_SHIFT; size *= max_huge_pages; do_div(size, 100); - rest++; } pconfig->nr_blocks = (size >> HPAGE_SHIFT); - value = rest; - } else if (!strcmp(opt,"nr_inodes")) { - pconfig->nr_inodes = memparse(value, &rest); - value = rest; - } else - return -EINVAL; + break; + } + + case Opt_nr_inodes: + /* memparse() will accept a K/M/G without a digit */ + if (!isdigit(*args[0].from)) + goto bad_val; + pconfig->nr_inodes = memparse(args[0].from, &rest); + break; - if (*value) + default: + printk(KERN_ERR "hugetlbfs: Bad mount option: \"%s\"\n", + p); return -EINVAL; + break; + } } return 0; + +bad_val: + printk(KERN_ERR "hugetlbfs: Bad value '%s' for mount option '%s'\n", + args[0].from, p); + return 1; } static int @@ -651,7 +696,6 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) config.gid = current->fsgid; config.mode = 0755; ret = hugetlbfs_parse_options(data, &config); - if (ret) return ret; @@ -804,7 +848,7 @@ static int __init init_hugetlbfs_fs(void) hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache", sizeof(struct hugetlbfs_inode_info), - 0, 0, init_once, NULL); + 0, 0, init_once); if (hugetlbfs_inode_cachep == NULL) return -ENOMEM; @@ -145,7 +145,7 @@ static struct inode *alloc_inode(struct super_block *sb) mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags = 0; - mapping_set_gfp_mask(mapping, GFP_HIGHUSER); + mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE); mapping->assoc_mapping = NULL; mapping->backing_dev_info = &default_backing_dev_info; @@ -462,6 +462,11 @@ static int shrink_icache_memory(int nr, gfp_t gfp_mask) return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; } +static struct shrinker icache_shrinker = { + .shrink = shrink_icache_memory, + .seeks = DEFAULT_SEEKS, +}; + static void __wait_on_freeing_inode(struct inode *inode); /* * Called with the inode lock held. @@ -519,7 +524,13 @@ repeat: * new_inode - obtain an inode * @sb: superblock * - * Allocates a new inode for given superblock. + * Allocates a new inode for given superblock. The default gfp_mask + * for allocations related to inode->i_mapping is GFP_HIGHUSER_PAGECACHE. + * If HIGHMEM pages are unsuitable or it is known that pages allocated + * for the page cache are not reclaimable or migratable, + * mapping_set_gfp_mask() must be called with suitable flags on the + * newly created inode's mapping + * */ struct inode *new_inode(struct super_block *sb) { @@ -1377,9 +1388,8 @@ void __init inode_init(unsigned long mempages) 0, (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC| SLAB_MEM_SPREAD), - init_once, - NULL); - set_shrinker(DEFAULT_SEEKS, shrink_icache_memory); + init_once); + register_shrinker(&icache_shrinker); /* Hash may have been set up in inode_init_early */ if (!hashdist) diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 9f2224f..9bf2f6c 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -716,10 +716,10 @@ static int __init inotify_user_setup(void) watch_cachep = kmem_cache_create("inotify_watch_cache", sizeof(struct inotify_user_watch), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); event_cachep = kmem_cache_create("inotify_event_cache", sizeof(struct inotify_kernel_event), - 0, SLAB_PANIC, NULL, NULL); + 0, SLAB_PANIC, NULL); return 0; } @@ -12,7 +12,6 @@ #include <linux/fs.h> #include <linux/security.h> #include <linux/module.h> -#include <linux/kallsyms.h> #include <asm/uaccess.h> #include <asm/ioctls.h> @@ -21,7 +20,6 @@ static long do_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int error = -ENOTTY; - void *f; if (!filp->f_op) goto out; @@ -31,16 +29,10 @@ static long do_ioctl(struct file *filp, unsigned int cmd, if (error == -ENOIOCTLCMD) error = -EINVAL; goto out; - } else if ((f = filp->f_op->ioctl)) { + } else if (filp->f_op->ioctl) { lock_kernel(); - if (!filp->f_op->ioctl) { - printk("%s: ioctl %p disappeared\n", __FUNCTION__, f); - print_symbol("symbol: %s\n", (unsigned long)f); - dump_stack(); - } else { - error = filp->f_op->ioctl(filp->f_path.dentry->d_inode, - filp, cmd, arg); - } + error = filp->f_op->ioctl(filp->f_path.dentry->d_inode, + filp, cmd, arg); unlock_kernel(); } @@ -182,11 +174,3 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) out: return error; } - -/* - * Platforms implementing 32 bit compatibility ioctl handlers in - * modules need this exported - */ -#ifdef CONFIG_COMPAT -EXPORT_SYMBOL(sys_ioctl); -#endif diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 0e94c31..1ba407c 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -7,34 +7,18 @@ * * Steve Beynon : Missing last directory entries fixed * (stephen@askone.demon.co.uk) : 21st June 1996 - * + * * isofs directory handling functions */ #include <linux/smp_lock.h> #include "isofs.h" -static int isofs_readdir(struct file *, void *, filldir_t); - -const struct file_operations isofs_dir_operations = -{ - .read = generic_read_dir, - .readdir = isofs_readdir, -}; - -/* - * directories can handle most operations... - */ -const struct inode_operations isofs_dir_inode_operations = -{ - .lookup = isofs_lookup, -}; - int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) { char * old = de->name; int len = de->name_len[0]; int i; - + for (i = 0; i < len; i++) { unsigned char c = old[i]; if (!c) @@ -62,22 +46,27 @@ int isofs_name_translate(struct iso_directory_record *de, char *new, struct inod } /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */ -int get_acorn_filename(struct iso_directory_record * de, - char * retname, struct inode * inode) +int get_acorn_filename(struct iso_directory_record *de, + char *retname, struct inode *inode) { int std; - unsigned char * chr; + unsigned char *chr; int retnamlen = isofs_name_translate(de, retname, inode); - if (retnamlen == 0) return 0; + + if (retnamlen == 0) + return 0; std = sizeof(struct iso_directory_record) + de->name_len[0]; - if (std & 1) std++; - if ((*((unsigned char *) de) - std) != 32) return retnamlen; + if (std & 1) + std++; + if ((*((unsigned char *) de) - std) != 32) + return retnamlen; chr = ((unsigned char *) de) + std; - if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen; - if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!'; + if (strncmp(chr, "ARCHIMEDES", 10)) + return retnamlen; + if ((*retname == '_') && ((chr[19] & 1) == 1)) + *retname = '!'; if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff) - && ((chr[12] & 0xf0) == 0xf0)) - { + && ((chr[12] & 0xf0) == 0xf0)) { retname[retnamlen] = ','; sprintf(retname+retnamlen+1, "%3.3x", ((chr[12] & 0xf) << 8) | chr[11]); @@ -91,7 +80,7 @@ int get_acorn_filename(struct iso_directory_record * de, */ static int do_isofs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir, - char * tmpname, struct iso_directory_record * tmpde) + char *tmpname, struct iso_directory_record *tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); @@ -121,9 +110,11 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, de_len = *(unsigned char *) de; - /* If the length byte is zero, we should move on to the next - CDROM sector. If we are at the end of the directory, we - kick out of the while loop. */ + /* + * If the length byte is zero, we should move on to the next + * CDROM sector. If we are at the end of the directory, we + * kick out of the while loop. + */ if (de_len == 0) { brelse(bh); @@ -157,11 +148,10 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, if (first_de) { isofs_normalize_block_and_offset(de, - &block_saved, - &offset_saved); + &block_saved, + &offset_saved); inode_number = isofs_get_ino(block_saved, - offset_saved, - bufbits); + offset_saved, bufbits); } if (de->flags[-sbi->s_high_sierra] & 0x80) { @@ -199,7 +189,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, */ if ((sbi->s_hide == 'y' && (de->flags[-sbi->s_high_sierra] & 1)) || - (sbi->s_showassoc =='n' && + (sbi->s_showassoc =='n' && (de->flags[-sbi->s_high_sierra] & 4))) { filp->f_pos += de_len; continue; @@ -240,7 +230,8 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, continue; } - if (bh) brelse(bh); + if (bh) + brelse(bh); return 0; } @@ -253,8 +244,8 @@ static int isofs_readdir(struct file *filp, void *dirent, filldir_t filldir) { int result; - char * tmpname; - struct iso_directory_record * tmpde; + char *tmpname; + struct iso_directory_record *tmpde; struct inode *inode = filp->f_path.dentry->d_inode; tmpname = (char *)__get_free_page(GFP_KERNEL); @@ -270,3 +261,19 @@ static int isofs_readdir(struct file *filp, unlock_kernel(); return result; } + +const struct file_operations isofs_dir_operations = +{ + .read = generic_read_dir, + .readdir = isofs_readdir, +}; + +/* + * directories can handle most operations... + */ +const struct inode_operations isofs_dir_inode_operations = +{ + .lookup = isofs_lookup, +}; + + diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 5c3eecf..95c72aa 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -73,20 +73,20 @@ static void isofs_destroy_inode(struct inode *inode) kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); } -static void init_once(void *foo, struct kmem_cache * cachep, unsigned long flags) +static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) { struct iso_inode_info *ei = foo; inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", - sizeof(struct iso_inode_info), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once, NULL); + sizeof(struct iso_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); if (isofs_inode_cachep == NULL) return -ENOMEM; return 0; @@ -150,9 +150,9 @@ struct iso9660_options{ uid_t uid; char *iocharset; unsigned char utf8; - /* LVE */ - s32 session; - s32 sbsector; + /* LVE */ + s32 session; + s32 sbsector; }; /* @@ -197,7 +197,7 @@ isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms) hash = init_name_hash(); while (len--) { c = tolower(*name++); - hash = partial_name_hash(tolower(c), hash); + hash = partial_name_hash(c, hash); } qstr->hash = end_name_hash(hash); @@ -360,10 +360,12 @@ static int parse_options(char *options, struct iso9660_options *popt) popt->check = 'u'; /* unset */ popt->nocompress = 0; popt->blocksize = 1024; - popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could - be shared with DOS machines so - virtually anything could be - a valid executable. */ + popt->mode = S_IRUGO | S_IXUGO; /* + * r-x for all. The disc could + * be shared with DOS machines so + * virtually anything could be + * a valid executable. + */ popt->gid = 0; popt->uid = 0; popt->iocharset = NULL; @@ -503,30 +505,30 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) Te.cdte_format=CDROM_LBA; i = ioctl_by_bdev(bdev, CDROMREADTOCENTRY, (unsigned long) &Te); if (!i) { - printk(KERN_DEBUG "Session %d start %d type %d\n", - session, Te.cdte_addr.lba, - Te.cdte_ctrl&CDROM_DATA_TRACK); + printk(KERN_DEBUG "ISOFS: Session %d start %d type %d\n", + session, Te.cdte_addr.lba, + Te.cdte_ctrl&CDROM_DATA_TRACK); if ((Te.cdte_ctrl&CDROM_DATA_TRACK) == 4) return Te.cdte_addr.lba; } - - printk(KERN_ERR "Invalid session number or type of track\n"); + + printk(KERN_ERR "ISOFS: Invalid session number or type of track\n"); } i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); if (session > 0) - printk(KERN_ERR "Invalid session number\n"); + printk(KERN_ERR "ISOFS: Invalid session number\n"); #if 0 - printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); + printk(KERN_DEBUG "isofs.inode: CDROMMULTISESSION: rc=%d\n",i); if (i==0) { - printk("isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no"); - printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); + printk(KERN_DEBUG "isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no"); + printk(KERN_DEBUG "isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); } #endif if (i==0) #if WE_OBEY_THE_WRITTEN_STANDARDS - if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ + if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif - vol_desc_start=ms_info.addr.lba; + vol_desc_start=ms_info.addr.lba; return vol_desc_start; } @@ -538,20 +540,20 @@ static unsigned int isofs_get_last_session(struct super_block *sb, s32 session) */ static int isofs_fill_super(struct super_block *s, void *data, int silent) { - struct buffer_head * bh = NULL, *pri_bh = NULL; - struct hs_primary_descriptor * h_pri = NULL; - struct iso_primary_descriptor * pri = NULL; + struct buffer_head *bh = NULL, *pri_bh = NULL; + struct hs_primary_descriptor *h_pri = NULL; + struct iso_primary_descriptor *pri = NULL; struct iso_supplementary_descriptor *sec = NULL; - struct iso_directory_record * rootp; - int joliet_level = 0; - int iso_blknum, block; - int orig_zonesize; - int table; - unsigned int vol_desc_start; - unsigned long first_data_zone; - struct inode * inode; - struct iso9660_options opt; - struct isofs_sb_info * sbi; + struct iso_directory_record *rootp; + struct inode *inode; + struct iso9660_options opt; + struct isofs_sb_info *sbi; + unsigned long first_data_zone; + int joliet_level = 0; + int iso_blknum, block; + int orig_zonesize; + int table; + unsigned int vol_desc_start; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -577,72 +579,73 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) vol_desc_start = (opt.sbsector != -1) ? opt.sbsector : isofs_get_last_session(s,opt.session); - for (iso_blknum = vol_desc_start+16; - iso_blknum < vol_desc_start+100; iso_blknum++) - { - struct hs_volume_descriptor * hdp; - struct iso_volume_descriptor * vdp; - - block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); - if (!(bh = sb_bread(s, block))) - goto out_no_read; - - vdp = (struct iso_volume_descriptor *)bh->b_data; - hdp = (struct hs_volume_descriptor *)bh->b_data; - - /* Due to the overlapping physical location of the descriptors, - * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure - * proper identification in this case, we first check for ISO. - */ - if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { - if (isonum_711 (vdp->type) == ISO_VD_END) - break; - if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) { - if (pri == NULL) { - pri = (struct iso_primary_descriptor *)vdp; - /* Save the buffer in case we need it ... */ - pri_bh = bh; - bh = NULL; - } - } + for (iso_blknum = vol_desc_start+16; + iso_blknum < vol_desc_start+100; iso_blknum++) { + struct hs_volume_descriptor *hdp; + struct iso_volume_descriptor *vdp; + + block = iso_blknum << (ISOFS_BLOCK_BITS - s->s_blocksize_bits); + if (!(bh = sb_bread(s, block))) + goto out_no_read; + + vdp = (struct iso_volume_descriptor *)bh->b_data; + hdp = (struct hs_volume_descriptor *)bh->b_data; + + /* + * Due to the overlapping physical location of the descriptors, + * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure + * proper identification in this case, we first check for ISO. + */ + if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) { + if (isonum_711(vdp->type) == ISO_VD_END) + break; + if (isonum_711(vdp->type) == ISO_VD_PRIMARY) { + if (pri == NULL) { + pri = (struct iso_primary_descriptor *)vdp; + /* Save the buffer in case we need it ... */ + pri_bh = bh; + bh = NULL; + } + } #ifdef CONFIG_JOLIET - else if (isonum_711 (vdp->type) == ISO_VD_SUPPLEMENTARY) { - sec = (struct iso_supplementary_descriptor *)vdp; - if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { - if (opt.joliet == 'y') { - if (sec->escape[2] == 0x40) { - joliet_level = 1; - } else if (sec->escape[2] == 0x43) { - joliet_level = 2; - } else if (sec->escape[2] == 0x45) { - joliet_level = 3; - } - printk(KERN_DEBUG"ISO 9660 Extensions: Microsoft Joliet Level %d\n", - joliet_level); + else if (isonum_711(vdp->type) == ISO_VD_SUPPLEMENTARY) { + sec = (struct iso_supplementary_descriptor *)vdp; + if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) { + if (opt.joliet == 'y') { + if (sec->escape[2] == 0x40) + joliet_level = 1; + else if (sec->escape[2] == 0x43) + joliet_level = 2; + else if (sec->escape[2] == 0x45) + joliet_level = 3; + + printk(KERN_DEBUG "ISO 9660 Extensions: " + "Microsoft Joliet Level %d\n", + joliet_level); + } + goto root_found; + } else { + /* Unknown supplementary volume descriptor */ + sec = NULL; + } } - goto root_found; - } else { - /* Unknown supplementary volume descriptor */ - sec = NULL; - } - } #endif - } else { - if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { - if (isonum_711 (hdp->type) != ISO_VD_PRIMARY) - goto out_freebh; - - sbi->s_high_sierra = 1; - opt.rock = 'n'; - h_pri = (struct hs_primary_descriptor *)vdp; - goto root_found; + } else { + if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { + if (isonum_711(hdp->type) != ISO_VD_PRIMARY) + goto out_freebh; + + sbi->s_high_sierra = 1; + opt.rock = 'n'; + h_pri = (struct hs_primary_descriptor *)vdp; + goto root_found; + } } - } - /* Just skip any volume descriptors we don't recognize */ + /* Just skip any volume descriptors we don't recognize */ - brelse(bh); - bh = NULL; + brelse(bh); + bh = NULL; } /* * If we fall through, either no volume descriptor was found, @@ -657,24 +660,24 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) root_found: if (joliet_level && (pri == NULL || opt.rock == 'n')) { - /* This is the case of Joliet with the norock mount flag. - * A disc with both Joliet and Rock Ridge is handled later - */ - pri = (struct iso_primary_descriptor *) sec; + /* This is the case of Joliet with the norock mount flag. + * A disc with both Joliet and Rock Ridge is handled later + */ + pri = (struct iso_primary_descriptor *) sec; } if(sbi->s_high_sierra){ - rootp = (struct iso_directory_record *) h_pri->root_directory_record; - sbi->s_nzones = isonum_733 (h_pri->volume_space_size); - sbi->s_log_zone_size = isonum_723 (h_pri->logical_block_size); - sbi->s_max_size = isonum_733(h_pri->volume_space_size); + rootp = (struct iso_directory_record *) h_pri->root_directory_record; + sbi->s_nzones = isonum_733(h_pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(h_pri->logical_block_size); + sbi->s_max_size = isonum_733(h_pri->volume_space_size); } else { - if (!pri) - goto out_freebh; - rootp = (struct iso_directory_record *) pri->root_directory_record; - sbi->s_nzones = isonum_733 (pri->volume_space_size); - sbi->s_log_zone_size = isonum_723 (pri->logical_block_size); - sbi->s_max_size = isonum_733(pri->volume_space_size); + if (!pri) + goto out_freebh; + rootp = (struct iso_directory_record *) pri->root_directory_record; + sbi->s_nzones = isonum_733(pri->volume_space_size); + sbi->s_log_zone_size = isonum_723(pri->logical_block_size); + sbi->s_max_size = isonum_733(pri->volume_space_size); } sbi->s_ninodes = 0; /* No way to figure this out easily */ @@ -687,42 +690,43 @@ root_found: * blocks that were 512 bytes (which should only very rarely * happen.) */ - if(orig_zonesize < opt.blocksize) + if (orig_zonesize < opt.blocksize) goto out_bad_size; /* RDE: convert log zone size to bit shift */ - switch (sbi->s_log_zone_size) - { case 512: sbi->s_log_zone_size = 9; break; - case 1024: sbi->s_log_zone_size = 10; break; - case 2048: sbi->s_log_zone_size = 11; break; + switch (sbi->s_log_zone_size) { + case 512: sbi->s_log_zone_size = 9; break; + case 1024: sbi->s_log_zone_size = 10; break; + case 2048: sbi->s_log_zone_size = 11; break; - default: + default: goto out_bad_zone_size; - } + } s->s_magic = ISOFS_SUPER_MAGIC; s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ - /* The CDROM is read-only, has no nodes (devices) on it, and since - all of the files appear to be owned by root, we really do not want - to allow suid. (suid or devices will not show up unless we have - Rock Ridge extensions) */ + /* + * The CDROM is read-only, has no nodes (devices) on it, and since + * all of the files appear to be owned by root, we really do not want + * to allow suid. (suid or devices will not show up unless we have + * Rock Ridge extensions) + */ s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */; /* Set this for reference. Its not currently used except on write which we don't have .. */ - - first_data_zone = isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length); + + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); sbi->s_firstdatazone = first_data_zone; #ifndef BEQUIET - printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n", - sbi->s_max_size, - 1UL << sbi->s_log_zone_size); - printk(KERN_DEBUG "First datazone:%ld\n", sbi->s_firstdatazone); + printk(KERN_DEBUG "ISOFS: Max size:%ld Log zone size:%ld\n", + sbi->s_max_size, 1UL << sbi->s_log_zone_size); + printk(KERN_DEBUG "ISOFS: First datazone:%ld\n", sbi->s_firstdatazone); if(sbi->s_high_sierra) - printk(KERN_DEBUG "Disc in High Sierra format.\n"); + printk(KERN_DEBUG "ISOFS: Disc in High Sierra format.\n"); #endif /* @@ -737,8 +741,8 @@ root_found: pri = (struct iso_primary_descriptor *) sec; rootp = (struct iso_directory_record *) pri->root_directory_record; - first_data_zone = isonum_733 (rootp->extent) + - isonum_711 (rootp->ext_attr_length); + first_data_zone = isonum_733(rootp->extent) + + isonum_711(rootp->ext_attr_length); } /* @@ -771,7 +775,7 @@ root_found: #ifdef CONFIG_JOLIET if (joliet_level && opt.utf8 == 0) { - char * p = opt.iocharset ? opt.iocharset : CONFIG_NLS_DEFAULT; + char *p = opt.iocharset ? opt.iocharset : CONFIG_NLS_DEFAULT; sbi->s_nls_iocharset = load_nls(p); if (! sbi->s_nls_iocharset) { /* Fail only if explicit charset specified */ @@ -821,7 +825,7 @@ root_found: sbi->s_rock = 0; if (sbi->s_firstdatazone != first_data_zone) { sbi->s_firstdatazone = first_data_zone; - printk(KERN_DEBUG + printk(KERN_DEBUG "ISOFS: changing to secondary root\n"); iput(inode); inode = isofs_iget(s, sbi->s_firstdatazone, 0); @@ -830,8 +834,10 @@ root_found: if (opt.check == 'u') { /* Only Joliet is case insensitive by default */ - if (joliet_level) opt.check = 'r'; - else opt.check = 's'; + if (joliet_level) + opt.check = 'r'; + else + opt.check = 's'; } sbi->s_joliet_level = joliet_level; @@ -846,8 +852,10 @@ root_found: goto out_no_root; table = 0; - if (joliet_level) table += 2; - if (opt.check == 'r') table++; + if (joliet_level) + table += 2; + if (opt.check == 'r') + table++; s->s_root->d_op = &isofs_dentry_ops[table]; kfree(opt.iocharset); @@ -858,10 +866,10 @@ root_found: * Display error messages and free resources. */ out_bad_root: - printk(KERN_WARNING "isofs_fill_super: root inode not initialized\n"); + printk(KERN_WARNING "%s: root inode not initialized\n", __func__); goto out_iput; out_no_root: - printk(KERN_WARNING "isofs_fill_super: get root inode failed\n"); + printk(KERN_WARNING "%s: get root inode failed\n", __func__); out_iput: iput(inode); #ifdef CONFIG_JOLIET @@ -870,21 +878,20 @@ out_iput: #endif goto out_freesbi; out_no_read: - printk(KERN_WARNING "isofs_fill_super: " - "bread failed, dev=%s, iso_blknum=%d, block=%d\n", - s->s_id, iso_blknum, block); + printk(KERN_WARNING "%s: bread failed, dev=%s, iso_blknum=%d, block=%d\n", + __func__, s->s_id, iso_blknum, block); goto out_freesbi; out_bad_zone_size: - printk(KERN_WARNING "Bad logical zone size %ld\n", + printk(KERN_WARNING "ISOFS: Bad logical zone size %ld\n", sbi->s_log_zone_size); goto out_freebh; out_bad_size: - printk(KERN_WARNING "Logical zone size(%d) < hardware blocksize(%u)\n", + printk(KERN_WARNING "ISOFS: Logical zone size(%d) < hardware blocksize(%u)\n", orig_zonesize, opt.blocksize); goto out_freebh; out_unknown_format: if (!silent) - printk(KERN_WARNING "Unable to identify CD-ROM format.\n"); + printk(KERN_WARNING "ISOFS: Unable to identify CD-ROM format.\n"); out_freebh: brelse(bh); @@ -902,7 +909,7 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) buf->f_type = ISOFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = (ISOFS_SB(sb)->s_nzones - << (ISOFS_SB(sb)->s_log_zone_size - sb->s_blocksize_bits)); + << (ISOFS_SB(sb)->s_log_zone_size - sb->s_blocksize_bits)); buf->f_bfree = 0; buf->f_bavail = 0; buf->f_files = ISOFS_SB(sb)->s_ninodes; @@ -931,20 +938,20 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, rv = 0; if (iblock < 0 || iblock != iblock_s) { - printk("isofs_get_blocks: block number too large\n"); + printk(KERN_DEBUG "%s: block number too large\n", __func__); goto abort; } b_off = iblock; - - offset = 0; - firstext = ei->i_first_extent; + + offset = 0; + firstext = ei->i_first_extent; sect_size = ei->i_section_size >> ISOFS_BUFFER_BITS(inode); - nextblk = ei->i_next_section_block; - nextoff = ei->i_next_section_offset; - section = 0; + nextblk = ei->i_next_section_block; + nextoff = ei->i_next_section_offset; + section = 0; - while ( nblocks ) { + while (nblocks) { /* If we are *way* beyond the end of the file, print a message. * Access beyond the end of the file up to the next page boundary * is normal, however because of the way the page cache works. @@ -953,11 +960,11 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, * I/O errors. */ if (b_off > ((inode->i_size + PAGE_CACHE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) { - printk("isofs_get_blocks: block >= EOF (%ld, %ld)\n", - iblock, (unsigned long) inode->i_size); + printk(KERN_DEBUG "%s: block >= EOF (%ld, %ld)\n", + __func__, iblock, (unsigned long) inode->i_size); goto abort; } - + /* On the last section, nextblk == 0, section size is likely to * exceed sect_size by a partial block, and access beyond the * end of the file will reach beyond the section size, too. @@ -976,20 +983,21 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, iput(ninode); if (++section > 100) { - printk("isofs_get_blocks: More than 100 file sections ?!?, aborting...\n"); - printk("isofs_get_blocks: block=%ld firstext=%u sect_size=%u " - "nextblk=%lu nextoff=%lu\n", - iblock, firstext, (unsigned) sect_size, - nextblk, nextoff); + printk(KERN_DEBUG "%s: More than 100 file sections ?!?" + " aborting...\n", __func__); + printk(KERN_DEBUG "%s: block=%ld firstext=%u sect_size=%u " + "nextblk=%lu nextoff=%lu\n", __func__, + iblock, firstext, (unsigned) sect_size, + nextblk, nextoff); goto abort; } } - - if ( *bh ) { + + if (*bh) { map_bh(*bh, inode->i_sb, firstext + b_off - offset); } else { *bh = sb_getblk(inode->i_sb, firstext+b_off-offset); - if ( !*bh ) + if (!*bh) goto abort; } bh++; /* Next buffer head */ @@ -1010,7 +1018,7 @@ static int isofs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { if (create) { - printk("isofs_get_block: Kernel tries to allocate a block\n"); + printk(KERN_DEBUG "%s: Kernel tries to allocate a block\n", __func__); return -EROFS; } @@ -1070,11 +1078,11 @@ static int isofs_read_level3_size(struct inode *inode) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); int high_sierra = ISOFS_SB(inode->i_sb)->s_high_sierra; - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; unsigned long block, offset, block_saved, offset_saved; int i = 0; int more_entries = 0; - struct iso_directory_record * tmpde = NULL; + struct iso_directory_record *tmpde = NULL; struct iso_inode_info *ei = ISOFS_I(inode); inode->i_size = 0; @@ -1089,7 +1097,7 @@ static int isofs_read_level3_size(struct inode *inode) offset = ei->i_iget5_offset; do { - struct iso_directory_record * de; + struct iso_directory_record *de; unsigned int de_len; if (!bh) { @@ -1163,10 +1171,9 @@ out_noread: return -EIO; out_toomany: - printk(KERN_INFO "isofs_read_level3_size: " - "More than 100 file sections ?!?, aborting...\n" - "isofs_read_level3_size: inode=%lu\n", - inode->i_ino); + printk(KERN_INFO "%s: More than 100 file sections ?!?, aborting...\n" + "isofs_read_level3_size: inode=%lu\n", + __func__, inode->i_ino); goto out; } @@ -1177,9 +1184,9 @@ static void isofs_read_inode(struct inode *inode) unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned long block; int high_sierra = sbi->s_high_sierra; - struct buffer_head * bh = NULL; - struct iso_directory_record * de; - struct iso_directory_record * tmpde = NULL; + struct buffer_head *bh = NULL; + struct iso_directory_record *de; + struct iso_directory_record *tmpde = NULL; unsigned int de_len; unsigned long offset; struct iso_inode_info *ei = ISOFS_I(inode); @@ -1199,7 +1206,7 @@ static void isofs_read_inode(struct inode *inode) tmpde = kmalloc(de_len, GFP_KERNEL); if (tmpde == NULL) { - printk(KERN_INFO "isofs_read_inode: out of memory\n"); + printk(KERN_INFO "%s: out of memory\n", __func__); goto fail; } memcpy(tmpde, bh->b_data + offset, frag1); @@ -1212,24 +1219,26 @@ static void isofs_read_inode(struct inode *inode) } inode->i_ino = isofs_get_ino(ei->i_iget5_block, - ei->i_iget5_offset, - ISOFS_BUFFER_BITS(inode)); + ei->i_iget5_offset, + ISOFS_BUFFER_BITS(inode)); /* Assume it is a normal-format file unless told otherwise */ ei->i_file_format = isofs_file_normal; if (de->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; - inode->i_nlink = 1; /* Set to 1. We know there are 2, but - the find utility tries to optimize - if it is 2, and it screws up. It is - easier to give 1 which tells find to - do it the hard way. */ + inode->i_nlink = 1; /* + * Set to 1. We know there are 2, but + * the find utility tries to optimize + * if it is 2, and it screws up. It is + * easier to give 1 which tells find to + * do it the hard way. + */ } else { - /* Everybody gets to read the file. */ + /* Everybody gets to read the file. */ inode->i_mode = sbi->s_mode; inode->i_nlink = 1; - inode->i_mode |= S_IFREG; + inode->i_mode |= S_IFREG; } inode->i_uid = sbi->s_uid; inode->i_gid = sbi->s_gid; @@ -1239,13 +1248,14 @@ static void isofs_read_inode(struct inode *inode) ei->i_format_parm[1] = 0; ei->i_format_parm[2] = 0; - ei->i_section_size = isonum_733 (de->size); + ei->i_section_size = isonum_733(de->size); if (de->flags[-high_sierra] & 0x80) { - if(isofs_read_level3_size(inode)) goto fail; + if(isofs_read_level3_size(inode)) + goto fail; } else { ei->i_next_section_block = 0; ei->i_next_section_offset = 0; - inode->i_size = isonum_733 (de->size); + inode->i_size = isonum_733(de->size); } /* @@ -1258,23 +1268,24 @@ static void isofs_read_inode(struct inode *inode) inode->i_size &= 0x00ffffff; if (de->interleave[0]) { - printk("Interleaved files not (yet) supported.\n"); + printk(KERN_DEBUG "ISOFS: Interleaved files not (yet) supported.\n"); inode->i_size = 0; } /* I have no idea what file_unit_size is used for, so we will flag it for now */ if (de->file_unit_size[0] != 0) { - printk("File unit size != 0 for ISO file (%ld).\n", - inode->i_ino); + printk(KERN_DEBUG "ISOFS: File unit size != 0 for ISO file (%ld).\n", + inode->i_ino); } /* I have no idea what other flag bits are used for, so we will flag it for now */ #ifdef DEBUG if((de->flags[-high_sierra] & ~2)!= 0){ - printk("Unusual flag settings for ISO file (%ld %x).\n", - inode->i_ino, de->flags[-high_sierra]); + printk(KERN_DEBUG "ISOFS: Unusual flag settings for ISO file " + "(%ld %x).\n", + inode->i_ino, de->flags[-high_sierra]); } #endif @@ -1285,11 +1296,11 @@ static void isofs_read_inode(struct inode *inode) inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0; - ei->i_first_extent = (isonum_733 (de->extent) + - isonum_711 (de->ext_attr_length)); + ei->i_first_extent = (isonum_733(de->extent) + + isonum_711(de->ext_attr_length)); /* Set the number of blocks for stat() - should be done before RR */ - inode->i_blocks = (inode->i_size + 511) >> 9; + inode->i_blocks = (inode->i_size + 511) >> 9; /* * Now test for possible Rock Ridge extensions which will override @@ -1306,7 +1317,7 @@ static void isofs_read_inode(struct inode *inode) /* Install the inode operations vector */ if (S_ISREG(inode->i_mode)) { inode->i_fop = &generic_ro_fops; - switch ( ei->i_file_format ) { + switch (ei->i_file_format) { #ifdef CONFIG_ZISOFS case isofs_file_compressed: inode->i_data.a_ops = &zisofs_aops; @@ -1350,7 +1361,7 @@ static int isofs_iget5_test(struct inode *ino, void *data) struct isofs_iget5_callback_data *d = (struct isofs_iget5_callback_data*)data; return (i->i_iget5_block == d->block) - && (i->i_iget5_offset == d->offset); + && (i->i_iget5_offset == d->offset); } static int isofs_iget5_set(struct inode *ino, void *data) @@ -1384,7 +1395,7 @@ struct inode *isofs_iget(struct super_block *sb, hashval = (block << sb->s_blocksize_bits) | offset; inode = iget5_locked(sb, hashval, &isofs_iget5_test, - &isofs_iget5_set, &data); + &isofs_iget5_set, &data); if (inode && (inode->i_state & I_NEW)) { sb->s_op->read_inode(inode); @@ -1398,7 +1409,7 @@ static int isofs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { return get_sb_bdev(fs_type, flags, dev_name, data, isofs_fill_super, - mnt); + mnt); } static struct file_system_type iso9660_fs_type = { diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index efe2872..a07e67b 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -1,5 +1,6 @@ #include <linux/fs.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/iso_fs.h> #include <asm/unaligned.h> diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c index fb8fe7a..92c14b8 100644 --- a/fs/isofs/joliet.c +++ b/fs/isofs/joliet.c @@ -80,22 +80,20 @@ get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, st if (utf8) { len = wcsntombs_be(outname, de->name, - de->name_len[0] >> 1, PAGE_SIZE); + de->name_len[0] >> 1, PAGE_SIZE); } else { len = uni16_to_x8(outname, (__be16 *) de->name, - de->name_len[0] >> 1, nls); + de->name_len[0] >> 1, nls); } - if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) { + if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) len -= 2; - } /* * Windows doesn't like periods at the end of a name, * so neither do we */ - while (len >= 2 && (outname[len-1] == '.')) { + while (len >= 2 && (outname[len-1] == '.')) len--; - } return len; } diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index c04b3a1..c8c7e51 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -15,7 +15,7 @@ * some sanity tests. */ static int -isofs_cmp(struct dentry * dentry, const char * compare, int dlen) +isofs_cmp(struct dentry *dentry, const char *compare, int dlen) { struct qstr qstr; @@ -48,24 +48,24 @@ isofs_cmp(struct dentry * dentry, const char * compare, int dlen) */ static unsigned long isofs_find_entry(struct inode *dir, struct dentry *dentry, - unsigned long *block_rv, unsigned long* offset_rv, - char * tmpname, struct iso_directory_record * tmpde) + unsigned long *block_rv, unsigned long *offset_rv, + char *tmpname, struct iso_directory_record *tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); unsigned long block, f_pos, offset, block_saved, offset_saved; - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); if (!ISOFS_I(dir)->i_first_extent) return 0; - + f_pos = 0; offset = 0; block = 0; while (f_pos < dir->i_size) { - struct iso_directory_record * de; + struct iso_directory_record *de; int de_len, match, i, dlen; char *dpnt; @@ -114,7 +114,7 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, if (sbi->s_rock && ((i = get_rock_ridge_filename(de, tmpname, dir)))) { - dlen = i; /* possibly -1 */ + dlen = i; /* possibly -1 */ dpnt = tmpname; #ifdef CONFIG_JOLIET } else if (sbi->s_joliet_level) { @@ -145,8 +145,8 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, isofs_normalize_block_and_offset(de, &block_saved, &offset_saved); - *block_rv = block_saved; - *offset_rv = offset_saved; + *block_rv = block_saved; + *offset_rv = offset_saved; brelse(bh); return 1; } @@ -155,7 +155,7 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, return 0; } -struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd) +struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { int found; unsigned long block, offset; @@ -170,9 +170,9 @@ struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct n lock_kernel(); found = isofs_find_entry(dir, dentry, - &block, &offset, - page_address(page), - 1024 + page_address(page)); + &block, &offset, + page_address(page), + 1024 + page_address(page)); __free_page(page); inode = NULL; diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 1facfaf..a003d50 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -887,7 +887,8 @@ restart_loop: journal->j_committing_transaction = NULL; spin_unlock(&journal->j_state_lock); - if (commit_transaction->t_checkpoint_list == NULL) { + if (commit_transaction->t_checkpoint_list == NULL && + commit_transaction->t_checkpoint_io_list == NULL) { __journal_drop_transaction(journal, commit_transaction); } else { if (journal->j_checkpoint_transactions == NULL) { diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 46fe743..06ab3c1 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -1668,7 +1668,7 @@ static int journal_create_jbd_slab(size_t slab_size) * boundary. */ jbd_slab[i] = kmem_cache_create(jbd_slab_names[i], - slab_size, slab_size, 0, NULL, NULL); + slab_size, slab_size, 0, NULL); if (!jbd_slab[i]) { printk(KERN_EMERG "JBD: no memory for jbd_slab cache\n"); return -ENOMEM; @@ -1711,8 +1711,7 @@ static int journal_init_journal_head_cache(void) sizeof(struct journal_head), 0, /* offset */ 0, /* flags */ - NULL, /* ctor */ - NULL); /* dtor */ + NULL); /* ctor */ retval = 0; if (journal_head_cache == 0) { retval = -ENOMEM; @@ -2008,8 +2007,7 @@ static int __init journal_init_handle_cache(void) sizeof(handle_t), 0, /* offset */ 0, /* flags */ - NULL, /* ctor */ - NULL); /* dtor */ + NULL); /* ctor */ if (jbd_handle_cache == NULL) { printk(KERN_EMERG "JBD: failed to create handle cache\n"); return -ENOMEM; diff --git a/fs/jbd/revoke.c b/fs/jbd/revoke.c index 824e3b7..62e13c8 100644 --- a/fs/jbd/revoke.c +++ b/fs/jbd/revoke.c @@ -68,6 +68,7 @@ #include <linux/list.h> #include <linux/init.h> #endif +#include <linux/log2.h> static struct kmem_cache *revoke_record_cache; static struct kmem_cache *revoke_table_cache; @@ -169,13 +170,13 @@ int __init journal_init_revoke_caches(void) { revoke_record_cache = kmem_cache_create("revoke_record", sizeof(struct jbd_revoke_record_s), - 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN, NULL); if (revoke_record_cache == 0) return -ENOMEM; revoke_table_cache = kmem_cache_create("revoke_table", sizeof(struct jbd_revoke_table_s), - 0, 0, NULL, NULL); + 0, 0, NULL); if (revoke_table_cache == 0) { kmem_cache_destroy(revoke_record_cache); revoke_record_cache = NULL; @@ -211,7 +212,7 @@ int journal_init_revoke(journal_t *journal, int hash_size) journal->j_revoke = journal->j_revoke_table[0]; /* Check that the hash_size is a power of two */ - J_ASSERT ((hash_size & (hash_size-1)) == 0); + J_ASSERT(is_power_of_2(hash_size)); journal->j_revoke->hash_size = hash_size; @@ -238,7 +239,7 @@ int journal_init_revoke(journal_t *journal, int hash_size) journal->j_revoke = journal->j_revoke_table[1]; /* Check that the hash_size is a power of two */ - J_ASSERT ((hash_size & (hash_size-1)) == 0); + J_ASSERT(is_power_of_2(hash_size)); journal->j_revoke->hash_size = hash_size; diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 2856e11..c0f59d1 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -896,7 +896,8 @@ restart_loop: journal->j_committing_transaction = NULL; spin_unlock(&journal->j_state_lock); - if (commit_transaction->t_checkpoint_list == NULL) { + if (commit_transaction->t_checkpoint_list == NULL && + commit_transaction->t_checkpoint_io_list == NULL) { __jbd2_journal_drop_transaction(journal, commit_transaction); } else { if (journal->j_checkpoint_transactions == NULL) { diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 78d63b8..f37324a 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -35,6 +35,7 @@ #include <linux/kthread.h> #include <linux/poison.h> #include <linux/proc_fs.h> +#include <linux/debugfs.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -528,7 +529,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) { int err = 0; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG spin_lock(&journal->j_state_lock); if (!tid_geq(journal->j_commit_request, tid)) { printk(KERN_EMERG @@ -1679,7 +1680,7 @@ static int jbd2_journal_create_jbd_slab(size_t slab_size) * boundary. */ jbd_slab[i] = kmem_cache_create(jbd_slab_names[i], - slab_size, slab_size, 0, NULL, NULL); + slab_size, slab_size, 0, NULL); if (!jbd_slab[i]) { printk(KERN_EMERG "JBD: no memory for jbd_slab cache\n"); return -ENOMEM; @@ -1709,7 +1710,7 @@ void jbd2_slab_free(void *ptr, size_t size) * Journal_head storage management */ static struct kmem_cache *jbd2_journal_head_cache; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG static atomic_t nr_journal_heads = ATOMIC_INIT(0); #endif @@ -1722,8 +1723,7 @@ static int journal_init_jbd2_journal_head_cache(void) sizeof(struct journal_head), 0, /* offset */ 0, /* flags */ - NULL, /* ctor */ - NULL); /* dtor */ + NULL); /* ctor */ retval = 0; if (jbd2_journal_head_cache == 0) { retval = -ENOMEM; @@ -1747,7 +1747,7 @@ static struct journal_head *journal_alloc_journal_head(void) struct journal_head *ret; static unsigned long last_warning; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG atomic_inc(&nr_journal_heads); #endif ret = kmem_cache_alloc(jbd2_journal_head_cache, GFP_NOFS); @@ -1768,7 +1768,7 @@ static struct journal_head *journal_alloc_journal_head(void) static void journal_free_journal_head(struct journal_head *jh) { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG atomic_dec(&nr_journal_heads); memset(jh, JBD_POISON_FREE, sizeof(*jh)); #endif @@ -1951,64 +1951,50 @@ void jbd2_journal_put_journal_head(struct journal_head *jh) } /* - * /proc tunables + * debugfs tunables */ -#if defined(CONFIG_JBD_DEBUG) -int jbd2_journal_enable_debug; +#if defined(CONFIG_JBD2_DEBUG) +u8 jbd2_journal_enable_debug; EXPORT_SYMBOL(jbd2_journal_enable_debug); #endif -#if defined(CONFIG_JBD_DEBUG) && defined(CONFIG_PROC_FS) +#if defined(CONFIG_JBD2_DEBUG) && defined(CONFIG_DEBUG_FS) -static struct proc_dir_entry *proc_jbd_debug; +#define JBD2_DEBUG_NAME "jbd2-debug" -static int read_jbd_debug(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int ret; +struct dentry *jbd2_debugfs_dir, *jbd2_debug; - ret = sprintf(page + off, "%d\n", jbd2_journal_enable_debug); - *eof = 1; - return ret; +static void __init jbd2_create_debugfs_entry(void) +{ + jbd2_debugfs_dir = debugfs_create_dir("jbd2", NULL); + if (jbd2_debugfs_dir) + jbd2_debug = debugfs_create_u8(JBD2_DEBUG_NAME, S_IRUGO, + jbd2_debugfs_dir, + &jbd2_journal_enable_debug); } -static int write_jbd_debug(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static void __exit jbd2_remove_debugfs_entry(void) { - char buf[32]; - - if (count > ARRAY_SIZE(buf) - 1) - count = ARRAY_SIZE(buf) - 1; - if (copy_from_user(buf, buffer, count)) - return -EFAULT; - buf[ARRAY_SIZE(buf) - 1] = '\0'; - jbd2_journal_enable_debug = simple_strtoul(buf, NULL, 10); - return count; + if (jbd2_debug) + debugfs_remove(jbd2_debug); + if (jbd2_debugfs_dir) + debugfs_remove(jbd2_debugfs_dir); } -#define JBD_PROC_NAME "sys/fs/jbd2-debug" +#else -static void __init create_jbd_proc_entry(void) +static void __init jbd2_create_debugfs_entry(void) { - proc_jbd_debug = create_proc_entry(JBD_PROC_NAME, 0644, NULL); - if (proc_jbd_debug) { - /* Why is this so hard? */ - proc_jbd_debug->read_proc = read_jbd_debug; - proc_jbd_debug->write_proc = write_jbd_debug; - } + do { + } while (0); } -static void __exit jbd2_remove_jbd_proc_entry(void) +static void __exit jbd2_remove_debugfs_entry(void) { - if (proc_jbd_debug) - remove_proc_entry(JBD_PROC_NAME, NULL); + do { + } while (0); } -#else - -#define create_jbd_proc_entry() do {} while (0) -#define jbd2_remove_jbd_proc_entry() do {} while (0) - #endif struct kmem_cache *jbd2_handle_cache; @@ -2019,8 +2005,7 @@ static int __init journal_init_handle_cache(void) sizeof(handle_t), 0, /* offset */ 0, /* flags */ - NULL, /* ctor */ - NULL); /* dtor */ + NULL); /* ctor */ if (jbd2_handle_cache == NULL) { printk(KERN_EMERG "JBD: failed to create handle cache\n"); return -ENOMEM; @@ -2067,18 +2052,18 @@ static int __init journal_init(void) ret = journal_init_caches(); if (ret != 0) jbd2_journal_destroy_caches(); - create_jbd_proc_entry(); + jbd2_create_debugfs_entry(); return ret; } static void __exit journal_exit(void) { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG int n = atomic_read(&nr_journal_heads); if (n) printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n); #endif - jbd2_remove_jbd_proc_entry(); + jbd2_remove_debugfs_entry(); jbd2_journal_destroy_caches(); } diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 395c92a..b50be8a 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -251,10 +251,10 @@ int jbd2_journal_recover(journal_t *journal) if (!err) err = do_one_pass(journal, &info, PASS_REPLAY); - jbd_debug(0, "JBD: recovery, exit status %d, " + jbd_debug(1, "JBD: recovery, exit status %d, " "recovered transactions %u to %u\n", err, info.start_transaction, info.end_transaction); - jbd_debug(0, "JBD: Replayed %d and revoked %d/%d blocks\n", + jbd_debug(1, "JBD: Replayed %d and revoked %d/%d blocks\n", info.nr_replays, info.nr_revoke_hits, info.nr_revokes); /* Restart the log at the next transaction ID, thus invalidating @@ -295,10 +295,10 @@ int jbd2_journal_skip_recovery(journal_t *journal) printk(KERN_ERR "JBD: error %d scanning journal\n", err); ++journal->j_transaction_sequence; } else { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG int dropped = info.end_transaction - be32_to_cpu(sb->s_sequence); #endif - jbd_debug(0, + jbd_debug(1, "JBD: ignoring %d transaction%s from the journal.\n", dropped, (dropped == 1) ? "" : "s"); journal->j_transaction_sequence = ++info.end_transaction; diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 9246e76..01d8897 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -68,6 +68,7 @@ #include <linux/list.h> #include <linux/init.h> #endif +#include <linux/log2.h> static struct kmem_cache *jbd2_revoke_record_cache; static struct kmem_cache *jbd2_revoke_table_cache; @@ -170,13 +171,13 @@ int __init jbd2_journal_init_revoke_caches(void) { jbd2_revoke_record_cache = kmem_cache_create("jbd2_revoke_record", sizeof(struct jbd2_revoke_record_s), - 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN, NULL); if (jbd2_revoke_record_cache == 0) return -ENOMEM; jbd2_revoke_table_cache = kmem_cache_create("jbd2_revoke_table", sizeof(struct jbd2_revoke_table_s), - 0, 0, NULL, NULL); + 0, 0, NULL); if (jbd2_revoke_table_cache == 0) { kmem_cache_destroy(jbd2_revoke_record_cache); jbd2_revoke_record_cache = NULL; @@ -212,7 +213,7 @@ int jbd2_journal_init_revoke(journal_t *journal, int hash_size) journal->j_revoke = journal->j_revoke_table[0]; /* Check that the hash_size is a power of two */ - J_ASSERT ((hash_size & (hash_size-1)) == 0); + J_ASSERT(is_power_of_2(hash_size)); journal->j_revoke->hash_size = hash_size; @@ -239,7 +240,7 @@ int jbd2_journal_init_revoke(journal_t *journal, int hash_size) journal->j_revoke = journal->j_revoke_table[1]; /* Check that the hash_size is a power of two */ - J_ASSERT ((hash_size & (hash_size-1)) == 0); + J_ASSERT(is_power_of_2(hash_size)); journal->j_revoke->hash_size = hash_size; diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index a46101e..65b3a1b 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -435,7 +435,7 @@ static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, struct posix_acl *acl; int rc; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 8754b27..d568ae8 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -81,6 +81,7 @@ static int jffs2_garbage_collect_thread(void *_c) set_user_nice(current, 10); + set_freezable(); for (;;) { allow_signal(SIGHUP); again: diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 35c1a5e..f921125 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -33,56 +33,56 @@ int __init jffs2_create_slab_caches(void) { full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!full_dnode_slab) goto err; raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!raw_dirent_slab) goto err; raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!raw_inode_slab) goto err; tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!tmp_dnode_info_slab) goto err; raw_node_ref_slab = kmem_cache_create("jffs2_refblock", sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!raw_node_ref_slab) goto err; node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!node_frag_slab) goto err; inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!inode_cache_slab) goto err; #ifdef CONFIG_JFFS2_FS_XATTR xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum", sizeof(struct jffs2_xattr_datum), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!xattr_datum_cache) goto err; xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref", sizeof(struct jffs2_xattr_ref), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!xattr_ref_cache) goto err; #endif diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index e220d3b..be2b70c2 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -192,7 +192,7 @@ static int __init init_jffs2_fs(void) sizeof(struct jffs2_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - jffs2_i_init_once, NULL); + jffs2_i_init_once); if (!jffs2_inode_cachep) { printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); return -ENOMEM; diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c index fe063af..3c8663b 100644 --- a/fs/jfs/ioctl.c +++ b/fs/jfs/ioctl.c @@ -69,7 +69,7 @@ int jfs_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EACCES; if (get_user(flags, (int __user *) arg)) diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index 2374b59..f0ec72b 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -32,6 +32,7 @@ extern void jfs_truncate_nolock(struct inode *, loff_t); extern void jfs_free_zero_link(struct inode *); extern struct dentry *jfs_get_parent(struct dentry *dentry); extern void jfs_get_inode_flags(struct jfs_inode_info *); +extern struct dentry *jfs_get_dentry(struct super_block *sb, void *vobjp); extern void jfs_set_inode_flags(struct inode *); extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int); diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 77c7f11..62e96be 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -213,7 +213,7 @@ int __init metapage_init(void) * Allocate the metapage structures */ metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage), - 0, 0, init_once, NULL); + 0, 0, init_once); if (metapage_cache == NULL) return -ENOMEM; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 25161c4..932797b 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1477,6 +1477,38 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc return dentry; } +struct dentry *jfs_get_dentry(struct super_block *sb, void *vobjp) +{ + __u32 *objp = vobjp; + unsigned long ino = objp[0]; + __u32 generation = objp[1]; + struct inode *inode; + struct dentry *result; + + if (ino == 0) + return ERR_PTR(-ESTALE); + inode = iget(sb, ino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + + if (is_bad_inode(inode) || + (generation && inode->i_generation != generation)) { + result = ERR_PTR(-ESTALE); + goto out_iput; + } + + result = d_alloc_anon(inode); + if (!result) { + result = ERR_PTR(-ENOMEM); + goto out_iput; + } + return result; + + out_iput: + iput(inode); + return result; +} + struct dentry *jfs_get_parent(struct dentry *dentry) { struct super_block *sb = dentry->d_inode->i_sb; diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 20e4ac1..4b372f5 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -27,6 +27,7 @@ #include <linux/kthread.h> #include <linux/posix_acl.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <asm/uaccess.h> #include <linux/seq_file.h> @@ -737,6 +738,7 @@ static const struct super_operations jfs_super_operations = { }; static struct export_operations jfs_export_operations = { + .get_dentry = jfs_get_dentry, .get_parent = jfs_get_parent, }; @@ -774,7 +776,7 @@ static int __init init_jfs_fs(void) jfs_inode_cachep = kmem_cache_create("jfs_ip", sizeof(struct jfs_inode_info), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - init_once, NULL); + init_once); if (jfs_inode_cachep == NULL) return -ENOMEM; diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index b2375f0..9b7f2cd 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -697,7 +697,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name, struct posix_acl *acl; int rc; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; /* diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 96070bf..572601e9 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -44,9 +44,8 @@ static struct nsm_handle * nsm_find(const struct sockaddr_in *sin, */ static struct nlm_host * nlm_lookup_host(int server, const struct sockaddr_in *sin, - int proto, int version, - const char *hostname, - int hostname_len) + int proto, int version, const char *hostname, + int hostname_len, const struct sockaddr_in *ssin) { struct hlist_head *chain; struct hlist_node *pos; @@ -54,7 +53,9 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, struct nsm_handle *nsm = NULL; int hash; - dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", + dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT + ", p=%d, v=%d, my role=%s, name=%.*s)\n", + NIPQUAD(ssin->sin_addr.s_addr), NIPQUAD(sin->sin_addr.s_addr), proto, version, server? "server" : "client", hostname_len, @@ -91,6 +92,8 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, continue; if (host->h_server != server) continue; + if (!nlm_cmp_addr(&host->h_saddr, ssin)) + continue; /* Move to head of hash chain. */ hlist_del(&host->h_hash); @@ -118,6 +121,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, host->h_name = nsm->sm_name; host->h_addr = *sin; host->h_addr.sin_port = 0; /* ouch! */ + host->h_saddr = *ssin; host->h_version = version; host->h_proto = proto; host->h_rpcclnt = NULL; @@ -161,15 +165,9 @@ nlm_destroy_host(struct nlm_host *host) */ nsm_unmonitor(host); - if ((clnt = host->h_rpcclnt) != NULL) { - if (atomic_read(&clnt->cl_users)) { - printk(KERN_WARNING - "lockd: active RPC handle\n"); - clnt->cl_dead = 1; - } else { - rpc_destroy_client(host->h_rpcclnt); - } - } + clnt = host->h_rpcclnt; + if (clnt != NULL) + rpc_shutdown_client(clnt); kfree(host); } @@ -180,8 +178,10 @@ struct nlm_host * nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version, const char *hostname, int hostname_len) { + struct sockaddr_in ssin = {0}; + return nlm_lookup_host(0, sin, proto, version, - hostname, hostname_len); + hostname, hostname_len, &ssin); } /* @@ -191,9 +191,12 @@ struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *rqstp, const char *hostname, int hostname_len) { + struct sockaddr_in ssin = {0}; + + ssin.sin_addr = rqstp->rq_daddr.addr; return nlm_lookup_host(1, svc_addr_in(rqstp), rqstp->rq_prot, rqstp->rq_vers, - hostname, hostname_len); + hostname, hostname_len, &ssin); } /* @@ -204,8 +207,9 @@ nlm_bind_host(struct nlm_host *host) { struct rpc_clnt *clnt; - dprintk("lockd: nlm_bind_host(%08x)\n", - (unsigned)ntohl(host->h_addr.sin_addr.s_addr)); + dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n", + NIPQUAD(host->h_saddr.sin_addr), + NIPQUAD(host->h_addr.sin_addr)); /* Lock host handle */ mutex_lock(&host->h_mutex); @@ -232,6 +236,7 @@ nlm_bind_host(struct nlm_host *host) .protocol = host->h_proto, .address = (struct sockaddr *)&host->h_addr, .addrsize = sizeof(host->h_addr), + .saddress = (struct sockaddr *)&host->h_saddr, .timeout = &timeparms, .servername = host->h_name, .program = &nlm_program, diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index 2102e2d..3353ed8 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -61,6 +61,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) status); else status = 0; + rpc_shutdown_client(clnt); out: return status; } @@ -138,7 +139,6 @@ nsm_create(void) .program = &nsm_program, .version = SM_VERSION, .authflavor = RPC_AUTH_NULL, - .flags = (RPC_CLNT_CREATE_ONESHOT), }; return rpc_create(&args); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 126b1bf..82e2192 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -25,6 +25,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/mutex.h> +#include <linux/freezer.h> #include <linux/sunrpc/types.h> #include <linux/sunrpc/stats.h> @@ -75,18 +76,31 @@ static const int nlm_port_min = 0, nlm_port_max = 65535; static struct ctl_table_header * nlm_sysctl_table; -static unsigned long set_grace_period(void) +static unsigned long get_lockd_grace_period(void) { - unsigned long grace_period; - /* Note: nlm_timeout should always be nonzero */ if (nlm_grace_period) - grace_period = ((nlm_grace_period + nlm_timeout - 1) - / nlm_timeout) * nlm_timeout * HZ; + return roundup(nlm_grace_period, nlm_timeout) * HZ; else - grace_period = nlm_timeout * 5 * HZ; + return nlm_timeout * 5 * HZ; +} + +unsigned long get_nfs_grace_period(void) +{ + unsigned long lockdgrace = get_lockd_grace_period(); + unsigned long nfsdgrace = 0; + + if (nlmsvc_ops) + nfsdgrace = nlmsvc_ops->get_grace_period(); + + return max(lockdgrace, nfsdgrace); +} +EXPORT_SYMBOL(get_nfs_grace_period); + +static unsigned long set_grace_period(void) +{ nlmsvc_grace_period = 1; - return grace_period + jiffies; + return get_nfs_grace_period() + jiffies; } static inline void clear_grace_period(void) @@ -119,13 +133,11 @@ lockd(struct svc_rqst *rqstp) complete(&lockd_start_done); daemonize("lockd"); + set_freezable(); /* Process request with signals blocked, but allow SIGKILL. */ allow_signal(SIGKILL); - /* kick rpciod */ - rpciod_up(); - dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); if (!nlm_timeout) @@ -202,9 +214,6 @@ lockd(struct svc_rqst *rqstp) /* Exit the RPC thread */ svc_exit_thread(rqstp); - /* release rpciod */ - rpciod_down(); - /* Release module */ unlock_kernel(); module_put_and_exit(0); @@ -458,22 +458,20 @@ static int lease_init(struct file *filp, int type, struct file_lock *fl) } /* Allocate a file_lock initialised to this type of lease */ -static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +static struct file_lock *lease_alloc(struct file *filp, int type) { struct file_lock *fl = locks_alloc_lock(); int error = -ENOMEM; if (fl == NULL) - goto out; + return ERR_PTR(error); error = lease_init(filp, type, fl); if (error) { locks_free_lock(fl); - fl = NULL; + return ERR_PTR(error); } -out: - *flp = fl; - return error; + return fl; } /* Check if two locks overlap each other. @@ -661,7 +659,7 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w return result; } -int +void posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; @@ -673,14 +671,12 @@ posix_test_lock(struct file *filp, struct file_lock *fl) if (posix_locks_conflict(cfl, fl)) break; } - if (cfl) { + if (cfl) __locks_copy_lock(fl, cfl); - unlock_kernel(); - return 1; - } else + else fl->fl_type = F_UNLCK; unlock_kernel(); - return 0; + return; } EXPORT_SYMBOL(posix_test_lock); @@ -1169,9 +1165,9 @@ static void time_out_leases(struct inode *inode) * @inode: the inode of the file to return * @mode: the open mode (read or write) * - * break_lease (inlined for speed) has checked there already - * is a lease on this file. Leases are broken on a call to open() - * or truncate(). This function can sleep unless you + * break_lease (inlined for speed) has checked there already is at least + * some kind of lock (maybe a lease) on this file. Leases are broken on + * a call to open() or truncate(). This function can sleep unless you * specified %O_NONBLOCK to your open(). */ int __break_lease(struct inode *inode, unsigned int mode) @@ -1179,12 +1175,10 @@ int __break_lease(struct inode *inode, unsigned int mode) int error = 0, future; struct file_lock *new_fl, *flock; struct file_lock *fl; - int alloc_err; unsigned long break_time; int i_have_this_lease = 0; - alloc_err = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK, - &new_fl); + new_fl = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK); lock_kernel(); @@ -1212,8 +1206,9 @@ int __break_lease(struct inode *inode, unsigned int mode) goto out; } - if (alloc_err && !i_have_this_lease && ((mode & O_NONBLOCK) == 0)) { - error = alloc_err; + if (IS_ERR(new_fl) && !i_have_this_lease + && ((mode & O_NONBLOCK) == 0)) { + error = PTR_ERR(new_fl); goto out; } @@ -1260,7 +1255,7 @@ restart: out: unlock_kernel(); - if (!alloc_err) + if (!IS_ERR(new_fl)) locks_free_lock(new_fl); return error; } @@ -1329,7 +1324,7 @@ int fcntl_getlease(struct file *filp) } /** - * __setlease - sets a lease on an open file + * setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain * @flp: input - file_lock to use, output - file_lock inserted @@ -1339,18 +1334,24 @@ int fcntl_getlease(struct file *filp) * * Called with kernel lock held. */ -static int __setlease(struct file *filp, long arg, struct file_lock **flp) +int setlease(struct file *filp, long arg, struct file_lock **flp) { struct file_lock *fl, **before, **my_before = NULL, *lease; struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; int error, rdlease_count = 0, wrlease_count = 0; + if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + error = security_file_lock(filp, arg); + if (error) + return error; + time_out_leases(inode); - error = -EINVAL; - if (!flp || !(*flp) || !(*flp)->fl_lmops || !(*flp)->fl_lmops->fl_break) - goto out; + BUG_ON(!(*flp)->fl_lmops->fl_break); lease = *flp; @@ -1418,39 +1419,49 @@ static int __setlease(struct file *filp, long arg, struct file_lock **flp) out: return error; } +EXPORT_SYMBOL(setlease); /** - * setlease - sets a lease on an open file + * vfs_setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain * @lease: file_lock to use * * Call this to establish a lease on the file. - * The fl_lmops fl_break function is required by break_lease + * The (*lease)->fl_lmops->fl_break operation must be set; if not, + * break_lease will oops! + * + * This will call the filesystem's setlease file method, if + * defined. Note that there is no getlease method; instead, the + * filesystem setlease method should call back to setlease() to + * add a lease to the inode's lease list, where fcntl_getlease() can + * find it. Since fcntl_getlease() only reports whether the current + * task holds a lease, a cluster filesystem need only do this for + * leases held by processes on this node. + * + * There is also no break_lease method; filesystems that + * handle their own leases shoud break leases themselves from the + * filesystem's open, create, and (on truncate) setattr methods. + * + * Warning: the only current setlease methods exist only to disable + * leases in certain cases. More vfs changes may be required to + * allow a full filesystem lease implementation. */ -int setlease(struct file *filp, long arg, struct file_lock **lease) +int vfs_setlease(struct file *filp, long arg, struct file_lock **lease) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; int error; - if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) - return -EACCES; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - error = security_file_lock(filp, arg); - if (error) - return error; - lock_kernel(); - error = __setlease(filp, arg, lease); + if (filp->f_op && filp->f_op->setlease) + error = filp->f_op->setlease(filp, arg, lease); + else + error = setlease(filp, arg, lease); unlock_kernel(); return error; } - -EXPORT_SYMBOL(setlease); +EXPORT_SYMBOL_GPL(vfs_setlease); /** * fcntl_setlease - sets a lease on an open file @@ -1469,14 +1480,6 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) struct inode *inode = dentry->d_inode; int error; - if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) - return -EACCES; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - error = security_file_lock(filp, arg); - if (error) - return error; - locks_init_lock(&fl); error = lease_init(filp, arg, &fl); if (error) @@ -1484,15 +1487,15 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) lock_kernel(); - error = __setlease(filp, arg, &flp); + error = vfs_setlease(filp, arg, &flp); if (error || arg == F_UNLCK) goto out_unlock; error = fasync_helper(fd, filp, 1, &flp->fl_fasync); if (error < 0) { - /* remove lease just inserted by __setlease */ + /* remove lease just inserted by setlease */ flp->fl_type = F_UNLCK | F_INPROGRESS; - flp->fl_break_time = jiffies- 10; + flp->fl_break_time = jiffies - 10; time_out_leases(inode); goto out_unlock; } @@ -1597,8 +1600,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) /** * vfs_test_lock - test file byte range lock * @filp: The file to test lock for - * @fl: The lock to test - * @conf: Place to return a copy of the conflicting lock, if found + * @fl: The lock to test; also used to hold result * * Returns -ERRNO on failure. Indicates presence of conflicting lock by * setting conf->fl_type to something other than F_UNLCK. @@ -2274,7 +2276,7 @@ static int __init filelock_init(void) { filelock_cache = kmem_cache_create("file_lock_cache", sizeof(struct file_lock), 0, SLAB_PANIC, - init_once, NULL); + init_once); return 0; } diff --git a/fs/mbcache.c b/fs/mbcache.c index deeb9dc..1046cbe 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -100,7 +100,6 @@ struct mb_cache { static LIST_HEAD(mb_cache_list); static LIST_HEAD(mb_cache_lru_list); static DEFINE_SPINLOCK(mb_cache_spinlock); -static struct shrinker *mb_shrinker; static inline int mb_cache_indexes(struct mb_cache *cache) @@ -118,6 +117,10 @@ mb_cache_indexes(struct mb_cache *cache) static int mb_cache_shrink_fn(int nr_to_scan, gfp_t gfp_mask); +static struct shrinker mb_cache_shrinker = { + .shrink = mb_cache_shrink_fn, + .seeks = DEFAULT_SEEKS, +}; static inline int __mb_cache_entry_is_hashed(struct mb_cache_entry *ce) @@ -289,7 +292,7 @@ mb_cache_create(const char *name, struct mb_cache_op *cache_op, INIT_LIST_HEAD(&cache->c_indexes_hash[m][n]); } cache->c_entry_cache = kmem_cache_create(name, entry_size, 0, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL, NULL); + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); if (!cache->c_entry_cache) goto fail; @@ -662,13 +665,13 @@ mb_cache_entry_find_next(struct mb_cache_entry *prev, int index, static int __init init_mbcache(void) { - mb_shrinker = set_shrinker(DEFAULT_SEEKS, mb_cache_shrink_fn); + register_shrinker(&mb_cache_shrinker); return 0; } static void __exit exit_mbcache(void) { - remove_shrinker(mb_shrinker); + unregister_shrinker(&mb_cache_shrinker); } module_init(init_mbcache) diff --git a/fs/minix/inode.c b/fs/minix/inode.c index be40446..43668d7 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -75,14 +75,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { minix_inode_cachep = kmem_cache_create("minix_inode_cache", sizeof(struct minix_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (minix_inode_cachep == NULL) return -ENOMEM; return 0; @@ -107,6 +107,8 @@ * any extra contention... */ +static int fastcall link_path_walk(const char *name, struct nameidata *nd); + /* In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -998,7 +1000,7 @@ return_err: * Retry the whole path once, forcing real lookup requests * instead of relying on the dcache. */ -int fastcall link_path_walk(const char *name, struct nameidata *nd) +static int fastcall link_path_walk(const char *name, struct nameidata *nd) { struct nameidata save = *nd; int result; @@ -1022,7 +1024,7 @@ int fastcall link_path_walk(const char *name, struct nameidata *nd) return result; } -int fastcall path_walk(const char * name, struct nameidata *nd) +static int fastcall path_walk(const char * name, struct nameidata *nd) { current->total_link_count = 0; return link_path_walk(name, nd); @@ -1172,6 +1174,37 @@ int fastcall path_lookup(const char *name, unsigned int flags, return do_path_lookup(AT_FDCWD, name, flags, nd); } +/** + * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair + * @dentry: pointer to dentry of the base directory + * @mnt: pointer to vfs mount of the base directory + * @name: pointer to file name + * @flags: lookup flags + * @nd: pointer to nameidata + */ +int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, + const char *name, unsigned int flags, + struct nameidata *nd) +{ + int retval; + + /* same as do_path_lookup */ + nd->last_type = LAST_ROOT; + nd->flags = flags; + nd->depth = 0; + + nd->mnt = mntget(mnt); + nd->dentry = dget(dentry); + + retval = path_walk(name, nd); + if (unlikely(!retval && !audit_dummy_context() && nd->dentry && + nd->dentry->d_inode)) + audit_inode(name, nd->dentry->d_inode); + + return retval; + +} + static int __path_lookup_intent_open(int dfd, const char *name, unsigned int lookup_flags, struct nameidata *nd, int open_flags, int create_mode) @@ -1576,7 +1609,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) /* O_NOATIME can only be set by the owner or superuser */ if (flag & O_NOATIME) - if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; /* @@ -2774,8 +2807,8 @@ EXPORT_SYMBOL(__page_symlink); EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); +EXPORT_SYMBOL(vfs_path_lookup); EXPORT_SYMBOL(path_release); -EXPORT_SYMBOL(path_walk); EXPORT_SYMBOL(permission); EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(file_permission); diff --git a/fs/namespace.c b/fs/namespace.c index b696e3a..ddbda13 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -28,6 +28,7 @@ #include <asm/uaccess.h> #include <asm/unistd.h> #include "pnode.h" +#include "internal.h" /* spinlock for vfsmount related operations, inplace of dcache_lock */ __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); @@ -320,22 +321,16 @@ EXPORT_SYMBOL(mnt_unpin); static void *m_start(struct seq_file *m, loff_t *pos) { struct mnt_namespace *n = m->private; - struct list_head *p; - loff_t l = *pos; down_read(&namespace_sem); - list_for_each(p, &n->list) - if (!l--) - return list_entry(p, struct vfsmount, mnt_list); - return NULL; + return seq_list_start(&n->list, *pos); } static void *m_next(struct seq_file *m, void *v, loff_t *pos) { struct mnt_namespace *n = m->private; - struct list_head *p = ((struct vfsmount *)v)->mnt_list.next; - (*pos)++; - return p == &n->list ? NULL : list_entry(p, struct vfsmount, mnt_list); + + return seq_list_next(v, &n->list, pos); } static void m_stop(struct seq_file *m, void *v) @@ -350,7 +345,7 @@ static inline void mangle(struct seq_file *m, const char *s) static int show_vfsmnt(struct seq_file *m, void *v) { - struct vfsmount *mnt = v; + struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); int err = 0; static struct proc_fs_info { int flag; @@ -405,7 +400,7 @@ struct seq_operations mounts_op = { static int show_vfsstat(struct seq_file *m, void *v) { - struct vfsmount *mnt = v; + struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); int err = 0; /* device */ @@ -1457,7 +1452,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); if (!new_ns) - return NULL; + return ERR_PTR(-ENOMEM); atomic_set(&new_ns->count, 1); INIT_LIST_HEAD(&new_ns->list); @@ -1471,7 +1466,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, if (!new_ns->root) { up_write(&namespace_sem); kfree(new_ns); - return NULL; + return ERR_PTR(-ENOMEM);; } spin_lock(&vfsmount_lock); list_add_tail(&new_ns->list, &new_ns->root->mnt_list); @@ -1515,7 +1510,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, return new_ns; } -struct mnt_namespace *copy_mnt_ns(int flags, struct mnt_namespace *ns, +struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, struct fs_struct *new_fs) { struct mnt_namespace *new_ns; @@ -1806,7 +1801,7 @@ void __init mnt_init(unsigned long mempages) init_rwsem(&namespace_sem); mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount), - 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index d3152f8..2b145de 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -203,7 +203,6 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { if (pos >= MAX_NON_LFS) { - send_sig(SIGXFSZ, current, 0); return -EFBIG; } if (count > MAX_NON_LFS - (u32)pos) { @@ -212,7 +211,6 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * } if (pos >= inode->i_sb->s_maxbytes) { if (count || pos > inode->i_sb->s_maxbytes) { - send_sig(SIGXFSZ, current, 0); return -EFBIG; } } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index cf06eb9..7f8536d 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -63,14 +63,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag mutex_init(&ei->open_mutex); inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { ncp_inode_cachep = kmem_cache_create("ncp_inode_cache", sizeof(struct ncp_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (ncp_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 70a6911..a94473d 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -24,31 +24,35 @@ /* * Fill in the supplied page for mmap + * XXX: how are we excluding truncate/invalidate here? Maybe need to lock + * page? */ -static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int ncp_file_mmap_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct file *file = area->vm_file; struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; - struct page* page; char *pg_addr; unsigned int already_read; unsigned int count; int bufsize; - int pos; + int pos; /* XXX: loff_t ? */ - page = alloc_page(GFP_HIGHUSER); /* ncpfs has nothing against high pages - as long as recvmsg and memset works on it */ - if (!page) - return page; - pg_addr = kmap(page); - address &= PAGE_MASK; - pos = address - area->vm_start + (area->vm_pgoff << PAGE_SHIFT); + /* + * ncpfs has nothing against high pages as long + * as recvmsg and memset works on it + */ + vmf->page = alloc_page(GFP_HIGHUSER); + if (!vmf->page) + return VM_FAULT_OOM; + pg_addr = kmap(vmf->page); + pos = vmf->pgoff << PAGE_SHIFT; count = PAGE_SIZE; - if (address + PAGE_SIZE > area->vm_end) { - count = area->vm_end - address; + if ((unsigned long)vmf->virtual_address + PAGE_SIZE > area->vm_end) { + WARN_ON(1); /* shouldn't happen? */ + count = area->vm_end - (unsigned long)vmf->virtual_address; } /* what we can read in one go */ bufsize = NCP_SERVER(inode)->buffer_size; @@ -83,23 +87,21 @@ static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, if (already_read < PAGE_SIZE) memset(pg_addr + already_read, 0, PAGE_SIZE - already_read); - flush_dcache_page(page); - kunmap(page); + flush_dcache_page(vmf->page); + kunmap(vmf->page); /* * If I understand ncp_read_kernel() properly, the above always * fetches from the network, here the analogue of disk. * -- wli */ - if (type) - *type = VM_FAULT_MAJOR; count_vm_event(PGMAJFAULT); - return page; + return VM_FAULT_MAJOR; } static struct vm_operations_struct ncp_file_mmap = { - .nopage = ncp_file_mmap_nopage, + .fault = ncp_file_mmap_fault, }; diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index f4580b4..b55cb23 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -6,8 +6,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ pagelist.o proc.o read.o symlink.o unlink.o \ - write.o namespace.o -nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o + write.o namespace.o mount_clnt.o +nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 75f309c..a796be5 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -14,6 +14,7 @@ #include <linux/sunrpc/svcsock.h> #include <linux/nfs_fs.h> #include <linux/mutex.h> +#include <linux/freezer.h> #include <net/inet_sock.h> @@ -67,6 +68,7 @@ static void nfs_callback_svc(struct svc_rqst *rqstp) daemonize("nfsv4-svc"); /* Process request with signals blocked, but allow SIGKILL. */ allow_signal(SIGKILL); + set_freezable(); complete(&nfs_callback_info.started); diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 849a202..058ade7 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -179,7 +179,7 @@ static __be32 decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr args->addr = svc_addr_in(rqstp); status = decode_bitmap(xdr, args->bitmap); out: - dprintk("%s: exit with status = %d\n", __FUNCTION__, status); + dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(status)); return status; } @@ -200,7 +200,7 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, args->truncate = ntohl(*p); status = decode_fh(xdr, &args->fh); out: - dprintk("%s: exit with status = %d\n", __FUNCTION__, status); + dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(status)); return status; } @@ -349,7 +349,7 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, status = encode_attr_mtime(xdr, res->bitmap, &res->mtime); *savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1))); out: - dprintk("%s: exit with status = %d\n", __FUNCTION__, status); + dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(status)); return status; } @@ -392,7 +392,7 @@ static __be32 process_op(struct svc_rqst *rqstp, status = res; if (op->encode_res != NULL && status == 0) status = op->encode_res(rqstp, xdr_out, resp); - dprintk("%s: done, status = %d\n", __FUNCTION__, status); + dprintk("%s: done, status = %d\n", __FUNCTION__, ntohl(status)); return status; } @@ -431,7 +431,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r } *hdr_res.status = status; *hdr_res.nops = htonl(nops); - dprintk("%s: done, status = %u\n", __FUNCTION__, status); + dprintk("%s: done, status = %u\n", __FUNCTION__, ntohl(status)); return rpc_success; } diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 881fa49..a49f9fe 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -102,19 +102,10 @@ static struct nfs_client *nfs_alloc_client(const char *hostname, int nfsversion) { struct nfs_client *clp; - int error; if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; - error = rpciod_up(); - if (error < 0) { - dprintk("%s: couldn't start rpciod! Error = %d\n", - __FUNCTION__, error); - goto error_1; - } - __set_bit(NFS_CS_RPCIOD, &clp->cl_res_state); - if (nfsversion == 4) { if (nfs_callback_up() < 0) goto error_2; @@ -139,8 +130,6 @@ static struct nfs_client *nfs_alloc_client(const char *hostname, #ifdef CONFIG_NFS_V4 init_rwsem(&clp->cl_sem); INIT_LIST_HEAD(&clp->cl_delegations); - INIT_LIST_HEAD(&clp->cl_state_owners); - INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); @@ -154,9 +143,6 @@ error_3: if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) nfs_callback_down(); error_2: - rpciod_down(); - __clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state); -error_1: kfree(clp); error_0: return NULL; @@ -167,16 +153,7 @@ static void nfs4_shutdown_client(struct nfs_client *clp) #ifdef CONFIG_NFS_V4 if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) nfs4_kill_renewd(clp); - while (!list_empty(&clp->cl_unused)) { - struct nfs4_state_owner *sp; - - sp = list_entry(clp->cl_unused.next, - struct nfs4_state_owner, - so_list); - list_del(&sp->so_list); - kfree(sp); - } - BUG_ON(!list_empty(&clp->cl_state_owners)); + BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners)); if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) nfs_idmap_delete(clp); #endif @@ -198,9 +175,6 @@ static void nfs_free_client(struct nfs_client *clp) if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) nfs_callback_down(); - if (__test_and_clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state)) - rpciod_down(); - kfree(clp->cl_hostname); kfree(clp); @@ -1232,23 +1206,9 @@ static int nfs_server_list_open(struct inode *inode, struct file *file) */ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) { - struct list_head *_p; - loff_t pos = *_pos; - /* lock the list against modification */ spin_lock(&nfs_client_lock); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &nfs_client_list) - if (!pos--) - break; - - return _p != &nfs_client_list ? _p : NULL; + return seq_list_start_head(&nfs_client_list, *_pos); } /* @@ -1256,14 +1216,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) */ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) { - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? nfs_client_list.next : _p->next; - - return _p != &nfs_client_list ? _p : NULL; + return seq_list_next(v, &nfs_client_list, pos); } /* @@ -1282,7 +1235,7 @@ static int nfs_server_list_show(struct seq_file *m, void *v) struct nfs_client *clp; /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { + if (v == &nfs_client_list) { seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); return 0; } @@ -1323,23 +1276,9 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file) */ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) { - struct list_head *_p; - loff_t pos = *_pos; - /* lock the list against modification */ spin_lock(&nfs_client_lock); - - /* allow for the header line */ - if (!pos) - return SEQ_START_TOKEN; - pos--; - - /* find the n'th element in the list */ - list_for_each(_p, &nfs_volume_list) - if (!pos--) - break; - - return _p != &nfs_volume_list ? _p : NULL; + return seq_list_start_head(&nfs_volume_list, *_pos); } /* @@ -1347,14 +1286,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) */ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) { - struct list_head *_p; - - (*pos)++; - - _p = v; - _p = (v == SEQ_START_TOKEN) ? nfs_volume_list.next : _p->next; - - return _p != &nfs_volume_list ? _p : NULL; + return seq_list_next(v, &nfs_volume_list, pos); } /* @@ -1375,7 +1307,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) char dev[8], fsid[17]; /* display header on line 1 */ - if (v == SEQ_START_TOKEN) { + if (v == &nfs_volume_list) { seq_puts(m, "NV SERVER PORT DEV FSID\n"); return 0; } diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 7f37d1b..20ac403 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -27,6 +27,13 @@ static void nfs_free_delegation(struct nfs_delegation *delegation) kfree(delegation); } +static void nfs_free_delegation_callback(struct rcu_head *head) +{ + struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu); + + nfs_free_delegation(delegation); +} + static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) { struct inode *inode = state->inode; @@ -57,7 +64,7 @@ out_err: return status; } -static void nfs_delegation_claim_opens(struct inode *inode) +static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_open_context *ctx; @@ -72,9 +79,11 @@ again: continue; if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) continue; + if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0) + continue; get_nfs_open_context(ctx); spin_unlock(&inode->i_lock); - err = nfs4_open_delegation_recall(ctx->dentry, state); + err = nfs4_open_delegation_recall(ctx, state, stateid); if (err >= 0) err = nfs_delegation_claim_locks(ctx, state); put_nfs_open_context(ctx); @@ -115,10 +124,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct struct nfs_delegation *delegation; int status = 0; - /* Ensure we first revalidate the attributes and page cache! */ - if ((nfsi->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR))) - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); if (delegation == NULL) return -ENOMEM; @@ -131,10 +136,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct delegation->inode = inode; spin_lock(&clp->cl_lock); - if (nfsi->delegation == NULL) { - list_add(&delegation->super_list, &clp->cl_delegations); - nfsi->delegation = delegation; + if (rcu_dereference(nfsi->delegation) == NULL) { + list_add_rcu(&delegation->super_list, &clp->cl_delegations); nfsi->delegation_state = delegation->type; + rcu_assign_pointer(nfsi->delegation, delegation); delegation = NULL; } else { if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, @@ -145,6 +150,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct status = -EIO; } } + + /* Ensure we revalidate the attributes and page cache! */ + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_REVAL_FORCED; + spin_unlock(&inode->i_lock); + spin_unlock(&clp->cl_lock); kfree(delegation); return status; @@ -155,7 +166,7 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation * int res = 0; res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); - nfs_free_delegation(delegation); + call_rcu(&delegation->rcu, nfs_free_delegation_callback); return res; } @@ -170,33 +181,55 @@ static void nfs_msync_inode(struct inode *inode) /* * Basic procedure for returning a delegation to the server */ -int __nfs_inode_return_delegation(struct inode *inode) +static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) { struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_inode *nfsi = NFS_I(inode); - struct nfs_delegation *delegation; - int res = 0; nfs_msync_inode(inode); down_read(&clp->cl_sem); /* Guard against new delegated open calls */ down_write(&nfsi->rwsem); - spin_lock(&clp->cl_lock); - delegation = nfsi->delegation; - if (delegation != NULL) { - list_del_init(&delegation->super_list); - nfsi->delegation = NULL; - nfsi->delegation_state = 0; - } - spin_unlock(&clp->cl_lock); - nfs_delegation_claim_opens(inode); + nfs_delegation_claim_opens(inode, &delegation->stateid); up_write(&nfsi->rwsem); up_read(&clp->cl_sem); nfs_msync_inode(inode); - if (delegation != NULL) - res = nfs_do_return_delegation(inode, delegation); - return res; + return nfs_do_return_delegation(inode, delegation); +} + +static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) +{ + struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); + + if (delegation == NULL) + goto nomatch; + if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, + sizeof(delegation->stateid.data)) != 0) + goto nomatch; + list_del_rcu(&delegation->super_list); + nfsi->delegation_state = 0; + rcu_assign_pointer(nfsi->delegation, NULL); + return delegation; +nomatch: + return NULL; +} + +int nfs_inode_return_delegation(struct inode *inode) +{ + struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_delegation *delegation; + int err = 0; + + if (rcu_dereference(nfsi->delegation) != NULL) { + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(nfsi, NULL); + spin_unlock(&clp->cl_lock); + if (delegation != NULL) + err = __nfs_inode_return_delegation(inode, delegation); + } + return err; } /* @@ -211,19 +244,23 @@ void nfs_return_all_delegations(struct super_block *sb) if (clp == NULL) return; restart: - spin_lock(&clp->cl_lock); - list_for_each_entry(delegation, &clp->cl_delegations, super_list) { + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { if (delegation->inode->i_sb != sb) continue; inode = igrab(delegation->inode); if (inode == NULL) continue; + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); spin_unlock(&clp->cl_lock); - nfs_inode_return_delegation(inode); + rcu_read_unlock(); + if (delegation != NULL) + __nfs_inode_return_delegation(inode, delegation); iput(inode); goto restart; } - spin_unlock(&clp->cl_lock); + rcu_read_unlock(); } static int nfs_do_expire_all_delegations(void *ptr) @@ -234,22 +271,26 @@ static int nfs_do_expire_all_delegations(void *ptr) allow_signal(SIGKILL); restart: - spin_lock(&clp->cl_lock); if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) goto out; if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) goto out; - list_for_each_entry(delegation, &clp->cl_delegations, super_list) { + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { inode = igrab(delegation->inode); if (inode == NULL) continue; + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); spin_unlock(&clp->cl_lock); - nfs_inode_return_delegation(inode); + rcu_read_unlock(); + if (delegation) + __nfs_inode_return_delegation(inode, delegation); iput(inode); goto restart; } + rcu_read_unlock(); out: - spin_unlock(&clp->cl_lock); nfs_put_client(clp); module_put_and_exit(0); } @@ -280,17 +321,21 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp) if (clp == NULL) return; restart: - spin_lock(&clp->cl_lock); - list_for_each_entry(delegation, &clp->cl_delegations, super_list) { + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { inode = igrab(delegation->inode); if (inode == NULL) continue; + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); spin_unlock(&clp->cl_lock); - nfs_inode_return_delegation(inode); + rcu_read_unlock(); + if (delegation != NULL) + __nfs_inode_return_delegation(inode, delegation); iput(inode); goto restart; } - spin_unlock(&clp->cl_lock); + rcu_read_unlock(); } struct recall_threadargs { @@ -316,21 +361,14 @@ static int recall_thread(void *data) down_read(&clp->cl_sem); down_write(&nfsi->rwsem); spin_lock(&clp->cl_lock); - delegation = nfsi->delegation; - if (delegation != NULL && memcmp(delegation->stateid.data, - args->stateid->data, - sizeof(delegation->stateid.data)) == 0) { - list_del_init(&delegation->super_list); - nfsi->delegation = NULL; - nfsi->delegation_state = 0; + delegation = nfs_detach_delegation_locked(nfsi, args->stateid); + if (delegation != NULL) args->result = 0; - } else { - delegation = NULL; + else args->result = -ENOENT; - } spin_unlock(&clp->cl_lock); complete(&args->started); - nfs_delegation_claim_opens(inode); + nfs_delegation_claim_opens(inode, args->stateid); up_write(&nfsi->rwsem); up_read(&clp->cl_sem); nfs_msync_inode(inode); @@ -371,14 +409,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs { struct nfs_delegation *delegation; struct inode *res = NULL; - spin_lock(&clp->cl_lock); - list_for_each_entry(delegation, &clp->cl_delegations, super_list) { + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { res = igrab(delegation->inode); break; } } - spin_unlock(&clp->cl_lock); + rcu_read_unlock(); return res; } @@ -388,10 +426,10 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs void nfs_delegation_mark_reclaim(struct nfs_client *clp) { struct nfs_delegation *delegation; - spin_lock(&clp->cl_lock); - list_for_each_entry(delegation, &clp->cl_delegations, super_list) + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; - spin_unlock(&clp->cl_lock); + rcu_read_unlock(); } /* @@ -399,39 +437,35 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) */ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) { - struct nfs_delegation *delegation, *n; - LIST_HEAD(head); - spin_lock(&clp->cl_lock); - list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) { + struct nfs_delegation *delegation; +restart: + rcu_read_lock(); + list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) continue; - list_move(&delegation->super_list, &head); - NFS_I(delegation->inode)->delegation = NULL; - NFS_I(delegation->inode)->delegation_state = 0; - } - spin_unlock(&clp->cl_lock); - while(!list_empty(&head)) { - delegation = list_entry(head.next, struct nfs_delegation, super_list); - list_del(&delegation->super_list); - nfs_free_delegation(delegation); + spin_lock(&clp->cl_lock); + delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); + spin_unlock(&clp->cl_lock); + rcu_read_unlock(); + if (delegation != NULL) + call_rcu(&delegation->rcu, nfs_free_delegation_callback); + goto restart; } + rcu_read_unlock(); } int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) { - struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_inode *nfsi = NFS_I(inode); struct nfs_delegation *delegation; - int res = 0; + int ret = 0; - if (nfsi->delegation_state == 0) - return 0; - spin_lock(&clp->cl_lock); - delegation = nfsi->delegation; + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); if (delegation != NULL) { memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); - res = 1; + ret = 1; } - spin_unlock(&clp->cl_lock); - return res; + rcu_read_unlock(); + return ret; } diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 2cfd4b2..5874ce7 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -22,11 +22,12 @@ struct nfs_delegation { long flags; loff_t maxsize; __u64 change_attr; + struct rcu_head rcu; }; int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); -int __nfs_inode_return_delegation(struct inode *inode); +int nfs_inode_return_delegation(struct inode *inode); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); @@ -39,27 +40,24 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp); /* NFSv4 delegation-related procedures */ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid); -int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state); +int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); static inline int nfs_have_delegation(struct inode *inode, int flags) { + struct nfs_delegation *delegation; + int ret = 0; + flags &= FMODE_READ|FMODE_WRITE; - smp_rmb(); - if ((NFS_I(inode)->delegation_state & flags) == flags) - return 1; - return 0; + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(inode)->delegation); + if (delegation != NULL && (delegation->type & flags) == flags) + ret = 1; + rcu_read_unlock(); + return ret; } -static inline int nfs_inode_return_delegation(struct inode *inode) -{ - int err = 0; - - if (NFS_I(inode)->delegation != NULL) - err = __nfs_inode_return_delegation(inode); - return err; -} #else static inline int nfs_have_delegation(struct inode *inode, int flags) { diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c27258b..ea97408 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -654,7 +654,7 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) if (IS_ROOT(dentry)) return 1; - verf = (unsigned long)dentry->d_fsdata; + verf = dentry->d_time; if (nfs_caches_unstable(dir) || verf != NFS_I(dir)->cache_change_attribute) return 0; @@ -663,7 +663,7 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) { - dentry->d_fsdata = (void *)verf; + dentry->d_time = verf; } static void nfs_refresh_verifier(struct dentry * dentry, unsigned long verf) @@ -869,7 +869,7 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { lock_kernel(); drop_nlink(inode); - nfs_complete_unlink(dentry); + nfs_complete_unlink(dentry, inode); unlock_kernel(); } /* When creating a negative dentry, we want to renew d_time */ @@ -897,14 +897,13 @@ int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) return (nd->intent.open.flags & O_EXCL) != 0; } -static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir, - struct nfs_fh *fh, struct nfs_fattr *fattr) +static inline int nfs_reval_fsid(struct inode *dir, const struct nfs_fattr *fattr) { struct nfs_server *server = NFS_SERVER(dir); if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) - /* Revalidate fsid on root dir */ - return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode); + /* Revalidate fsid using the parent directory */ + return __nfs_revalidate_inode(server, dir); return 0; } @@ -946,7 +945,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru res = ERR_PTR(error); goto out_unlock; } - error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr); + error = nfs_reval_fsid(dir, &fattr); if (error < 0) { res = ERR_PTR(error); goto out_unlock; @@ -1244,7 +1243,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; - if (nd && (nd->flags & LOOKUP_CREATE)) + if ((nd->flags & LOOKUP_CREATE) != 0) open_flags = nd->intent.open.flags; lock_kernel(); @@ -1412,7 +1411,7 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry) nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); d_move(dentry, sdentry); - error = nfs_async_unlink(dentry); + error = nfs_async_unlink(dir, dentry); /* If we return 0 we don't unlink */ } dput(sdentry); @@ -1535,7 +1534,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym lock_kernel(); - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_HIGHUSER); if (!page) { unlock_kernel(); return -ENOMEM; @@ -1744,8 +1743,8 @@ int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask) struct nfs_inode *nfsi; struct nfs_access_entry *cache; - spin_lock(&nfs_access_lru_lock); restart: + spin_lock(&nfs_access_lru_lock); list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) { struct inode *inode; @@ -1770,6 +1769,7 @@ remove_lru_entry: clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags); } spin_unlock(&inode->i_lock); + spin_unlock(&nfs_access_lru_lock); iput(inode); goto restart; } diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 00eee87..fcf4d38 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -266,7 +266,7 @@ static const struct rpc_call_ops nfs_read_direct_ops = { static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) { struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; + struct inode *inode = ctx->path.dentry->d_inode; size_t rsize = NFS_SERVER(inode)->rsize; unsigned int pgbase; int result; @@ -295,9 +295,14 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo break; } if ((unsigned)result < data->npages) { - nfs_direct_release_pages(data->pagevec, result); - nfs_readdata_release(data); - break; + bytes = result * PAGE_SIZE; + if (bytes <= pgbase) { + nfs_direct_release_pages(data->pagevec, result); + nfs_readdata_release(data); + break; + } + bytes -= pgbase; + data->npages = result; } get_dreq(dreq); @@ -601,7 +606,7 @@ static const struct rpc_call_ops nfs_write_direct_ops = { static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync) { struct nfs_open_context *ctx = dreq->ctx; - struct inode *inode = ctx->dentry->d_inode; + struct inode *inode = ctx->path.dentry->d_inode; size_t wsize = NFS_SERVER(inode)->wsize; unsigned int pgbase; int result; @@ -630,9 +635,14 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l break; } if ((unsigned)result < data->npages) { - nfs_direct_release_pages(data->pagevec, result); - nfs_writedata_release(data); - break; + bytes = result * PAGE_SIZE; + if (bytes <= pgbase) { + nfs_direct_release_pages(data->pagevec, result); + nfs_writedata_release(data); + break; + } + bytes -= pgbase; + data->npages = result; } get_dreq(dreq); @@ -763,10 +773,8 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, (unsigned long) count, (long long) pos); if (nr_segs != 1) - return -EINVAL; - - if (count < 0) goto out; + retval = -EFAULT; if (!access_ok(VERIFY_WRITE, buf, count)) goto out; @@ -814,7 +822,7 @@ out: ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { - ssize_t retval; + ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; /* XXX: temporary */ @@ -827,7 +835,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, (unsigned long) count, (long long) pos); if (nr_segs != 1) - return -EINVAL; + goto out; retval = generic_write_checks(file, &pos, &count, 0); if (retval) @@ -867,7 +875,7 @@ int __init nfs_init_directcache(void) sizeof(struct nfs_direct_req), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - NULL, NULL); + NULL); if (nfs_direct_cachep == NULL) return -ENOMEM; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8689b73..c87dc71 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -53,6 +53,7 @@ static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); static int nfs_check_flags(int flags); static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); +static int nfs_setlease(struct file *file, long arg, struct file_lock **fl); const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, @@ -69,6 +70,7 @@ const struct file_operations nfs_file_operations = { .flock = nfs_flock, .splice_read = nfs_file_splice_read, .check_flags = nfs_check_flags, + .setlease = nfs_setlease, }; const struct inode_operations nfs_file_inode_operations = { @@ -400,7 +402,9 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) lock_kernel(); /* Try local locking first */ - if (posix_test_lock(filp, fl)) { + posix_test_lock(filp, fl); + if (fl->fl_type != F_UNLCK) { + /* found a conflict */ goto out; } @@ -558,3 +562,13 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) return do_unlk(filp, cmd, fl); return do_setlk(filp, cmd, fl); } + +static int nfs_setlease(struct file *file, long arg, struct file_lock **fl) +{ + /* + * There is no protocol support for leases, so we have no way + * to implement them correctly in the face of opens by other + * clients. + */ + return -EINVAL; +} diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index bd9f5a8..bca6cdc 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -461,14 +461,14 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (ctx != NULL) { - atomic_set(&ctx->count, 1); - ctx->dentry = dget(dentry); - ctx->vfsmnt = mntget(mnt); + ctx->path.dentry = dget(dentry); + ctx->path.mnt = mntget(mnt); ctx->cred = get_rpccred(cred); ctx->state = NULL; ctx->lockowner = current->files; ctx->error = 0; ctx->dir_cookie = 0; + kref_init(&ctx->kref); } return ctx; } @@ -476,27 +476,33 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) { if (ctx != NULL) - atomic_inc(&ctx->count); + kref_get(&ctx->kref); return ctx; } -void put_nfs_open_context(struct nfs_open_context *ctx) +static void nfs_free_open_context(struct kref *kref) { - if (atomic_dec_and_test(&ctx->count)) { - if (!list_empty(&ctx->list)) { - struct inode *inode = ctx->dentry->d_inode; - spin_lock(&inode->i_lock); - list_del(&ctx->list); - spin_unlock(&inode->i_lock); - } - if (ctx->state != NULL) - nfs4_close_state(ctx->state, ctx->mode); - if (ctx->cred != NULL) - put_rpccred(ctx->cred); - dput(ctx->dentry); - mntput(ctx->vfsmnt); - kfree(ctx); + struct nfs_open_context *ctx = container_of(kref, + struct nfs_open_context, kref); + + if (!list_empty(&ctx->list)) { + struct inode *inode = ctx->path.dentry->d_inode; + spin_lock(&inode->i_lock); + list_del(&ctx->list); + spin_unlock(&inode->i_lock); } + if (ctx->state != NULL) + nfs4_close_state(&ctx->path, ctx->state, ctx->mode); + if (ctx->cred != NULL) + put_rpccred(ctx->cred); + dput(ctx->path.dentry); + mntput(ctx->path.mnt); + kfree(ctx); +} + +void put_nfs_open_context(struct nfs_open_context *ctx) +{ + kref_put(&ctx->kref, nfs_free_open_context); } /* @@ -961,8 +967,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) goto out_changed; server = NFS_SERVER(inode); - /* Update the fsid if and only if this is the root directory */ - if (inode == inode->i_sb->s_root->d_inode + /* Update the fsid? */ + if (S_ISDIR(inode->i_mode) && !nfs_fsid_equal(&server->fsid, &fattr->fsid)) server->fsid = fattr->fsid; @@ -1066,8 +1072,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) invalid &= ~NFS_INO_INVALID_DATA; if (data_stable) invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); - if (!nfs_have_delegation(inode, FMODE_READ)) + if (!nfs_have_delegation(inode, FMODE_READ) || + (nfsi->cache_validity & NFS_INO_REVAL_FORCED)) nfsi->cache_validity |= invalid; + nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED; return 0; out_changed: @@ -1103,27 +1111,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) */ void nfs4_clear_inode(struct inode *inode) { - struct nfs_inode *nfsi = NFS_I(inode); - /* If we are holding a delegation, return it! */ nfs_inode_return_delegation(inode); /* First call standard NFS clear_inode() code */ nfs_clear_inode(inode); - /* Now clear out any remaining state */ - while (!list_empty(&nfsi->open_states)) { - struct nfs4_state *state; - - state = list_entry(nfsi->open_states.next, - struct nfs4_state, - inode_states); - dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n", - __FUNCTION__, - inode->i_sb->s_id, - (long long)NFS_FILEID(inode), - state); - BUG_ON(atomic_read(&state->count) != 1); - nfs4_close_state(state, state->state); - } } #endif @@ -1165,27 +1156,23 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag struct nfs_inode *nfsi = (struct nfs_inode *) foo; inode_init_once(&nfsi->vfs_inode); - spin_lock_init(&nfsi->req_lock); - INIT_LIST_HEAD(&nfsi->dirty); - INIT_LIST_HEAD(&nfsi->commit); INIT_LIST_HEAD(&nfsi->open_files); INIT_LIST_HEAD(&nfsi->access_cache_entry_lru); INIT_LIST_HEAD(&nfsi->access_cache_inode_lru); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); atomic_set(&nfsi->data_updates, 0); - nfsi->ndirty = 0; nfsi->ncommit = 0; nfsi->npages = 0; nfs4_init_once(nfsi); } - + static int __init nfs_init_inodecache(void) { nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", sizeof(struct nfs_inode), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (nfs_inode_cachep == NULL) return -ENOMEM; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index ad2b40d..76cf55d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -183,9 +183,9 @@ unsigned long nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp) /* * Calculate the number of 512byte blocks used. */ -static inline unsigned long nfs_calc_block_size(u64 tsize) +static inline blkcnt_t nfs_calc_block_size(u64 tsize) { - loff_t used = (tsize + 511) >> 9; + blkcnt_t used = (tsize + 511) >> 9; return (used > ULONG_MAX) ? ULONG_MAX : used; } diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index ca5a266..8afd9f7 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -1,7 +1,5 @@ /* - * linux/fs/nfs/mount_clnt.c - * - * MOUNT client to support NFSroot. + * In-kernel MOUNT protocol client * * Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de> */ @@ -18,33 +16,31 @@ #include <linux/nfs_fs.h> #ifdef RPC_DEBUG -# define NFSDBG_FACILITY NFSDBG_ROOT +# define NFSDBG_FACILITY NFSDBG_MOUNT #endif -/* -#define MOUNT_PROGRAM 100005 -#define MOUNT_VERSION 1 -#define MOUNT_MNT 1 -#define MOUNT_UMNT 3 - */ - -static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *, - int, int); static struct rpc_program mnt_program; struct mnt_fhstatus { - unsigned int status; - struct nfs_fh * fh; + u32 status; + struct nfs_fh *fh; }; -/* - * Obtain an NFS file handle for the given host and path +/** + * nfs_mount - Obtain an NFS file handle for the given host and path + * @addr: pointer to server's address + * @len: size of server's address + * @hostname: name of server host, or NULL + * @path: pointer to string containing export path to mount + * @version: mount version to use for this request + * @protocol: transport protocol to use for thie request + * @fh: pointer to location to place returned file handle + * + * Uses default timeout parameters specified by underlying transport. */ -int -nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, - int version, int protocol) +int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path, + int version, int protocol, struct nfs_fh *fh) { - struct rpc_clnt *mnt_clnt; struct mnt_fhstatus result = { .fh = fh }; @@ -52,16 +48,25 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, .rpc_argp = path, .rpc_resp = &result, }; - char hostname[32]; + struct rpc_create_args args = { + .protocol = protocol, + .address = addr, + .addrsize = len, + .servername = hostname, + .program = &mnt_program, + .version = version, + .authflavor = RPC_AUTH_UNIX, + .flags = RPC_CLNT_CREATE_INTR, + }; + struct rpc_clnt *mnt_clnt; int status; - dprintk("NFS: nfs_mount(%08x:%s)\n", - (unsigned)ntohl(addr->sin_addr.s_addr), path); + dprintk("NFS: sending MNT request for %s:%s\n", + (hostname ? hostname : "server"), path); - sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr)); - mnt_clnt = mnt_create(hostname, addr, version, protocol); + mnt_clnt = rpc_create(&args); if (IS_ERR(mnt_clnt)) - return PTR_ERR(mnt_clnt); + goto out_clnt_err; if (version == NFS_MNT3_VERSION) msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT]; @@ -69,33 +74,39 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT]; status = rpc_call_sync(mnt_clnt, &msg, 0); - return status < 0? status : (result.status? -EACCES : 0); -} + rpc_shutdown_client(mnt_clnt); -static struct rpc_clnt * -mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version, - int protocol) -{ - struct rpc_create_args args = { - .protocol = protocol, - .address = (struct sockaddr *)srvaddr, - .addrsize = sizeof(*srvaddr), - .servername = hostname, - .program = &mnt_program, - .version = version, - .authflavor = RPC_AUTH_UNIX, - .flags = (RPC_CLNT_CREATE_ONESHOT | - RPC_CLNT_CREATE_INTR), - }; + if (status < 0) + goto out_call_err; + if (result.status != 0) + goto out_mnt_err; + + dprintk("NFS: MNT request succeeded\n"); + status = 0; + +out: + return status; + +out_clnt_err: + status = PTR_ERR(mnt_clnt); + dprintk("NFS: failed to create RPC client, status=%d\n", status); + goto out; + +out_call_err: + dprintk("NFS: failed to start MNT request, status=%d\n", status); + goto out; - return rpc_create(&args); +out_mnt_err: + dprintk("NFS: MNT server returned result %d\n", result.status); + status = -EACCES; + goto out; } /* * XDR encode/decode functions for MOUNT */ -static int -xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, const char *path) +static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, + const char *path) { p = xdr_encode_string(p, path); @@ -103,8 +114,8 @@ xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p, const char *path) return 0; } -static int -xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) +static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, + struct mnt_fhstatus *res) { struct nfs_fh *fh = res->fh; @@ -115,8 +126,8 @@ xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) return 0; } -static int -xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) +static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, + struct mnt_fhstatus *res) { struct nfs_fh *fh = res->fh; @@ -135,53 +146,53 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) #define MNT_fhstatus_sz (1 + 8) #define MNT_fhstatus3_sz (1 + 16) -static struct rpc_procinfo mnt_procedures[] = { -[MNTPROC_MNT] = { - .p_proc = MNTPROC_MNT, - .p_encode = (kxdrproc_t) xdr_encode_dirpath, - .p_decode = (kxdrproc_t) xdr_decode_fhstatus, - .p_arglen = MNT_dirpath_sz, - .p_replen = MNT_fhstatus_sz, - .p_statidx = MNTPROC_MNT, - .p_name = "MOUNT", +static struct rpc_procinfo mnt_procedures[] = { + [MNTPROC_MNT] = { + .p_proc = MNTPROC_MNT, + .p_encode = (kxdrproc_t) xdr_encode_dirpath, + .p_decode = (kxdrproc_t) xdr_decode_fhstatus, + .p_arglen = MNT_dirpath_sz, + .p_replen = MNT_fhstatus_sz, + .p_statidx = MNTPROC_MNT, + .p_name = "MOUNT", }, }; static struct rpc_procinfo mnt3_procedures[] = { -[MOUNTPROC3_MNT] = { - .p_proc = MOUNTPROC3_MNT, - .p_encode = (kxdrproc_t) xdr_encode_dirpath, - .p_decode = (kxdrproc_t) xdr_decode_fhstatus3, - .p_arglen = MNT_dirpath_sz, - .p_replen = MNT_fhstatus3_sz, - .p_statidx = MOUNTPROC3_MNT, - .p_name = "MOUNT", + [MOUNTPROC3_MNT] = { + .p_proc = MOUNTPROC3_MNT, + .p_encode = (kxdrproc_t) xdr_encode_dirpath, + .p_decode = (kxdrproc_t) xdr_decode_fhstatus3, + .p_arglen = MNT_dirpath_sz, + .p_replen = MNT_fhstatus3_sz, + .p_statidx = MOUNTPROC3_MNT, + .p_name = "MOUNT", }, }; -static struct rpc_version mnt_version1 = { - .number = 1, - .nrprocs = 2, - .procs = mnt_procedures +static struct rpc_version mnt_version1 = { + .number = 1, + .nrprocs = 2, + .procs = mnt_procedures, }; -static struct rpc_version mnt_version3 = { - .number = 3, - .nrprocs = 2, - .procs = mnt3_procedures +static struct rpc_version mnt_version3 = { + .number = 3, + .nrprocs = 2, + .procs = mnt3_procedures, }; -static struct rpc_version * mnt_version[] = { +static struct rpc_version *mnt_version[] = { NULL, &mnt_version1, NULL, &mnt_version3, }; -static struct rpc_stat mnt_stats; +static struct rpc_stat mnt_stats; -static struct rpc_program mnt_program = { +static struct rpc_program mnt_program = { .name = "mount", .number = NFS_MNT_PROGRAM, .nrvers = ARRAY_SIZE(mnt_version), diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index cd3ca7b..c5fce75 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -43,6 +43,7 @@ #define NFS_entry_sz (NFS_filename_sz+3) #define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz) +#define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz) #define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz) #define NFS_readlinkargs_sz (NFS_fhandle_sz) #define NFS_readargs_sz (NFS_fhandle_sz+3) @@ -66,7 +67,7 @@ * Common NFS XDR functions as inlines */ static inline __be32 * -xdr_encode_fhandle(__be32 *p, struct nfs_fh *fhandle) +xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle) { memcpy(p, fhandle->data, NFS2_FHSIZE); return p + XDR_QUADLEN(NFS2_FHSIZE); @@ -204,7 +205,7 @@ nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args) /* * Encode directory ops argument - * LOOKUP, REMOVE, RMDIR + * LOOKUP, RMDIR */ static int nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args) @@ -216,6 +217,18 @@ nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args) } /* + * Encode REMOVE argument + */ +static int +nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_array(p, args->name.name, args->name.len); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* * Arguments to a READ call. Since we read data directly into the page * cache, we also set up the reply iovec here so that iov[1] points * exactly to the page we want to fetch. @@ -223,7 +236,7 @@ nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args) static int nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; u32 offset = (u32)args->offset; u32 count = args->count; @@ -380,7 +393,7 @@ static int nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) { struct rpc_task *task = req->rq_task; - struct rpc_auth *auth = task->tk_auth; + struct rpc_auth *auth = task->tk_msg.rpc_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -541,7 +554,7 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res) static int nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -705,7 +718,7 @@ struct rpc_procinfo nfs_procedures[] = { PROC(READ, readargs, readres, 3), PROC(WRITE, writeargs, writeres, 4), PROC(CREATE, createargs, diropres, 0), - PROC(REMOVE, diropargs, stat, 0), + PROC(REMOVE, removeargs, stat, 0), PROC(RENAME, renameargs, stat, 0), PROC(LINK, linkargs, stat, 0), PROC(SYMLINK, symlinkargs, stat, 0), diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 45268d6..c7ca5d7 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -335,9 +335,7 @@ again: * not sure this buys us anything (and I'd have * to revamp the NFSv3 XDR code) */ status = nfs3_proc_setattr(dentry, &fattr, sattr); - if (status == 0) - nfs_setattr_update_inode(dentry->d_inode, sattr); - nfs_refresh_inode(dentry->d_inode, &fattr); + nfs_post_op_update_inode(dentry->d_inode, &fattr); dprintk("NFS reply setattr (post-create): %d\n", status); } if (status != 0) @@ -351,62 +349,42 @@ out: static int nfs3_proc_remove(struct inode *dir, struct qstr *name) { - struct nfs_fattr dir_attr; - struct nfs3_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len + struct nfs_removeargs arg = { + .fh = NFS_FH(dir), + .name.len = name->len, + .name.name = name->name, }; - struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE], - .rpc_argp = &arg, - .rpc_resp = &dir_attr, + struct nfs_removeres res; + struct rpc_message msg = { + .rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE], + .rpc_argp = &arg, + .rpc_resp = &res, }; int status; dprintk("NFS call remove %s\n", name->name); - nfs_fattr_init(&dir_attr); + nfs_fattr_init(&res.dir_attr); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); - nfs_post_op_update_inode(dir, &dir_attr); + nfs_post_op_update_inode(dir, &res.dir_attr); dprintk("NFS reply remove: %d\n", status); return status; } -static int -nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +static void +nfs3_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) { - struct unlinkxdr { - struct nfs3_diropargs arg; - struct nfs_fattr res; - } *ptr; - - ptr = kmalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - ptr->arg.fh = NFS_FH(dir->d_inode); - ptr->arg.name = name->name; - ptr->arg.len = name->len; - nfs_fattr_init(&ptr->res); msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; - msg->rpc_argp = &ptr->arg; - msg->rpc_resp = &ptr->res; - return 0; } static int -nfs3_proc_unlink_done(struct dentry *dir, struct rpc_task *task) +nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir) { - struct rpc_message *msg = &task->tk_msg; - struct nfs_fattr *dir_attr; - - if (nfs3_async_handle_jukebox(task, dir->d_inode)) - return 1; - if (msg->rpc_argp) { - dir_attr = (struct nfs_fattr*)msg->rpc_resp; - nfs_post_op_update_inode(dir->d_inode, dir_attr); - kfree(msg->rpc_argp); - } - return 0; + struct nfs_removeres *res; + if (nfs3_async_handle_jukebox(task, dir)) + return 0; + res = task->tk_msg.rpc_resp; + nfs_post_op_update_inode(dir, &res->dir_attr); + return 1; } static int diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index b51df8e..d9e08f0 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -50,6 +50,7 @@ #define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3) #define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz) +#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz) #define NFS3_accessargs_sz (NFS3_fh_sz+1) #define NFS3_readlinkargs_sz (NFS3_fh_sz) #define NFS3_readargs_sz (NFS3_fh_sz+3) @@ -65,6 +66,7 @@ #define NFS3_attrstat_sz (1+NFS3_fattr_sz) #define NFS3_wccstat_sz (1+NFS3_wcc_data_sz) +#define NFS3_removeres_sz (NFS3_wccstat_sz) #define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)) #define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1) #define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1) @@ -106,7 +108,7 @@ static struct { * Common NFS XDR functions as inlines */ static inline __be32 * -xdr_encode_fhandle(__be32 *p, struct nfs_fh *fh) +xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh) { return xdr_encode_array(p, fh->data, fh->size); } @@ -300,6 +302,18 @@ nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args) } /* + * Encode REMOVE argument + */ +static int +nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args) +{ + p = xdr_encode_fhandle(p, args->fh); + p = xdr_encode_array(p, args->name.name, args->name.len); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* * Encode access() argument */ static int @@ -319,7 +333,7 @@ nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *arg static int nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -458,7 +472,7 @@ nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args) static int nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -643,7 +657,7 @@ static int nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p, struct nfs3_getaclargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -736,6 +750,12 @@ nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) return status; } +static int +nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res) +{ + return nfs3_xdr_wccstat(req, p, &res->dir_attr); +} + /* * Decode LOOKUP reply */ @@ -773,7 +793,7 @@ nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res) static int nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -1126,7 +1146,7 @@ struct rpc_procinfo nfs3_procedures[] = { PROC(MKDIR, mkdirargs, createres, 0), PROC(SYMLINK, symlinkargs, createres, 0), PROC(MKNOD, mknodargs, createres, 0), - PROC(REMOVE, diropargs, wccstat, 0), + PROC(REMOVE, removeargs, removeres, 0), PROC(RMDIR, diropargs, wccstat, 0), PROC(RENAME, renameargs, renameres, 0), PROC(LINK, linkargs, linkres, 0), diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index cf3a17e..d2802b1 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -70,19 +70,26 @@ static inline void nfs_confirm_seqid(struct nfs_seqid_counter *seqid, int status seqid->flags |= NFS_SEQID_CONFIRMED; } +struct nfs_unique_id { + struct rb_node rb_node; + __u64 id; +}; + /* * NFS4 state_owners and lock_owners are simply labels for ordered * sequences of RPC calls. Their sole purpose is to provide once-only * semantics by allowing the server to identify replayed requests. */ struct nfs4_state_owner { - spinlock_t so_lock; - struct list_head so_list; /* per-clientid list of state_owners */ + struct nfs_unique_id so_owner_id; struct nfs_client *so_client; - u32 so_id; /* 32-bit identifier, unique */ - atomic_t so_count; + struct nfs_server *so_server; + struct rb_node so_client_node; struct rpc_cred *so_cred; /* Associated cred */ + + spinlock_t so_lock; + atomic_t so_count; struct list_head so_states; struct list_head so_delegations; struct nfs_seqid_counter so_seqid; @@ -108,7 +115,7 @@ struct nfs4_lock_state { #define NFS_LOCK_INITIALIZED 1 int ls_flags; struct nfs_seqid_counter ls_seqid; - u32 ls_id; + struct nfs_unique_id ls_id; nfs4_stateid ls_stateid; atomic_t ls_count; }; @@ -116,7 +123,10 @@ struct nfs4_lock_state { /* bits for nfs4_state->flags */ enum { LK_STATE_IN_USE, - NFS_DELEGATED_STATE, + NFS_DELEGATED_STATE, /* Current stateid is delegation */ + NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */ + NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */ + NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */ }; struct nfs4_state { @@ -130,11 +140,14 @@ struct nfs4_state { unsigned long flags; /* Do we hold any locks? */ spinlock_t state_lock; /* Protects the lock_states list */ - nfs4_stateid stateid; + seqlock_t seqlock; /* Protects the stateid/open_stateid */ + nfs4_stateid stateid; /* Current stateid: may be delegation */ + nfs4_stateid open_stateid; /* OPEN stateid */ - unsigned int n_rdonly; - unsigned int n_wronly; - unsigned int n_rdwr; + /* The following 3 fields are protected by owner->so_lock */ + unsigned int n_rdonly; /* Number of read-only references */ + unsigned int n_wronly; /* Number of write-only references */ + unsigned int n_rdwr; /* Number of read/write references */ int state; /* State on the server (R,W, or RW) */ atomic_t count; }; @@ -165,11 +178,11 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); -extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); +extern int nfs4_do_close(struct path *path, struct nfs4_state *state); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); -extern int nfs4_proc_fs_locations(struct inode *dir, struct qstr *name, +extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; @@ -189,14 +202,13 @@ extern void nfs4_renew_state(struct work_struct *); /* nfs4state.c */ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp); -extern u32 nfs4_alloc_lockowner_id(struct nfs_client *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern void nfs4_drop_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); -extern void nfs4_close_state(struct nfs4_state *, mode_t); +extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t); extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); extern void nfs4_schedule_state_recovery(struct nfs_client *); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); @@ -222,7 +234,7 @@ extern struct svc_version nfs4_callback_version1; #else -#define nfs4_close_state(a, b) do { } while (0) +#define nfs4_close_state(a, b, c) do { } while (0) #endif /* CONFIG_NFS_V4 */ #endif /* __LINUX_FS_NFS_NFS4_FS.H */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 648e0ac..6ca2795 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -65,6 +65,9 @@ static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *) static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp); +static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags); +static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr); +static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); /* Prevent leaks of NFSv4 errors into userland */ int nfs4_map_errors(int err) @@ -214,27 +217,39 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) } struct nfs4_opendata { - atomic_t count; + struct kref kref; struct nfs_openargs o_arg; struct nfs_openres o_res; struct nfs_open_confirmargs c_arg; struct nfs_open_confirmres c_res; struct nfs_fattr f_attr; struct nfs_fattr dir_attr; - struct dentry *dentry; + struct path path; struct dentry *dir; struct nfs4_state_owner *owner; + struct nfs4_state *state; struct iattr attrs; unsigned long timestamp; + unsigned int rpc_done : 1; int rpc_status; int cancelled; }; -static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, + +static void nfs4_init_opendata_res(struct nfs4_opendata *p) +{ + p->o_res.f_attr = &p->f_attr; + p->o_res.dir_attr = &p->dir_attr; + p->o_res.server = p->o_arg.server; + nfs_fattr_init(&p->f_attr); + nfs_fattr_init(&p->dir_attr); +} + +static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, struct nfs4_state_owner *sp, int flags, const struct iattr *attrs) { - struct dentry *parent = dget_parent(dentry); + struct dentry *parent = dget_parent(path->dentry); struct inode *dir = parent->d_inode; struct nfs_server *server = NFS_SERVER(dir); struct nfs4_opendata *p; @@ -245,24 +260,19 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); if (p->o_arg.seqid == NULL) goto err_free; - atomic_set(&p->count, 1); - p->dentry = dget(dentry); + p->path.mnt = mntget(path->mnt); + p->path.dentry = dget(path->dentry); p->dir = parent; p->owner = sp; atomic_inc(&sp->so_count); p->o_arg.fh = NFS_FH(dir); p->o_arg.open_flags = flags, p->o_arg.clientid = server->nfs_client->cl_clientid; - p->o_arg.id = sp->so_id; - p->o_arg.name = &dentry->d_name; + p->o_arg.id = sp->so_owner_id.id; + p->o_arg.name = &p->path.dentry->d_name; p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; - p->o_res.f_attr = &p->f_attr; - p->o_res.dir_attr = &p->dir_attr; - p->o_res.server = server; - nfs_fattr_init(&p->f_attr); - nfs_fattr_init(&p->dir_attr); if (flags & O_EXCL) { u32 *s = (u32 *) p->o_arg.u.verifier.data; s[0] = jiffies; @@ -274,6 +284,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->c_arg.fh = &p->o_res.fh; p->c_arg.stateid = &p->o_res.stateid; p->c_arg.seqid = p->o_arg.seqid; + nfs4_init_opendata_res(p); + kref_init(&p->kref); return p; err_free: kfree(p); @@ -282,27 +294,25 @@ err: return NULL; } -static void nfs4_opendata_free(struct nfs4_opendata *p) +static void nfs4_opendata_free(struct kref *kref) { - if (p != NULL && atomic_dec_and_test(&p->count)) { - nfs_free_seqid(p->o_arg.seqid); - nfs4_put_state_owner(p->owner); - dput(p->dir); - dput(p->dentry); - kfree(p); - } + struct nfs4_opendata *p = container_of(kref, + struct nfs4_opendata, kref); + + nfs_free_seqid(p->o_arg.seqid); + if (p->state != NULL) + nfs4_put_open_state(p->state); + nfs4_put_state_owner(p->owner); + dput(p->dir); + dput(p->path.dentry); + mntput(p->path.mnt); + kfree(p); } -/* Helper for asynchronous RPC calls */ -static int nfs4_call_async(struct rpc_clnt *clnt, - const struct rpc_call_ops *tk_ops, void *calldata) +static void nfs4_opendata_put(struct nfs4_opendata *p) { - struct rpc_task *task; - - if (!(task = rpc_new_task(clnt, RPC_TASK_ASYNC, tk_ops, calldata))) - return -ENOMEM; - rpc_execute(task); - return 0; + if (p != NULL) + kref_put(&p->kref, nfs4_opendata_free); } static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) @@ -316,7 +326,34 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) return ret; } -static inline void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) +static int can_open_cached(struct nfs4_state *state, int mode) +{ + int ret = 0; + switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) { + case FMODE_READ: + ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0; + ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0; + break; + case FMODE_WRITE: + ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0; + ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0; + break; + case FMODE_READ|FMODE_WRITE: + ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0; + } + return ret; +} + +static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags) +{ + if ((delegation->type & open_flags) != open_flags) + return 0; + if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) + return 0; + return 1; +} + +static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) { switch (open_flags) { case FMODE_WRITE: @@ -328,41 +365,176 @@ static inline void update_open_stateflags(struct nfs4_state *state, mode_t open_ case FMODE_READ|FMODE_WRITE: state->n_rdwr++; } + nfs4_state_set_mode_locked(state, state->state | open_flags); } -static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) +static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) { - struct inode *inode = state->inode; + if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) + memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data)); + memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data)); + switch (open_flags) { + case FMODE_READ: + set_bit(NFS_O_RDONLY_STATE, &state->flags); + break; + case FMODE_WRITE: + set_bit(NFS_O_WRONLY_STATE, &state->flags); + break; + case FMODE_READ|FMODE_WRITE: + set_bit(NFS_O_RDWR_STATE, &state->flags); + } +} + +static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) +{ + write_seqlock(&state->seqlock); + nfs_set_open_stateid_locked(state, stateid, open_flags); + write_sequnlock(&state->seqlock); +} +static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *deleg_stateid, int open_flags) +{ open_flags &= (FMODE_READ|FMODE_WRITE); - /* Protect against nfs4_find_state_byowner() */ + /* + * Protect the call to nfs4_state_set_mode_locked and + * serialise the stateid update + */ + write_seqlock(&state->seqlock); + if (deleg_stateid != NULL) { + memcpy(state->stateid.data, deleg_stateid->data, sizeof(state->stateid.data)); + set_bit(NFS_DELEGATED_STATE, &state->flags); + } + if (open_stateid != NULL) + nfs_set_open_stateid_locked(state, open_stateid, open_flags); + write_sequnlock(&state->seqlock); spin_lock(&state->owner->so_lock); - spin_lock(&inode->i_lock); - memcpy(&state->stateid, stateid, sizeof(state->stateid)); update_open_stateflags(state, open_flags); - nfs4_state_set_mode_locked(state, state->state | open_flags); - spin_unlock(&inode->i_lock); spin_unlock(&state->owner->so_lock); } +static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags) +{ + struct nfs_delegation *delegation; + + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(inode)->delegation); + if (delegation == NULL || (delegation->type & open_flags) == open_flags) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + nfs_inode_return_delegation(inode); +} + +static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) +{ + struct nfs4_state *state = opendata->state; + struct nfs_inode *nfsi = NFS_I(state->inode); + struct nfs_delegation *delegation; + int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL); + nfs4_stateid stateid; + int ret = -EAGAIN; + + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); + for (;;) { + if (can_open_cached(state, open_mode)) { + spin_lock(&state->owner->so_lock); + if (can_open_cached(state, open_mode)) { + update_open_stateflags(state, open_mode); + spin_unlock(&state->owner->so_lock); + rcu_read_unlock(); + goto out_return_state; + } + spin_unlock(&state->owner->so_lock); + } + if (delegation == NULL) + break; + if (!can_open_delegated(delegation, open_mode)) + break; + /* Save the delegation */ + memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data)); + rcu_read_unlock(); + lock_kernel(); + ret = _nfs4_do_access(state->inode, state->owner->so_cred, open_mode); + unlock_kernel(); + if (ret != 0) + goto out; + ret = -EAGAIN; + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); + /* If no delegation, try a cached open */ + if (delegation == NULL) + continue; + /* Is the delegation still valid? */ + if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0) + continue; + rcu_read_unlock(); + update_open_stateid(state, NULL, &stateid, open_mode); + goto out_return_state; + } + rcu_read_unlock(); +out: + return ERR_PTR(ret); +out_return_state: + atomic_inc(&state->count); + return state; +} + static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) { struct inode *inode; struct nfs4_state *state = NULL; + struct nfs_delegation *delegation; + nfs4_stateid *deleg_stateid = NULL; + int ret; - if (!(data->f_attr.valid & NFS_ATTR_FATTR)) + if (!data->rpc_done) { + state = nfs4_try_open_cached(data); goto out; + } + + ret = -EAGAIN; + if (!(data->f_attr.valid & NFS_ATTR_FATTR)) + goto err; inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr); + ret = PTR_ERR(inode); if (IS_ERR(inode)) - goto out; + goto err; + ret = -ENOMEM; state = nfs4_get_open_state(inode, data->owner); if (state == NULL) - goto put_inode; - update_open_stateid(state, &data->o_res.stateid, data->o_arg.open_flags); -put_inode: + goto err_put_inode; + if (data->o_res.delegation_type != 0) { + int delegation_flags = 0; + + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(inode)->delegation); + if (delegation) + delegation_flags = delegation->flags; + rcu_read_unlock(); + if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM)) + nfs_inode_set_delegation(state->inode, + data->owner->so_cred, + &data->o_res); + else + nfs_inode_reclaim_delegation(state->inode, + data->owner->so_cred, + &data->o_res); + } + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(inode)->delegation); + if (delegation != NULL) + deleg_stateid = &delegation->stateid; + update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags); + rcu_read_unlock(); iput(inode); out: return state; +err_put_inode: + iput(inode); +err: + return ERR_PTR(ret); } static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state) @@ -382,79 +554,78 @@ static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state * return ERR_PTR(-ENOENT); } -static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, nfs4_stateid *stateid) +static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state) { + struct nfs4_opendata *opendata; + + opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL); + if (opendata == NULL) + return ERR_PTR(-ENOMEM); + opendata->state = state; + atomic_inc(&state->count); + return opendata; +} + +static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res) +{ + struct nfs4_state *newstate; int ret; opendata->o_arg.open_flags = openflags; + memset(&opendata->o_res, 0, sizeof(opendata->o_res)); + memset(&opendata->c_res, 0, sizeof(opendata->c_res)); + nfs4_init_opendata_res(opendata); ret = _nfs4_proc_open(opendata); if (ret != 0) return ret; - memcpy(stateid->data, opendata->o_res.stateid.data, - sizeof(stateid->data)); + newstate = nfs4_opendata_to_nfs4_state(opendata); + if (IS_ERR(newstate)) + return PTR_ERR(newstate); + nfs4_close_state(&opendata->path, newstate, openflags); + *res = newstate; return 0; } static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state) { - nfs4_stateid stateid; struct nfs4_state *newstate; - int mode = 0; - int delegation = 0; int ret; /* memory barrier prior to reading state->n_* */ + clear_bit(NFS_DELEGATED_STATE, &state->flags); smp_rmb(); if (state->n_rdwr != 0) { - ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &stateid); + ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); if (ret != 0) return ret; - mode |= FMODE_READ|FMODE_WRITE; - if (opendata->o_res.delegation_type != 0) - delegation = opendata->o_res.delegation_type; - smp_rmb(); + if (newstate != state) + return -ESTALE; } if (state->n_wronly != 0) { - ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &stateid); + ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); if (ret != 0) return ret; - mode |= FMODE_WRITE; - if (opendata->o_res.delegation_type != 0) - delegation = opendata->o_res.delegation_type; - smp_rmb(); + if (newstate != state) + return -ESTALE; } if (state->n_rdonly != 0) { - ret = nfs4_open_recover_helper(opendata, FMODE_READ, &stateid); + ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); if (ret != 0) return ret; - mode |= FMODE_READ; + if (newstate != state) + return -ESTALE; } - clear_bit(NFS_DELEGATED_STATE, &state->flags); - if (mode == 0) - return 0; - if (opendata->o_res.delegation_type == 0) - opendata->o_res.delegation_type = delegation; - opendata->o_arg.open_flags |= mode; - newstate = nfs4_opendata_to_nfs4_state(opendata); - if (newstate != NULL) { - if (opendata->o_res.delegation_type != 0) { - struct nfs_inode *nfsi = NFS_I(newstate->inode); - int delegation_flags = 0; - if (nfsi->delegation) - delegation_flags = nfsi->delegation->flags; - if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM)) - nfs_inode_set_delegation(newstate->inode, - opendata->owner->so_cred, - &opendata->o_res); - else - nfs_inode_reclaim_delegation(newstate->inode, - opendata->owner->so_cred, - &opendata->o_res); - } - nfs4_close_state(newstate, opendata->o_arg.open_flags); + /* + * We may have performed cached opens for all three recoveries. + * Check if we need to update the current stateid. + */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0 && + memcmp(state->stateid.data, state->open_stateid.data, sizeof(state->stateid.data)) != 0) { + write_seqlock(&state->seqlock); + if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) + memcpy(state->stateid.data, state->open_stateid.data, sizeof(state->stateid.data)); + write_sequnlock(&state->seqlock); } - if (newstate != state) - return -ESTALE; return 0; } @@ -462,41 +633,36 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state * * OPEN_RECLAIM: * reclaim state on the server after a reboot. */ -static int _nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) +static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state) { - struct nfs_delegation *delegation = NFS_I(state->inode)->delegation; + struct nfs_delegation *delegation; struct nfs4_opendata *opendata; int delegation_type = 0; int status; - if (delegation != NULL) { - if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { - memcpy(&state->stateid, &delegation->stateid, - sizeof(state->stateid)); - set_bit(NFS_DELEGATED_STATE, &state->flags); - return 0; - } - delegation_type = delegation->type; - } - opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); - if (opendata == NULL) - return -ENOMEM; + opendata = nfs4_open_recoverdata_alloc(ctx, state); + if (IS_ERR(opendata)) + return PTR_ERR(opendata); opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS; opendata->o_arg.fh = NFS_FH(state->inode); - nfs_copy_fh(&opendata->o_res.fh, opendata->o_arg.fh); + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(state->inode)->delegation); + if (delegation != NULL && (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) != 0) + delegation_type = delegation->flags; + rcu_read_unlock(); opendata->o_arg.u.delegation_type = delegation_type; status = nfs4_open_recover(opendata, state); - nfs4_opendata_free(opendata); + nfs4_opendata_put(opendata); return status; } -static int nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) +static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { }; int err; do { - err = _nfs4_do_open_reclaim(sp, state, dentry); + err = _nfs4_do_open_reclaim(ctx, state); if (err != -NFS4ERR_DELAY) break; nfs4_handle_exception(server, err, &exception); @@ -512,37 +678,34 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta ctx = nfs4_state_find_open_context(state); if (IS_ERR(ctx)) return PTR_ERR(ctx); - ret = nfs4_do_open_reclaim(sp, state, ctx->dentry); + ret = nfs4_do_open_reclaim(ctx, state); put_nfs_open_context(ctx); return ret; } -static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) +static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) { - struct nfs4_state_owner *sp = state->owner; struct nfs4_opendata *opendata; int ret; - if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) - return 0; - opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); - if (opendata == NULL) - return -ENOMEM; + opendata = nfs4_open_recoverdata_alloc(ctx, state); + if (IS_ERR(opendata)) + return PTR_ERR(opendata); opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR; - memcpy(opendata->o_arg.u.delegation.data, state->stateid.data, + memcpy(opendata->o_arg.u.delegation.data, stateid->data, sizeof(opendata->o_arg.u.delegation.data)); ret = nfs4_open_recover(opendata, state); - nfs4_opendata_free(opendata); + nfs4_opendata_put(opendata); return ret; } -int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) +int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) { struct nfs4_exception exception = { }; - struct nfs_server *server = NFS_SERVER(dentry->d_inode); + struct nfs_server *server = NFS_SERVER(state->inode); int err; do { - err = _nfs4_open_delegation_recall(dentry, state); + err = _nfs4_open_delegation_recall(ctx, state, stateid); switch (err) { case 0: return err; @@ -582,9 +745,10 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata) memcpy(data->o_res.stateid.data, data->c_res.stateid.data, sizeof(data->o_res.stateid.data)); renew_lease(data->o_res.server, data->timestamp); + data->rpc_done = 1; } - nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid); nfs_confirm_seqid(&data->owner->so_seqid, data->rpc_status); + nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid); } static void nfs4_open_confirm_release(void *calldata) @@ -596,14 +760,14 @@ static void nfs4_open_confirm_release(void *calldata) if (data->cancelled == 0) goto out_free; /* In case of error, no cleanup! */ - if (data->rpc_status != 0) + if (!data->rpc_done) goto out_free; nfs_confirm_seqid(&data->owner->so_seqid, 0); state = nfs4_opendata_to_nfs4_state(data); - if (state != NULL) - nfs4_close_state(state, data->o_arg.open_flags); + if (!IS_ERR(state)) + nfs4_close_state(&data->path, state, data->o_arg.open_flags); out_free: - nfs4_opendata_free(data); + nfs4_opendata_put(data); } static const struct rpc_call_ops nfs4_open_confirm_ops = { @@ -621,12 +785,9 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data) struct rpc_task *task; int status; - atomic_inc(&data->count); - /* - * If rpc_run_task() ends up calling ->rpc_release(), we - * want to ensure that it takes the 'error' code path. - */ - data->rpc_status = -ENOMEM; + kref_get(&data->kref); + data->rpc_done = 0; + data->rpc_status = 0; task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_confirm_ops, data); if (IS_ERR(task)) return PTR_ERR(task); @@ -653,13 +814,37 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) return; + /* + * Check if we still need to send an OPEN call, or if we can use + * a delegation instead. + */ + if (data->state != NULL) { + struct nfs_delegation *delegation; + + if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL))) + goto out_no_action; + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(data->state->inode)->delegation); + if (delegation != NULL && + (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) { + rcu_read_unlock(); + goto out_no_action; + } + rcu_read_unlock(); + } /* Update sequence id. */ - data->o_arg.id = sp->so_id; + data->o_arg.id = sp->so_owner_id.id; data->o_arg.clientid = sp->so_client->cl_clientid; - if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) + if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; + nfs_copy_fh(&data->o_res.fh, data->o_arg.fh); + } data->timestamp = jiffies; rpc_call_setup(task, &msg, 0); + return; +out_no_action: + task->tk_action = NULL; + } static void nfs4_open_done(struct rpc_task *task, void *calldata) @@ -683,8 +868,11 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata) data->rpc_status = -ENOTDIR; } renew_lease(data->o_res.server, data->timestamp); + if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)) + nfs_confirm_seqid(&data->owner->so_seqid, 0); } nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid); + data->rpc_done = 1; } static void nfs4_open_release(void *calldata) @@ -696,17 +884,17 @@ static void nfs4_open_release(void *calldata) if (data->cancelled == 0) goto out_free; /* In case of error, no cleanup! */ - if (data->rpc_status != 0) + if (data->rpc_status != 0 || !data->rpc_done) goto out_free; /* In case we need an open_confirm, no cleanup! */ if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) goto out_free; nfs_confirm_seqid(&data->owner->so_seqid, 0); state = nfs4_opendata_to_nfs4_state(data); - if (state != NULL) - nfs4_close_state(state, data->o_arg.open_flags); + if (!IS_ERR(state)) + nfs4_close_state(&data->path, state, data->o_arg.open_flags); out_free: - nfs4_opendata_free(data); + nfs4_opendata_put(data); } static const struct rpc_call_ops nfs4_open_ops = { @@ -727,12 +915,10 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) struct rpc_task *task; int status; - atomic_inc(&data->count); - /* - * If rpc_run_task() ends up calling ->rpc_release(), we - * want to ensure that it takes the 'error' code path. - */ - data->rpc_status = -ENOMEM; + kref_get(&data->kref); + data->rpc_done = 0; + data->rpc_status = 0; + data->cancelled = 0; task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data); if (IS_ERR(task)) return PTR_ERR(task); @@ -743,9 +929,12 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) } else status = data->rpc_status; rpc_put_task(task); - if (status != 0) + if (status != 0 || !data->rpc_done) return status; + if (o_res->fh.size == 0) + _nfs4_proc_lookup(dir, o_arg->name, &o_res->fh, o_res->f_attr); + if (o_arg->open_flags & O_CREAT) { update_changeattr(dir, &o_res->cinfo); nfs_post_op_update_inode(dir, o_res->dir_attr); @@ -756,9 +945,8 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) if (status != 0) return status; } - nfs_confirm_seqid(&data->owner->so_seqid, 0); if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) - return server->nfs_client->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); + _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr); return 0; } @@ -772,6 +960,8 @@ static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openf mask |= MAY_READ; if (openflags & FMODE_WRITE) mask |= MAY_WRITE; + if (openflags & FMODE_EXEC) + mask |= MAY_EXEC; status = nfs_access_get_cached(inode, cred, &cache); if (status == 0) goto out; @@ -811,43 +1001,32 @@ static int nfs4_recover_expired_lease(struct nfs_server *server) * reclaim state on the server after a network partition. * Assumes caller holds the appropriate lock */ -static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) +static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state) { - struct inode *inode = state->inode; - struct nfs_delegation *delegation = NFS_I(inode)->delegation; struct nfs4_opendata *opendata; - int openflags = state->state & (FMODE_READ|FMODE_WRITE); int ret; - if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { - ret = _nfs4_do_access(inode, sp->so_cred, openflags); - if (ret < 0) - return ret; - memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); - set_bit(NFS_DELEGATED_STATE, &state->flags); - return 0; - } - opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL); - if (opendata == NULL) - return -ENOMEM; + opendata = nfs4_open_recoverdata_alloc(ctx, state); + if (IS_ERR(opendata)) + return PTR_ERR(opendata); ret = nfs4_open_recover(opendata, state); if (ret == -ESTALE) { /* Invalidate the state owner so we don't ever use it again */ - nfs4_drop_state_owner(sp); - d_drop(dentry); + nfs4_drop_state_owner(state->owner); + d_drop(ctx->path.dentry); } - nfs4_opendata_free(opendata); + nfs4_opendata_put(opendata); return ret; } -static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) +static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state) { - struct nfs_server *server = NFS_SERVER(dentry->d_inode); + struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { }; int err; do { - err = _nfs4_open_expired(sp, state, dentry); + err = _nfs4_open_expired(ctx, state); if (err == -NFS4ERR_DELAY) nfs4_handle_exception(server, err, &exception); } while (exception.retry); @@ -862,107 +1041,38 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta ctx = nfs4_state_find_open_context(state); if (IS_ERR(ctx)) return PTR_ERR(ctx); - ret = nfs4_do_open_expired(sp, state, ctx->dentry); + ret = nfs4_do_open_expired(ctx, state); put_nfs_open_context(ctx); return ret; } /* - * Returns a referenced nfs4_state if there is an open delegation on the file + * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-* + * fields corresponding to attributes that were used to store the verifier. + * Make sure we clobber those fields in the later setattr call */ -static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) +static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct iattr *sattr) { - struct nfs_delegation *delegation; - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_client *clp = server->nfs_client; - struct nfs_inode *nfsi = NFS_I(inode); - struct nfs4_state_owner *sp = NULL; - struct nfs4_state *state = NULL; - int open_flags = flags & (FMODE_READ|FMODE_WRITE); - int err; + if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_ACCESS) && + !(sattr->ia_valid & ATTR_ATIME_SET)) + sattr->ia_valid |= ATTR_ATIME; - err = -ENOMEM; - if (!(sp = nfs4_get_state_owner(server, cred))) { - dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__); - return err; - } - err = nfs4_recover_expired_lease(server); - if (err != 0) - goto out_put_state_owner; - /* Protect against reboot recovery - NOTE ORDER! */ - down_read(&clp->cl_sem); - /* Protect against delegation recall */ - down_read(&nfsi->rwsem); - delegation = NFS_I(inode)->delegation; - err = -ENOENT; - if (delegation == NULL || (delegation->type & open_flags) != open_flags) - goto out_err; - err = -ENOMEM; - state = nfs4_get_open_state(inode, sp); - if (state == NULL) - goto out_err; - - err = -ENOENT; - if ((state->state & open_flags) == open_flags) { - spin_lock(&inode->i_lock); - update_open_stateflags(state, open_flags); - spin_unlock(&inode->i_lock); - goto out_ok; - } else if (state->state != 0) - goto out_put_open_state; - - lock_kernel(); - err = _nfs4_do_access(inode, cred, open_flags); - unlock_kernel(); - if (err != 0) - goto out_put_open_state; - set_bit(NFS_DELEGATED_STATE, &state->flags); - update_open_stateid(state, &delegation->stateid, open_flags); -out_ok: - nfs4_put_state_owner(sp); - up_read(&nfsi->rwsem); - up_read(&clp->cl_sem); - *res = state; - return 0; -out_put_open_state: - nfs4_put_open_state(state); -out_err: - up_read(&nfsi->rwsem); - up_read(&clp->cl_sem); - if (err != -EACCES) - nfs_inode_return_delegation(inode); -out_put_state_owner: - nfs4_put_state_owner(sp); - return err; -} - -static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred) -{ - struct nfs4_exception exception = { }; - struct nfs4_state *res = ERR_PTR(-EIO); - int err; - - do { - err = _nfs4_open_delegated(inode, flags, cred, &res); - if (err == 0) - break; - res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode), - err, &exception)); - } while (exception.retry); - return res; + if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_MODIFY) && + !(sattr->ia_valid & ATTR_MTIME_SET)) + sattr->ia_valid |= ATTR_MTIME; } /* * Returns a referenced nfs4_state */ -static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) +static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) { struct nfs4_state_owner *sp; struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); struct nfs_client *clp = server->nfs_client; struct nfs4_opendata *opendata; - int status; + int status; /* Protect against reboot recovery conflicts */ status = -ENOMEM; @@ -973,29 +1083,35 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st status = nfs4_recover_expired_lease(server); if (status != 0) goto err_put_state_owner; + if (path->dentry->d_inode != NULL) + nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE)); down_read(&clp->cl_sem); status = -ENOMEM; - opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr); + opendata = nfs4_opendata_alloc(path, sp, flags, sattr); if (opendata == NULL) goto err_release_rwsem; + if (path->dentry->d_inode != NULL) + opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp); + status = _nfs4_proc_open(opendata); if (status != 0) - goto err_opendata_free; + goto err_opendata_put; + + if (opendata->o_arg.open_flags & O_EXCL) + nfs4_exclusive_attrset(opendata, sattr); - status = -ENOMEM; state = nfs4_opendata_to_nfs4_state(opendata); - if (state == NULL) - goto err_opendata_free; - if (opendata->o_res.delegation_type != 0) - nfs_inode_set_delegation(state->inode, cred, &opendata->o_res); - nfs4_opendata_free(opendata); + status = PTR_ERR(state); + if (IS_ERR(state)) + goto err_opendata_put; + nfs4_opendata_put(opendata); nfs4_put_state_owner(sp); up_read(&clp->cl_sem); *res = state; return 0; -err_opendata_free: - nfs4_opendata_free(opendata); +err_opendata_put: + nfs4_opendata_put(opendata); err_release_rwsem: up_read(&clp->cl_sem); err_put_state_owner: @@ -1006,14 +1122,14 @@ out_err: } -static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred) +static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred) { struct nfs4_exception exception = { }; struct nfs4_state *res; int status; do { - status = _nfs4_do_open(dir, dentry, flags, sattr, cred, &res); + status = _nfs4_do_open(dir, path, flags, sattr, cred, &res); if (status == 0) break; /* NOTE: BAD_SEQID means the server and client disagree about the @@ -1028,7 +1144,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, * the user though... */ if (status == -NFS4ERR_BAD_SEQID) { - printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n"); + printk(KERN_WARNING "NFS: v4 server %s " + " returned a bad sequence-id error!\n", + NFS_SERVER(dir)->nfs_client->cl_hostname); exception.retry = 1; continue; } @@ -1042,6 +1160,11 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, exception.retry = 1; continue; } + if (status == -EAGAIN) { + /* We must have found a delegation */ + exception.retry = 1; + continue; + } res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir), status, &exception)); } while (exception.retry); @@ -1101,6 +1224,7 @@ static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, } struct nfs4_closedata { + struct path path; struct inode *inode; struct nfs4_state *state; struct nfs_closeargs arg; @@ -1117,6 +1241,8 @@ static void nfs4_free_closedata(void *data) nfs4_put_open_state(calldata->state); nfs_free_seqid(calldata->arg.seqid); nfs4_put_state_owner(sp); + dput(calldata->path.dentry); + mntput(calldata->path.mnt); kfree(calldata); } @@ -1134,8 +1260,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data) nfs_increment_open_seqid(task->tk_status, calldata->arg.seqid); switch (task->tk_status) { case 0: - memcpy(&state->stateid, &calldata->res.stateid, - sizeof(state->stateid)); + nfs_set_open_stateid(state, &calldata->res.stateid, calldata->arg.open_flags); renew_lease(server, calldata->timestamp); break; case -NFS4ERR_STALE_STATEID: @@ -1160,26 +1285,30 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) .rpc_resp = &calldata->res, .rpc_cred = state->owner->so_cred, }; - int mode = 0, old_mode; + int clear_rd, clear_wr, clear_rdwr; + int mode; if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) return; - /* Recalculate the new open mode in case someone reopened the file - * while we were waiting in line to be scheduled. - */ + + mode = FMODE_READ|FMODE_WRITE; + clear_rd = clear_wr = clear_rdwr = 0; spin_lock(&state->owner->so_lock); - spin_lock(&calldata->inode->i_lock); - mode = old_mode = state->state; + /* Calculate the change in open mode */ if (state->n_rdwr == 0) { - if (state->n_rdonly == 0) + if (state->n_rdonly == 0) { mode &= ~FMODE_READ; - if (state->n_wronly == 0) + clear_rd |= test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags); + clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags); + } + if (state->n_wronly == 0) { mode &= ~FMODE_WRITE; + clear_wr |= test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags); + clear_rdwr |= test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags); + } } - nfs4_state_set_mode_locked(state, mode); - spin_unlock(&calldata->inode->i_lock); spin_unlock(&state->owner->so_lock); - if (mode == old_mode || test_bit(NFS_DELEGATED_STATE, &state->flags)) { + if (!clear_rd && !clear_wr && !clear_rdwr) { /* Note: exit _without_ calling nfs4_close_done */ task->tk_action = NULL; return; @@ -1209,19 +1338,21 @@ static const struct rpc_call_ops nfs4_close_ops = { * * NOTE: Caller must be holding the sp->so_owner semaphore! */ -int nfs4_do_close(struct inode *inode, struct nfs4_state *state) +int nfs4_do_close(struct path *path, struct nfs4_state *state) { - struct nfs_server *server = NFS_SERVER(inode); + struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_closedata *calldata; + struct nfs4_state_owner *sp = state->owner; + struct rpc_task *task; int status = -ENOMEM; calldata = kmalloc(sizeof(*calldata), GFP_KERNEL); if (calldata == NULL) goto out; - calldata->inode = inode; + calldata->inode = state->inode; calldata->state = state; - calldata->arg.fh = NFS_FH(inode); - calldata->arg.stateid = &state->stateid; + calldata->arg.fh = NFS_FH(state->inode); + calldata->arg.stateid = &state->open_stateid; /* Serialization for the sequence id */ calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); if (calldata->arg.seqid == NULL) @@ -1229,36 +1360,55 @@ int nfs4_do_close(struct inode *inode, struct nfs4_state *state) calldata->arg.bitmask = server->attr_bitmask; calldata->res.fattr = &calldata->fattr; calldata->res.server = server; + calldata->path.mnt = mntget(path->mnt); + calldata->path.dentry = dget(path->dentry); - status = nfs4_call_async(server->client, &nfs4_close_ops, calldata); - if (status == 0) - goto out; - - nfs_free_seqid(calldata->arg.seqid); + task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_close_ops, calldata); + if (IS_ERR(task)) + return PTR_ERR(task); + rpc_put_task(task); + return 0; out_free_calldata: kfree(calldata); out: + nfs4_put_open_state(state); + nfs4_put_state_owner(sp); return status; } -static int nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state) +static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state) { struct file *filp; + int ret; - filp = lookup_instantiate_filp(nd, dentry, NULL); + /* If the open_intent is for execute, we have an extra check to make */ + if (nd->intent.open.flags & FMODE_EXEC) { + ret = _nfs4_do_access(state->inode, + state->owner->so_cred, + nd->intent.open.flags); + if (ret < 0) + goto out_close; + } + filp = lookup_instantiate_filp(nd, path->dentry, NULL); if (!IS_ERR(filp)) { struct nfs_open_context *ctx; ctx = (struct nfs_open_context *)filp->private_data; ctx->state = state; return 0; } - nfs4_close_state(state, nd->intent.open.flags); - return PTR_ERR(filp); + ret = PTR_ERR(filp); +out_close: + nfs4_close_state(path, state, nd->intent.open.flags); + return ret; } struct dentry * nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { + struct path path = { + .mnt = nd->mnt, + .dentry = dentry, + }; struct iattr attr; struct rpc_cred *cred; struct nfs4_state *state; @@ -1277,7 +1427,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); if (IS_ERR(cred)) return (struct dentry *)cred; - state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); + state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); put_rpccred(cred); if (IS_ERR(state)) { if (PTR_ERR(state) == -ENOENT) @@ -1287,22 +1437,24 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) res = d_add_unique(dentry, igrab(state->inode)); if (res != NULL) dentry = res; - nfs4_intent_set_file(nd, dentry, state); + nfs4_intent_set_file(nd, &path, state); return res; } int nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd) { + struct path path = { + .mnt = nd->mnt, + .dentry = dentry, + }; struct rpc_cred *cred; struct nfs4_state *state; cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); if (IS_ERR(cred)) return PTR_ERR(cred); - state = nfs4_open_delegated(dentry->d_inode, openflags, cred); - if (IS_ERR(state)) - state = nfs4_do_open(dir, dentry, openflags, NULL, cred); + state = nfs4_do_open(dir, &path, openflags, NULL, cred); put_rpccred(cred); if (IS_ERR(state)) { switch (PTR_ERR(state)) { @@ -1318,10 +1470,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st } } if (state->inode == dentry->d_inode) { - nfs4_intent_set_file(nd, dentry, state); + nfs4_intent_set_file(nd, &path, state); return 1; } - nfs4_close_state(state, openflags); + nfs4_close_state(&path, state, openflags); out_drop: d_drop(dentry); return 0; @@ -1418,7 +1570,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * Note that we'll actually follow the referral later when * we detect fsid mismatch in inode revalidation */ -static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle) +static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle) { int status = -ENOMEM; struct page *page = NULL; @@ -1533,8 +1685,8 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, return status; } -static int _nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh, - struct qstr *name, struct nfs_fh *fhandle, +static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *dirfh, + const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { int status; @@ -1559,8 +1711,6 @@ static int _nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh, dprintk("NFS call lookupfh %s\n", name->name); status = rpc_call_sync(server->client, &msg, 0); dprintk("NFS reply lookupfh: %d\n", status); - if (status == -NFS4ERR_MOVED) - status = -EREMOTE; return status; } @@ -1571,39 +1721,24 @@ static int nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh, struct nfs4_exception exception = { }; int err; do { - err = nfs4_handle_exception(server, - _nfs4_proc_lookupfh(server, dirfh, name, - fhandle, fattr), - &exception); + err = _nfs4_proc_lookupfh(server, dirfh, name, fhandle, fattr); + /* FIXME: !!!! */ + if (err == -NFS4ERR_MOVED) { + err = -EREMOTE; + break; + } + err = nfs4_handle_exception(server, err, &exception); } while (exception.retry); return err; } -static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name, +static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - int status; - struct nfs_server *server = NFS_SERVER(dir); - struct nfs4_lookup_arg args = { - .bitmask = server->attr_bitmask, - .dir_fh = NFS_FH(dir), - .name = name, - }; - struct nfs4_lookup_res res = { - .server = server, - .fattr = fattr, - .fh = fhandle, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP], - .rpc_argp = &args, - .rpc_resp = &res, - }; - - nfs_fattr_init(fattr); + int status; dprintk("NFS call lookup %s\n", name->name); - status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + status = _nfs4_proc_lookupfh(NFS_SERVER(dir), NFS_FH(dir), name, fhandle, fattr); if (status == -NFS4ERR_MOVED) status = nfs4_get_referral(dir, name, fattr, fhandle); dprintk("NFS reply lookup: %d\n", status); @@ -1752,6 +1887,10 @@ static int nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags, struct nameidata *nd) { + struct path path = { + .mnt = nd->mnt, + .dentry = dentry, + }; struct nfs4_state *state; struct rpc_cred *cred; int status = 0; @@ -1761,7 +1900,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, status = PTR_ERR(cred); goto out; } - state = nfs4_do_open(dir, dentry, flags, sattr, cred); + state = nfs4_do_open(dir, &path, flags, sattr, cred); put_rpccred(cred); if (IS_ERR(state)) { status = PTR_ERR(state); @@ -1773,11 +1912,12 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, status = nfs4_do_setattr(state->inode, &fattr, sattr, state); if (status == 0) nfs_setattr_update_inode(state->inode, sattr); + nfs_post_op_update_inode(state->inode, &fattr); } - if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN)) - status = nfs4_intent_set_file(nd, dentry, state); + if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) + status = nfs4_intent_set_file(nd, &path, state); else - nfs4_close_state(state, flags); + nfs4_close_state(&path, state, flags); out: return status; } @@ -1785,28 +1925,27 @@ out: static int _nfs4_proc_remove(struct inode *dir, struct qstr *name) { struct nfs_server *server = NFS_SERVER(dir); - struct nfs4_remove_arg args = { + struct nfs_removeargs args = { .fh = NFS_FH(dir), - .name = name, + .name.len = name->len, + .name.name = name->name, .bitmask = server->attr_bitmask, }; - struct nfs_fattr dir_attr; - struct nfs4_remove_res res = { + struct nfs_removeres res = { .server = server, - .dir_attr = &dir_attr, }; struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE], - .rpc_argp = &args, - .rpc_resp = &res, + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE], + .rpc_argp = &args, + .rpc_resp = &res, }; int status; - nfs_fattr_init(res.dir_attr); + nfs_fattr_init(&res.dir_attr); status = rpc_call_sync(server->client, &msg, 0); if (status == 0) { update_changeattr(dir, &res.cinfo); - nfs_post_op_update_inode(dir, res.dir_attr); + nfs_post_op_update_inode(dir, &res.dir_attr); } return status; } @@ -1823,48 +1962,26 @@ static int nfs4_proc_remove(struct inode *dir, struct qstr *name) return err; } -struct unlink_desc { - struct nfs4_remove_arg args; - struct nfs4_remove_res res; - struct nfs_fattr dir_attr; -}; - -static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, - struct qstr *name) +static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) { - struct nfs_server *server = NFS_SERVER(dir->d_inode); - struct unlink_desc *up; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs_removeargs *args = msg->rpc_argp; + struct nfs_removeres *res = msg->rpc_resp; - up = kmalloc(sizeof(*up), GFP_KERNEL); - if (!up) - return -ENOMEM; - - up->args.fh = NFS_FH(dir->d_inode); - up->args.name = name; - up->args.bitmask = server->attr_bitmask; - up->res.server = server; - up->res.dir_attr = &up->dir_attr; - + args->bitmask = server->attr_bitmask; + res->server = server; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; - msg->rpc_argp = &up->args; - msg->rpc_resp = &up->res; - return 0; } -static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task) +static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) { - struct rpc_message *msg = &task->tk_msg; - struct unlink_desc *up; - - if (msg->rpc_resp != NULL) { - up = container_of(msg->rpc_resp, struct unlink_desc, res); - update_changeattr(dir->d_inode, &up->res.cinfo); - nfs_post_op_update_inode(dir->d_inode, up->res.dir_attr); - kfree(up); - msg->rpc_resp = NULL; - msg->rpc_argp = NULL; - } - return 0; + struct nfs_removeres *res = task->tk_msg.rpc_resp; + + if (nfs4_async_handle_error(task, res->server) == -EAGAIN) + return 0; + update_changeattr(dir, &res->cinfo); + nfs_post_op_update_inode(dir, &res->dir_attr); + return 1; } static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, @@ -3008,7 +3125,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock if (status != 0) goto out; lsp = request->fl_u.nfs4_fl.owner; - arg.lock_owner.id = lsp->ls_id; + arg.lock_owner.id = lsp->ls_id.id; status = rpc_call_sync(server->client, &msg, 0); switch (status) { case 0: @@ -3152,6 +3269,11 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, { struct nfs4_unlockdata *data; + /* Ensure this is an unlock - when canceling a lock, the + * canceled lock is passed in, and it won't be an unlock. + */ + fl->fl_type = F_UNLCK; + data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid); if (data == NULL) { nfs_free_seqid(seqid); @@ -3222,7 +3344,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, goto out_free; p->arg.lock_stateid = &lsp->ls_stateid; p->arg.lock_owner.clientid = server->nfs_client->cl_clientid; - p->arg.lock_owner.id = lsp->ls_id; + p->arg.lock_owner.id = lsp->ls_id.id; p->lsp = lsp; atomic_inc(&lsp->ls_count); p->ctx = get_nfs_open_context(ctx); @@ -3285,7 +3407,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) memcpy(data->lsp->ls_stateid.data, data->res.stateid.data, sizeof(data->lsp->ls_stateid.data)); data->lsp->ls_flags |= NFS_LOCK_INITIALIZED; - renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp); + renew_lease(NFS_SERVER(data->ctx->path.dentry->d_inode), data->timestamp); } nfs_increment_lock_seqid(data->rpc_status, data->arg.lock_seqid); out: @@ -3544,7 +3666,7 @@ ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen) return len; } -int nfs4_proc_fs_locations(struct inode *dir, struct qstr *name, +int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page) { struct nfs_server *server = NFS_SERVER(dir); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8ed79d5..e9662ba 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -38,12 +38,14 @@ * subsequent patch. */ +#include <linux/kernel.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/nfs_fs.h> #include <linux/nfs_idmap.h> #include <linux/kthread.h> #include <linux/module.h> +#include <linux/random.h> #include <linux/workqueue.h> #include <linux/bitops.h> @@ -69,33 +71,14 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) return status; } -u32 -nfs4_alloc_lockowner_id(struct nfs_client *clp) -{ - return clp->cl_lockowner_id ++; -} - -static struct nfs4_state_owner * -nfs4_client_grab_unused(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct nfs4_state_owner *sp = NULL; - - if (!list_empty(&clp->cl_unused)) { - sp = list_entry(clp->cl_unused.next, struct nfs4_state_owner, so_list); - atomic_inc(&sp->so_count); - sp->so_cred = cred; - list_move(&sp->so_list, &clp->cl_state_owners); - clp->cl_nunused--; - } - return sp; -} - struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) { struct nfs4_state_owner *sp; + struct rb_node *pos; struct rpc_cred *cred = NULL; - list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { + sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); if (list_empty(&sp->so_states)) continue; cred = get_rpccred(sp->so_cred); @@ -107,32 +90,146 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) { struct nfs4_state_owner *sp; + struct rb_node *pos; - if (!list_empty(&clp->cl_state_owners)) { - sp = list_entry(clp->cl_state_owners.next, - struct nfs4_state_owner, so_list); + pos = rb_first(&clp->cl_state_owners); + if (pos != NULL) { + sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); return get_rpccred(sp->so_cred); } return NULL; } +static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new, + __u64 minval, int maxbits) +{ + struct rb_node **p, *parent; + struct nfs_unique_id *pos; + __u64 mask = ~0ULL; + + if (maxbits < 64) + mask = (1ULL << maxbits) - 1ULL; + + /* Ensure distribution is more or less flat */ + get_random_bytes(&new->id, sizeof(new->id)); + new->id &= mask; + if (new->id < minval) + new->id += minval; +retry: + p = &root->rb_node; + parent = NULL; + + while (*p != NULL) { + parent = *p; + pos = rb_entry(parent, struct nfs_unique_id, rb_node); + + if (new->id < pos->id) + p = &(*p)->rb_left; + else if (new->id > pos->id) + p = &(*p)->rb_right; + else + goto id_exists; + } + rb_link_node(&new->rb_node, parent, p); + rb_insert_color(&new->rb_node, root); + return; +id_exists: + for (;;) { + new->id++; + if (new->id < minval || (new->id & mask) != new->id) { + new->id = minval; + break; + } + parent = rb_next(parent); + if (parent == NULL) + break; + pos = rb_entry(parent, struct nfs_unique_id, rb_node); + if (new->id < pos->id) + break; + } + goto retry; +} + +static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id) +{ + rb_erase(&id->rb_node, root); +} + static struct nfs4_state_owner * -nfs4_find_state_owner(struct nfs_client *clp, struct rpc_cred *cred) +nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred) { + struct nfs_client *clp = server->nfs_client; + struct rb_node **p = &clp->cl_state_owners.rb_node, + *parent = NULL; struct nfs4_state_owner *sp, *res = NULL; - list_for_each_entry(sp, &clp->cl_state_owners, so_list) { - if (sp->so_cred != cred) + while (*p != NULL) { + parent = *p; + sp = rb_entry(parent, struct nfs4_state_owner, so_client_node); + + if (server < sp->so_server) { + p = &parent->rb_left; continue; - atomic_inc(&sp->so_count); - /* Move to the head of the list */ - list_move(&sp->so_list, &clp->cl_state_owners); - res = sp; - break; + } + if (server > sp->so_server) { + p = &parent->rb_right; + continue; + } + if (cred < sp->so_cred) + p = &parent->rb_left; + else if (cred > sp->so_cred) + p = &parent->rb_right; + else { + atomic_inc(&sp->so_count); + res = sp; + break; + } } return res; } +static struct nfs4_state_owner * +nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new) +{ + struct rb_node **p = &clp->cl_state_owners.rb_node, + *parent = NULL; + struct nfs4_state_owner *sp; + + while (*p != NULL) { + parent = *p; + sp = rb_entry(parent, struct nfs4_state_owner, so_client_node); + + if (new->so_server < sp->so_server) { + p = &parent->rb_left; + continue; + } + if (new->so_server > sp->so_server) { + p = &parent->rb_right; + continue; + } + if (new->so_cred < sp->so_cred) + p = &parent->rb_left; + else if (new->so_cred > sp->so_cred) + p = &parent->rb_right; + else { + atomic_inc(&sp->so_count); + return sp; + } + } + nfs_alloc_unique_id(&clp->cl_openowner_id, &new->so_owner_id, 1, 64); + rb_link_node(&new->so_client_node, parent, p); + rb_insert_color(&new->so_client_node, &clp->cl_state_owners); + return new; +} + +static void +nfs4_remove_state_owner(struct nfs_client *clp, struct nfs4_state_owner *sp) +{ + if (!RB_EMPTY_NODE(&sp->so_client_node)) + rb_erase(&sp->so_client_node, &clp->cl_state_owners); + nfs_free_unique_id(&clp->cl_openowner_id, &sp->so_owner_id); +} + /* * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to * create a new state_owner. @@ -160,10 +257,14 @@ nfs4_alloc_state_owner(void) void nfs4_drop_state_owner(struct nfs4_state_owner *sp) { - struct nfs_client *clp = sp->so_client; - spin_lock(&clp->cl_lock); - list_del_init(&sp->so_list); - spin_unlock(&clp->cl_lock); + if (!RB_EMPTY_NODE(&sp->so_client_node)) { + struct nfs_client *clp = sp->so_client; + + spin_lock(&clp->cl_lock); + rb_erase(&sp->so_client_node, &clp->cl_state_owners); + RB_CLEAR_NODE(&sp->so_client_node); + spin_unlock(&clp->cl_lock); + } } /* @@ -175,26 +276,25 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct struct nfs_client *clp = server->nfs_client; struct nfs4_state_owner *sp, *new; - get_rpccred(cred); - new = nfs4_alloc_state_owner(); spin_lock(&clp->cl_lock); - sp = nfs4_find_state_owner(clp, cred); - if (sp == NULL) - sp = nfs4_client_grab_unused(clp, cred); - if (sp == NULL && new != NULL) { - list_add(&new->so_list, &clp->cl_state_owners); - new->so_client = clp; - new->so_id = nfs4_alloc_lockowner_id(clp); - new->so_cred = cred; - sp = new; - new = NULL; - } + sp = nfs4_find_state_owner(server, cred); spin_unlock(&clp->cl_lock); - kfree(new); if (sp != NULL) return sp; - put_rpccred(cred); - return NULL; + new = nfs4_alloc_state_owner(); + if (new == NULL) + return NULL; + new->so_client = clp; + new->so_server = server; + new->so_cred = cred; + spin_lock(&clp->cl_lock); + sp = nfs4_insert_state_owner(clp, new); + spin_unlock(&clp->cl_lock); + if (sp == new) + get_rpccred(cred); + else + kfree(new); + return sp; } /* @@ -208,18 +308,7 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp) if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) return; - if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) - goto out_free; - if (list_empty(&sp->so_list)) - goto out_free; - list_move(&sp->so_list, &clp->cl_unused); - clp->cl_nunused++; - spin_unlock(&clp->cl_lock); - put_rpccred(cred); - cred = NULL; - return; -out_free: - list_del(&sp->so_list); + nfs4_remove_state_owner(clp, sp); spin_unlock(&clp->cl_lock); put_rpccred(cred); kfree(sp); @@ -236,6 +325,7 @@ nfs4_alloc_open_state(void) atomic_set(&state->count, 1); INIT_LIST_HEAD(&state->lock_states); spin_lock_init(&state->state_lock); + seqlock_init(&state->seqlock); return state; } @@ -263,13 +353,10 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) struct nfs4_state *state; list_for_each_entry(state, &nfsi->open_states, inode_states) { - /* Is this in the process of being freed? */ - if (state->state == 0) + if (state->owner != owner) continue; - if (state->owner == owner) { - atomic_inc(&state->count); + if (atomic_inc_not_zero(&state->count)) return state; - } } return NULL; } @@ -341,16 +428,15 @@ void nfs4_put_open_state(struct nfs4_state *state) /* * Close the current file. */ -void nfs4_close_state(struct nfs4_state *state, mode_t mode) +void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) { - struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; - int oldstate, newstate = 0; + int call_close = 0; + int newstate; atomic_inc(&owner->so_count); /* Protect against nfs4_find_state() */ spin_lock(&owner->so_lock); - spin_lock(&inode->i_lock); switch (mode & (FMODE_READ | FMODE_WRITE)) { case FMODE_READ: state->n_rdonly--; @@ -361,24 +447,29 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode) case FMODE_READ|FMODE_WRITE: state->n_rdwr--; } - oldstate = newstate = state->state; + newstate = FMODE_READ|FMODE_WRITE; if (state->n_rdwr == 0) { - if (state->n_rdonly == 0) + if (state->n_rdonly == 0) { newstate &= ~FMODE_READ; - if (state->n_wronly == 0) + call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags); + call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); + } + if (state->n_wronly == 0) { newstate &= ~FMODE_WRITE; + call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags); + call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags); + } + if (newstate == 0) + clear_bit(NFS_DELEGATED_STATE, &state->flags); } - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { - nfs4_state_set_mode_locked(state, newstate); - oldstate = newstate; - } - spin_unlock(&inode->i_lock); + nfs4_state_set_mode_locked(state, newstate); spin_unlock(&owner->so_lock); - if (oldstate != newstate && nfs4_do_close(inode, state) == 0) - return; - nfs4_put_open_state(state); - nfs4_put_state_owner(owner); + if (!call_close) { + nfs4_put_open_state(state); + nfs4_put_state_owner(owner); + } else + nfs4_do_close(path, state); } /* @@ -415,12 +506,22 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f atomic_set(&lsp->ls_count, 1); lsp->ls_owner = fl_owner; spin_lock(&clp->cl_lock); - lsp->ls_id = nfs4_alloc_lockowner_id(clp); + nfs_alloc_unique_id(&clp->cl_lockowner_id, &lsp->ls_id, 1, 64); spin_unlock(&clp->cl_lock); INIT_LIST_HEAD(&lsp->ls_locks); return lsp; } +static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) +{ + struct nfs_client *clp = lsp->ls_state->owner->so_client; + + spin_lock(&clp->cl_lock); + nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id); + spin_unlock(&clp->cl_lock); + kfree(lsp); +} + /* * Return a compatible lock_state. If no initialized lock_state structure * exists, return an uninitialized one. @@ -450,7 +551,8 @@ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_ return NULL; } spin_unlock(&state->state_lock); - kfree(new); + if (new != NULL) + nfs4_free_lock_state(new); return lsp; } @@ -471,7 +573,7 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp) if (list_empty(&state->lock_states)) clear_bit(LK_STATE_IN_USE, &state->flags); spin_unlock(&state->state_lock); - kfree(lsp); + nfs4_free_lock_state(lsp); } static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) @@ -513,8 +615,12 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) { struct nfs4_lock_state *lsp; + int seq; - memcpy(dst, &state->stateid, sizeof(*dst)); + do { + seq = read_seqbegin(&state->seqlock); + memcpy(dst, &state->stateid, sizeof(*dst)); + } while (read_seqretry(&state->seqlock, seq)); if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) return; @@ -557,12 +663,18 @@ void nfs_free_seqid(struct nfs_seqid *seqid) * failed with a seqid incrementing error - * see comments nfs_fs.h:seqid_mutating_error() */ -static inline void nfs_increment_seqid(int status, struct nfs_seqid *seqid) +static void nfs_increment_seqid(int status, struct nfs_seqid *seqid) { switch (status) { case 0: break; case -NFS4ERR_BAD_SEQID: + if (seqid->sequence->flags & NFS_SEQID_CONFIRMED) + return; + printk(KERN_WARNING "NFS: v4 server returned a bad" + "sequence-id error on an" + "unconfirmed sequence %p!\n", + seqid->sequence); case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_BAD_STATEID: @@ -586,7 +698,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid) struct nfs4_state_owner, so_seqid); nfs4_drop_state_owner(sp); } - return nfs_increment_seqid(status, seqid); + nfs_increment_seqid(status, seqid); } /* @@ -596,7 +708,7 @@ void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid) */ void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid) { - return nfs_increment_seqid(status, seqid); + nfs_increment_seqid(status, seqid); } int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task) @@ -748,15 +860,21 @@ out_err: static void nfs4_state_mark_reclaim(struct nfs_client *clp) { struct nfs4_state_owner *sp; + struct rb_node *pos; struct nfs4_state *state; struct nfs4_lock_state *lock; /* Reset all sequence ids to zero */ - list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { + sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); sp->so_seqid.counter = 0; sp->so_seqid.flags = 0; spin_lock(&sp->so_lock); list_for_each_entry(state, &sp->so_states, open_states) { + clear_bit(NFS_DELEGATED_STATE, &state->flags); + clear_bit(NFS_O_RDONLY_STATE, &state->flags); + clear_bit(NFS_O_WRONLY_STATE, &state->flags); + clear_bit(NFS_O_RDWR_STATE, &state->flags); list_for_each_entry(lock, &state->lock_states, ls_locks) { lock->ls_seqid.counter = 0; lock->ls_seqid.flags = 0; @@ -771,6 +889,7 @@ static int reclaimer(void *ptr) { struct nfs_client *clp = ptr; struct nfs4_state_owner *sp; + struct rb_node *pos; struct nfs4_state_recovery_ops *ops; struct rpc_cred *cred; int status = 0; @@ -816,7 +935,8 @@ restart_loop: /* Mark all delegations for reclaim */ nfs_delegation_mark_reclaim(clp); /* Note: list is protected by exclusive lock on cl->cl_sem */ - list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { + sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); status = nfs4_reclaim_open_state(ops, sp); if (status < 0) { if (status == -NFS4ERR_NO_GRACE) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 8003c91..badd73b 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -68,13 +68,19 @@ static int nfs4_stat_to_errno(int); #endif /* lock,open owner id: - * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) + * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2) */ -#define owner_id_maxsz (1 + 1) +#define open_owner_id_maxsz (1 + 4) +#define lock_owner_id_maxsz (1 + 4) +#define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define op_encode_hdr_maxsz (1) #define op_decode_hdr_maxsz (2) +#define encode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) +#define decode_stateid_maxsz (XDR_QUADLEN(NFS4_STATEID_SIZE)) +#define encode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) +#define decode_verifier_maxsz (XDR_QUADLEN(NFS4_VERIFIER_SIZE)) #define encode_putfh_maxsz (op_encode_hdr_maxsz + 1 + \ (NFS4_FHSIZE >> 2)) #define decode_putfh_maxsz (op_decode_hdr_maxsz) @@ -87,12 +93,19 @@ static int nfs4_stat_to_errno(int); #define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) #define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) +#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) +#define nfs4_group_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) /* This is based on getfattr, which uses the most attributes: */ #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ - 3 + 3 + 3 + 2 * nfs4_name_maxsz)) + 3 + 3 + 3 + nfs4_owner_maxsz + nfs4_group_maxsz)) #define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ nfs4_fattr_value_maxsz) #define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz) +#define encode_attrs_maxsz (nfs4_fattr_bitmap_maxsz + \ + 1 + 2 + 1 + \ + nfs4_owner_maxsz + \ + nfs4_group_maxsz + \ + 4 + 4) #define encode_savefh_maxsz (op_encode_hdr_maxsz) #define decode_savefh_maxsz (op_decode_hdr_maxsz) #define encode_restorefh_maxsz (op_encode_hdr_maxsz) @@ -116,8 +129,65 @@ static int nfs4_stat_to_errno(int); 3 + (NFS4_VERIFIER_SIZE >> 2)) #define decode_setclientid_confirm_maxsz \ (op_decode_hdr_maxsz) -#define encode_lookup_maxsz (op_encode_hdr_maxsz + \ - 1 + ((3 + NFS4_FHSIZE) >> 2)) +#define encode_lookup_maxsz (op_encode_hdr_maxsz + nfs4_name_maxsz) +#define decode_lookup_maxsz (op_decode_hdr_maxsz) +#define encode_share_access_maxsz \ + (2) +#define encode_createmode_maxsz (1 + encode_attrs_maxsz) +#define encode_opentype_maxsz (1 + encode_createmode_maxsz) +#define encode_claim_null_maxsz (1 + nfs4_name_maxsz) +#define encode_open_maxsz (op_encode_hdr_maxsz + \ + 2 + encode_share_access_maxsz + 2 + \ + open_owner_id_maxsz + \ + encode_opentype_maxsz + \ + encode_claim_null_maxsz) +#define decode_ace_maxsz (3 + nfs4_owner_maxsz) +#define decode_delegation_maxsz (1 + decode_stateid_maxsz + 1 + \ + decode_ace_maxsz) +#define decode_change_info_maxsz (5) +#define decode_open_maxsz (op_decode_hdr_maxsz + \ + decode_stateid_maxsz + \ + decode_change_info_maxsz + 1 + \ + nfs4_fattr_bitmap_maxsz + \ + decode_delegation_maxsz) +#define encode_open_confirm_maxsz \ + (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + 1) +#define decode_open_confirm_maxsz \ + (op_decode_hdr_maxsz + \ + decode_stateid_maxsz) +#define encode_open_downgrade_maxsz \ + (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + 1 + \ + encode_share_access_maxsz) +#define decode_open_downgrade_maxsz \ + (op_decode_hdr_maxsz + \ + decode_stateid_maxsz) +#define encode_close_maxsz (op_encode_hdr_maxsz + \ + 1 + encode_stateid_maxsz) +#define decode_close_maxsz (op_decode_hdr_maxsz + \ + decode_stateid_maxsz) +#define encode_setattr_maxsz (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + \ + encode_attrs_maxsz) +#define decode_setattr_maxsz (op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz) +#define encode_read_maxsz (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + 3) +#define decode_read_maxsz (op_decode_hdr_maxsz + 2) +#define encode_readdir_maxsz (op_encode_hdr_maxsz + \ + 2 + encode_verifier_maxsz + 5) +#define decode_readdir_maxsz (op_decode_hdr_maxsz + \ + decode_verifier_maxsz) +#define encode_readlink_maxsz (op_encode_hdr_maxsz) +#define decode_readlink_maxsz (op_decode_hdr_maxsz + 1) +#define encode_write_maxsz (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + 4) +#define decode_write_maxsz (op_decode_hdr_maxsz + \ + 2 + decode_verifier_maxsz) +#define encode_commit_maxsz (op_encode_hdr_maxsz + 3) +#define decode_commit_maxsz (op_decode_hdr_maxsz + \ + decode_verifier_maxsz) #define encode_remove_maxsz (op_encode_hdr_maxsz + \ nfs4_name_maxsz) #define encode_rename_maxsz (op_encode_hdr_maxsz + \ @@ -126,106 +196,142 @@ static int nfs4_stat_to_errno(int); #define encode_link_maxsz (op_encode_hdr_maxsz + \ nfs4_name_maxsz) #define decode_link_maxsz (op_decode_hdr_maxsz + 5) +#define encode_lock_maxsz (op_encode_hdr_maxsz + \ + 7 + \ + 1 + encode_stateid_maxsz + 8) +#define decode_lock_denied_maxsz \ + (8 + decode_lockowner_maxsz) +#define decode_lock_maxsz (op_decode_hdr_maxsz + \ + decode_lock_denied_maxsz) +#define encode_lockt_maxsz (op_encode_hdr_maxsz + 12) +#define decode_lockt_maxsz (op_decode_hdr_maxsz + \ + decode_lock_denied_maxsz) +#define encode_locku_maxsz (op_encode_hdr_maxsz + 3 + \ + encode_stateid_maxsz + \ + 4) +#define decode_locku_maxsz (op_decode_hdr_maxsz + \ + decode_stateid_maxsz) +#define encode_access_maxsz (op_encode_hdr_maxsz + 1) +#define decode_access_maxsz (op_decode_hdr_maxsz + 2) #define encode_symlink_maxsz (op_encode_hdr_maxsz + \ 1 + nfs4_name_maxsz + \ 1 + \ nfs4_fattr_maxsz) #define decode_symlink_maxsz (op_decode_hdr_maxsz + 8) #define encode_create_maxsz (op_encode_hdr_maxsz + \ - 2 + nfs4_name_maxsz + \ - nfs4_fattr_maxsz) -#define decode_create_maxsz (op_decode_hdr_maxsz + 8) + 1 + 2 + nfs4_name_maxsz + \ + encode_attrs_maxsz) +#define decode_create_maxsz (op_decode_hdr_maxsz + \ + decode_change_info_maxsz + \ + nfs4_fattr_bitmap_maxsz) +#define encode_statfs_maxsz (encode_getattr_maxsz) +#define decode_statfs_maxsz (decode_getattr_maxsz) #define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4) #define decode_delegreturn_maxsz (op_decode_hdr_maxsz) +#define encode_getacl_maxsz (encode_getattr_maxsz) +#define decode_getacl_maxsz (op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz + 1) +#define encode_setacl_maxsz (op_encode_hdr_maxsz + \ + encode_stateid_maxsz + 3) +#define decode_setacl_maxsz (decode_setattr_maxsz) +#define encode_fs_locations_maxsz \ + (encode_getattr_maxsz) +#define decode_fs_locations_maxsz \ + (0) #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 7) + encode_read_maxsz) #define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 2) + decode_read_maxsz) #define NFS4_enc_readlink_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz) + encode_readlink_maxsz) #define NFS4_dec_readlink_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz) + decode_readlink_maxsz) #define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 9) + encode_readdir_maxsz) #define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 2) + decode_readdir_maxsz) #define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 8 + \ + encode_write_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 + \ + decode_write_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 3 + \ + encode_commit_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 2 + \ + decode_commit_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - op_encode_hdr_maxsz + \ - 13 + 3 + 2 + 64 + \ - encode_getattr_maxsz + \ - encode_getfh_maxsz) + encode_putfh_maxsz + \ + encode_savefh_maxsz + \ + encode_open_maxsz + \ + encode_getfh_maxsz + \ + encode_getattr_maxsz + \ + encode_restorefh_maxsz + \ + encode_getattr_maxsz) #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ - decode_getattr_maxsz + \ - decode_getfh_maxsz) -#define NFS4_enc_open_confirm_sz \ - (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 5) -#define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4) + decode_putfh_maxsz + \ + decode_savefh_maxsz + \ + decode_open_maxsz + \ + decode_getfh_maxsz + \ + decode_getattr_maxsz + \ + decode_restorefh_maxsz + \ + decode_getattr_maxsz) +#define NFS4_enc_open_confirm_sz \ + (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_open_confirm_maxsz) +#define NFS4_dec_open_confirm_sz \ + (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_open_confirm_maxsz) #define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + \ - 11) + encode_open_maxsz + \ + encode_getattr_maxsz) #define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + \ - 4 + 5 + 2 + 3) + decode_open_maxsz + \ + decode_getattr_maxsz) #define NFS4_enc_open_downgrade_sz \ (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 7 + \ - encode_getattr_maxsz) + encode_putfh_maxsz + \ + encode_open_downgrade_maxsz + \ + encode_getattr_maxsz) #define NFS4_dec_open_downgrade_sz \ (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 + \ - decode_getattr_maxsz) -#define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 5 + \ - encode_getattr_maxsz) -#define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 + \ - decode_getattr_maxsz) -#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 4 + \ - nfs4_fattr_maxsz + \ - encode_getattr_maxsz) -#define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ - decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 3 + \ - nfs4_fattr_maxsz) + decode_putfh_maxsz + \ + decode_open_downgrade_maxsz + \ + decode_getattr_maxsz) +#define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_close_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_close_maxsz + \ + decode_getattr_maxsz) +#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_setattr_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_setattr_maxsz + \ + decode_getattr_maxsz) #define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_fsinfo_maxsz) @@ -252,39 +358,28 @@ static int nfs4_stat_to_errno(int); decode_fsinfo_maxsz) #define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz + \ - op_encode_hdr_maxsz + \ - 1 + 1 + 2 + 2 + \ - 1 + 4 + 1 + 2 + \ - owner_id_maxsz) + encode_lock_maxsz) #define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_getattr_maxsz + \ - op_decode_hdr_maxsz + \ - 2 + 2 + 1 + 2 + \ - owner_id_maxsz) + decode_lock_maxsz) #define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz + \ - op_encode_hdr_maxsz + \ - 1 + 2 + 2 + 2 + \ - owner_id_maxsz) -#define NFS4_dec_lockt_sz (NFS4_dec_lock_sz) + encode_lockt_maxsz) +#define NFS4_dec_lockt_sz (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_lockt_maxsz) #define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz + \ - op_encode_hdr_maxsz + \ - 1 + 1 + 4 + 2 + 2) + encode_locku_maxsz) #define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_getattr_maxsz + \ - op_decode_hdr_maxsz + 4) + decode_locku_maxsz) #define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 1) + encode_access_maxsz) #define NFS4_dec_access_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 2) + decode_access_maxsz) #define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) @@ -298,7 +393,7 @@ static int nfs4_stat_to_errno(int); encode_getfh_maxsz) #define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + \ + decode_lookup_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ @@ -383,10 +478,10 @@ static int nfs4_stat_to_errno(int); decode_getattr_maxsz) #define NFS4_enc_statfs_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz) + encode_statfs_maxsz) #define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 12) + decode_statfs_maxsz) #define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) @@ -402,27 +497,26 @@ static int nfs4_stat_to_errno(int); decode_getattr_maxsz) #define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz) + encode_getacl_maxsz) #define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz + 1) + decode_getacl_maxsz) #define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 4 + \ - nfs4_fattr_bitmap_maxsz + 1) + encode_setacl_maxsz) #define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) + decode_setacl_maxsz) #define NFS4_enc_fs_locations_sz \ (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_getattr_maxsz) + encode_lookup_maxsz + \ + encode_fs_locations_maxsz) #define NFS4_dec_fs_locations_sz \ (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + \ - nfs4_fattr_bitmap_maxsz) + decode_lookup_maxsz + \ + decode_fs_locations_maxsz) static struct { unsigned int mode; @@ -793,13 +887,14 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args) WRITE64(nfs4_lock_length(args->fl)); WRITE32(args->new_lock_owner); if (args->new_lock_owner){ - RESERVE_SPACE(4+NFS4_STATEID_SIZE+20); + RESERVE_SPACE(4+NFS4_STATEID_SIZE+32); WRITE32(args->open_seqid->sequence->counter); WRITEMEM(args->open_stateid->data, NFS4_STATEID_SIZE); WRITE32(args->lock_seqid->sequence->counter); WRITE64(args->lock_owner.clientid); - WRITE32(4); - WRITE32(args->lock_owner.id); + WRITE32(16); + WRITEMEM("lock id:", 8); + WRITE64(args->lock_owner.id); } else { RESERVE_SPACE(NFS4_STATEID_SIZE+4); @@ -814,14 +909,15 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg { __be32 *p; - RESERVE_SPACE(40); + RESERVE_SPACE(52); WRITE32(OP_LOCKT); WRITE32(nfs4_lock_type(args->fl, 0)); WRITE64(args->fl->fl_start); WRITE64(nfs4_lock_length(args->fl)); WRITE64(args->lock_owner.clientid); - WRITE32(4); - WRITE32(args->lock_owner.id); + WRITE32(16); + WRITEMEM("lock id:", 8); + WRITE64(args->lock_owner.id); return 0; } @@ -886,10 +982,11 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena WRITE32(OP_OPEN); WRITE32(arg->seqid->sequence->counter); encode_share_access(xdr, arg->open_flags); - RESERVE_SPACE(16); + RESERVE_SPACE(28); WRITE64(arg->clientid); - WRITE32(4); - WRITE32(arg->id); + WRITE32(16); + WRITEMEM("open id:", 8); + WRITE64(arg->id); } static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg) @@ -1071,12 +1168,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args) static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req) { - struct rpc_auth *auth = req->rq_task->tk_auth; uint32_t attrs[2] = { FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, FATTR4_WORD1_MOUNTED_ON_FILEID, }; - int replen; __be32 *p; RESERVE_SPACE(12+NFS4_VERIFIER_SIZE+20); @@ -1101,37 +1196,16 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg attrs[0] & readdir->bitmask[0], attrs[1] & readdir->bitmask[1]); - /* set up reply kvec - * toplevel_status + taglen + rescount + OP_PUTFH + status - * + OP_READDIR + status + verifer(2) = 9 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 9) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->pages, - readdir->pgbase, readdir->count); - dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", - __FUNCTION__, replen, readdir->pages, - readdir->pgbase, readdir->count); - return 0; } static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req) { - struct rpc_auth *auth = req->rq_task->tk_auth; - unsigned int replen; __be32 *p; RESERVE_SPACE(4); WRITE32(OP_READLINK); - /* set up reply kvec - * toplevel_status + taglen + rescount + OP_PUTFH + status - * + OP_READLINK + status + string length = 8 - */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 8) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, - readlink->pgbase, readlink->pglen); - return 0; } @@ -1361,7 +1435,7 @@ out: /* * Encode REMOVE request */ -static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs4_remove_arg *args) +static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1373,7 +1447,7 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs encode_compound_hdr(&xdr, &hdr); if ((status = encode_putfh(&xdr, args->fh)) != 0) goto out; - if ((status = encode_remove(&xdr, args->name)) != 0) + if ((status = encode_remove(&xdr, &args->name)) != 0) goto out; status = encode_getfattr(&xdr, args->bitmask); out: @@ -1697,6 +1771,8 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n struct compound_hdr hdr = { .nops = 2, }; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + unsigned int replen; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1705,6 +1781,15 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n if(status) goto out; status = encode_readlink(&xdr, args, req); + + /* set up reply kvec + * toplevel_status + taglen + rescount + OP_PUTFH + status + * + OP_READLINK + status + string length = 8 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, + args->pgbase, args->pglen); + out: return status; } @@ -1718,6 +1803,8 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf struct compound_hdr hdr = { .nops = 2, }; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + int replen; int status; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -1726,6 +1813,18 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf if(status) goto out; status = encode_readdir(&xdr, args, req); + + /* set up reply kvec + * toplevel_status + taglen + rescount + OP_PUTFH + status + * + OP_READDIR + status + verifer(2) = 9 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readdir_sz) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, + args->pgbase, args->count); + dprintk("%s: inlined page args = (%u, %p, %u, %u)\n", + __FUNCTION__, replen, args->pages, + args->pgbase, args->count); + out: return status; } @@ -1735,7 +1834,7 @@ out: */ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 2, @@ -1795,7 +1894,7 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p, struct nfs_getaclargs *args) { struct xdr_stream xdr; - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; struct compound_hdr hdr = { .nops = 2, }; @@ -2030,7 +2129,7 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs struct compound_hdr hdr = { .nops = 3, }; - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; int replen; int status; @@ -3124,11 +3223,12 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh) uint32_t len; int status; + /* Zero handle first to allow comparisons */ + memset(fh, 0, sizeof(*fh)); + status = decode_op_hdr(xdr, OP_GETFH); if (status) return status; - /* Zero handle first to allow comparisons */ - memset(fh, 0, sizeof(*fh)); READ_BUF(4); READ32(len); @@ -3269,7 +3369,7 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res) static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) { __be32 *p; - uint32_t bmlen; + uint32_t savewords, bmlen, i; int status; status = decode_op_hdr(xdr, OP_OPEN); @@ -3287,7 +3387,12 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) goto xdr_error; READ_BUF(bmlen << 2); - p += bmlen; + savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE); + for (i = 0; i < savewords; ++i) + READ32(res->attrset[i]); + for (; i < NFS4_BITMAP_SIZE; i++) + res->attrset[i] = 0; + return decode_delegation(xdr, res); xdr_error: dprintk("%s: Bitmap too large! Length = %u\n", __FUNCTION__, bmlen); @@ -3730,7 +3835,7 @@ out: /* * Decode REMOVE response */ -static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_remove_res *res) +static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_removeres *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3743,7 +3848,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re goto out; if ((status = decode_remove(&xdr, &res->cinfo)) != 0) goto out; - decode_getfattr(&xdr, res->dir_attr, res->server); + decode_getfattr(&xdr, &res->dir_attr, res->server); out: return status; } @@ -3988,12 +4093,11 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr status = decode_open(&xdr, res); if (status) goto out; - status = decode_getfh(&xdr, &res->fh); - if (status) + if (decode_getfh(&xdr, &res->fh) != 0) goto out; if (decode_getfattr(&xdr, res->f_attr, res->server) != 0) goto out; - if ((status = decode_restorefh(&xdr)) != 0) + if (decode_restorefh(&xdr) != 0) goto out; decode_getfattr(&xdr, res->dir_attr, res->server); out: diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 49d1008..3490322 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -428,7 +428,7 @@ static int __init root_nfs_getport(int program, int version, int proto) printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n", program, version, NIPQUAD(servaddr)); set_sockaddr(&sin, servaddr, 0); - return rpcb_getport_external(&sin, program, version, proto); + return rpcb_getport_sync(&sin, program, version, proto); } @@ -496,7 +496,8 @@ static int __init root_nfs_get_handle(void) NFS_MNT3_VERSION : NFS_MNT_VERSION; set_sockaddr(&sin, servaddr, htons(mount_port)); - status = nfsroot_mount(&sin, nfs_path, &fh, version, protocol); + status = nfs_mount((struct sockaddr *) &sin, sizeof(sin), NULL, + nfs_path, version, protocol, &fh); if (status < 0) printk(KERN_ERR "Root-NFS: Server returned error %d " "while mounting %s\n", status, nfs_path); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index c5bb51a..345bb9b 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -85,9 +85,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, req->wb_offset = offset; req->wb_pgbase = offset; req->wb_bytes = count; - atomic_set(&req->wb_count, 1); req->wb_context = get_nfs_open_context(ctx); - + kref_init(&req->wb_kref); return req; } @@ -109,30 +108,31 @@ void nfs_unlock_request(struct nfs_page *req) } /** - * nfs_set_page_writeback_locked - Lock a request for writeback + * nfs_set_page_tag_locked - Tag a request as locked * @req: */ -int nfs_set_page_writeback_locked(struct nfs_page *req) +static int nfs_set_page_tag_locked(struct nfs_page *req) { - struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode); + struct nfs_inode *nfsi = NFS_I(req->wb_context->path.dentry->d_inode); if (!nfs_lock_request(req)) return 0; - radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK); + radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED); return 1; } /** - * nfs_clear_page_writeback - Unlock request and wake up sleepers + * nfs_clear_page_tag_locked - Clear request tag and wake up sleepers */ -void nfs_clear_page_writeback(struct nfs_page *req) +void nfs_clear_page_tag_locked(struct nfs_page *req) { - struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode); + struct inode *inode = req->wb_context->path.dentry->d_inode; + struct nfs_inode *nfsi = NFS_I(inode); if (req->wb_page != NULL) { - spin_lock(&nfsi->req_lock); - radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_WRITEBACK); - spin_unlock(&nfsi->req_lock); + spin_lock(&inode->i_lock); + radix_tree_tag_clear(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED); + spin_unlock(&inode->i_lock); } nfs_unlock_request(req); } @@ -160,11 +160,9 @@ void nfs_clear_request(struct nfs_page *req) * * Note: Should never be called with the spinlock held! */ -void -nfs_release_request(struct nfs_page *req) +static void nfs_free_request(struct kref *kref) { - if (!atomic_dec_and_test(&req->wb_count)) - return; + struct nfs_page *req = container_of(kref, struct nfs_page, wb_kref); /* Release struct file or cached credential */ nfs_clear_request(req); @@ -172,6 +170,11 @@ nfs_release_request(struct nfs_page *req) nfs_page_free(req); } +void nfs_release_request(struct nfs_page *req) +{ + kref_put(&req->wb_kref, nfs_free_request); +} + static int nfs_wait_bit_interruptible(void *word) { int ret = 0; @@ -193,7 +196,7 @@ static int nfs_wait_bit_interruptible(void *word) int nfs_wait_on_request(struct nfs_page *req) { - struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->dentry->d_inode); + struct rpc_clnt *clnt = NFS_CLIENT(req->wb_context->path.dentry->d_inode); sigset_t oldmask; int ret = 0; @@ -379,20 +382,20 @@ void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) /** * nfs_scan_list - Scan a list for matching requests * @nfsi: NFS inode - * @head: One of the NFS inode request lists * @dst: Destination list * @idx_start: lower bound of page->index to scan * @npages: idx_start + npages sets the upper bound to scan. + * @tag: tag to scan for * * Moves elements from one of the inode request lists. * If the number of requests is set to 0, the entire address_space * starting at index idx_start, is scanned. * The requests are *not* checked to ensure that they form a contiguous set. - * You must be holding the inode's req_lock when calling this function + * You must be holding the inode's i_lock when calling this function */ -int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, +int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *dst, pgoff_t idx_start, - unsigned int npages) + unsigned int npages, int tag) { struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; struct nfs_page *req; @@ -407,9 +410,9 @@ int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, idx_end = idx_start + npages - 1; for (;;) { - found = radix_tree_gang_lookup(&nfsi->nfs_page_tree, + found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&pgvec[0], idx_start, - NFS_SCAN_MAXENTRIES); + NFS_SCAN_MAXENTRIES, tag); if (found <= 0) break; for (i = 0; i < found; i++) { @@ -417,15 +420,18 @@ int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, if (req->wb_index > idx_end) goto out; idx_start = req->wb_index + 1; - if (req->wb_list_head != head) - continue; - if (nfs_set_page_writeback_locked(req)) { + if (nfs_set_page_tag_locked(req)) { nfs_list_remove_request(req); + radix_tree_tag_clear(&nfsi->nfs_page_tree, + req->wb_index, tag); nfs_list_add_request(req, dst); res++; + if (res == INT_MAX) + goto out; } } - + /* for latency reduction */ + cond_resched_lock(&nfsi->vfs_inode.i_lock); } out: return res; @@ -436,7 +442,7 @@ int __init nfs_init_nfspagecache(void) nfs_page_cachep = kmem_cache_create("nfs_page", sizeof(struct nfs_page), 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (nfs_page_cachep == NULL) return -ENOMEM; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 7be0ee2..845cdde 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -272,14 +272,14 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, static int nfs_proc_remove(struct inode *dir, struct qstr *name) { - struct nfs_diropargs arg = { - .fh = NFS_FH(dir), - .name = name->name, - .len = name->len + struct nfs_removeargs arg = { + .fh = NFS_FH(dir), + .name.len = name->len, + .name.name = name->name, }; - struct rpc_message msg = { - .rpc_proc = &nfs_procedures[NFSPROC_REMOVE], - .rpc_argp = &arg, + struct rpc_message msg = { + .rpc_proc = &nfs_procedures[NFSPROC_REMOVE], + .rpc_argp = &arg, }; int status; @@ -291,32 +291,16 @@ nfs_proc_remove(struct inode *dir, struct qstr *name) return status; } -static int -nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +static void +nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) { - struct nfs_diropargs *arg; - - arg = kmalloc(sizeof(*arg), GFP_KERNEL); - if (!arg) - return -ENOMEM; - arg->fh = NFS_FH(dir->d_inode); - arg->name = name->name; - arg->len = name->len; msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; - msg->rpc_argp = arg; - return 0; } -static int -nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task) +static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) { - struct rpc_message *msg = &task->tk_msg; - - if (msg->rpc_argp) { - nfs_mark_for_revalidate(dir->d_inode); - kfree(msg->rpc_argp); - } - return 0; + nfs_mark_for_revalidate(dir); + return 1; } static int diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 7bd7cb9..19e0563 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -145,8 +145,8 @@ static void nfs_readpage_release(struct nfs_page *req) unlock_page(req->wb_page); dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), + req->wb_context->path.dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); nfs_clear_request(req); @@ -164,7 +164,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, int flags; data->req = req; - data->inode = inode = req->wb_context->dentry->d_inode; + data->inode = inode = req->wb_context->path.dentry->d_inode; data->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); @@ -483,17 +483,19 @@ int nfs_readpage(struct file *file, struct page *page) */ error = nfs_wb_page(inode, page); if (error) - goto out_error; + goto out_unlock; + if (PageUptodate(page)) + goto out_unlock; error = -ESTALE; if (NFS_STALE(inode)) - goto out_error; + goto out_unlock; if (file == NULL) { error = -EBADF; ctx = nfs_find_open_context(inode, NULL, FMODE_READ); if (ctx == NULL) - goto out_error; + goto out_unlock; } else ctx = get_nfs_open_context((struct nfs_open_context *) file->private_data); @@ -502,8 +504,7 @@ int nfs_readpage(struct file *file, struct page *page) put_nfs_open_context(ctx); return error; - -out_error: +out_unlock: unlock_page(page); return error; } @@ -520,21 +521,32 @@ readpage_async_filler(void *data, struct page *page) struct inode *inode = page->mapping->host; struct nfs_page *new; unsigned int len; + int error; + + error = nfs_wb_page(inode, page); + if (error) + goto out_unlock; + if (PageUptodate(page)) + goto out_unlock; - nfs_wb_page(inode, page); len = nfs_page_length(page); if (len == 0) return nfs_return_empty_page(page); + new = nfs_create_request(desc->ctx, inode, page, 0, len); - if (IS_ERR(new)) { - SetPageError(page); - unlock_page(page); - return PTR_ERR(new); - } + if (IS_ERR(new)) + goto out_error; + if (len < PAGE_CACHE_SIZE) zero_user_page(page, len, PAGE_CACHE_SIZE - len, KM_USER0); nfs_pageio_add_request(desc->pgio, new); return 0; +out_error: + error = PTR_ERR(new); + SetPageError(page); +out_unlock: + unlock_page(page); + return error; } int nfs_readpages(struct file *filp, struct address_space *mapping, @@ -586,7 +598,7 @@ int __init nfs_init_readpagecache(void) nfs_rdata_cachep = kmem_cache_create("nfs_read_data", sizeof(struct nfs_read_data), 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (nfs_rdata_cachep == NULL) return -ENOMEM; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index ca20d3c..b2a851c 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -45,6 +45,7 @@ #include <linux/inet.h> #include <linux/nfs_xdr.h> #include <linux/magic.h> +#include <linux/parser.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -57,6 +58,167 @@ #define NFSDBG_FACILITY NFSDBG_VFS + +struct nfs_parsed_mount_data { + int flags; + int rsize, wsize; + int timeo, retrans; + int acregmin, acregmax, + acdirmin, acdirmax; + int namlen; + unsigned int bsize; + unsigned int auth_flavor_len; + rpc_authflavor_t auth_flavors[1]; + char *client_address; + + struct { + struct sockaddr_in address; + unsigned int program; + unsigned int version; + unsigned short port; + int protocol; + } mount_server; + + struct { + struct sockaddr_in address; + char *hostname; + char *export_path; + unsigned int program; + int protocol; + } nfs_server; +}; + +enum { + /* Mount options that take no arguments */ + Opt_soft, Opt_hard, + Opt_intr, Opt_nointr, + Opt_posix, Opt_noposix, + Opt_cto, Opt_nocto, + Opt_ac, Opt_noac, + Opt_lock, Opt_nolock, + Opt_v2, Opt_v3, + Opt_udp, Opt_tcp, + Opt_acl, Opt_noacl, + Opt_rdirplus, Opt_nordirplus, + Opt_sharecache, Opt_nosharecache, + + /* Mount options that take integer arguments */ + Opt_port, + Opt_rsize, Opt_wsize, Opt_bsize, + Opt_timeo, Opt_retrans, + Opt_acregmin, Opt_acregmax, + Opt_acdirmin, Opt_acdirmax, + Opt_actimeo, + Opt_namelen, + Opt_mountport, + Opt_mountprog, Opt_mountvers, + Opt_nfsprog, Opt_nfsvers, + + /* Mount options that take string arguments */ + Opt_sec, Opt_proto, Opt_mountproto, + Opt_addr, Opt_mounthost, Opt_clientaddr, + + /* Mount options that are ignored */ + Opt_userspace, Opt_deprecated, + + Opt_err +}; + +static match_table_t nfs_mount_option_tokens = { + { Opt_userspace, "bg" }, + { Opt_userspace, "fg" }, + { Opt_soft, "soft" }, + { Opt_hard, "hard" }, + { Opt_intr, "intr" }, + { Opt_nointr, "nointr" }, + { Opt_posix, "posix" }, + { Opt_noposix, "noposix" }, + { Opt_cto, "cto" }, + { Opt_nocto, "nocto" }, + { Opt_ac, "ac" }, + { Opt_noac, "noac" }, + { Opt_lock, "lock" }, + { Opt_nolock, "nolock" }, + { Opt_v2, "v2" }, + { Opt_v3, "v3" }, + { Opt_udp, "udp" }, + { Opt_tcp, "tcp" }, + { Opt_acl, "acl" }, + { Opt_noacl, "noacl" }, + { Opt_rdirplus, "rdirplus" }, + { Opt_nordirplus, "nordirplus" }, + { Opt_sharecache, "sharecache" }, + { Opt_nosharecache, "nosharecache" }, + + { Opt_port, "port=%u" }, + { Opt_rsize, "rsize=%u" }, + { Opt_wsize, "wsize=%u" }, + { Opt_bsize, "bsize=%u" }, + { Opt_timeo, "timeo=%u" }, + { Opt_retrans, "retrans=%u" }, + { Opt_acregmin, "acregmin=%u" }, + { Opt_acregmax, "acregmax=%u" }, + { Opt_acdirmin, "acdirmin=%u" }, + { Opt_acdirmax, "acdirmax=%u" }, + { Opt_actimeo, "actimeo=%u" }, + { Opt_userspace, "retry=%u" }, + { Opt_namelen, "namlen=%u" }, + { Opt_mountport, "mountport=%u" }, + { Opt_mountprog, "mountprog=%u" }, + { Opt_mountvers, "mountvers=%u" }, + { Opt_nfsprog, "nfsprog=%u" }, + { Opt_nfsvers, "nfsvers=%u" }, + { Opt_nfsvers, "vers=%u" }, + + { Opt_sec, "sec=%s" }, + { Opt_proto, "proto=%s" }, + { Opt_mountproto, "mountproto=%s" }, + { Opt_addr, "addr=%s" }, + { Opt_clientaddr, "clientaddr=%s" }, + { Opt_mounthost, "mounthost=%s" }, + + { Opt_err, NULL } +}; + +enum { + Opt_xprt_udp, Opt_xprt_tcp, + + Opt_xprt_err +}; + +static match_table_t nfs_xprt_protocol_tokens = { + { Opt_xprt_udp, "udp" }, + { Opt_xprt_tcp, "tcp" }, + + { Opt_xprt_err, NULL } +}; + +enum { + Opt_sec_none, Opt_sec_sys, + Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p, + Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp, + Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp, + + Opt_sec_err +}; + +static match_table_t nfs_secflavor_tokens = { + { Opt_sec_none, "none" }, + { Opt_sec_none, "null" }, + { Opt_sec_sys, "sys" }, + + { Opt_sec_krb5, "krb5" }, + { Opt_sec_krb5i, "krb5i" }, + { Opt_sec_krb5p, "krb5p" }, + + { Opt_sec_lkey, "lkey" }, + { Opt_sec_lkeyi, "lkeyi" }, + { Opt_sec_lkeyp, "lkeyp" }, + + { Opt_sec_err, NULL } +}; + + static void nfs_umount_begin(struct vfsmount *, int); static int nfs_statfs(struct dentry *, struct kstatfs *); static int nfs_show_options(struct seq_file *, struct vfsmount *); @@ -138,7 +300,10 @@ static const struct super_operations nfs4_sops = { }; #endif -static struct shrinker *acl_shrinker; +static struct shrinker acl_shrinker = { + .shrink = nfs_access_cache_shrinker, + .seeks = DEFAULT_SEEKS, +}; /* * Register the NFS filesystems @@ -159,7 +324,7 @@ int __init register_nfs_fs(void) if (ret < 0) goto error_2; #endif - acl_shrinker = set_shrinker(DEFAULT_SEEKS, nfs_access_cache_shrinker); + register_shrinker(&acl_shrinker); return 0; #ifdef CONFIG_NFS_V4 @@ -177,8 +342,7 @@ error_0: */ void __exit unregister_nfs_fs(void) { - if (acl_shrinker != NULL) - remove_shrinker(acl_shrinker); + unregister_shrinker(&acl_shrinker); #ifdef CONFIG_NFS_V4 unregister_filesystem(&nfs4_fs_type); nfs_unregister_sysctl(); @@ -263,11 +427,11 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour) { RPC_AUTH_GSS_SPKM, "spkm" }, { RPC_AUTH_GSS_SPKMI, "spkmi" }, { RPC_AUTH_GSS_SPKMP, "spkmp" }, - { -1, "unknown" } + { UINT_MAX, "unknown" } }; int i; - for (i=0; sec_flavours[i].flavour != -1; i++) { + for (i = 0; sec_flavours[i].flavour != UINT_MAX; i++) { if (sec_flavours[i].flavour == flavour) break; } @@ -291,6 +455,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, { NFS_MOUNT_NONLM, ",nolock", "" }, { NFS_MOUNT_NOACL, ",noacl", "" }, { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" }, + { NFS_MOUNT_UNSHARED, ",nosharecache", ""}, { 0, NULL, NULL } }; const struct proc_nfs_info *nfs_infop; @@ -430,87 +595,641 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) */ static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) { + struct nfs_server *server = NFS_SB(vfsmnt->mnt_sb); + struct rpc_clnt *rpc; + shrink_submounts(vfsmnt, &nfs_automount_list); + + if (!(flags & MNT_FORCE)) + return; + /* -EIO all pending I/O */ + rpc = server->client_acl; + if (!IS_ERR(rpc)) + rpc_killall_tasks(rpc); + rpc = server->client; + if (!IS_ERR(rpc)) + rpc_killall_tasks(rpc); } /* - * Validate the NFS2/NFS3 mount data - * - fills in the mount root filehandle + * Sanity-check a server address provided by the mount command */ -static int nfs_validate_mount_data(struct nfs_mount_data *data, - struct nfs_fh *mntfh) +static int nfs_verify_server_address(struct sockaddr *addr) { - if (data == NULL) { - dprintk("%s: missing data argument\n", __FUNCTION__); - return -EINVAL; + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *sa = (struct sockaddr_in *) addr; + if (sa->sin_addr.s_addr != INADDR_ANY) + return 1; + break; + } } - if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { - dprintk("%s: bad mount version\n", __FUNCTION__); - return -EINVAL; + return 0; +} + +/* + * Error-check and convert a string of mount options from user space into + * a data structure + */ +static int nfs_parse_mount_options(char *raw, + struct nfs_parsed_mount_data *mnt) +{ + char *p, *string; + + if (!raw) { + dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); + return 1; } + dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw); - switch (data->version) { - case 1: - data->namlen = 0; - case 2: - data->bsize = 0; - case 3: - if (data->flags & NFS_MOUNT_VER3) { - dprintk("%s: mount structure version %d does not support NFSv3\n", - __FUNCTION__, - data->version); - return -EINVAL; + while ((p = strsep(&raw, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int option, token; + + if (!*p) + continue; + + dfprintk(MOUNT, "NFS: parsing nfs mount option '%s'\n", p); + + token = match_token(p, nfs_mount_option_tokens, args); + switch (token) { + case Opt_soft: + mnt->flags |= NFS_MOUNT_SOFT; + break; + case Opt_hard: + mnt->flags &= ~NFS_MOUNT_SOFT; + break; + case Opt_intr: + mnt->flags |= NFS_MOUNT_INTR; + break; + case Opt_nointr: + mnt->flags &= ~NFS_MOUNT_INTR; + break; + case Opt_posix: + mnt->flags |= NFS_MOUNT_POSIX; + break; + case Opt_noposix: + mnt->flags &= ~NFS_MOUNT_POSIX; + break; + case Opt_cto: + mnt->flags &= ~NFS_MOUNT_NOCTO; + break; + case Opt_nocto: + mnt->flags |= NFS_MOUNT_NOCTO; + break; + case Opt_ac: + mnt->flags &= ~NFS_MOUNT_NOAC; + break; + case Opt_noac: + mnt->flags |= NFS_MOUNT_NOAC; + break; + case Opt_lock: + mnt->flags &= ~NFS_MOUNT_NONLM; + break; + case Opt_nolock: + mnt->flags |= NFS_MOUNT_NONLM; + break; + case Opt_v2: + mnt->flags &= ~NFS_MOUNT_VER3; + break; + case Opt_v3: + mnt->flags |= NFS_MOUNT_VER3; + break; + case Opt_udp: + mnt->flags &= ~NFS_MOUNT_TCP; + mnt->nfs_server.protocol = IPPROTO_UDP; + mnt->timeo = 7; + mnt->retrans = 5; + break; + case Opt_tcp: + mnt->flags |= NFS_MOUNT_TCP; + mnt->nfs_server.protocol = IPPROTO_TCP; + mnt->timeo = 600; + mnt->retrans = 2; + break; + case Opt_acl: + mnt->flags &= ~NFS_MOUNT_NOACL; + break; + case Opt_noacl: + mnt->flags |= NFS_MOUNT_NOACL; + break; + case Opt_rdirplus: + mnt->flags &= ~NFS_MOUNT_NORDIRPLUS; + break; + case Opt_nordirplus: + mnt->flags |= NFS_MOUNT_NORDIRPLUS; + break; + case Opt_sharecache: + mnt->flags &= ~NFS_MOUNT_UNSHARED; + break; + case Opt_nosharecache: + mnt->flags |= NFS_MOUNT_UNSHARED; + break; + + case Opt_port: + if (match_int(args, &option)) + return 0; + if (option < 0 || option > 65535) + return 0; + mnt->nfs_server.address.sin_port = htons(option); + break; + case Opt_rsize: + if (match_int(args, &mnt->rsize)) + return 0; + break; + case Opt_wsize: + if (match_int(args, &mnt->wsize)) + return 0; + break; + case Opt_bsize: + if (match_int(args, &option)) + return 0; + if (option < 0) + return 0; + mnt->bsize = option; + break; + case Opt_timeo: + if (match_int(args, &mnt->timeo)) + return 0; + break; + case Opt_retrans: + if (match_int(args, &mnt->retrans)) + return 0; + break; + case Opt_acregmin: + if (match_int(args, &mnt->acregmin)) + return 0; + break; + case Opt_acregmax: + if (match_int(args, &mnt->acregmax)) + return 0; + break; + case Opt_acdirmin: + if (match_int(args, &mnt->acdirmin)) + return 0; + break; + case Opt_acdirmax: + if (match_int(args, &mnt->acdirmax)) + return 0; + break; + case Opt_actimeo: + if (match_int(args, &option)) + return 0; + if (option < 0) + return 0; + mnt->acregmin = + mnt->acregmax = + mnt->acdirmin = + mnt->acdirmax = option; + break; + case Opt_namelen: + if (match_int(args, &mnt->namlen)) + return 0; + break; + case Opt_mountport: + if (match_int(args, &option)) + return 0; + if (option < 0 || option > 65535) + return 0; + mnt->mount_server.port = option; + break; + case Opt_mountprog: + if (match_int(args, &option)) + return 0; + if (option < 0) + return 0; + mnt->mount_server.program = option; + break; + case Opt_mountvers: + if (match_int(args, &option)) + return 0; + if (option < 0) + return 0; + mnt->mount_server.version = option; + break; + case Opt_nfsprog: + if (match_int(args, &option)) + return 0; + if (option < 0) + return 0; + mnt->nfs_server.program = option; + break; + case Opt_nfsvers: + if (match_int(args, &option)) + return 0; + switch (option) { + case 2: + mnt->flags &= ~NFS_MOUNT_VER3; + break; + case 3: + mnt->flags |= NFS_MOUNT_VER3; + break; + default: + goto out_unrec_vers; } - data->root.size = NFS2_FHSIZE; - memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); - case 4: - if (data->flags & NFS_MOUNT_SECFLAVOUR) { - dprintk("%s: mount structure version %d does not support strong security\n", - __FUNCTION__, - data->version); - return -EINVAL; + break; + + case Opt_sec: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + token = match_token(string, nfs_secflavor_tokens, args); + kfree(string); + + /* + * The flags setting is for v2/v3. The flavor_len + * setting is for v4. v2/v3 also need to know the + * difference between NULL and UNIX. + */ + switch (token) { + case Opt_sec_none: + mnt->flags &= ~NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 0; + mnt->auth_flavors[0] = RPC_AUTH_NULL; + break; + case Opt_sec_sys: + mnt->flags &= ~NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 0; + mnt->auth_flavors[0] = RPC_AUTH_UNIX; + break; + case Opt_sec_krb5: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5; + break; + case Opt_sec_krb5i: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I; + break; + case Opt_sec_krb5p: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P; + break; + case Opt_sec_lkey: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY; + break; + case Opt_sec_lkeyi: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI; + break; + case Opt_sec_lkeyp: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP; + break; + case Opt_sec_spkm: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM; + break; + case Opt_sec_spkmi: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI; + break; + case Opt_sec_spkmp: + mnt->flags |= NFS_MOUNT_SECFLAVOUR; + mnt->auth_flavor_len = 1; + mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP; + break; + default: + goto out_unrec_sec; } - case 5: - memset(data->context, 0, sizeof(data->context)); - } + break; + case Opt_proto: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + token = match_token(string, + nfs_xprt_protocol_tokens, args); + kfree(string); + + switch (token) { + case Opt_udp: + mnt->flags &= ~NFS_MOUNT_TCP; + mnt->nfs_server.protocol = IPPROTO_UDP; + mnt->timeo = 7; + mnt->retrans = 5; + break; + case Opt_tcp: + mnt->flags |= NFS_MOUNT_TCP; + mnt->nfs_server.protocol = IPPROTO_TCP; + mnt->timeo = 600; + mnt->retrans = 2; + break; + default: + goto out_unrec_xprt; + } + break; + case Opt_mountproto: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + token = match_token(string, + nfs_xprt_protocol_tokens, args); + kfree(string); + + switch (token) { + case Opt_udp: + mnt->mount_server.protocol = IPPROTO_UDP; + break; + case Opt_tcp: + mnt->mount_server.protocol = IPPROTO_TCP; + break; + default: + goto out_unrec_xprt; + } + break; + case Opt_addr: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + mnt->nfs_server.address.sin_family = AF_INET; + mnt->nfs_server.address.sin_addr.s_addr = + in_aton(string); + kfree(string); + break; + case Opt_clientaddr: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + mnt->client_address = string; + break; + case Opt_mounthost: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + mnt->mount_server.address.sin_family = AF_INET; + mnt->mount_server.address.sin_addr.s_addr = + in_aton(string); + kfree(string); + break; - /* Set the pseudoflavor */ - if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) - data->pseudoflavor = RPC_AUTH_UNIX; + case Opt_userspace: + case Opt_deprecated: + break; -#ifndef CONFIG_NFS_V3 - /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ - if (data->flags & NFS_MOUNT_VER3) { - dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); - return -EPROTONOSUPPORT; + default: + goto out_unknown; + } } -#endif /* CONFIG_NFS_V3 */ - /* We now require that the mount process passes the remote address */ - if (data->addr.sin_addr.s_addr == INADDR_ANY) { - dprintk("%s: mount program didn't pass remote address!\n", - __FUNCTION__); - return -EINVAL; + return 1; + +out_nomem: + printk(KERN_INFO "NFS: not enough memory to parse option\n"); + return 0; + +out_unrec_vers: + printk(KERN_INFO "NFS: unrecognized NFS version number\n"); + return 0; + +out_unrec_xprt: + printk(KERN_INFO "NFS: unrecognized transport protocol\n"); + return 0; + +out_unrec_sec: + printk(KERN_INFO "NFS: unrecognized security flavor\n"); + return 0; + +out_unknown: + printk(KERN_INFO "NFS: unknown mount option: %s\n", p); + return 0; +} + +/* + * Use the remote server's MOUNT service to request the NFS file handle + * corresponding to the provided path. + */ +static int nfs_try_mount(struct nfs_parsed_mount_data *args, + struct nfs_fh *root_fh) +{ + struct sockaddr_in sin; + int status; + + if (args->mount_server.version == 0) { + if (args->flags & NFS_MOUNT_VER3) + args->mount_server.version = NFS_MNT3_VERSION; + else + args->mount_server.version = NFS_MNT_VERSION; } - /* Prepare the root filehandle */ - if (data->flags & NFS_MOUNT_VER3) - mntfh->size = data->root.size; + /* + * Construct the mount server's address. + */ + if (args->mount_server.address.sin_addr.s_addr != INADDR_ANY) + sin = args->mount_server.address; else - mntfh->size = NFS2_FHSIZE; + sin = args->nfs_server.address; + if (args->mount_server.port == 0) { + status = rpcb_getport_sync(&sin, + args->mount_server.program, + args->mount_server.version, + args->mount_server.protocol); + if (status < 0) + goto out_err; + sin.sin_port = htons(status); + } else + sin.sin_port = htons(args->mount_server.port); + + /* + * Now ask the mount server to map our export path + * to a file handle. + */ + status = nfs_mount((struct sockaddr *) &sin, + sizeof(sin), + args->nfs_server.hostname, + args->nfs_server.export_path, + args->mount_server.version, + args->mount_server.protocol, + root_fh); + if (status < 0) + goto out_err; + + return status; - if (mntfh->size > sizeof(mntfh->data)) { - dprintk("%s: invalid root filehandle\n", __FUNCTION__); - return -EINVAL; +out_err: + dfprintk(MOUNT, "NFS: unable to contact server on host " + NIPQUAD_FMT "\n", NIPQUAD(sin.sin_addr.s_addr)); + return status; +} + +/* + * Validate the NFS2/NFS3 mount data + * - fills in the mount root filehandle + * + * For option strings, user space handles the following behaviors: + * + * + DNS: mapping server host name to IP address ("addr=" option) + * + * + failure mode: how to behave if a mount request can't be handled + * immediately ("fg/bg" option) + * + * + retry: how often to retry a mount request ("retry=" option) + * + * + breaking back: trying proto=udp after proto=tcp, v2 after v3, + * mountproto=tcp after mountproto=udp, and so on + * + * XXX: as far as I can tell, changing the NFS program number is not + * supported in the NFS client. + */ +static int nfs_validate_mount_data(struct nfs_mount_data **options, + struct nfs_fh *mntfh, + const char *dev_name) +{ + struct nfs_mount_data *data = *options; + + if (data == NULL) + goto out_no_data; + + switch (data->version) { + case 1: + data->namlen = 0; + case 2: + data->bsize = 0; + case 3: + if (data->flags & NFS_MOUNT_VER3) + goto out_no_v3; + data->root.size = NFS2_FHSIZE; + memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); + case 4: + if (data->flags & NFS_MOUNT_SECFLAVOUR) + goto out_no_sec; + case 5: + memset(data->context, 0, sizeof(data->context)); + case 6: + if (data->flags & NFS_MOUNT_VER3) + mntfh->size = data->root.size; + else + mntfh->size = NFS2_FHSIZE; + + if (mntfh->size > sizeof(mntfh->data)) + goto out_invalid_fh; + + memcpy(mntfh->data, data->root.data, mntfh->size); + if (mntfh->size < sizeof(mntfh->data)) + memset(mntfh->data + mntfh->size, 0, + sizeof(mntfh->data) - mntfh->size); + break; + default: { + unsigned int len; + char *c; + int status; + struct nfs_parsed_mount_data args = { + .flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP), + .rsize = NFS_MAX_FILE_IO_SIZE, + .wsize = NFS_MAX_FILE_IO_SIZE, + .timeo = 600, + .retrans = 2, + .acregmin = 3, + .acregmax = 60, + .acdirmin = 30, + .acdirmax = 60, + .mount_server.protocol = IPPROTO_UDP, + .mount_server.program = NFS_MNT_PROGRAM, + .nfs_server.protocol = IPPROTO_TCP, + .nfs_server.program = NFS_PROGRAM, + }; + + if (nfs_parse_mount_options((char *) *options, &args) == 0) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + /* + * NB: after this point, caller will free "data" + * if we return an error + */ + *options = data; + + c = strchr(dev_name, ':'); + if (c == NULL) + return -EINVAL; + len = c - dev_name - 1; + if (len > sizeof(data->hostname)) + return -EINVAL; + strncpy(data->hostname, dev_name, len); + args.nfs_server.hostname = data->hostname; + + c++; + if (strlen(c) > NFS_MAXPATHLEN) + return -EINVAL; + args.nfs_server.export_path = c; + + status = nfs_try_mount(&args, mntfh); + if (status) + return -EINVAL; + + /* + * Translate to nfs_mount_data, which nfs_fill_super + * can deal with. + */ + data->version = 6; + data->flags = args.flags; + data->rsize = args.rsize; + data->wsize = args.wsize; + data->timeo = args.timeo; + data->retrans = args.retrans; + data->acregmin = args.acregmin; + data->acregmax = args.acregmax; + data->acdirmin = args.acdirmin; + data->acdirmax = args.acdirmax; + data->addr = args.nfs_server.address; + data->namlen = args.namlen; + data->bsize = args.bsize; + data->pseudoflavor = args.auth_flavors[0]; + + break; + } } - memcpy(mntfh->data, data->root.data, mntfh->size); - if (mntfh->size < sizeof(mntfh->data)) - memset(mntfh->data + mntfh->size, 0, - sizeof(mntfh->data) - mntfh->size); + if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) + data->pseudoflavor = RPC_AUTH_UNIX; + +#ifndef CONFIG_NFS_V3 + if (data->flags & NFS_MOUNT_VER3) + goto out_v3_not_compiled; +#endif /* !CONFIG_NFS_V3 */ + + if (!nfs_verify_server_address((struct sockaddr *) &data->addr)) + goto out_no_address; return 0; + +out_no_data: + dfprintk(MOUNT, "NFS: mount program didn't pass any mount data\n"); + return -EINVAL; + +out_no_v3: + dfprintk(MOUNT, "NFS: nfs_mount_data version %d does not support v3\n", + data->version); + return -EINVAL; + +out_no_sec: + dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n"); + return -EINVAL; + +#ifndef CONFIG_NFS_V3 +out_v3_not_compiled: + dfprintk(MOUNT, "NFS: NFSv3 is not compiled into kernel\n"); + return -EPROTONOSUPPORT; +#endif /* !CONFIG_NFS_V3 */ + +out_no_address: + dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n"); + return -EINVAL; + +out_invalid_fh: + dfprintk(MOUNT, "NFS: invalid root filehandle\n"); + return -EINVAL; } /* @@ -600,13 +1319,51 @@ static int nfs_compare_super(struct super_block *sb, void *data) { struct nfs_server *server = data, *old = NFS_SB(sb); - if (old->nfs_client != server->nfs_client) + if (memcmp(&old->nfs_client->cl_addr, + &server->nfs_client->cl_addr, + sizeof(old->nfs_client->cl_addr)) != 0) + return 0; + /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ + if (old->flags & NFS_MOUNT_UNSHARED) return 0; if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) return 0; return 1; } +#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) + +static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) +{ + const struct nfs_server *a = s->s_fs_info; + const struct rpc_clnt *clnt_a = a->client; + const struct rpc_clnt *clnt_b = b->client; + + if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK)) + goto Ebusy; + if (a->nfs_client != b->nfs_client) + goto Ebusy; + if (a->flags != b->flags) + goto Ebusy; + if (a->wsize != b->wsize) + goto Ebusy; + if (a->rsize != b->rsize) + goto Ebusy; + if (a->acregmin != b->acregmin) + goto Ebusy; + if (a->acregmax != b->acregmax) + goto Ebusy; + if (a->acdirmin != b->acdirmin) + goto Ebusy; + if (a->acdirmax != b->acdirmax) + goto Ebusy; + if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) + goto Ebusy; + return 0; +Ebusy: + return -EBUSY; +} + static int nfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) { @@ -615,30 +1372,37 @@ static int nfs_get_sb(struct file_system_type *fs_type, struct nfs_fh mntfh; struct nfs_mount_data *data = raw_data; struct dentry *mntroot; + int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int error; /* Validate the mount data */ - error = nfs_validate_mount_data(data, &mntfh); + error = nfs_validate_mount_data(&data, &mntfh, dev_name); if (error < 0) - return error; + goto out; /* Get a volume representation */ server = nfs_create_server(data, &mntfh); if (IS_ERR(server)) { error = PTR_ERR(server); - goto out_err_noserver; + goto out; } + if (server->flags & NFS_MOUNT_UNSHARED) + compare_super = NULL; + /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, nfs_compare_super, nfs_set_super, server); + s = sget(fs_type, compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; } if (s->s_fs_info != server) { + error = nfs_compare_mount_options(s, server, flags); nfs_free_server(server); server = NULL; + if (error < 0) + goto error_splat_super; } if (!s->s_root) { @@ -656,17 +1420,21 @@ static int nfs_get_sb(struct file_system_type *fs_type, s->s_flags |= MS_ACTIVE; mnt->mnt_sb = s; mnt->mnt_root = mntroot; - return 0; + error = 0; + +out: + if (data != raw_data) + kfree(data); + return error; out_err_nosb: nfs_free_server(server); -out_err_noserver: - return error; + goto out; error_splat_super: up_write(&s->s_umount); deactivate_super(s); - return error; + goto out; } /* @@ -691,6 +1459,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, struct super_block *s; struct nfs_server *server; struct dentry *mntroot; + int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int error; dprintk("--> nfs_xdev_get_sb()\n"); @@ -702,16 +1471,22 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, goto out_err_noserver; } + if (server->flags & NFS_MOUNT_UNSHARED) + compare_super = NULL; + /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; } if (s->s_fs_info != server) { + error = nfs_compare_mount_options(s, server, flags); nfs_free_server(server); server = NULL; + if (error < 0) + goto error_splat_super; } if (!s->s_root) { @@ -772,25 +1547,171 @@ static void nfs4_fill_super(struct super_block *sb) nfs_initialise_sb(sb); } -static void *nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) +/* + * Validate NFSv4 mount options + */ +static int nfs4_validate_mount_data(struct nfs4_mount_data **options, + const char *dev_name, + struct sockaddr_in *addr, + rpc_authflavor_t *authflavour, + char **hostname, + char **mntpath, + char **ip_addr) { - void *p = NULL; - - if (!src->len) - return ERR_PTR(-EINVAL); - if (src->len < maxlen) - maxlen = src->len; - if (dst == NULL) { - p = dst = kmalloc(maxlen + 1, GFP_KERNEL); - if (p == NULL) - return ERR_PTR(-ENOMEM); - } - if (copy_from_user(dst, src->data, maxlen)) { - kfree(p); - return ERR_PTR(-EFAULT); + struct nfs4_mount_data *data = *options; + char *c; + + if (data == NULL) + goto out_no_data; + + switch (data->version) { + case 1: + if (data->host_addrlen != sizeof(*addr)) + goto out_no_address; + if (copy_from_user(addr, data->host_addr, sizeof(*addr))) + return -EFAULT; + if (addr->sin_port == 0) + addr->sin_port = htons(NFS_PORT); + if (!nfs_verify_server_address((struct sockaddr *) addr)) + goto out_no_address; + + switch (data->auth_flavourlen) { + case 0: + *authflavour = RPC_AUTH_UNIX; + break; + case 1: + if (copy_from_user(authflavour, data->auth_flavours, + sizeof(*authflavour))) + return -EFAULT; + break; + default: + goto out_inval_auth; + } + + c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN); + if (IS_ERR(c)) + return PTR_ERR(c); + *hostname = c; + + c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN); + if (IS_ERR(c)) + return PTR_ERR(c); + *mntpath = c; + dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *mntpath); + + c = strndup_user(data->client_addr.data, 16); + if (IS_ERR(c)) + return PTR_ERR(c); + *ip_addr = c; + + break; + default: { + unsigned int len; + struct nfs_parsed_mount_data args = { + .rsize = NFS_MAX_FILE_IO_SIZE, + .wsize = NFS_MAX_FILE_IO_SIZE, + .timeo = 600, + .retrans = 2, + .acregmin = 3, + .acregmax = 60, + .acdirmin = 30, + .acdirmax = 60, + .nfs_server.protocol = IPPROTO_TCP, + }; + + if (nfs_parse_mount_options((char *) *options, &args) == 0) + return -EINVAL; + + if (!nfs_verify_server_address((struct sockaddr *) + &args.nfs_server.address)) + return -EINVAL; + *addr = args.nfs_server.address; + + switch (args.auth_flavor_len) { + case 0: + *authflavour = RPC_AUTH_UNIX; + break; + case 1: + *authflavour = (rpc_authflavor_t) args.auth_flavors[0]; + break; + default: + goto out_inval_auth; + } + + /* + * Translate to nfs4_mount_data, which nfs4_fill_super + * can deal with. + */ + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + *options = data; + + data->version = 1; + data->flags = args.flags & NFS4_MOUNT_FLAGMASK; + data->rsize = args.rsize; + data->wsize = args.wsize; + data->timeo = args.timeo; + data->retrans = args.retrans; + data->acregmin = args.acregmin; + data->acregmax = args.acregmax; + data->acdirmin = args.acdirmin; + data->acdirmax = args.acdirmax; + data->proto = args.nfs_server.protocol; + + /* + * Split "dev_name" into "hostname:mntpath". + */ + c = strchr(dev_name, ':'); + if (c == NULL) + return -EINVAL; + /* while calculating len, pretend ':' is '\0' */ + len = c - dev_name; + if (len > NFS4_MAXNAMLEN) + return -EINVAL; + *hostname = kzalloc(len, GFP_KERNEL); + if (*hostname == NULL) + return -ENOMEM; + strncpy(*hostname, dev_name, len - 1); + + c++; /* step over the ':' */ + len = strlen(c); + if (len > NFS4_MAXPATHLEN) + return -EINVAL; + *mntpath = kzalloc(len + 1, GFP_KERNEL); + if (*mntpath == NULL) + return -ENOMEM; + strncpy(*mntpath, c, len); + + dprintk("MNTPATH: %s\n", *mntpath); + + if (args.client_address == NULL) + goto out_no_client_address; + + *ip_addr = args.client_address; + + break; + } } - dst[maxlen] = '\0'; - return dst; + + return 0; + +out_no_data: + dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n"); + return -EINVAL; + +out_inval_auth: + dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n", + data->auth_flavourlen); + return -EINVAL; + +out_no_address: + dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); + return -EINVAL; + +out_no_client_address: + dfprintk(MOUNT, "NFS4: mount program didn't pass callback address\n"); + return -EINVAL; } /* @@ -806,81 +1727,29 @@ static int nfs4_get_sb(struct file_system_type *fs_type, rpc_authflavor_t authflavour; struct nfs_fh mntfh; struct dentry *mntroot; - char *mntpath = NULL, *hostname = NULL, ip_addr[16]; - void *p; + char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL; + int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int error; - if (data == NULL) { - dprintk("%s: missing data argument\n", __FUNCTION__); - return -EINVAL; - } - if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) { - dprintk("%s: bad mount version\n", __FUNCTION__); - return -EINVAL; - } - - /* We now require that the mount process passes the remote address */ - if (data->host_addrlen != sizeof(addr)) - return -EINVAL; - - if (copy_from_user(&addr, data->host_addr, sizeof(addr))) - return -EFAULT; - - if (addr.sin_family != AF_INET || - addr.sin_addr.s_addr == INADDR_ANY - ) { - dprintk("%s: mount program didn't pass remote IP address!\n", - __FUNCTION__); - return -EINVAL; - } - /* RFC3530: The default port for NFS is 2049 */ - if (addr.sin_port == 0) - addr.sin_port = htons(NFS_PORT); - - /* Grab the authentication type */ - authflavour = RPC_AUTH_UNIX; - if (data->auth_flavourlen != 0) { - if (data->auth_flavourlen != 1) { - dprintk("%s: Invalid number of RPC auth flavours %d.\n", - __FUNCTION__, data->auth_flavourlen); - error = -EINVAL; - goto out_err_noserver; - } - - if (copy_from_user(&authflavour, data->auth_flavours, - sizeof(authflavour))) { - error = -EFAULT; - goto out_err_noserver; - } - } - - p = nfs_copy_user_string(NULL, &data->hostname, 256); - if (IS_ERR(p)) - goto out_err; - hostname = p; - - p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); - if (IS_ERR(p)) - goto out_err; - mntpath = p; - - dprintk("MNTPATH: %s\n", mntpath); - - p = nfs_copy_user_string(ip_addr, &data->client_addr, - sizeof(ip_addr) - 1); - if (IS_ERR(p)) - goto out_err; + /* Validate the mount data */ + error = nfs4_validate_mount_data(&data, dev_name, &addr, &authflavour, + &hostname, &mntpath, &ip_addr); + if (error < 0) + goto out; /* Get a volume representation */ server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr, authflavour, &mntfh); if (IS_ERR(server)) { error = PTR_ERR(server); - goto out_err_noserver; + goto out; } + if (server->flags & NFS4_MOUNT_UNSHARED) + compare_super = NULL; + /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, nfs_compare_super, nfs_set_super, server); + s = sget(fs_type, compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_free; @@ -906,25 +1775,22 @@ static int nfs4_get_sb(struct file_system_type *fs_type, s->s_flags |= MS_ACTIVE; mnt->mnt_sb = s; mnt->mnt_root = mntroot; + error = 0; + +out: + kfree(ip_addr); kfree(mntpath); kfree(hostname); - return 0; - -out_err: - error = PTR_ERR(p); - goto out_err_noserver; + return error; out_free: nfs_free_server(server); -out_err_noserver: - kfree(mntpath); - kfree(hostname); - return error; + goto out; error_splat_super: up_write(&s->s_umount); deactivate_super(s); - goto out_err_noserver; + goto out; } static void nfs4_kill_super(struct super_block *sb) @@ -949,6 +1815,7 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, struct super_block *s; struct nfs_server *server; struct dentry *mntroot; + int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int error; dprintk("--> nfs4_xdev_get_sb()\n"); @@ -960,8 +1827,11 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, goto out_err_noserver; } + if (server->flags & NFS4_MOUNT_UNSHARED) + compare_super = NULL; + /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; @@ -1016,6 +1886,7 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, struct nfs_server *server; struct dentry *mntroot; struct nfs_fh mntfh; + int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int error; dprintk("--> nfs4_referral_get_sb()\n"); @@ -1027,8 +1898,11 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, goto out_err_noserver; } + if (server->flags & NFS4_MOUNT_UNSHARED) + compare_super = NULL; + /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 0e28189..045ab80 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -3,7 +3,6 @@ * * nfs sillydelete handling * - * NOTE: we rely on holding the BKL for list manipulation protection. */ #include <linux/slab.h> @@ -15,46 +14,23 @@ struct nfs_unlinkdata { - struct nfs_unlinkdata *next; - struct dentry *dir, *dentry; - struct qstr name; - struct rpc_task task; + struct nfs_removeargs args; + struct nfs_removeres res; + struct inode *dir; struct rpc_cred *cred; - unsigned int count; }; -static struct nfs_unlinkdata *nfs_deletes; -static RPC_WAITQ(nfs_delete_queue, "nfs_delete_queue"); - -/** - * nfs_detach_unlinkdata - Remove asynchronous unlink from global list - * @data: pointer to descriptor - */ -static inline void -nfs_detach_unlinkdata(struct nfs_unlinkdata *data) -{ - struct nfs_unlinkdata **q; - - for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { - if (*q == data) { - *q = data->next; - break; - } - } -} - /** - * nfs_put_unlinkdata - release data from a sillydelete operation. + * nfs_free_unlinkdata - release data from a sillydelete operation. * @data: pointer to unlink structure. */ static void -nfs_put_unlinkdata(struct nfs_unlinkdata *data) +nfs_free_unlinkdata(struct nfs_unlinkdata *data) { - if (--data->count == 0) { - nfs_detach_unlinkdata(data); - kfree(data->name.name); - kfree(data); - } + iput(data->dir); + put_rpccred(data->cred); + kfree(data->args.name.name); + kfree(data); } #define NAME_ALLOC_LEN(len) ((len+16) & ~15) @@ -63,50 +39,36 @@ nfs_put_unlinkdata(struct nfs_unlinkdata *data) * @dentry: pointer to dentry * @data: nfs_unlinkdata */ -static inline void -nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) +static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) { char *str; int len = dentry->d_name.len; - str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + str = kmemdup(dentry->d_name.name, NAME_ALLOC_LEN(len), GFP_KERNEL); if (!str) - return; - memcpy(str, dentry->d_name.name, len); - if (!data->name.len) { - data->name.len = len; - data->name.name = str; - } else - kfree(str); + return -ENOMEM; + data->args.name.len = len; + data->args.name.name = str; + return 0; } /** * nfs_async_unlink_init - Initialize the RPC info - * @task: rpc_task of the sillydelete - * - * We delay initializing RPC info until after the call to dentry_iput() - * in order to minimize races against rename(). + * task: rpc_task of the sillydelete */ static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) { - struct nfs_unlinkdata *data = calldata; - struct dentry *dir = data->dir; - struct rpc_message msg = { - .rpc_cred = data->cred, + struct nfs_unlinkdata *data = calldata; + struct inode *dir = data->dir; + struct rpc_message msg = { + .rpc_argp = &data->args, + .rpc_resp = &data->res, + .rpc_cred = data->cred, }; - int status = -ENOENT; - - if (!data->name.len) - goto out_err; - status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); - if (status < 0) - goto out_err; - nfs_begin_data_update(dir->d_inode); + nfs_begin_data_update(dir); + NFS_PROTO(dir)->unlink_setup(&msg, dir); rpc_call_setup(task, &msg, 0); - return; - out_err: - rpc_exit(task, status); } /** @@ -117,19 +79,13 @@ static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) */ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) { - struct nfs_unlinkdata *data = calldata; - struct dentry *dir = data->dir; - struct inode *dir_i; - - if (!dir) - return; - dir_i = dir->d_inode; - nfs_end_data_update(dir_i); - if (NFS_PROTO(dir_i)->unlink_done(dir, task)) - return; - put_rpccred(data->cred); - data->cred = NULL; - dput(dir); + struct nfs_unlinkdata *data = calldata; + struct inode *dir = data->dir; + + if (!NFS_PROTO(dir)->unlink_done(task, dir)) + rpc_restart_call(task); + else + nfs_end_data_update(dir); } /** @@ -142,7 +98,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) static void nfs_async_unlink_release(void *calldata) { struct nfs_unlinkdata *data = calldata; - nfs_put_unlinkdata(data); + nfs_free_unlinkdata(data); } static const struct rpc_call_ops nfs_unlink_ops = { @@ -151,73 +107,94 @@ static const struct rpc_call_ops nfs_unlink_ops = { .rpc_release = nfs_async_unlink_release, }; +static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) +{ + struct rpc_task *task; + struct dentry *parent; + struct inode *dir; + + if (nfs_copy_dname(dentry, data) < 0) + goto out_free; + + parent = dget_parent(dentry); + if (parent == NULL) + goto out_free; + dir = igrab(parent->d_inode); + dput(parent); + if (dir == NULL) + goto out_free; + + data->dir = dir; + data->args.fh = NFS_FH(dir); + nfs_fattr_init(&data->res.dir_attr); + + task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); + if (!IS_ERR(task)) + rpc_put_task(task); + return 1; +out_free: + return 0; +} + /** * nfs_async_unlink - asynchronous unlinking of a file + * @dir: parent directory of dentry * @dentry: dentry to unlink */ int -nfs_async_unlink(struct dentry *dentry) +nfs_async_unlink(struct inode *dir, struct dentry *dentry) { - struct dentry *dir = dentry->d_parent; - struct nfs_unlinkdata *data; - struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); - int status = -ENOMEM; + struct nfs_unlinkdata *data; + int status = -ENOMEM; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) + if (data == NULL) goto out; - data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); if (IS_ERR(data->cred)) { status = PTR_ERR(data->cred); goto out_free; } - data->dir = dget(dir); - data->dentry = dentry; - - data->next = nfs_deletes; - nfs_deletes = data; - data->count = 1; - - rpc_init_task(&data->task, clnt, RPC_TASK_ASYNC, &nfs_unlink_ops, data); + status = -EBUSY; spin_lock(&dentry->d_lock); + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) + goto out_unlock; dentry->d_flags |= DCACHE_NFSFS_RENAMED; + dentry->d_fsdata = data; spin_unlock(&dentry->d_lock); - - rpc_sleep_on(&nfs_delete_queue, &data->task, NULL, NULL); - status = 0; - out: - return status; + return 0; +out_unlock: + spin_unlock(&dentry->d_lock); + put_rpccred(data->cred); out_free: kfree(data); +out: return status; } /** * nfs_complete_unlink - Initialize completion of the sillydelete * @dentry: dentry to delete + * @inode: inode * * Since we're most likely to be called by dentry_iput(), we * only use the dentry to find the sillydelete. We then copy the name * into the qstr. */ void -nfs_complete_unlink(struct dentry *dentry) +nfs_complete_unlink(struct dentry *dentry, struct inode *inode) { - struct nfs_unlinkdata *data; + struct nfs_unlinkdata *data = NULL; - for(data = nfs_deletes; data != NULL; data = data->next) { - if (dentry == data->dentry) - break; - } - if (!data) - return; - data->count++; - nfs_copy_dname(dentry, data); spin_lock(&dentry->d_lock); - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + data = dentry->d_fsdata; + } spin_unlock(&dentry->d_lock); - rpc_wake_up_task(&data->task); - nfs_put_unlinkdata(data); + + if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data))) + nfs_free_unlinkdata(data); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index af344a1..ef97e0c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -117,19 +117,19 @@ static struct nfs_page *nfs_page_find_request_locked(struct page *page) if (PagePrivate(page)) { req = (struct nfs_page *)page_private(page); if (req != NULL) - atomic_inc(&req->wb_count); + kref_get(&req->wb_kref); } return req; } static struct nfs_page *nfs_page_find_request(struct page *page) { + struct inode *inode = page->mapping->host; struct nfs_page *req = NULL; - spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; - spin_lock(req_lock); + spin_lock(&inode->i_lock); req = nfs_page_find_request_locked(page); - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); return req; } @@ -191,8 +191,6 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, } /* Update file length */ nfs_grow_file(page, offset, count); - /* Set the PG_uptodate flag? */ - nfs_mark_uptodate(page, offset, count); nfs_unlock_request(req); return 0; } @@ -253,16 +251,16 @@ static void nfs_end_page_writeback(struct page *page) static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, struct page *page) { + struct inode *inode = page->mapping->host; + struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *req; - struct nfs_inode *nfsi = NFS_I(page->mapping->host); - spinlock_t *req_lock = &nfsi->req_lock; int ret; - spin_lock(req_lock); + spin_lock(&inode->i_lock); for(;;) { req = nfs_page_find_request_locked(page); if (req == NULL) { - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); return 1; } if (nfs_lock_request_dontget(req)) @@ -272,28 +270,28 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, * succeed provided that someone hasn't already marked the * request as dirty (in which case we don't care). */ - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); ret = nfs_wait_on_request(req); nfs_release_request(req); if (ret != 0) return ret; - spin_lock(req_lock); + spin_lock(&inode->i_lock); } if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { /* This request is marked for commit */ - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); nfs_unlock_request(req); nfs_pageio_complete(pgio); return 1; } if (nfs_set_page_writeback(page) != 0) { - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); BUG(); } radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, - NFS_PAGE_TAG_WRITEBACK); + NFS_PAGE_TAG_LOCKED); ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); nfs_pageio_add_request(pgio, req); return ret; } @@ -400,7 +398,7 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) if (PageDirty(req->wb_page)) set_bit(PG_NEED_FLUSH, &req->wb_flags); nfsi->npages++; - atomic_inc(&req->wb_count); + kref_get(&req->wb_kref); return 0; } @@ -409,12 +407,12 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req) */ static void nfs_inode_remove_request(struct nfs_page *req) { - struct inode *inode = req->wb_context->dentry->d_inode; + struct inode *inode = req->wb_context->path.dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); BUG_ON (!NFS_WBACK_BUSY(req)); - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); set_page_private(req->wb_page, 0); ClearPagePrivate(req->wb_page); radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index); @@ -422,11 +420,11 @@ static void nfs_inode_remove_request(struct nfs_page *req) __set_page_dirty_nobuffers(req->wb_page); nfsi->npages--; if (!nfsi->npages) { - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); nfs_end_data_update(inode); iput(inode); } else - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); nfs_clear_request(req); nfs_release_request(req); } @@ -457,14 +455,16 @@ nfs_dirty_request(struct nfs_page *req) static void nfs_mark_request_commit(struct nfs_page *req) { - struct inode *inode = req->wb_context->dentry->d_inode; + struct inode *inode = req->wb_context->path.dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); - spin_lock(&nfsi->req_lock); - nfs_list_add_request(req, &nfsi->commit); + spin_lock(&inode->i_lock); nfsi->ncommit++; set_bit(PG_NEED_COMMIT, &(req)->wb_flags); - spin_unlock(&nfsi->req_lock); + radix_tree_tag_set(&nfsi->nfs_page_tree, + req->wb_index, + NFS_PAGE_TAG_COMMIT); + spin_unlock(&inode->i_lock); inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); __mark_inode_dirty(inode, I_DIRTY_DATASYNC); } @@ -526,18 +526,18 @@ static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, u idx_end = idx_start + npages - 1; next = idx_start; - while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_WRITEBACK)) { + while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_LOCKED)) { if (req->wb_index > idx_end) break; next = req->wb_index + 1; BUG_ON(!NFS_WBACK_BUSY(req)); - atomic_inc(&req->wb_count); - spin_unlock(&nfsi->req_lock); + kref_get(&req->wb_kref); + spin_unlock(&inode->i_lock); error = nfs_wait_on_request(req); nfs_release_request(req); - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); if (error < 0) return error; res++; @@ -577,10 +577,9 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, u int res = 0; if (nfsi->ncommit != 0) { - res = nfs_scan_list(nfsi, &nfsi->commit, dst, idx_start, npages); + res = nfs_scan_list(nfsi, dst, idx_start, npages, + NFS_PAGE_TAG_COMMIT); nfsi->ncommit -= res; - if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) - printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); } return res; } @@ -603,7 +602,6 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, { struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; - struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *req, *new = NULL; pgoff_t rqend, end; @@ -613,13 +611,13 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, /* Loop over all inode entries and see if we find * A request for the page we wish to update */ - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); req = nfs_page_find_request_locked(page); if (req) { if (!nfs_lock_request_dontget(req)) { int error; - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); error = nfs_wait_on_request(req); nfs_release_request(req); if (error < 0) { @@ -629,7 +627,7 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, } continue; } - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); if (new) nfs_release_request(new); break; @@ -640,14 +638,14 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, nfs_lock_request_dontget(new); error = nfs_inode_add_request(inode, new); if (error) { - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); nfs_unlock_request(new); return ERR_PTR(error); } - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); return new; } - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); new = nfs_create_request(ctx, inode, page, offset, bytes); if (IS_ERR(new)) @@ -751,12 +749,17 @@ int nfs_updatepage(struct file *file, struct page *page, static void nfs_writepage_release(struct nfs_page *req) { - if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) { + if (PageError(req->wb_page)) { + nfs_end_page_writeback(req->wb_page); + nfs_inode_remove_request(req); + } else if (!nfs_reschedule_unstable_write(req)) { + /* Set the PG_uptodate flag */ + nfs_mark_uptodate(req->wb_page, req->wb_pgbase, req->wb_bytes); nfs_end_page_writeback(req->wb_page); nfs_inode_remove_request(req); } else nfs_end_page_writeback(req->wb_page); - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); } static inline int flush_task_priority(int how) @@ -786,7 +789,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req, * NB: take care not to mess about with data->commit et al. */ data->req = req; - data->inode = inode = req->wb_context->dentry->d_inode; + data->inode = inode = req->wb_context->path.dentry->d_inode; data->cred = req->wb_context->cred; data->args.fh = NFS_FH(inode); @@ -885,7 +888,7 @@ out_bad: } nfs_redirty_request(req); nfs_end_page_writeback(req->wb_page); - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); return -ENOMEM; } @@ -928,7 +931,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i nfs_list_remove_request(req); nfs_redirty_request(req); nfs_end_page_writeback(req->wb_page); - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); } return -ENOMEM; } @@ -954,8 +957,8 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) struct page *page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), + req->wb_context->path.dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); @@ -970,9 +973,9 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) } if (nfs_write_need_commit(data)) { - spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; + struct inode *inode = page->mapping->host; - spin_lock(req_lock); + spin_lock(&inode->i_lock); if (test_bit(PG_NEED_RESCHED, &req->wb_flags)) { /* Do nothing we need to resend the writes */ } else if (!test_and_set_bit(PG_NEED_COMMIT, &req->wb_flags)) { @@ -983,7 +986,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) clear_bit(PG_NEED_COMMIT, &req->wb_flags); dprintk(" server reboot detected\n"); } - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); } else dprintk(" OK\n"); @@ -1020,8 +1023,8 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) page = req->wb_page; dprintk("NFS: write (%s/%Ld %d@%Ld)", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), + req->wb_context->path.dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); @@ -1039,12 +1042,14 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) dprintk(" marked for commit\n"); goto next; } + /* Set the PG_uptodate flag? */ + nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); dprintk(" OK\n"); remove_request: nfs_end_page_writeback(page); nfs_inode_remove_request(req); next: - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); } } @@ -1157,7 +1162,7 @@ static void nfs_commit_rpcsetup(struct list_head *head, list_splice_init(head, &data->pages); first = nfs_list_entry(data->pages.next); - inode = first->wb_context->dentry->d_inode; + inode = first->wb_context->path.dentry->d_inode; data->inode = inode; data->cred = first->wb_context->cred; @@ -1207,7 +1212,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how) nfs_list_remove_request(req); nfs_mark_request_commit(req); dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); } return -ENOMEM; } @@ -1234,8 +1239,8 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dprintk("NFS: commit (%s/%Ld %d@%Ld)", - req->wb_context->dentry->d_inode->i_sb->s_id, - (long long)NFS_FILEID(req->wb_context->dentry->d_inode), + req->wb_context->path.dentry->d_inode->i_sb->s_id, + (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (task->tk_status < 0) { @@ -1249,6 +1254,9 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) * returned by the server against all stored verfs. */ if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { /* We have a match */ + /* Set the PG_uptodate flag */ + nfs_mark_uptodate(req->wb_page, req->wb_pgbase, + req->wb_bytes); nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; @@ -1257,7 +1265,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata) dprintk(" mismatch\n"); nfs_redirty_request(req); next: - nfs_clear_page_writeback(req); + nfs_clear_page_tag_locked(req); } } @@ -1268,13 +1276,12 @@ static const struct rpc_call_ops nfs_commit_ops = { int nfs_commit_inode(struct inode *inode, int how) { - struct nfs_inode *nfsi = NFS_I(inode); LIST_HEAD(head); int res; - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); res = nfs_scan_commit(inode, &head, 0, 0); - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); if (res) { int error = nfs_commit_list(inode, &head, how); if (error < 0) @@ -1292,7 +1299,6 @@ static inline int nfs_commit_list(struct inode *inode, struct list_head *head, i long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how) { struct inode *inode = mapping->host; - struct nfs_inode *nfsi = NFS_I(inode); pgoff_t idx_start, idx_end; unsigned int npages = 0; LIST_HEAD(head); @@ -1314,7 +1320,7 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr } } how &= ~FLUSH_NOCOMMIT; - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); do { ret = nfs_wait_on_requests_locked(inode, idx_start, npages); if (ret != 0) @@ -1325,18 +1331,19 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr if (pages == 0) break; if (how & FLUSH_INVALIDATE) { - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); nfs_cancel_commit_list(&head); ret = pages; - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); continue; } pages += nfs_scan_commit(inode, &head, 0, 0); - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); ret = nfs_commit_list(inode, &head, how); - spin_lock(&nfsi->req_lock); + spin_lock(&inode->i_lock); + } while (ret >= 0); - spin_unlock(&nfsi->req_lock); + spin_unlock(&inode->i_lock); return ret; } @@ -1430,7 +1437,6 @@ int nfs_set_page_dirty(struct page *page) { struct address_space *mapping = page->mapping; struct inode *inode; - spinlock_t *req_lock; struct nfs_page *req; int ret; @@ -1439,18 +1445,17 @@ int nfs_set_page_dirty(struct page *page) inode = mapping->host; if (!inode) goto out_raced; - req_lock = &NFS_I(inode)->req_lock; - spin_lock(req_lock); + spin_lock(&inode->i_lock); req = nfs_page_find_request_locked(page); if (req != NULL) { /* Mark any existing write requests for flushing */ ret = !test_and_set_bit(PG_NEED_FLUSH, &req->wb_flags); - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); nfs_release_request(req); return ret; } ret = __set_page_dirty_nobuffers(page); - spin_unlock(req_lock); + spin_unlock(&inode->i_lock); return ret; out_raced: return !TestSetPageDirty(page); @@ -1462,7 +1467,7 @@ int __init nfs_init_writepagecache(void) nfs_wdata_cachep = kmem_cache_create("nfs_write_data", sizeof(struct nfs_write_data), 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (nfs_wdata_cachep == NULL) return -ENOMEM; diff --git a/fs/nfsctl.c b/fs/nfsctl.c index c043136a..51f1b31 100644 --- a/fs/nfsctl.c +++ b/fs/nfsctl.c @@ -23,19 +23,15 @@ static struct file *do_open(char *name, int flags) { struct nameidata nd; + struct vfsmount *mnt; int error; - nd.mnt = do_kern_mount("nfsd", 0, "nfsd", NULL); + mnt = do_kern_mount("nfsd", 0, "nfsd", NULL); + if (IS_ERR(mnt)) + return (struct file *)mnt; - if (IS_ERR(nd.mnt)) - return (struct file *)nd.mnt; - - nd.dentry = dget(nd.mnt->mnt_root); - nd.last_type = LAST_ROOT; - nd.flags = 0; - nd.depth = 0; - - error = path_walk(name, &nd); + error = vfs_path_lookup(mnt->mnt_root, mnt, name, 0, &nd); + mntput(mnt); /* drop do_kern_mount reference */ if (error) return ERR_PTR(error); diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 6e92b0f..2192805 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -9,20 +9,35 @@ #include <linux/sunrpc/svc.h> #include <linux/sunrpc/svcauth.h> #include <linux/nfsd/nfsd.h> +#include <linux/nfsd/export.h> #define CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE)) +int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + + for (f = exp->ex_flavors; f < end; f++) { + if (f->pseudoflavor == rqstp->rq_flavor) + return f->flags; + } + return exp->ex_flags; + +} + int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) { struct svc_cred cred = rqstp->rq_cred; int i; + int flags = nfsexp_flags(rqstp, exp); int ret; - if (exp->ex_flags & NFSEXP_ALLSQUASH) { + if (flags & NFSEXP_ALLSQUASH) { cred.cr_uid = exp->ex_anon_uid; cred.cr_gid = exp->ex_anon_gid; cred.cr_group_info = groups_alloc(0); - } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) { + } else if (flags & NFSEXP_ROOTSQUASH) { struct group_info *gi; if (!cred.cr_uid) cred.cr_uid = exp->ex_anon_uid; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 79bd03b..2d295dd 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -26,12 +26,15 @@ #include <linux/mount.h> #include <linux/hash.h> #include <linux/module.h> +#include <linux/exportfs.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsfh.h> #include <linux/nfsd/syscall.h> #include <linux/lockd/bind.h> +#include <linux/sunrpc/msg_prot.h> +#include <linux/sunrpc/gss_api.h> #define NFSDDBG_FACILITY NFSDDBG_EXPORT @@ -451,8 +454,48 @@ out_free_all: return err; } +static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp) +{ + int listsize, err; + struct exp_flavor_info *f; + + err = get_int(mesg, &listsize); + if (err) + return err; + if (listsize < 0 || listsize > MAX_SECINFO_LIST) + return -EINVAL; + + for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) { + err = get_int(mesg, &f->pseudoflavor); + if (err) + return err; + /* + * Just a quick sanity check; we could also try to check + * whether this pseudoflavor is supported, but at worst + * an unsupported pseudoflavor on the export would just + * be a pseudoflavor that won't match the flavor of any + * authenticated request. The administrator will + * probably discover the problem when someone fails to + * authenticate. + */ + if (f->pseudoflavor < 0) + return -EINVAL; + err = get_int(mesg, &f->flags); + if (err) + return err; + /* Only some flags are allowed to differ between flavors: */ + if (~NFSEXP_SECINFO_FLAGS & (f->flags ^ exp->ex_flags)) + return -EINVAL; + } + exp->ex_nflavors = listsize; + return 0; +} + #else /* CONFIG_NFSD_V4 */ -static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; } +static inline int +fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc){return 0;} +static inline int +secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; } #endif static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) @@ -476,6 +519,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) exp.ex_uuid = NULL; + /* secinfo */ + exp.ex_nflavors = 0; + if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; @@ -553,7 +599,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (exp.ex_uuid == NULL) err = -ENOMEM; } - } else + } else if (strcmp(buf, "secinfo") == 0) + err = secinfo_parse(&mesg, buf, &exp); + else /* quietly ignore unknown words and anything * following. Newer user-space can try to set * new values, then see what the result was. @@ -593,6 +641,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); +static void show_secinfo(struct seq_file *m, struct svc_export *exp); static int svc_export_show(struct seq_file *m, struct cache_detail *cd, @@ -622,6 +671,7 @@ static int svc_export_show(struct seq_file *m, seq_printf(m, "%02x", exp->ex_uuid[i]); } } + show_secinfo(m, exp); } seq_puts(m, ")\n"); return 0; @@ -654,6 +704,7 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) { struct svc_export *new = container_of(cnew, struct svc_export, h); struct svc_export *item = container_of(citem, struct svc_export, h); + int i; new->ex_flags = item->ex_flags; new->ex_anon_uid = item->ex_anon_uid; @@ -669,6 +720,10 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) item->ex_fslocs.locations_count = 0; new->ex_fslocs.migrated = item->ex_fslocs.migrated; item->ex_fslocs.migrated = 0; + new->ex_nflavors = item->ex_nflavors; + for (i = 0; i < MAX_SECINFO_LIST; i++) { + new->ex_flavors[i] = item->ex_flavors[i]; + } } static struct cache_head *svc_export_alloc(void) @@ -738,16 +793,18 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) int err; if (!clp) - return NULL; + return ERR_PTR(-ENOENT); key.ek_client = clp; key.ek_fsidtype = fsid_type; memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); ek = svc_expkey_lookup(&key); - if (ek != NULL) - if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) - ek = ERR_PTR(err); + if (ek == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_expkey_cache, &ek->h, reqp); + if (err) + return ERR_PTR(err); return ek; } @@ -808,30 +865,21 @@ exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, struct cache_req *reqp) { struct svc_export *exp, key; + int err; if (!clp) - return NULL; + return ERR_PTR(-ENOENT); key.ex_client = clp; key.ex_mnt = mnt; key.ex_dentry = dentry; exp = svc_export_lookup(&key); - if (exp != NULL) { - int err; - - err = cache_check(&svc_export_cache, &exp->h, reqp); - switch (err) { - case 0: break; - case -EAGAIN: - case -ETIMEDOUT: - exp = ERR_PTR(err); - break; - default: - exp = NULL; - } - } - + if (exp == NULL) + return ERR_PTR(-ENOMEM); + err = cache_check(&svc_export_cache, &exp->h, reqp); + if (err) + return ERR_PTR(err); return exp; } @@ -847,7 +895,7 @@ exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, dget(dentry); exp = exp_get_by_name(clp, mnt, dentry, reqp); - while (exp == NULL && !IS_ROOT(dentry)) { + while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) { struct dentry *parent; parent = dget_parent(dentry); @@ -900,7 +948,7 @@ static void exp_fsid_unhash(struct svc_export *exp) return; ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); - if (ek && !IS_ERR(ek)) { + if (!IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; cache_put(&ek->h, &svc_expkey_cache); } @@ -938,7 +986,7 @@ static void exp_unhash(struct svc_export *exp) struct inode *inode = exp->ex_dentry->d_inode; ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); - if (ek && !IS_ERR(ek)) { + if (!IS_ERR(ek)) { ek->h.expiry_time = get_seconds()-1; cache_put(&ek->h, &svc_expkey_cache); } @@ -989,13 +1037,12 @@ exp_export(struct nfsctl_export *nxp) /* must make sure there won't be an ex_fsid clash */ if ((nxp->ex_flags & NFSEXP_FSID) && - (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && - !IS_ERR(fsid_key) && + (!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) && fsid_key->ek_mnt && (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) goto finish; - if (exp) { + if (!IS_ERR(exp)) { /* just a flags/id/fsid update */ exp_fsid_unhash(exp); @@ -1104,7 +1151,7 @@ exp_unexport(struct nfsctl_export *nxp) err = -EINVAL; exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); path_release(&nd); - if (!exp) + if (IS_ERR(exp)) goto out_domain; exp_do_unexport(exp); @@ -1149,10 +1196,6 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) err = PTR_ERR(exp); goto out; } - if (!exp) { - dprintk("nfsd: exp_rootfh export not found.\n"); - goto out; - } /* * fh must be initialized before calling fh_compose @@ -1176,17 +1219,130 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, { struct svc_export *exp; struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); - if (!ek || IS_ERR(ek)) + if (IS_ERR(ek)) return ERR_PTR(PTR_ERR(ek)); exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); cache_put(&ek->h, &svc_expkey_cache); - if (!exp || IS_ERR(exp)) + if (IS_ERR(exp)) return ERR_PTR(PTR_ERR(exp)); return exp; } +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + + /* legacy gss-only clients are always OK: */ + if (exp->ex_client == rqstp->rq_gssclient) + return 0; + /* ip-address based client; check sec= export option: */ + for (f = exp->ex_flavors; f < end; f++) { + if (f->pseudoflavor == rqstp->rq_flavor) + return 0; + } + /* defaults in absence of sec= options: */ + if (exp->ex_nflavors == 0) { + if (rqstp->rq_flavor == RPC_AUTH_NULL || + rqstp->rq_flavor == RPC_AUTH_UNIX) + return 0; + } + return nfserr_wrongsec; +} + +/* + * Uses rq_client and rq_gssclient to find an export; uses rq_client (an + * auth_unix client) if it's available and has secinfo information; + * otherwise, will try to use rq_gssclient. + * + * Called from functions that handle requests; functions that do work on + * behalf of mountd are passed a single client name to use, and should + * use exp_get_by_name() or exp_find(). + */ +struct svc_export * +rqst_exp_get_by_name(struct svc_rqst *rqstp, struct vfsmount *mnt, + struct dentry *dentry) +{ + struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); + + if (rqstp->rq_client == NULL) + goto gss; + + /* First try the auth_unix client: */ + exp = exp_get_by_name(rqstp->rq_client, mnt, dentry, + &rqstp->rq_chandle); + if (PTR_ERR(exp) == -ENOENT) + goto gss; + if (IS_ERR(exp)) + return exp; + /* If it has secinfo, assume there are no gss/... clients */ + if (exp->ex_nflavors > 0) + return exp; +gss: + /* Otherwise, try falling back on gss client */ + if (rqstp->rq_gssclient == NULL) + return exp; + gssexp = exp_get_by_name(rqstp->rq_gssclient, mnt, dentry, + &rqstp->rq_chandle); + if (PTR_ERR(gssexp) == -ENOENT) + return exp; + if (!IS_ERR(exp)) + exp_put(exp); + return gssexp; +} + +struct svc_export * +rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv) +{ + struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); + + if (rqstp->rq_client == NULL) + goto gss; + + /* First try the auth_unix client: */ + exp = exp_find(rqstp->rq_client, fsid_type, fsidv, &rqstp->rq_chandle); + if (PTR_ERR(exp) == -ENOENT) + goto gss; + if (IS_ERR(exp)) + return exp; + /* If it has secinfo, assume there are no gss/... clients */ + if (exp->ex_nflavors > 0) + return exp; +gss: + /* Otherwise, try falling back on gss client */ + if (rqstp->rq_gssclient == NULL) + return exp; + gssexp = exp_find(rqstp->rq_gssclient, fsid_type, fsidv, + &rqstp->rq_chandle); + if (PTR_ERR(gssexp) == -ENOENT) + return exp; + if (!IS_ERR(exp)) + exp_put(exp); + return gssexp; +} + +struct svc_export * +rqst_exp_parent(struct svc_rqst *rqstp, struct vfsmount *mnt, + struct dentry *dentry) +{ + struct svc_export *exp; + + dget(dentry); + exp = rqst_exp_get_by_name(rqstp, mnt, dentry); + + while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) { + struct dentry *parent; + + parent = dget_parent(dentry); + dput(dentry); + dentry = parent; + exp = rqst_exp_get_by_name(rqstp, mnt, dentry); + } + dput(dentry); + return exp; +} /* * Called when we need the filehandle for the root of the pseudofs, @@ -1194,8 +1350,7 @@ exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, * export point with fsid==0 */ __be32 -exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, - struct cache_req *creq) +exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp) { struct svc_export *exp; __be32 rv; @@ -1203,12 +1358,16 @@ exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL); - exp = exp_find(clp, FSID_NUM, fsidv, creq); + exp = rqst_exp_find(rqstp, FSID_NUM, fsidv); + if (PTR_ERR(exp) == -ENOENT) + return nfserr_perm; if (IS_ERR(exp)) return nfserrno(PTR_ERR(exp)); - if (exp == NULL) - return nfserr_perm; rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); + if (rv) + goto out; + rv = check_nfsd_access(exp, rqstp); +out: exp_put(exp); return rv; } @@ -1296,28 +1455,62 @@ static struct flags { { 0, {"", ""}} }; -static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) +static void show_expflags(struct seq_file *m, int flags, int mask) { - int first = 0; struct flags *flg; + int state, first = 0; for (flg = expflags; flg->flag; flg++) { - int state = (flg->flag & flag)?0:1; + if (flg->flag & ~mask) + continue; + state = (flg->flag & flags) ? 0 : 1; if (*flg->name[state]) seq_printf(m, "%s%s", first++?",":"", flg->name[state]); } +} + +static void show_secinfo_flags(struct seq_file *m, int flags) +{ + seq_printf(m, ","); + show_expflags(m, flags, NFSEXP_SECINFO_FLAGS); +} + +static void show_secinfo(struct seq_file *m, struct svc_export *exp) +{ + struct exp_flavor_info *f; + struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; + int lastflags = 0, first = 0; + + if (exp->ex_nflavors == 0) + return; + for (f = exp->ex_flavors; f < end; f++) { + if (first || f->flags != lastflags) { + if (!first) + show_secinfo_flags(m, lastflags); + seq_printf(m, ",sec=%d", f->pseudoflavor); + lastflags = f->flags; + } else { + seq_printf(m, ":%d", f->pseudoflavor); + } + } + show_secinfo_flags(m, lastflags); +} + +static void exp_flags(struct seq_file *m, int flag, int fsid, + uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) +{ + show_expflags(m, flag, NFSEXP_ALLFLAGS); if (flag & NFSEXP_FSID) - seq_printf(m, "%sfsid=%d", first++?",":"", fsid); + seq_printf(m, ",fsid=%d", fsid); if (anonu != (uid_t)-2 && anonu != (0x10000-2)) - seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); + seq_printf(m, ",anonuid=%u", anonu); if (anong != (gid_t)-2 && anong != (0x10000-2)) - seq_printf(m, "%sanongid=%d", first++?",":"", anong); + seq_printf(m, ",anongid=%u", anong); if (fsloc && fsloc->locations_count > 0) { char *loctype = (fsloc->migrated) ? "refer" : "replicas"; int i; - seq_printf(m, "%s%s=", first++?",":"", loctype); + seq_printf(m, ",%s=", loctype); seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); seq_putc(m, '@'); seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 221acd1..9e4a568 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -65,6 +65,7 @@ nlm_fclose(struct file *filp) static struct nlmsvc_binding nfsd_nlm_ops = { .fopen = nlm_fopen, /* open file for locking */ .fclose = nlm_fclose, /* close file */ + .get_grace_period = get_nfs4_grace_period, }; void diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index cc3b7ba..b6ed383 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -183,8 +183,13 @@ static void summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas) { struct posix_acl_entry *pa, *pe; - pas->users = 0; - pas->groups = 0; + + /* + * Only pas.users and pas.groups need initialization; previous + * posix_acl_valid() calls ensure that the other fields will be + * initialized in the following loop. But, just to placate gcc: + */ + memset(pas, 0, sizeof(*pas)); pas->mask = 07; pe = acl->a_entries + acl->a_count; @@ -732,13 +737,16 @@ int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, *pacl = posix_state_to_acl(&effective_acl_state, flags); if (IS_ERR(*pacl)) { ret = PTR_ERR(*pacl); + *pacl = NULL; goto out_dstate; } *dpacl = posix_state_to_acl(&default_acl_state, flags | NFS4_ACL_TYPE_DEFAULT); if (IS_ERR(*dpacl)) { ret = PTR_ERR(*dpacl); + *dpacl = NULL; posix_acl_release(*pacl); + *pacl = NULL; goto out_dstate; } sort_pacl(*pacl); diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 864090e..31d6633 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -75,7 +75,7 @@ enum nfs_cb_opnum4 { #define op_enc_sz 1 #define op_dec_sz 2 #define enc_nfs4_fh_sz (1 + (NFS4_FHSIZE >> 2)) -#define enc_stateid_sz 16 +#define enc_stateid_sz (NFS4_STATEID_SIZE >> 2) #define NFS4_enc_cb_recall_sz (cb_compound_enc_hdr_sz + \ 1 + enc_stateid_sz + \ enc_nfs4_fh_sz) @@ -394,7 +394,6 @@ nfsd4_probe_callback(struct nfs4_client *clp) .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_argp = clp, }; - char clientname[16]; int status; if (atomic_read(&cb->cb_set)) @@ -417,11 +416,6 @@ nfsd4_probe_callback(struct nfs4_client *clp) memset(program->stats, 0, sizeof(cb->cb_stat)); program->stats->program = program; - /* Just here to make some printk's more useful: */ - snprintf(clientname, sizeof(clientname), - "%u.%u.%u.%u", NIPQUAD(addr.sin_addr)); - args.servername = clientname; - /* Create RPC client */ cb->cb_client = rpc_create(&args); if (IS_ERR(cb->cb_client)) { @@ -429,29 +423,23 @@ nfsd4_probe_callback(struct nfs4_client *clp) goto out_err; } - /* Kick rpciod, put the call on the wire. */ - if (rpciod_up() != 0) - goto out_clnt; - /* the task holds a reference to the nfs4_client struct */ atomic_inc(&clp->cl_count); msg.rpc_cred = nfsd4_lookupcred(clp,0); if (IS_ERR(msg.rpc_cred)) - goto out_rpciod; + goto out_release_clp; status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL); put_rpccred(msg.rpc_cred); if (status != 0) { dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n"); - goto out_rpciod; + goto out_release_clp; } return; -out_rpciod: +out_release_clp: atomic_dec(&clp->cl_count); - rpciod_down(); -out_clnt: rpc_shutdown_client(cb->cb_client); out_err: cb->cb_client = NULL; diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 45aa21c..2cf9a9a 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -587,6 +587,15 @@ idmap_lookup(struct svc_rqst *rqstp, return ret; } +static char * +rqst_authname(struct svc_rqst *rqstp) +{ + struct auth_domain *clp; + + clp = rqstp->rq_gssclient ? rqstp->rq_gssclient : rqstp->rq_client; + return clp->name; +} + static int idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) @@ -600,7 +609,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen return -EINVAL; memcpy(key.name, name, namelen); key.name[namelen] = '\0'; - strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); + strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item); if (ret == -ENOENT) ret = -ESRCH; /* nfserr_badname */ @@ -620,7 +629,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) }; int ret; - strlcpy(key.authname, rqstp->rq_client->name, sizeof(key.authname)); + strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); ret = idmap_lookup(rqstp, idtoname_lookup, &key, &idtoname_cache, &item); if (ret == -ENOENT) return sprintf(name, "%u", id); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8522729..3c62712 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -47,6 +47,7 @@ #include <linux/nfsd/state.h> #include <linux/nfsd/xdr4.h> #include <linux/nfs4_acl.h> +#include <linux/sunrpc/gss_api.h> #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -286,8 +287,7 @@ nfsd4_putrootfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; fh_put(&cstate->current_fh); - status = exp_pseudoroot(rqstp->rq_client, &cstate->current_fh, - &rqstp->rq_chandle); + status = exp_pseudoroot(rqstp, &cstate->current_fh); return status; } @@ -474,8 +474,8 @@ nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 ret; fh_init(&tmp_fh, NFS4_FHSIZE); - if((ret = exp_pseudoroot(rqstp->rq_client, &tmp_fh, - &rqstp->rq_chandle)) != 0) + ret = exp_pseudoroot(rqstp, &tmp_fh); + if (ret) return ret; if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) { fh_put(&tmp_fh); @@ -611,6 +611,30 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } static __be32 +nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, + struct nfsd4_secinfo *secinfo) +{ + struct svc_fh resfh; + struct svc_export *exp; + struct dentry *dentry; + __be32 err; + + fh_init(&resfh, NFS4_FHSIZE); + err = nfsd_lookup_dentry(rqstp, &cstate->current_fh, + secinfo->si_name, secinfo->si_namelen, + &exp, &dentry); + if (err) + return err; + if (dentry->d_inode == NULL) { + exp_put(exp); + err = nfserr_noent; + } else + secinfo->si_exp = exp; + dput(dentry); + return err; +} + +static __be32 nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr) { @@ -1009,6 +1033,9 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { [OP_SAVEFH] = { .op_func = (nfsd4op_func)nfsd4_savefh, }, + [OP_SECINFO] = { + .op_func = (nfsd4op_func)nfsd4_secinfo, + }, [OP_SETATTR] = { .op_func = (nfsd4op_func)nfsd4_setattr, }, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3cc8ce4..3f55970 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -49,8 +49,10 @@ #include <linux/nfsd/state.h> #include <linux/nfsd/xdr4.h> #include <linux/namei.h> +#include <linux/swap.h> #include <linux/mutex.h> #include <linux/lockd/bind.h> +#include <linux/module.h> #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -149,6 +151,7 @@ get_nfs4_file(struct nfs4_file *fi) } static int num_delegations; +unsigned int max_delegations; /* * Open owner state (share locks) @@ -192,7 +195,9 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; dprintk("NFSD alloc_init_deleg\n"); - if (num_delegations > STATEID_HASH_SIZE * 4) + if (fp->fi_had_conflict) + return NULL; + if (num_delegations > max_delegations) return NULL; dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); if (dp == NULL) @@ -251,7 +256,7 @@ nfs4_close_delegation(struct nfs4_delegation *dp) /* The following nfsd_close may not actually close the file, * but we want to remove the lease in any case. */ if (dp->dl_flock) - setlease(filp, F_UNLCK, &dp->dl_flock); + vfs_setlease(filp, F_UNLCK, &dp->dl_flock); nfsd_close(filp); } @@ -378,7 +383,6 @@ shutdown_callback_client(struct nfs4_client *clp) if (clnt) { clp->cl_callback.cb_client = NULL; rpc_shutdown_client(clnt); - rpciod_down(); } } @@ -1000,6 +1004,7 @@ alloc_init_file(struct inode *ino) list_add(&fp->fi_hash, &file_hashtbl[hashval]); fp->fi_inode = igrab(ino); fp->fi_id = current_fileid++; + fp->fi_had_conflict = false; return fp; } return NULL; @@ -1027,19 +1032,19 @@ static int nfsd4_init_slabs(void) { stateowner_slab = kmem_cache_create("nfsd4_stateowners", - sizeof(struct nfs4_stateowner), 0, 0, NULL, NULL); + sizeof(struct nfs4_stateowner), 0, 0, NULL); if (stateowner_slab == NULL) goto out_nomem; file_slab = kmem_cache_create("nfsd4_files", - sizeof(struct nfs4_file), 0, 0, NULL, NULL); + sizeof(struct nfs4_file), 0, 0, NULL); if (file_slab == NULL) goto out_nomem; stateid_slab = kmem_cache_create("nfsd4_stateids", - sizeof(struct nfs4_stateid), 0, 0, NULL, NULL); + sizeof(struct nfs4_stateid), 0, 0, NULL); if (stateid_slab == NULL) goto out_nomem; deleg_slab = kmem_cache_create("nfsd4_delegations", - sizeof(struct nfs4_delegation), 0, 0, NULL, NULL); + sizeof(struct nfs4_delegation), 0, 0, NULL); if (deleg_slab == NULL) goto out_nomem; return 0; @@ -1326,6 +1331,7 @@ do_recall(void *__dp) { struct nfs4_delegation *dp = __dp; + dp->dl_file->fi_had_conflict = true; nfsd4_cb_recall(dp); return 0; } @@ -1396,7 +1402,7 @@ void nfsd_release_deleg_cb(struct file_lock *fl) /* * Set the delegation file_lock back pointer. * - * Called from __setlease() with lock_kernel() held. + * Called from setlease() with lock_kernel() held. */ static void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) @@ -1410,7 +1416,7 @@ void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) } /* - * Called from __setlease() with lock_kernel() held + * Called from setlease() with lock_kernel() held */ static int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try) @@ -1710,10 +1716,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta fl.fl_file = stp->st_vfs_file; fl.fl_pid = current->tgid; - /* setlease checks to see if delegation should be handed out. + /* vfs_setlease checks to see if delegation should be handed out. * the lock_manager callbacks fl_mylease and fl_change are used */ - if ((status = setlease(stp->st_vfs_file, + if ((status = vfs_setlease(stp->st_vfs_file, flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK, &flp))) { dprintk("NFSD: setlease failed [%d], no delegation\n", status); unhash_delegation(dp); @@ -3191,20 +3197,49 @@ nfsd4_load_reboot_recovery_data(void) printk("NFSD: Failure reading reboot recovery data\n"); } +unsigned long +get_nfs4_grace_period(void) +{ + return max(user_lease_time, lease_time) * HZ; +} + +/* + * Since the lifetime of a delegation isn't limited to that of an open, a + * client may quite reasonably hang on to a delegation as long as it has + * the inode cached. This becomes an obvious problem the first time a + * client's inode cache approaches the size of the server's total memory. + * + * For now we avoid this problem by imposing a hard limit on the number + * of delegations, which varies according to the server's memory size. + */ +static void +set_max_delegations(void) +{ + /* + * Allow at most 4 delegations per megabyte of RAM. Quick + * estimates suggest that in the worst case (where every delegation + * is for a different inode), a delegation could take about 1.5K, + * giving a worst case usage of about 6% of memory. + */ + max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); +} + /* initialization to perform when the nfsd service is started: */ static void __nfs4_state_start(void) { - time_t grace_time; + unsigned long grace_time; boot_time = get_seconds(); - grace_time = max(user_lease_time, lease_time); + grace_time = get_nfs_grace_period(); lease_time = user_lease_time; in_grace = 1; - printk("NFSD: starting %ld-second grace period\n", grace_time); + printk(KERN_INFO "NFSD: starting %ld-second grace period\n", + grace_time/HZ); laundry_wq = create_singlethread_workqueue("nfsd4"); - queue_delayed_work(laundry_wq, &laundromat_work, grace_time*HZ); + queue_delayed_work(laundry_wq, &laundromat_work, grace_time); + set_max_delegations(); } int diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 15809df..b3d55c6 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -56,6 +56,8 @@ #include <linux/nfsd_idmap.h> #include <linux/nfs4.h> #include <linux/nfs4_acl.h> +#include <linux/sunrpc/gss_api.h> +#include <linux/sunrpc/svcauth_gss.h> #define NFSDDBG_FACILITY NFSDDBG_XDR @@ -819,6 +821,23 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) } static __be32 +nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, + struct nfsd4_secinfo *secinfo) +{ + DECODE_HEAD; + + READ_BUF(4); + READ32(secinfo->si_namelen); + READ_BUF(secinfo->si_namelen); + SAVEMEM(secinfo->si_name, secinfo->si_namelen); + status = check_filename(secinfo->si_name, secinfo->si_namelen, + nfserr_noent); + if (status) + return status; + DECODE_TAIL; +} + +static __be32 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) { DECODE_HEAD; @@ -1131,6 +1150,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) case OP_SAVEFH: op->status = nfs_ok; break; + case OP_SECINFO: + op->status = nfsd4_decode_secinfo(argp, &op->u.secinfo); + break; case OP_SETATTR: op->status = nfsd4_decode_setattr(argp, &op->u.setattr); break; @@ -1296,7 +1318,7 @@ static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 * char *path, *rootpath; fh_init(&tmp_fh, NFS4_FHSIZE); - *stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle); + *stat = exp_pseudoroot(rqstp, &tmp_fh); if (*stat) return NULL; rootpath = tmp_fh.fh_export->ex_path; @@ -1847,11 +1869,19 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, if (d_mountpoint(dentry)) { int err; + /* + * Why the heck aren't we just using nfsd_lookup?? + * Different "."/".." handling? Something else? + * At least, add a comment here to explain.... + */ err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp); if (err) { nfserr = nfserrno(err); goto out_put; } + nfserr = check_nfsd_access(exp, cd->rd_rqstp); + if (nfserr) + goto out_put; } nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, @@ -2419,6 +2449,72 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ } } +static void +nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, int nfserr, + struct nfsd4_secinfo *secinfo) +{ + int i = 0; + struct svc_export *exp = secinfo->si_exp; + u32 nflavs; + struct exp_flavor_info *flavs; + struct exp_flavor_info def_flavs[2]; + ENCODE_HEAD; + + if (nfserr) + goto out; + if (exp->ex_nflavors) { + flavs = exp->ex_flavors; + nflavs = exp->ex_nflavors; + } else { /* Handling of some defaults in absence of real secinfo: */ + flavs = def_flavs; + if (exp->ex_client->flavour->flavour == RPC_AUTH_UNIX) { + nflavs = 2; + flavs[0].pseudoflavor = RPC_AUTH_UNIX; + flavs[1].pseudoflavor = RPC_AUTH_NULL; + } else if (exp->ex_client->flavour->flavour == RPC_AUTH_GSS) { + nflavs = 1; + flavs[0].pseudoflavor + = svcauth_gss_flavor(exp->ex_client); + } else { + nflavs = 1; + flavs[0].pseudoflavor + = exp->ex_client->flavour->flavour; + } + } + + RESERVE_SPACE(4); + WRITE32(nflavs); + ADJUST_ARGS(); + for (i = 0; i < nflavs; i++) { + u32 flav = flavs[i].pseudoflavor; + struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav); + + if (gm) { + RESERVE_SPACE(4); + WRITE32(RPC_AUTH_GSS); + ADJUST_ARGS(); + RESERVE_SPACE(4 + gm->gm_oid.len); + WRITE32(gm->gm_oid.len); + WRITEMEM(gm->gm_oid.data, gm->gm_oid.len); + ADJUST_ARGS(); + RESERVE_SPACE(4); + WRITE32(0); /* qop */ + ADJUST_ARGS(); + RESERVE_SPACE(4); + WRITE32(gss_pseudoflavor_to_service(gm, flav)); + ADJUST_ARGS(); + gss_mech_put(gm); + } else { + RESERVE_SPACE(4); + WRITE32(flav); + ADJUST_ARGS(); + } + } +out: + if (exp) + exp_put(exp); +} + /* * The SETATTR encode routine is special -- it always encodes a bitmap, * regardless of the error status. @@ -2559,6 +2655,9 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) break; case OP_SAVEFH: break; + case OP_SECINFO: + nfsd4_encode_secinfo(resp, op->status, &op->u.secinfo); + break; case OP_SETATTR: nfsd4_encode_setattr(resp, op->status, &op->u.setattr); break; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 71c686d..baac89d 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -35,7 +35,6 @@ #include <linux/nfsd/cache.h> #include <linux/nfsd/xdr.h> #include <linux/nfsd/syscall.h> -#include <linux/nfsd/interface.h> #include <asm/uaccess.h> @@ -245,7 +244,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size) } exp_readunlock(); if (err == 0) - err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; + err = res->fh_size + offsetof(struct knfsd_fh, fh_base); out: return err; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 6ca2d24..0eb464a 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -15,10 +15,12 @@ #include <linux/string.h> #include <linux/stat.h> #include <linux/dcache.h> +#include <linux/exportfs.h> #include <linux/mount.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svc.h> +#include <linux/sunrpc/svcauth_gss.h> #include <linux/nfsd/nfsd.h> #define NFSDDBG_FACILITY NFSDDBG_FH @@ -27,10 +29,6 @@ static int nfsd_nr_verified; static int nfsd_nr_put; -extern struct export_operations export_op_default; - -#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) - /* * our acceptability function. * if NOSUBTREECHECK, accept anything @@ -123,8 +121,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) int data_left = fh->fh_size/4; error = nfserr_stale; - if (rqstp->rq_client == NULL) - goto out; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (rqstp->rq_vers == 4 && fh->fh_size == 0) @@ -148,7 +144,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fh->fh_fsid[1] = fh->fh_fsid[2]; } if ((data_left -= len)<0) goto out; - exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); + exp = rqst_exp_find(rqstp, fh->fh_fsid_type, datap); datap += len; } else { dev_t xdev; @@ -159,19 +155,17 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) xdev = old_decode_dev(fh->ofh_xdev); xino = u32_to_ino_t(fh->ofh_xino); mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); - exp = exp_find(rqstp->rq_client, FSID_DEV, tfh, - &rqstp->rq_chandle); + exp = rqst_exp_find(rqstp, FSID_DEV, tfh); } - if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN - || PTR_ERR(exp) == -ETIMEDOUT)) { - error = nfserrno(PTR_ERR(exp)); + error = nfserr_stale; + if (PTR_ERR(exp) == -ENOENT) goto out; - } - error = nfserr_stale; - if (!exp || IS_ERR(exp)) + if (IS_ERR(exp)) { + error = nfserrno(PTR_ERR(exp)); goto out; + } /* Check if the request originated from a secure port. */ error = nfserr_perm; @@ -211,11 +205,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (fileid_type == 0) dentry = dget(exp->ex_dentry); else { - struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; - dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, - datap, data_left, - fileid_type, - nfsd_acceptable, exp); + dentry = exportfs_decode_fh(exp->ex_mnt, datap, + data_left, fileid_type, + nfsd_acceptable, exp); } if (dentry == NULL) goto out; @@ -257,8 +249,19 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; + if (!(access & MAY_LOCK)) { + /* + * pseudoflavor restrictions are not enforced on NLM, + * which clients virtually always use auth_sys for, + * even while using RPCSEC_GSS for NFS. + */ + error = check_nfsd_access(exp, rqstp); + if (error) + goto out; + } + /* Finally, check access permissions. */ - error = nfsd_permission(exp, dentry, access); + error = nfsd_permission(rqstp, exp, dentry, access); if (error) { dprintk("fh_verify: %s/%s permission failure, " @@ -286,15 +289,13 @@ out: static inline int _fh_update(struct dentry *dentry, struct svc_export *exp, __u32 *datap, int *maxsize) { - struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; - if (dentry == exp->ex_dentry) { *maxsize = 0; return 0; } - return CALL(nop,encode_fh)(dentry, datap, maxsize, - !(exp->ex_flags&NFSEXP_NOSUBTREECHECK)); + return exportfs_encode_fh(dentry, datap, maxsize, + !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } /* diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index b2c7147..977a71f 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -278,7 +278,8 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, * echo thing > device-special-file-or-pipe * by doing a CREATE with type==0 */ - nfserr = nfsd_permission(newfhp->fh_export, + nfserr = nfsd_permission(rqstp, + newfhp->fh_export, newfhp->fh_dentry, MAY_WRITE|MAY_LOCAL_ACCESS); if (nfserr && nfserr != nfserr_rofs) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ff55950..a8c89ae 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/freezer.h> #include <linux/fs_struct.h> #include <linux/sunrpc/types.h> @@ -432,6 +433,7 @@ nfsd(struct svc_rqst *rqstp) * dirty pages. */ current->flags |= PF_LESS_THROTTLE; + set_freezable(); /* * The main request loop @@ -492,6 +494,15 @@ out: module_put_and_exit(0); } +static __be32 map_new_errors(u32 vers, __be32 nfserr) +{ + if (nfserr == nfserr_jukebox && vers == 2) + return nfserr_dropit; + if (nfserr == nfserr_wrongsec && vers < 4) + return nfserr_acces; + return nfserr; +} + int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) { @@ -534,6 +545,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) /* Now call the procedure handler, and encode NFS status. */ nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); + nfserr = map_new_errors(rqstp->rq_vers, nfserr); if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2) nfserr = nfserr_dropit; if (nfserr == nfserr_dropit) { diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 8604e35..ee96a89 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -113,21 +113,21 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); - exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle); + exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); if (IS_ERR(exp2)) { err = PTR_ERR(exp2); dput(mounts); mntput(mnt); goto out; } - if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) { + if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { /* successfully crossed mount point */ exp_put(exp); *expp = exp2; dput(dentry); *dpp = mounts; } else { - if (exp2) exp_put(exp2); + exp_put(exp2); dput(mounts); } mntput(mnt); @@ -135,21 +135,10 @@ out: return err; } -/* - * Look up one component of a pathname. - * N.B. After this call _both_ fhp and resfh need an fh_put - * - * If the lookup would cross a mountpoint, and the mounted filesystem - * is exported to the client with NFSEXP_NOHIDE, then the lookup is - * accepted as it stands and the mounted directory is - * returned. Otherwise the covered directory is returned. - * NOTE: this mountpoint crossing is not supported properly by all - * clients and is explicitly disallowed for NFSv3 - * NeilBrown <neilb@cse.unsw.edu.au> - */ __be32 -nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, - int len, struct svc_fh *resfh) +nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, + const char *name, int len, + struct svc_export **exp_ret, struct dentry **dentry_ret) { struct svc_export *exp; struct dentry *dparent; @@ -168,8 +157,6 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, exp = fhp->fh_export; exp_get(exp); - err = nfserr_acces; - /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) @@ -190,17 +177,15 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dput(dentry); dentry = dp; - exp2 = exp_parent(exp->ex_client, mnt, dentry, - &rqstp->rq_chandle); - if (IS_ERR(exp2)) { + exp2 = rqst_exp_parent(rqstp, mnt, dentry); + if (PTR_ERR(exp2) == -ENOENT) { + dput(dentry); + dentry = dget(dparent); + } else if (IS_ERR(exp2)) { host_err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; - } - if (!exp2) { - dput(dentry); - dentry = dget(dparent); } else { exp_put(exp); exp = exp2; @@ -223,6 +208,41 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, } } } + *dentry_ret = dentry; + *exp_ret = exp; + return 0; + +out_nfserr: + exp_put(exp); + return nfserrno(host_err); +} + +/* + * Look up one component of a pathname. + * N.B. After this call _both_ fhp and resfh need an fh_put + * + * If the lookup would cross a mountpoint, and the mounted filesystem + * is exported to the client with NFSEXP_NOHIDE, then the lookup is + * accepted as it stands and the mounted directory is + * returned. Otherwise the covered directory is returned. + * NOTE: this mountpoint crossing is not supported properly by all + * clients and is explicitly disallowed for NFSv3 + * NeilBrown <neilb@cse.unsw.edu.au> + */ +__be32 +nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, + int len, struct svc_fh *resfh) +{ + struct svc_export *exp; + struct dentry *dentry; + __be32 err; + + err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); + if (err) + return err; + err = check_nfsd_access(exp, rqstp); + if (err) + goto out; /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. @@ -230,16 +250,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; - dput(dentry); out: + dput(dentry); exp_put(exp); return err; - -out_nfserr: - err = nfserrno(host_err); - goto out; } + /* * Set various file attributes. * N.B. After this call fhp needs an fh_put @@ -311,7 +328,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, /* The size case is special. It changes the file as well as the attributes. */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { - err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); + err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); if (err) goto out; } @@ -435,7 +452,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, /* Get inode */ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); if (error) - goto out; + return error; dentry = fhp->fh_dentry; inode = dentry->d_inode; @@ -444,33 +461,25 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); if (host_error == -EINVAL) { - error = nfserr_attrnotsupp; - goto out; + return nfserr_attrnotsupp; } else if (host_error < 0) goto out_nfserr; host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); if (host_error < 0) - goto out_nfserr; + goto out_release; - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); - if (host_error < 0) - goto out_nfserr; - } - error = nfs_ok; - -out: +out_release: posix_acl_release(pacl); posix_acl_release(dpacl); - return (error); out_nfserr: if (host_error == -EOPNOTSUPP) - error = nfserr_attrnotsupp; + return nfserr_attrnotsupp; else - error = nfserrno(host_error); - goto out; + return nfserrno(host_error); } static struct posix_acl * @@ -607,7 +616,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor sresult |= map->access; - err2 = nfsd_permission(export, dentry, map->how); + err2 = nfsd_permission(rqstp, export, dentry, map->how); switch (err2) { case nfs_ok: result |= map->access; @@ -879,6 +888,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, .u.data = rqstp, }; + rqstp->rq_resused = 1; host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); } else { oldfs = get_fs(); @@ -1033,7 +1043,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, __be32 err; if (file) { - err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, MAY_READ|MAY_OWNER_OVERRIDE); if (err) goto out; @@ -1062,7 +1072,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, __be32 err = 0; if (file) { - err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, MAY_WRITE|MAY_OWNER_OVERRIDE); if (err) goto out; @@ -1787,11 +1797,17 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) return err; } +static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp) +{ + return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY; +} + /* * Check for a user's access permissions to this inode. */ __be32 -nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) +nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, + struct dentry *dentry, int acc) { struct inode *inode = dentry->d_inode; int err; @@ -1822,7 +1838,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) */ if (!(acc & MAY_LOCAL_ACCESS)) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { - if (EX_RDONLY(exp) || IS_RDONLY(inode)) + if (exp_rdonly(rqstp, exp) || IS_RDONLY(inode)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; @@ -1905,7 +1921,7 @@ nfsd_racache_init(int cache_size) raparm_hash[i].pb_head = NULL; spin_lock_init(&raparm_hash[i].pb_lock); } - nperbucket = cache_size >> RAPARM_HASH_BITS; + nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); for (i = 0; i < cache_size - 1; i++) { if (i % nperbucket == 0) raparm_hash[j++].pb_head = raparml + i; diff --git a/fs/nls/Makefile b/fs/nls/Makefile index a7ade13..f499dd7 100644 --- a/fs/nls/Makefile +++ b/fs/nls/Makefile @@ -36,11 +36,9 @@ obj-$(CONFIG_NLS_ISO8859_6) += nls_iso8859-6.o obj-$(CONFIG_NLS_ISO8859_7) += nls_iso8859-7.o obj-$(CONFIG_NLS_ISO8859_8) += nls_cp1255.o obj-$(CONFIG_NLS_ISO8859_9) += nls_iso8859-9.o -obj-$(CONFIG_NLS_ISO8859_10) += nls_iso8859-10.o obj-$(CONFIG_NLS_ISO8859_13) += nls_iso8859-13.o obj-$(CONFIG_NLS_ISO8859_14) += nls_iso8859-14.o obj-$(CONFIG_NLS_ISO8859_15) += nls_iso8859-15.o obj-$(CONFIG_NLS_KOI8_R) += nls_koi8-r.o obj-$(CONFIG_NLS_KOI8_U) += nls_koi8-u.o nls_koi8-ru.o -obj-$(CONFIG_NLS_ABC) += nls_abc.o obj-$(CONFIG_NLS_UTF8) += nls_utf8.o diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index bff01a5..e93c614 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -21,6 +21,7 @@ */ #include <linux/dcache.h> +#include <linux/exportfs.h> #include <linux/security.h> #include "attrib.h" diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 4566b91..90c4e3a 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -3143,7 +3143,7 @@ static int __init init_ntfs_fs(void) ntfs_index_ctx_cache = kmem_cache_create(ntfs_index_ctx_cache_name, sizeof(ntfs_index_context), 0 /* offset */, - SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */); + SLAB_HWCACHE_ALIGN, NULL /* ctor */); if (!ntfs_index_ctx_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_index_ctx_cache_name); @@ -3151,7 +3151,7 @@ static int __init init_ntfs_fs(void) } ntfs_attr_ctx_cache = kmem_cache_create(ntfs_attr_ctx_cache_name, sizeof(ntfs_attr_search_ctx), 0 /* offset */, - SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */); + SLAB_HWCACHE_ALIGN, NULL /* ctor */); if (!ntfs_attr_ctx_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_attr_ctx_cache_name); @@ -3160,7 +3160,7 @@ static int __init init_ntfs_fs(void) ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name, (NTFS_MAX_NAME_LEN+1) * sizeof(ntfschar), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); + SLAB_HWCACHE_ALIGN, NULL); if (!ntfs_name_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_name_cache_name); @@ -3169,7 +3169,7 @@ static int __init init_ntfs_fs(void) ntfs_inode_cache = kmem_cache_create(ntfs_inode_cache_name, sizeof(ntfs_inode), 0, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL, NULL); + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); if (!ntfs_inode_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_inode_cache_name); @@ -3179,7 +3179,7 @@ static int __init init_ntfs_fs(void) ntfs_big_inode_cache = kmem_cache_create(ntfs_big_inode_cache_name, sizeof(big_ntfs_inode), 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - ntfs_big_inode_init_once, NULL); + ntfs_big_inode_init_once); if (!ntfs_big_inode_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_big_inode_cache_name); diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 19712a7..f5e11f4 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -50,6 +50,8 @@ #include "buffer_head_io.h" static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc); +static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt, + struct ocfs2_extent_block *eb); /* * Structures which describe a path through a btree, and functions to @@ -117,6 +119,31 @@ static void ocfs2_free_path(struct ocfs2_path *path) } /* + * All the elements of src into dest. After this call, src could be freed + * without affecting dest. + * + * Both paths should have the same root. Any non-root elements of dest + * will be freed. + */ +static void ocfs2_cp_path(struct ocfs2_path *dest, struct ocfs2_path *src) +{ + int i; + + BUG_ON(path_root_bh(dest) != path_root_bh(src)); + BUG_ON(path_root_el(dest) != path_root_el(src)); + + ocfs2_reinit_path(dest, 1); + + for(i = 1; i < OCFS2_MAX_PATH_DEPTH; i++) { + dest->p_node[i].bh = src->p_node[i].bh; + dest->p_node[i].el = src->p_node[i].el; + + if (dest->p_node[i].bh) + get_bh(dest->p_node[i].bh); + } +} + +/* * Make the *dest path the same as src and re-initialize src path to * have a root only. */ @@ -212,10 +239,41 @@ out: return ret; } +/* + * Return the index of the extent record which contains cluster #v_cluster. + * -1 is returned if it was not found. + * + * Should work fine on interior and exterior nodes. + */ +int ocfs2_search_extent_list(struct ocfs2_extent_list *el, u32 v_cluster) +{ + int ret = -1; + int i; + struct ocfs2_extent_rec *rec; + u32 rec_end, rec_start, clusters; + + for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + rec = &el->l_recs[i]; + + rec_start = le32_to_cpu(rec->e_cpos); + clusters = ocfs2_rec_clusters(el, rec); + + rec_end = rec_start + clusters; + + if (v_cluster >= rec_start && v_cluster < rec_end) { + ret = i; + break; + } + } + + return ret; +} + enum ocfs2_contig_type { CONTIG_NONE = 0, CONTIG_LEFT, - CONTIG_RIGHT + CONTIG_RIGHT, + CONTIG_LEFTRIGHT, }; @@ -253,6 +311,14 @@ static enum ocfs2_contig_type { u64 blkno = le64_to_cpu(insert_rec->e_blkno); + /* + * Refuse to coalesce extent records with different flag + * fields - we don't want to mix unwritten extents with user + * data. + */ + if (ext->e_flags != insert_rec->e_flags) + return CONTIG_NONE; + if (ocfs2_extents_adjacent(ext, insert_rec) && ocfs2_block_extent_contig(inode->i_sb, ext, blkno)) return CONTIG_RIGHT; @@ -277,7 +343,14 @@ enum ocfs2_append_type { APPEND_TAIL, }; +enum ocfs2_split_type { + SPLIT_NONE = 0, + SPLIT_LEFT, + SPLIT_RIGHT, +}; + struct ocfs2_insert_type { + enum ocfs2_split_type ins_split; enum ocfs2_append_type ins_appending; enum ocfs2_contig_type ins_contig; int ins_contig_index; @@ -285,6 +358,13 @@ struct ocfs2_insert_type { int ins_tree_depth; }; +struct ocfs2_merge_ctxt { + enum ocfs2_contig_type c_contig_type; + int c_has_empty_extent; + int c_split_covers_rec; + int c_used_tail_recs; +}; + /* * How many free extents have we got before we need more meta data? */ @@ -384,13 +464,7 @@ static int ocfs2_create_new_meta_bhs(struct ocfs2_super *osb, strcpy(eb->h_signature, OCFS2_EXTENT_BLOCK_SIGNATURE); eb->h_blkno = cpu_to_le64(first_blkno); eb->h_fs_generation = cpu_to_le32(osb->fs_generation); - -#ifndef OCFS2_USE_ALL_METADATA_SUBALLOCATORS - /* we always use slot zero's suballocator */ - eb->h_suballoc_slot = 0; -#else eb->h_suballoc_slot = cpu_to_le16(osb->slot_num); -#endif eb->h_suballoc_bit = cpu_to_le16(suballoc_bit_start); eb->h_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_eb(osb->sb)); @@ -461,7 +535,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh, struct buffer_head *eb_bh, - struct buffer_head *last_eb_bh, + struct buffer_head **last_eb_bh, struct ocfs2_alloc_context *meta_ac) { int status, new_blocks, i; @@ -476,7 +550,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, mlog_entry_void(); - BUG_ON(!last_eb_bh); + BUG_ON(!last_eb_bh || !*last_eb_bh); fe = (struct ocfs2_dinode *) fe_bh->b_data; @@ -507,7 +581,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, goto bail; } - eb = (struct ocfs2_extent_block *)last_eb_bh->b_data; + eb = (struct ocfs2_extent_block *)(*last_eb_bh)->b_data; new_cpos = ocfs2_sum_rightmost_rec(&eb->h_list); /* Note: new_eb_bhs[new_blocks - 1] is the guy which will be @@ -568,7 +642,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, * journal_dirty erroring as it won't unless we've aborted the * handle (in which case we would never be here) so reserving * the write with journal_access is all we need to do. */ - status = ocfs2_journal_access(handle, inode, last_eb_bh, + status = ocfs2_journal_access(handle, inode, *last_eb_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); @@ -601,10 +675,10 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, * next_leaf on the previously last-extent-block. */ fe->i_last_eb_blk = cpu_to_le64(new_last_eb_blk); - eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; + eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data; eb->h_next_leaf_blk = cpu_to_le64(new_last_eb_blk); - status = ocfs2_journal_dirty(handle, last_eb_bh); + status = ocfs2_journal_dirty(handle, *last_eb_bh); if (status < 0) mlog_errno(status); status = ocfs2_journal_dirty(handle, fe_bh); @@ -616,6 +690,14 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, mlog_errno(status); } + /* + * Some callers want to track the rightmost leaf so pass it + * back here. + */ + brelse(*last_eb_bh); + get_bh(new_eb_bhs[0]); + *last_eb_bh = new_eb_bhs[0]; + status = 0; bail: if (new_eb_bhs) { @@ -829,6 +911,87 @@ bail: } /* + * Grow a b-tree so that it has more records. + * + * We might shift the tree depth in which case existing paths should + * be considered invalid. + * + * Tree depth after the grow is returned via *final_depth. + * + * *last_eb_bh will be updated by ocfs2_add_branch(). + */ +static int ocfs2_grow_tree(struct inode *inode, handle_t *handle, + struct buffer_head *di_bh, int *final_depth, + struct buffer_head **last_eb_bh, + struct ocfs2_alloc_context *meta_ac) +{ + int ret, shift; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + int depth = le16_to_cpu(di->id2.i_list.l_tree_depth); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct buffer_head *bh = NULL; + + BUG_ON(meta_ac == NULL); + + shift = ocfs2_find_branch_target(osb, inode, di_bh, &bh); + if (shift < 0) { + ret = shift; + mlog_errno(ret); + goto out; + } + + /* We traveled all the way to the bottom of the allocation tree + * and didn't find room for any more extents - we need to add + * another tree level */ + if (shift) { + BUG_ON(bh); + mlog(0, "need to shift tree depth (current = %d)\n", depth); + + /* ocfs2_shift_tree_depth will return us a buffer with + * the new extent block (so we can pass that to + * ocfs2_add_branch). */ + ret = ocfs2_shift_tree_depth(osb, handle, inode, di_bh, + meta_ac, &bh); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + depth++; + if (depth == 1) { + /* + * Special case: we have room now if we shifted from + * tree_depth 0, so no more work needs to be done. + * + * We won't be calling add_branch, so pass + * back *last_eb_bh as the new leaf. At depth + * zero, it should always be null so there's + * no reason to brelse. + */ + BUG_ON(*last_eb_bh); + get_bh(bh); + *last_eb_bh = bh; + goto out; + } + } + + /* call ocfs2_add_branch to add the final part of the tree with + * the new data. */ + mlog(0, "add branch. bh = %p\n", bh); + ret = ocfs2_add_branch(osb, handle, inode, di_bh, bh, last_eb_bh, + meta_ac); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + +out: + if (final_depth) + *final_depth = depth; + brelse(bh); + return ret; +} + +/* * This is only valid for leaf nodes, which are the only ones that can * have empty extents anyway. */ @@ -934,6 +1097,22 @@ static void ocfs2_rotate_leaf(struct ocfs2_extent_list *el, } +static void ocfs2_remove_empty_extent(struct ocfs2_extent_list *el) +{ + int size, num_recs = le16_to_cpu(el->l_next_free_rec); + + BUG_ON(num_recs == 0); + + if (ocfs2_is_empty_extent(&el->l_recs[0])) { + num_recs--; + size = num_recs * sizeof(struct ocfs2_extent_rec); + memmove(&el->l_recs[0], &el->l_recs[1], size); + memset(&el->l_recs[num_recs], 0, + sizeof(struct ocfs2_extent_rec)); + el->l_next_free_rec = cpu_to_le16(num_recs); + } +} + /* * Create an empty extent record . * @@ -1211,6 +1390,10 @@ static void ocfs2_adjust_adjacent_records(struct ocfs2_extent_rec *left_rec, * immediately to their right. */ left_clusters = le32_to_cpu(right_child_el->l_recs[0].e_cpos); + if (ocfs2_is_empty_extent(&right_child_el->l_recs[0])) { + BUG_ON(le16_to_cpu(right_child_el->l_next_free_rec) <= 1); + left_clusters = le32_to_cpu(right_child_el->l_recs[1].e_cpos); + } left_clusters -= le32_to_cpu(left_rec->e_cpos); left_rec->e_int_clusters = cpu_to_le32(left_clusters); @@ -1531,10 +1714,16 @@ out: return ret; } +/* + * Extend the transaction by enough credits to complete the rotation, + * and still leave at least the original number of credits allocated + * to this transaction. + */ static int ocfs2_extend_rotate_transaction(handle_t *handle, int subtree_depth, + int op_credits, struct ocfs2_path *path) { - int credits = (path->p_tree_depth - subtree_depth) * 2 + 1; + int credits = (path->p_tree_depth - subtree_depth) * 2 + 1 + op_credits; if (handle->h_buffer_credits < credits) return ocfs2_extend_trans(handle, credits); @@ -1568,6 +1757,29 @@ static int ocfs2_rotate_requires_path_adjustment(struct ocfs2_path *left_path, return 0; } +static int ocfs2_leftmost_rec_contains(struct ocfs2_extent_list *el, u32 cpos) +{ + int next_free = le16_to_cpu(el->l_next_free_rec); + unsigned int range; + struct ocfs2_extent_rec *rec; + + if (next_free == 0) + return 0; + + rec = &el->l_recs[0]; + if (ocfs2_is_empty_extent(rec)) { + /* Empty list. */ + if (next_free == 1) + return 0; + rec = &el->l_recs[1]; + } + + range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + if (cpos >= le32_to_cpu(rec->e_cpos) && cpos < range) + return 1; + return 0; +} + /* * Rotate all the records in a btree right one record, starting at insert_cpos. * @@ -1586,11 +1798,12 @@ static int ocfs2_rotate_requires_path_adjustment(struct ocfs2_path *left_path, */ static int ocfs2_rotate_tree_right(struct inode *inode, handle_t *handle, + enum ocfs2_split_type split, u32 insert_cpos, struct ocfs2_path *right_path, struct ocfs2_path **ret_left_path) { - int ret, start; + int ret, start, orig_credits = handle->h_buffer_credits; u32 cpos; struct ocfs2_path *left_path = NULL; @@ -1657,9 +1870,9 @@ static int ocfs2_rotate_tree_right(struct inode *inode, (unsigned long long) path_leaf_bh(left_path)->b_blocknr); - if (ocfs2_rotate_requires_path_adjustment(left_path, + if (split == SPLIT_NONE && + ocfs2_rotate_requires_path_adjustment(left_path, insert_cpos)) { - mlog(0, "Path adjustment required\n"); /* * We've rotated the tree as much as we @@ -1687,7 +1900,7 @@ static int ocfs2_rotate_tree_right(struct inode *inode, right_path->p_tree_depth); ret = ocfs2_extend_rotate_transaction(handle, start, - right_path); + orig_credits, right_path); if (ret) { mlog_errno(ret); goto out; @@ -1700,6 +1913,24 @@ static int ocfs2_rotate_tree_right(struct inode *inode, goto out; } + if (split != SPLIT_NONE && + ocfs2_leftmost_rec_contains(path_leaf_el(right_path), + insert_cpos)) { + /* + * A rotate moves the rightmost left leaf + * record over to the leftmost right leaf + * slot. If we're doing an extent split + * instead of a real insert, then we have to + * check that the extent to be split wasn't + * just moved over. If it was, then we can + * exit here, passing left_path back - + * ocfs2_split_extent() is smart enough to + * search both leaves. + */ + *ret_left_path = left_path; + goto out_ret_path; + } + /* * There is no need to re-read the next right path * as we know that it'll be our current left @@ -1722,6 +1953,1031 @@ out_ret_path: return ret; } +static void ocfs2_update_edge_lengths(struct inode *inode, handle_t *handle, + struct ocfs2_path *path) +{ + int i, idx; + struct ocfs2_extent_rec *rec; + struct ocfs2_extent_list *el; + struct ocfs2_extent_block *eb; + u32 range; + + /* Path should always be rightmost. */ + eb = (struct ocfs2_extent_block *)path_leaf_bh(path)->b_data; + BUG_ON(eb->h_next_leaf_blk != 0ULL); + + el = &eb->h_list; + BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0); + idx = le16_to_cpu(el->l_next_free_rec) - 1; + rec = &el->l_recs[idx]; + range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + + for (i = 0; i < path->p_tree_depth; i++) { + el = path->p_node[i].el; + idx = le16_to_cpu(el->l_next_free_rec) - 1; + rec = &el->l_recs[idx]; + + rec->e_int_clusters = cpu_to_le32(range); + le32_add_cpu(&rec->e_int_clusters, -le32_to_cpu(rec->e_cpos)); + + ocfs2_journal_dirty(handle, path->p_node[i].bh); + } +} + +static void ocfs2_unlink_path(struct inode *inode, handle_t *handle, + struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_path *path, int unlink_start) +{ + int ret, i; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *el; + struct buffer_head *bh; + + for(i = unlink_start; i < path_num_items(path); i++) { + bh = path->p_node[i].bh; + + eb = (struct ocfs2_extent_block *)bh->b_data; + /* + * Not all nodes might have had their final count + * decremented by the caller - handle this here. + */ + el = &eb->h_list; + if (le16_to_cpu(el->l_next_free_rec) > 1) { + mlog(ML_ERROR, + "Inode %llu, attempted to remove extent block " + "%llu with %u records\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)le64_to_cpu(eb->h_blkno), + le16_to_cpu(el->l_next_free_rec)); + + ocfs2_journal_dirty(handle, bh); + ocfs2_remove_from_cache(inode, bh); + continue; + } + + el->l_next_free_rec = 0; + memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); + + ocfs2_journal_dirty(handle, bh); + + ret = ocfs2_cache_extent_block_free(dealloc, eb); + if (ret) + mlog_errno(ret); + + ocfs2_remove_from_cache(inode, bh); + } +} + +static void ocfs2_unlink_subtree(struct inode *inode, handle_t *handle, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + int subtree_index, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int i; + struct buffer_head *root_bh = left_path->p_node[subtree_index].bh; + struct ocfs2_extent_list *root_el = left_path->p_node[subtree_index].el; + struct ocfs2_extent_list *el; + struct ocfs2_extent_block *eb; + + el = path_leaf_el(left_path); + + eb = (struct ocfs2_extent_block *)right_path->p_node[subtree_index + 1].bh->b_data; + + for(i = 1; i < le16_to_cpu(root_el->l_next_free_rec); i++) + if (root_el->l_recs[i].e_blkno == eb->h_blkno) + break; + + BUG_ON(i >= le16_to_cpu(root_el->l_next_free_rec)); + + memset(&root_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec)); + le16_add_cpu(&root_el->l_next_free_rec, -1); + + eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data; + eb->h_next_leaf_blk = 0; + + ocfs2_journal_dirty(handle, root_bh); + ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); + + ocfs2_unlink_path(inode, handle, dealloc, right_path, + subtree_index + 1); +} + +static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + int subtree_index, + struct ocfs2_cached_dealloc_ctxt *dealloc, + int *deleted) +{ + int ret, i, del_right_subtree = 0, right_has_empty = 0; + struct buffer_head *root_bh, *di_bh = path_root_bh(right_path); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_extent_list *right_leaf_el, *left_leaf_el; + struct ocfs2_extent_block *eb; + + *deleted = 0; + + right_leaf_el = path_leaf_el(right_path); + left_leaf_el = path_leaf_el(left_path); + root_bh = left_path->p_node[subtree_index].bh; + BUG_ON(root_bh != right_path->p_node[subtree_index].bh); + + if (!ocfs2_is_empty_extent(&left_leaf_el->l_recs[0])) + return 0; + + eb = (struct ocfs2_extent_block *)path_leaf_bh(right_path)->b_data; + if (ocfs2_is_empty_extent(&right_leaf_el->l_recs[0])) { + /* + * It's legal for us to proceed if the right leaf is + * the rightmost one and it has an empty extent. There + * are two cases to handle - whether the leaf will be + * empty after removal or not. If the leaf isn't empty + * then just remove the empty extent up front. The + * next block will handle empty leaves by flagging + * them for unlink. + * + * Non rightmost leaves will throw -EAGAIN and the + * caller can manually move the subtree and retry. + */ + + if (eb->h_next_leaf_blk != 0ULL) + return -EAGAIN; + + if (le16_to_cpu(right_leaf_el->l_next_free_rec) > 1) { + ret = ocfs2_journal_access(handle, inode, + path_leaf_bh(right_path), + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ocfs2_remove_empty_extent(right_leaf_el); + } else + right_has_empty = 1; + } + + if (eb->h_next_leaf_blk == 0ULL && + le16_to_cpu(right_leaf_el->l_next_free_rec) == 1) { + /* + * We have to update i_last_eb_blk during the meta + * data delete. + */ + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + del_right_subtree = 1; + } + + /* + * Getting here with an empty extent in the right path implies + * that it's the rightmost path and will be deleted. + */ + BUG_ON(right_has_empty && !del_right_subtree); + + ret = ocfs2_journal_access(handle, inode, root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + for(i = subtree_index + 1; i < path_num_items(right_path); i++) { + ret = ocfs2_journal_access(handle, inode, + right_path->p_node[i].bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, + left_path->p_node[i].bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + if (!right_has_empty) { + /* + * Only do this if we're moving a real + * record. Otherwise, the action is delayed until + * after removal of the right path in which case we + * can do a simple shift to remove the empty extent. + */ + ocfs2_rotate_leaf(left_leaf_el, &right_leaf_el->l_recs[0]); + memset(&right_leaf_el->l_recs[0], 0, + sizeof(struct ocfs2_extent_rec)); + } + if (eb->h_next_leaf_blk == 0ULL) { + /* + * Move recs over to get rid of empty extent, decrease + * next_free. This is allowed to remove the last + * extent in our leaf (setting l_next_free_rec to + * zero) - the delete code below won't care. + */ + ocfs2_remove_empty_extent(right_leaf_el); + } + + ret = ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); + if (ret) + mlog_errno(ret); + ret = ocfs2_journal_dirty(handle, path_leaf_bh(right_path)); + if (ret) + mlog_errno(ret); + + if (del_right_subtree) { + ocfs2_unlink_subtree(inode, handle, left_path, right_path, + subtree_index, dealloc); + ocfs2_update_edge_lengths(inode, handle, left_path); + + eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data; + di->i_last_eb_blk = eb->h_blkno; + + /* + * Removal of the extent in the left leaf was skipped + * above so we could delete the right path + * 1st. + */ + if (right_has_empty) + ocfs2_remove_empty_extent(left_leaf_el); + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret) + mlog_errno(ret); + + *deleted = 1; + } else + ocfs2_complete_edge_insert(inode, handle, left_path, right_path, + subtree_index); + +out: + return ret; +} + +/* + * Given a full path, determine what cpos value would return us a path + * containing the leaf immediately to the right of the current one. + * + * Will return zero if the path passed in is already the rightmost path. + * + * This looks similar, but is subtly different to + * ocfs2_find_cpos_for_left_leaf(). + */ +static int ocfs2_find_cpos_for_right_leaf(struct super_block *sb, + struct ocfs2_path *path, u32 *cpos) +{ + int i, j, ret = 0; + u64 blkno; + struct ocfs2_extent_list *el; + + *cpos = 0; + + if (path->p_tree_depth == 0) + return 0; + + blkno = path_leaf_bh(path)->b_blocknr; + + /* Start at the tree node just above the leaf and work our way up. */ + i = path->p_tree_depth - 1; + while (i >= 0) { + int next_free; + + el = path->p_node[i].el; + + /* + * Find the extent record just after the one in our + * path. + */ + next_free = le16_to_cpu(el->l_next_free_rec); + for(j = 0; j < le16_to_cpu(el->l_next_free_rec); j++) { + if (le64_to_cpu(el->l_recs[j].e_blkno) == blkno) { + if (j == (next_free - 1)) { + if (i == 0) { + /* + * We've determined that the + * path specified is already + * the rightmost one - return a + * cpos of zero. + */ + goto out; + } + /* + * The rightmost record points to our + * leaf - we need to travel up the + * tree one level. + */ + goto next_node; + } + + *cpos = le32_to_cpu(el->l_recs[j + 1].e_cpos); + goto out; + } + } + + /* + * If we got here, we never found a valid node where + * the tree indicated one should be. + */ + ocfs2_error(sb, + "Invalid extent tree at extent block %llu\n", + (unsigned long long)blkno); + ret = -EROFS; + goto out; + +next_node: + blkno = path->p_node[i].bh->b_blocknr; + i--; + } + +out: + return ret; +} + +static int ocfs2_rotate_rightmost_leaf_left(struct inode *inode, + handle_t *handle, + struct buffer_head *bh, + struct ocfs2_extent_list *el) +{ + int ret; + + if (!ocfs2_is_empty_extent(&el->l_recs[0])) + return 0; + + ret = ocfs2_journal_access(handle, inode, bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ocfs2_remove_empty_extent(el); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret) + mlog_errno(ret); + +out: + return ret; +} + +static int __ocfs2_rotate_tree_left(struct inode *inode, + handle_t *handle, int orig_credits, + struct ocfs2_path *path, + struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_path **empty_extent_path) +{ + int ret, subtree_root, deleted; + u32 right_cpos; + struct ocfs2_path *left_path = NULL; + struct ocfs2_path *right_path = NULL; + + BUG_ON(!ocfs2_is_empty_extent(&(path_leaf_el(path)->l_recs[0]))); + + *empty_extent_path = NULL; + + ret = ocfs2_find_cpos_for_right_leaf(inode->i_sb, path, + &right_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + left_path = ocfs2_new_path(path_root_bh(path), + path_root_el(path)); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ocfs2_cp_path(left_path, path); + + right_path = ocfs2_new_path(path_root_bh(path), + path_root_el(path)); + if (!right_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + while (right_cpos) { + ret = ocfs2_find_path(inode, right_path, right_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + subtree_root = ocfs2_find_subtree_root(inode, left_path, + right_path); + + mlog(0, "Subtree root at index %d (blk %llu, depth %d)\n", + subtree_root, + (unsigned long long) + right_path->p_node[subtree_root].bh->b_blocknr, + right_path->p_tree_depth); + + ret = ocfs2_extend_rotate_transaction(handle, subtree_root, + orig_credits, left_path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_rotate_subtree_left(inode, handle, left_path, + right_path, subtree_root, + dealloc, &deleted); + if (ret == -EAGAIN) { + /* + * The rotation has to temporarily stop due to + * the right subtree having an empty + * extent. Pass it back to the caller for a + * fixup. + */ + *empty_extent_path = right_path; + right_path = NULL; + goto out; + } + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * The subtree rotate might have removed records on + * the rightmost edge. If so, then rotation is + * complete. + */ + if (deleted) + break; + + ocfs2_mv_path(left_path, right_path); + + ret = ocfs2_find_cpos_for_right_leaf(inode->i_sb, left_path, + &right_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + } + +out: + ocfs2_free_path(right_path); + ocfs2_free_path(left_path); + + return ret; +} + +static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle, + struct ocfs2_path *path, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret, subtree_index; + u32 cpos; + struct ocfs2_path *left_path = NULL; + struct ocfs2_dinode *di; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *el; + + /* + * XXX: This code assumes that the root is an inode, which is + * true for now but may change as tree code gets generic. + */ + di = (struct ocfs2_dinode *)path_root_bh(path)->b_data; + if (!OCFS2_IS_VALID_DINODE(di)) { + ret = -EIO; + ocfs2_error(inode->i_sb, + "Inode %llu has invalid path root", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + goto out; + } + + /* + * There's two ways we handle this depending on + * whether path is the only existing one. + */ + ret = ocfs2_extend_rotate_transaction(handle, 0, + handle->h_buffer_credits, + path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access_path(inode, handle, path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, &cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (cpos) { + /* + * We have a path to the left of this one - it needs + * an update too. + */ + left_path = ocfs2_new_path(path_root_bh(path), + path_root_el(path)); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_path(inode, left_path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access_path(inode, handle, left_path); + if (ret) { + mlog_errno(ret); + goto out; + } + + subtree_index = ocfs2_find_subtree_root(inode, left_path, path); + + ocfs2_unlink_subtree(inode, handle, left_path, path, + subtree_index, dealloc); + ocfs2_update_edge_lengths(inode, handle, left_path); + + eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data; + di->i_last_eb_blk = eb->h_blkno; + } else { + /* + * 'path' is also the leftmost path which + * means it must be the only one. This gets + * handled differently because we want to + * revert the inode back to having extents + * in-line. + */ + ocfs2_unlink_path(inode, handle, dealloc, path, 1); + + el = &di->id2.i_list; + el->l_tree_depth = 0; + el->l_next_free_rec = 0; + memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); + + di->i_last_eb_blk = 0; + } + + ocfs2_journal_dirty(handle, path_root_bh(path)); + +out: + ocfs2_free_path(left_path); + return ret; +} + +/* + * Left rotation of btree records. + * + * In many ways, this is (unsurprisingly) the opposite of right + * rotation. We start at some non-rightmost path containing an empty + * extent in the leaf block. The code works its way to the rightmost + * path by rotating records to the left in every subtree. + * + * This is used by any code which reduces the number of extent records + * in a leaf. After removal, an empty record should be placed in the + * leftmost list position. + * + * This won't handle a length update of the rightmost path records if + * the rightmost tree leaf record is removed so the caller is + * responsible for detecting and correcting that. + */ +static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle, + struct ocfs2_path *path, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret, orig_credits = handle->h_buffer_credits; + struct ocfs2_path *tmp_path = NULL, *restart_path = NULL; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *el; + + el = path_leaf_el(path); + if (!ocfs2_is_empty_extent(&el->l_recs[0])) + return 0; + + if (path->p_tree_depth == 0) { +rightmost_no_delete: + /* + * In-inode extents. This is trivially handled, so do + * it up front. + */ + ret = ocfs2_rotate_rightmost_leaf_left(inode, handle, + path_leaf_bh(path), + path_leaf_el(path)); + if (ret) + mlog_errno(ret); + goto out; + } + + /* + * Handle rightmost branch now. There's several cases: + * 1) simple rotation leaving records in there. That's trivial. + * 2) rotation requiring a branch delete - there's no more + * records left. Two cases of this: + * a) There are branches to the left. + * b) This is also the leftmost (the only) branch. + * + * 1) is handled via ocfs2_rotate_rightmost_leaf_left() + * 2a) we need the left branch so that we can update it with the unlink + * 2b) we need to bring the inode back to inline extents. + */ + + eb = (struct ocfs2_extent_block *)path_leaf_bh(path)->b_data; + el = &eb->h_list; + if (eb->h_next_leaf_blk == 0) { + /* + * This gets a bit tricky if we're going to delete the + * rightmost path. Get the other cases out of the way + * 1st. + */ + if (le16_to_cpu(el->l_next_free_rec) > 1) + goto rightmost_no_delete; + + if (le16_to_cpu(el->l_next_free_rec) == 0) { + ret = -EIO; + ocfs2_error(inode->i_sb, + "Inode %llu has empty extent block at %llu", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)le64_to_cpu(eb->h_blkno)); + goto out; + } + + /* + * XXX: The caller can not trust "path" any more after + * this as it will have been deleted. What do we do? + * + * In theory the rotate-for-merge code will never get + * here because it'll always ask for a rotate in a + * nonempty list. + */ + + ret = ocfs2_remove_rightmost_path(inode, handle, path, + dealloc); + if (ret) + mlog_errno(ret); + goto out; + } + + /* + * Now we can loop, remembering the path we get from -EAGAIN + * and restarting from there. + */ +try_rotate: + ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, path, + dealloc, &restart_path); + if (ret && ret != -EAGAIN) { + mlog_errno(ret); + goto out; + } + + while (ret == -EAGAIN) { + tmp_path = restart_path; + restart_path = NULL; + + ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, + tmp_path, dealloc, + &restart_path); + if (ret && ret != -EAGAIN) { + mlog_errno(ret); + goto out; + } + + ocfs2_free_path(tmp_path); + tmp_path = NULL; + + if (ret == 0) + goto try_rotate; + } + +out: + ocfs2_free_path(tmp_path); + ocfs2_free_path(restart_path); + return ret; +} + +static void ocfs2_cleanup_merge(struct ocfs2_extent_list *el, + int index) +{ + struct ocfs2_extent_rec *rec = &el->l_recs[index]; + unsigned int size; + + if (rec->e_leaf_clusters == 0) { + /* + * We consumed all of the merged-from record. An empty + * extent cannot exist anywhere but the 1st array + * position, so move things over if the merged-from + * record doesn't occupy that position. + * + * This creates a new empty extent so the caller + * should be smart enough to have removed any existing + * ones. + */ + if (index > 0) { + BUG_ON(ocfs2_is_empty_extent(&el->l_recs[0])); + size = index * sizeof(struct ocfs2_extent_rec); + memmove(&el->l_recs[1], &el->l_recs[0], size); + } + + /* + * Always memset - the caller doesn't check whether it + * created an empty extent, so there could be junk in + * the other fields. + */ + memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); + } +} + +/* + * Remove split_rec clusters from the record at index and merge them + * onto the beginning of the record at index + 1. + */ +static int ocfs2_merge_rec_right(struct inode *inode, struct buffer_head *bh, + handle_t *handle, + struct ocfs2_extent_rec *split_rec, + struct ocfs2_extent_list *el, int index) +{ + int ret; + unsigned int split_clusters = le16_to_cpu(split_rec->e_leaf_clusters); + struct ocfs2_extent_rec *left_rec; + struct ocfs2_extent_rec *right_rec; + + BUG_ON(index >= le16_to_cpu(el->l_next_free_rec)); + + left_rec = &el->l_recs[index]; + right_rec = &el->l_recs[index + 1]; + + ret = ocfs2_journal_access(handle, inode, bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + le16_add_cpu(&left_rec->e_leaf_clusters, -split_clusters); + + le32_add_cpu(&right_rec->e_cpos, -split_clusters); + le64_add_cpu(&right_rec->e_blkno, + -ocfs2_clusters_to_blocks(inode->i_sb, split_clusters)); + le16_add_cpu(&right_rec->e_leaf_clusters, split_clusters); + + ocfs2_cleanup_merge(el, index); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret) + mlog_errno(ret); + +out: + return ret; +} + +/* + * Remove split_rec clusters from the record at index and merge them + * onto the tail of the record at index - 1. + */ +static int ocfs2_merge_rec_left(struct inode *inode, struct buffer_head *bh, + handle_t *handle, + struct ocfs2_extent_rec *split_rec, + struct ocfs2_extent_list *el, int index) +{ + int ret, has_empty_extent = 0; + unsigned int split_clusters = le16_to_cpu(split_rec->e_leaf_clusters); + struct ocfs2_extent_rec *left_rec; + struct ocfs2_extent_rec *right_rec; + + BUG_ON(index <= 0); + + left_rec = &el->l_recs[index - 1]; + right_rec = &el->l_recs[index]; + if (ocfs2_is_empty_extent(&el->l_recs[0])) + has_empty_extent = 1; + + ret = ocfs2_journal_access(handle, inode, bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (has_empty_extent && index == 1) { + /* + * The easy case - we can just plop the record right in. + */ + *left_rec = *split_rec; + + has_empty_extent = 0; + } else { + le16_add_cpu(&left_rec->e_leaf_clusters, split_clusters); + } + + le32_add_cpu(&right_rec->e_cpos, split_clusters); + le64_add_cpu(&right_rec->e_blkno, + ocfs2_clusters_to_blocks(inode->i_sb, split_clusters)); + le16_add_cpu(&right_rec->e_leaf_clusters, -split_clusters); + + ocfs2_cleanup_merge(el, index); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret) + mlog_errno(ret); + +out: + return ret; +} + +static int ocfs2_try_to_merge_extent(struct inode *inode, + handle_t *handle, + struct ocfs2_path *left_path, + int split_index, + struct ocfs2_extent_rec *split_rec, + struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_merge_ctxt *ctxt) + +{ + int ret = 0, delete_tail_recs = 0; + struct ocfs2_extent_list *el = path_leaf_el(left_path); + struct ocfs2_extent_rec *rec = &el->l_recs[split_index]; + + BUG_ON(ctxt->c_contig_type == CONTIG_NONE); + + if (ctxt->c_split_covers_rec) { + delete_tail_recs++; + + if (ctxt->c_contig_type == CONTIG_LEFTRIGHT || + ctxt->c_has_empty_extent) + delete_tail_recs++; + + if (ctxt->c_has_empty_extent) { + /* + * The merge code will need to create an empty + * extent to take the place of the newly + * emptied slot. Remove any pre-existing empty + * extents - having more than one in a leaf is + * illegal. + */ + ret = ocfs2_rotate_tree_left(inode, handle, left_path, + dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + split_index--; + rec = &el->l_recs[split_index]; + } + } + + if (ctxt->c_contig_type == CONTIG_LEFTRIGHT) { + /* + * Left-right contig implies this. + */ + BUG_ON(!ctxt->c_split_covers_rec); + BUG_ON(split_index == 0); + + /* + * Since the leftright insert always covers the entire + * extent, this call will delete the insert record + * entirely, resulting in an empty extent record added to + * the extent block. + * + * Since the adding of an empty extent shifts + * everything back to the right, there's no need to + * update split_index here. + */ + ret = ocfs2_merge_rec_left(inode, path_leaf_bh(left_path), + handle, split_rec, el, split_index); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * We can only get this from logic error above. + */ + BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0])); + + /* + * The left merge left us with an empty extent, remove + * it. + */ + ret = ocfs2_rotate_tree_left(inode, handle, left_path, dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + split_index--; + rec = &el->l_recs[split_index]; + + /* + * Note that we don't pass split_rec here on purpose - + * we've merged it into the left side. + */ + ret = ocfs2_merge_rec_right(inode, path_leaf_bh(left_path), + handle, rec, el, split_index); + if (ret) { + mlog_errno(ret); + goto out; + } + + BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0])); + + ret = ocfs2_rotate_tree_left(inode, handle, left_path, + dealloc); + /* + * Error from this last rotate is not critical, so + * print but don't bubble it up. + */ + if (ret) + mlog_errno(ret); + ret = 0; + } else { + /* + * Merge a record to the left or right. + * + * 'contig_type' is relative to the existing record, + * so for example, if we're "right contig", it's to + * the record on the left (hence the left merge). + */ + if (ctxt->c_contig_type == CONTIG_RIGHT) { + ret = ocfs2_merge_rec_left(inode, + path_leaf_bh(left_path), + handle, split_rec, el, + split_index); + if (ret) { + mlog_errno(ret); + goto out; + } + } else { + ret = ocfs2_merge_rec_right(inode, + path_leaf_bh(left_path), + handle, split_rec, el, + split_index); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + if (ctxt->c_split_covers_rec) { + /* + * The merge may have left an empty extent in + * our leaf. Try to rotate it away. + */ + ret = ocfs2_rotate_tree_left(inode, handle, left_path, + dealloc); + if (ret) + mlog_errno(ret); + ret = 0; + } + } + +out: + return ret; +} + +static void ocfs2_subtract_from_rec(struct super_block *sb, + enum ocfs2_split_type split, + struct ocfs2_extent_rec *rec, + struct ocfs2_extent_rec *split_rec) +{ + u64 len_blocks; + + len_blocks = ocfs2_clusters_to_blocks(sb, + le16_to_cpu(split_rec->e_leaf_clusters)); + + if (split == SPLIT_LEFT) { + /* + * Region is on the left edge of the existing + * record. + */ + le32_add_cpu(&rec->e_cpos, + le16_to_cpu(split_rec->e_leaf_clusters)); + le64_add_cpu(&rec->e_blkno, len_blocks); + le16_add_cpu(&rec->e_leaf_clusters, + -le16_to_cpu(split_rec->e_leaf_clusters)); + } else { + /* + * Region is on the right edge of the existing + * record. + */ + le16_add_cpu(&rec->e_leaf_clusters, + -le16_to_cpu(split_rec->e_leaf_clusters)); + } +} + /* * Do the final bits of extent record insertion at the target leaf * list. If this leaf is part of an allocation tree, it is assumed @@ -1738,6 +2994,15 @@ static void ocfs2_insert_at_leaf(struct ocfs2_extent_rec *insert_rec, BUG_ON(le16_to_cpu(el->l_tree_depth) != 0); + if (insert->ins_split != SPLIT_NONE) { + i = ocfs2_search_extent_list(el, le32_to_cpu(insert_rec->e_cpos)); + BUG_ON(i == -1); + rec = &el->l_recs[i]; + ocfs2_subtract_from_rec(inode->i_sb, insert->ins_split, rec, + insert_rec); + goto rotate; + } + /* * Contiguous insert - either left or right. */ @@ -1792,6 +3057,7 @@ static void ocfs2_insert_at_leaf(struct ocfs2_extent_rec *insert_rec, return; } +rotate: /* * Ok, we have to rotate. * @@ -1815,13 +3081,53 @@ static inline void ocfs2_update_dinode_clusters(struct inode *inode, spin_unlock(&OCFS2_I(inode)->ip_lock); } +static void ocfs2_adjust_rightmost_records(struct inode *inode, + handle_t *handle, + struct ocfs2_path *path, + struct ocfs2_extent_rec *insert_rec) +{ + int ret, i, next_free; + struct buffer_head *bh; + struct ocfs2_extent_list *el; + struct ocfs2_extent_rec *rec; + + /* + * Update everything except the leaf block. + */ + for (i = 0; i < path->p_tree_depth; i++) { + bh = path->p_node[i].bh; + el = path->p_node[i].el; + + next_free = le16_to_cpu(el->l_next_free_rec); + if (next_free == 0) { + ocfs2_error(inode->i_sb, + "Dinode %llu has a bad extent list", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + ret = -EIO; + return; + } + + rec = &el->l_recs[next_free - 1]; + + rec->e_int_clusters = insert_rec->e_cpos; + le32_add_cpu(&rec->e_int_clusters, + le16_to_cpu(insert_rec->e_leaf_clusters)); + le32_add_cpu(&rec->e_int_clusters, + -le32_to_cpu(rec->e_cpos)); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret) + mlog_errno(ret); + + } +} + static int ocfs2_append_rec_to_path(struct inode *inode, handle_t *handle, struct ocfs2_extent_rec *insert_rec, struct ocfs2_path *right_path, struct ocfs2_path **ret_left_path) { - int ret, i, next_free; - struct buffer_head *bh; + int ret, next_free; struct ocfs2_extent_list *el; struct ocfs2_path *left_path = NULL; @@ -1887,40 +3193,7 @@ static int ocfs2_append_rec_to_path(struct inode *inode, handle_t *handle, goto out; } - el = path_root_el(right_path); - bh = path_root_bh(right_path); - i = 0; - while (1) { - struct ocfs2_extent_rec *rec; - - next_free = le16_to_cpu(el->l_next_free_rec); - if (next_free == 0) { - ocfs2_error(inode->i_sb, - "Dinode %llu has a bad extent list", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - ret = -EIO; - goto out; - } - - rec = &el->l_recs[next_free - 1]; - - rec->e_int_clusters = insert_rec->e_cpos; - le32_add_cpu(&rec->e_int_clusters, - le16_to_cpu(insert_rec->e_leaf_clusters)); - le32_add_cpu(&rec->e_int_clusters, - -le32_to_cpu(rec->e_cpos)); - - ret = ocfs2_journal_dirty(handle, bh); - if (ret) - mlog_errno(ret); - - /* Don't touch the leaf node */ - if (++i >= right_path->p_tree_depth) - break; - - bh = right_path->p_node[i].bh; - el = right_path->p_node[i].el; - } + ocfs2_adjust_rightmost_records(inode, handle, right_path, insert_rec); *ret_left_path = left_path; ret = 0; @@ -1931,6 +3204,83 @@ out: return ret; } +static void ocfs2_split_record(struct inode *inode, + struct ocfs2_path *left_path, + struct ocfs2_path *right_path, + struct ocfs2_extent_rec *split_rec, + enum ocfs2_split_type split) +{ + int index; + u32 cpos = le32_to_cpu(split_rec->e_cpos); + struct ocfs2_extent_list *left_el = NULL, *right_el, *insert_el, *el; + struct ocfs2_extent_rec *rec, *tmprec; + + right_el = path_leaf_el(right_path);; + if (left_path) + left_el = path_leaf_el(left_path); + + el = right_el; + insert_el = right_el; + index = ocfs2_search_extent_list(el, cpos); + if (index != -1) { + if (index == 0 && left_path) { + BUG_ON(ocfs2_is_empty_extent(&el->l_recs[0])); + + /* + * This typically means that the record + * started in the left path but moved to the + * right as a result of rotation. We either + * move the existing record to the left, or we + * do the later insert there. + * + * In this case, the left path should always + * exist as the rotate code will have passed + * it back for a post-insert update. + */ + + if (split == SPLIT_LEFT) { + /* + * It's a left split. Since we know + * that the rotate code gave us an + * empty extent in the left path, we + * can just do the insert there. + */ + insert_el = left_el; + } else { + /* + * Right split - we have to move the + * existing record over to the left + * leaf. The insert will be into the + * newly created empty extent in the + * right leaf. + */ + tmprec = &right_el->l_recs[index]; + ocfs2_rotate_leaf(left_el, tmprec); + el = left_el; + + memset(tmprec, 0, sizeof(*tmprec)); + index = ocfs2_search_extent_list(left_el, cpos); + BUG_ON(index == -1); + } + } + } else { + BUG_ON(!left_path); + BUG_ON(!ocfs2_is_empty_extent(&left_el->l_recs[0])); + /* + * Left path is easy - we can just allow the insert to + * happen. + */ + el = left_el; + insert_el = left_el; + index = ocfs2_search_extent_list(el, cpos); + BUG_ON(index == -1); + } + + rec = &el->l_recs[index]; + ocfs2_subtract_from_rec(inode->i_sb, split, rec, split_rec); + ocfs2_rotate_leaf(insert_el, split_rec); +} + /* * This function only does inserts on an allocation b-tree. For dinode * lists, ocfs2_insert_at_leaf() is called directly. @@ -1948,7 +3298,6 @@ static int ocfs2_insert_path(struct inode *inode, { int ret, subtree_index; struct buffer_head *leaf_bh = path_leaf_bh(right_path); - struct ocfs2_extent_list *el; /* * Pass both paths to the journal. The majority of inserts @@ -1984,9 +3333,18 @@ static int ocfs2_insert_path(struct inode *inode, } } - el = path_leaf_el(right_path); + if (insert->ins_split != SPLIT_NONE) { + /* + * We could call ocfs2_insert_at_leaf() for some types + * of splits, but it's easier to just let one seperate + * function sort it all out. + */ + ocfs2_split_record(inode, left_path, right_path, + insert_rec, insert->ins_split); + } else + ocfs2_insert_at_leaf(insert_rec, path_leaf_el(right_path), + insert, inode); - ocfs2_insert_at_leaf(insert_rec, el, insert, inode); ret = ocfs2_journal_dirty(handle, leaf_bh); if (ret) mlog_errno(ret); @@ -2075,7 +3433,7 @@ static int ocfs2_do_insert_extent(struct inode *inode, * can wind up skipping both of these two special cases... */ if (rotate) { - ret = ocfs2_rotate_tree_right(inode, handle, + ret = ocfs2_rotate_tree_right(inode, handle, type->ins_split, le32_to_cpu(insert_rec->e_cpos), right_path, &left_path); if (ret) { @@ -2100,8 +3458,9 @@ static int ocfs2_do_insert_extent(struct inode *inode, } out_update_clusters: - ocfs2_update_dinode_clusters(inode, di, - le16_to_cpu(insert_rec->e_leaf_clusters)); + if (type->ins_split == SPLIT_NONE) + ocfs2_update_dinode_clusters(inode, di, + le16_to_cpu(insert_rec->e_leaf_clusters)); ret = ocfs2_journal_dirty(handle, di_bh); if (ret) @@ -2114,6 +3473,44 @@ out: return ret; } +static enum ocfs2_contig_type +ocfs2_figure_merge_contig_type(struct inode *inode, + struct ocfs2_extent_list *el, int index, + struct ocfs2_extent_rec *split_rec) +{ + struct ocfs2_extent_rec *rec; + enum ocfs2_contig_type ret = CONTIG_NONE; + + /* + * We're careful to check for an empty extent record here - + * the merge code will know what to do if it sees one. + */ + + if (index > 0) { + rec = &el->l_recs[index - 1]; + if (index == 1 && ocfs2_is_empty_extent(rec)) { + if (split_rec->e_cpos == el->l_recs[index].e_cpos) + ret = CONTIG_RIGHT; + } else { + ret = ocfs2_extent_contig(inode, rec, split_rec); + } + } + + if (index < (le16_to_cpu(el->l_next_free_rec) - 1)) { + enum ocfs2_contig_type contig_type; + + rec = &el->l_recs[index + 1]; + contig_type = ocfs2_extent_contig(inode, rec, split_rec); + + if (contig_type == CONTIG_LEFT && ret == CONTIG_RIGHT) + ret = CONTIG_LEFTRIGHT; + else if (ret == CONTIG_NONE) + ret = contig_type; + } + + return ret; +} + static void ocfs2_figure_contig_type(struct inode *inode, struct ocfs2_insert_type *insert, struct ocfs2_extent_list *el, @@ -2205,6 +3602,8 @@ static int ocfs2_figure_insert_type(struct inode *inode, struct ocfs2_path *path = NULL; struct buffer_head *bh = NULL; + insert->ins_split = SPLIT_NONE; + el = &di->id2.i_list; insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth); @@ -2327,9 +3726,10 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, u32 cpos, u64 start_blk, u32 new_clusters, + u8 flags, struct ocfs2_alloc_context *meta_ac) { - int status, shift; + int status; struct buffer_head *last_eb_bh = NULL; struct buffer_head *bh = NULL; struct ocfs2_insert_type insert = {0, }; @@ -2350,6 +3750,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, rec.e_cpos = cpu_to_le32(cpos); rec.e_blkno = cpu_to_le64(start_blk); rec.e_leaf_clusters = cpu_to_le16(new_clusters); + rec.e_flags = flags; status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec, &insert); @@ -2364,55 +3765,16 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, insert.ins_appending, insert.ins_contig, insert.ins_contig_index, insert.ins_free_records, insert.ins_tree_depth); - /* - * Avoid growing the tree unless we're out of records and the - * insert type requres one. - */ - if (insert.ins_contig != CONTIG_NONE || insert.ins_free_records) - goto out_add; - - shift = ocfs2_find_branch_target(osb, inode, fe_bh, &bh); - if (shift < 0) { - status = shift; - mlog_errno(status); - goto bail; - } - - /* We traveled all the way to the bottom of the allocation tree - * and didn't find room for any more extents - we need to add - * another tree level */ - if (shift) { - BUG_ON(bh); - mlog(0, "need to shift tree depth " - "(current = %d)\n", insert.ins_tree_depth); - - /* ocfs2_shift_tree_depth will return us a buffer with - * the new extent block (so we can pass that to - * ocfs2_add_branch). */ - status = ocfs2_shift_tree_depth(osb, handle, inode, fe_bh, - meta_ac, &bh); - if (status < 0) { + if (insert.ins_contig == CONTIG_NONE && insert.ins_free_records == 0) { + status = ocfs2_grow_tree(inode, handle, fe_bh, + &insert.ins_tree_depth, &last_eb_bh, + meta_ac); + if (status) { mlog_errno(status); goto bail; } - insert.ins_tree_depth++; - /* Special case: we have room now if we shifted from - * tree_depth 0 */ - if (insert.ins_tree_depth == 1) - goto out_add; - } - - /* call ocfs2_add_branch to add the final part of the tree with - * the new data. */ - mlog(0, "add branch. bh = %p\n", bh); - status = ocfs2_add_branch(osb, handle, inode, fe_bh, bh, last_eb_bh, - meta_ac); - if (status < 0) { - mlog_errno(status); - goto bail; } -out_add: /* Finally, we can add clusters. This might rotate the tree for us. */ status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert); if (status < 0) @@ -2431,7 +3793,720 @@ bail: return status; } -static inline int ocfs2_truncate_log_needs_flush(struct ocfs2_super *osb) +static void ocfs2_make_right_split_rec(struct super_block *sb, + struct ocfs2_extent_rec *split_rec, + u32 cpos, + struct ocfs2_extent_rec *rec) +{ + u32 rec_cpos = le32_to_cpu(rec->e_cpos); + u32 rec_range = rec_cpos + le16_to_cpu(rec->e_leaf_clusters); + + memset(split_rec, 0, sizeof(struct ocfs2_extent_rec)); + + split_rec->e_cpos = cpu_to_le32(cpos); + split_rec->e_leaf_clusters = cpu_to_le16(rec_range - cpos); + + split_rec->e_blkno = rec->e_blkno; + le64_add_cpu(&split_rec->e_blkno, + ocfs2_clusters_to_blocks(sb, cpos - rec_cpos)); + + split_rec->e_flags = rec->e_flags; +} + +static int ocfs2_split_and_insert(struct inode *inode, + handle_t *handle, + struct ocfs2_path *path, + struct buffer_head *di_bh, + struct buffer_head **last_eb_bh, + int split_index, + struct ocfs2_extent_rec *orig_split_rec, + struct ocfs2_alloc_context *meta_ac) +{ + int ret = 0, depth; + unsigned int insert_range, rec_range, do_leftright = 0; + struct ocfs2_extent_rec tmprec; + struct ocfs2_extent_list *rightmost_el; + struct ocfs2_extent_rec rec; + struct ocfs2_extent_rec split_rec = *orig_split_rec; + struct ocfs2_insert_type insert; + struct ocfs2_extent_block *eb; + struct ocfs2_dinode *di; + +leftright: + /* + * Store a copy of the record on the stack - it might move + * around as the tree is manipulated below. + */ + rec = path_leaf_el(path)->l_recs[split_index]; + + di = (struct ocfs2_dinode *)di_bh->b_data; + rightmost_el = &di->id2.i_list; + + depth = le16_to_cpu(rightmost_el->l_tree_depth); + if (depth) { + BUG_ON(!(*last_eb_bh)); + eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data; + rightmost_el = &eb->h_list; + } + + if (le16_to_cpu(rightmost_el->l_next_free_rec) == + le16_to_cpu(rightmost_el->l_count)) { + int old_depth = depth; + + ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, last_eb_bh, + meta_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (old_depth != depth) { + eb = (struct ocfs2_extent_block *)(*last_eb_bh)->b_data; + rightmost_el = &eb->h_list; + } + } + + memset(&insert, 0, sizeof(struct ocfs2_insert_type)); + insert.ins_appending = APPEND_NONE; + insert.ins_contig = CONTIG_NONE; + insert.ins_free_records = le16_to_cpu(rightmost_el->l_count) + - le16_to_cpu(rightmost_el->l_next_free_rec); + insert.ins_tree_depth = depth; + + insert_range = le32_to_cpu(split_rec.e_cpos) + + le16_to_cpu(split_rec.e_leaf_clusters); + rec_range = le32_to_cpu(rec.e_cpos) + + le16_to_cpu(rec.e_leaf_clusters); + + if (split_rec.e_cpos == rec.e_cpos) { + insert.ins_split = SPLIT_LEFT; + } else if (insert_range == rec_range) { + insert.ins_split = SPLIT_RIGHT; + } else { + /* + * Left/right split. We fake this as a right split + * first and then make a second pass as a left split. + */ + insert.ins_split = SPLIT_RIGHT; + + ocfs2_make_right_split_rec(inode->i_sb, &tmprec, insert_range, + &rec); + + split_rec = tmprec; + + BUG_ON(do_leftright); + do_leftright = 1; + } + + ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, + &insert); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (do_leftright == 1) { + u32 cpos; + struct ocfs2_extent_list *el; + + do_leftright++; + split_rec = *orig_split_rec; + + ocfs2_reinit_path(path, 1); + + cpos = le32_to_cpu(split_rec.e_cpos); + ret = ocfs2_find_path(inode, path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + el = path_leaf_el(path); + split_index = ocfs2_search_extent_list(el, cpos); + goto leftright; + } +out: + + return ret; +} + +/* + * Mark part or all of the extent record at split_index in the leaf + * pointed to by path as written. This removes the unwritten + * extent flag. + * + * Care is taken to handle contiguousness so as to not grow the tree. + * + * meta_ac is not strictly necessary - we only truly need it if growth + * of the tree is required. All other cases will degrade into a less + * optimal tree layout. + * + * last_eb_bh should be the rightmost leaf block for any inode with a + * btree. Since a split may grow the tree or a merge might shrink it, the caller cannot trust the contents of that buffer after this call. + * + * This code is optimized for readability - several passes might be + * made over certain portions of the tree. All of those blocks will + * have been brought into cache (and pinned via the journal), so the + * extra overhead is not expressed in terms of disk reads. + */ +static int __ocfs2_mark_extent_written(struct inode *inode, + struct buffer_head *di_bh, + handle_t *handle, + struct ocfs2_path *path, + int split_index, + struct ocfs2_extent_rec *split_rec, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret = 0; + struct ocfs2_extent_list *el = path_leaf_el(path); + struct buffer_head *eb_bh, *last_eb_bh = NULL; + struct ocfs2_extent_rec *rec = &el->l_recs[split_index]; + struct ocfs2_merge_ctxt ctxt; + struct ocfs2_extent_list *rightmost_el; + + if (!rec->e_flags & OCFS2_EXT_UNWRITTEN) { + ret = -EIO; + mlog_errno(ret); + goto out; + } + + if (le32_to_cpu(rec->e_cpos) > le32_to_cpu(split_rec->e_cpos) || + ((le32_to_cpu(rec->e_cpos) + le16_to_cpu(rec->e_leaf_clusters)) < + (le32_to_cpu(split_rec->e_cpos) + le16_to_cpu(split_rec->e_leaf_clusters)))) { + ret = -EIO; + mlog_errno(ret); + goto out; + } + + eb_bh = path_leaf_bh(path); + ret = ocfs2_journal_access(handle, inode, eb_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ctxt.c_contig_type = ocfs2_figure_merge_contig_type(inode, el, + split_index, + split_rec); + + /* + * The core merge / split code wants to know how much room is + * left in this inodes allocation tree, so we pass the + * rightmost extent list. + */ + if (path->p_tree_depth) { + struct ocfs2_extent_block *eb; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + le64_to_cpu(di->i_last_eb_blk), + &last_eb_bh, OCFS2_BH_CACHED, inode); + if (ret) { + mlog_exit(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; + if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) { + OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb); + ret = -EROFS; + goto out; + } + + rightmost_el = &eb->h_list; + } else + rightmost_el = path_root_el(path); + + ctxt.c_used_tail_recs = le16_to_cpu(rightmost_el->l_next_free_rec); + if (ctxt.c_used_tail_recs > 0 && + ocfs2_is_empty_extent(&rightmost_el->l_recs[0])) + ctxt.c_used_tail_recs--; + + if (rec->e_cpos == split_rec->e_cpos && + rec->e_leaf_clusters == split_rec->e_leaf_clusters) + ctxt.c_split_covers_rec = 1; + else + ctxt.c_split_covers_rec = 0; + + ctxt.c_has_empty_extent = ocfs2_is_empty_extent(&el->l_recs[0]); + + mlog(0, "index: %d, contig: %u, used_tail_recs: %u, " + "has_empty: %u, split_covers: %u\n", split_index, + ctxt.c_contig_type, ctxt.c_used_tail_recs, + ctxt.c_has_empty_extent, ctxt.c_split_covers_rec); + + if (ctxt.c_contig_type == CONTIG_NONE) { + if (ctxt.c_split_covers_rec) + el->l_recs[split_index] = *split_rec; + else + ret = ocfs2_split_and_insert(inode, handle, path, di_bh, + &last_eb_bh, split_index, + split_rec, meta_ac); + if (ret) + mlog_errno(ret); + } else { + ret = ocfs2_try_to_merge_extent(inode, handle, path, + split_index, split_rec, + dealloc, &ctxt); + if (ret) + mlog_errno(ret); + } + + ocfs2_journal_dirty(handle, eb_bh); + +out: + brelse(last_eb_bh); + return ret; +} + +/* + * Mark the already-existing extent at cpos as written for len clusters. + * + * If the existing extent is larger than the request, initiate a + * split. An attempt will be made at merging with adjacent extents. + * + * The caller is responsible for passing down meta_ac if we'll need it. + */ +int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, + handle_t *handle, u32 cpos, u32 len, u32 phys, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret, index; + u64 start_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys); + struct ocfs2_extent_rec split_rec; + struct ocfs2_path *left_path = NULL; + struct ocfs2_extent_list *el; + + mlog(0, "Inode %lu cpos %u, len %u, phys %u (%llu)\n", + inode->i_ino, cpos, len, phys, (unsigned long long)start_blkno); + + if (!ocfs2_writes_unwritten_extents(OCFS2_SB(inode->i_sb))) { + ocfs2_error(inode->i_sb, "Inode %llu has unwritten extents " + "that are being written to, but the feature bit " + "is not set in the super block.", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + ret = -EROFS; + goto out; + } + + /* + * XXX: This should be fixed up so that we just re-insert the + * next extent records. + */ + ocfs2_extent_map_trunc(inode, 0); + + left_path = ocfs2_new_inode_path(di_bh); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_path(inode, left_path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + el = path_leaf_el(left_path); + + index = ocfs2_search_extent_list(el, cpos); + if (index == -1 || index >= le16_to_cpu(el->l_next_free_rec)) { + ocfs2_error(inode->i_sb, + "Inode %llu has an extent at cpos %u which can no " + "longer be found.\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos); + ret = -EROFS; + goto out; + } + + memset(&split_rec, 0, sizeof(struct ocfs2_extent_rec)); + split_rec.e_cpos = cpu_to_le32(cpos); + split_rec.e_leaf_clusters = cpu_to_le16(len); + split_rec.e_blkno = cpu_to_le64(start_blkno); + split_rec.e_flags = path_leaf_el(left_path)->l_recs[index].e_flags; + split_rec.e_flags &= ~OCFS2_EXT_UNWRITTEN; + + ret = __ocfs2_mark_extent_written(inode, di_bh, handle, left_path, + index, &split_rec, meta_ac, dealloc); + if (ret) + mlog_errno(ret); + +out: + ocfs2_free_path(left_path); + return ret; +} + +static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, + handle_t *handle, struct ocfs2_path *path, + int index, u32 new_range, + struct ocfs2_alloc_context *meta_ac) +{ + int ret, depth, credits = handle->h_buffer_credits; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct buffer_head *last_eb_bh = NULL; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_list *rightmost_el, *el; + struct ocfs2_extent_rec split_rec; + struct ocfs2_extent_rec *rec; + struct ocfs2_insert_type insert; + + /* + * Setup the record to split before we grow the tree. + */ + el = path_leaf_el(path); + rec = &el->l_recs[index]; + ocfs2_make_right_split_rec(inode->i_sb, &split_rec, new_range, rec); + + depth = path->p_tree_depth; + if (depth > 0) { + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + le64_to_cpu(di->i_last_eb_blk), + &last_eb_bh, OCFS2_BH_CACHED, inode); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; + rightmost_el = &eb->h_list; + } else + rightmost_el = path_leaf_el(path); + + credits += path->p_tree_depth + ocfs2_extend_meta_needed(di); + ret = ocfs2_extend_trans(handle, credits); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (le16_to_cpu(rightmost_el->l_next_free_rec) == + le16_to_cpu(rightmost_el->l_count)) { + int old_depth = depth; + + ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, &last_eb_bh, + meta_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (old_depth != depth) { + eb = (struct ocfs2_extent_block *)last_eb_bh->b_data; + rightmost_el = &eb->h_list; + } + } + + memset(&insert, 0, sizeof(struct ocfs2_insert_type)); + insert.ins_appending = APPEND_NONE; + insert.ins_contig = CONTIG_NONE; + insert.ins_split = SPLIT_RIGHT; + insert.ins_free_records = le16_to_cpu(rightmost_el->l_count) + - le16_to_cpu(rightmost_el->l_next_free_rec); + insert.ins_tree_depth = depth; + + ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, &insert); + if (ret) + mlog_errno(ret); + +out: + brelse(last_eb_bh); + return ret; +} + +static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle, + struct ocfs2_path *path, int index, + struct ocfs2_cached_dealloc_ctxt *dealloc, + u32 cpos, u32 len) +{ + int ret; + u32 left_cpos, rec_range, trunc_range; + int wants_rotate = 0, is_rightmost_tree_rec = 0; + struct super_block *sb = inode->i_sb; + struct ocfs2_path *left_path = NULL; + struct ocfs2_extent_list *el = path_leaf_el(path); + struct ocfs2_extent_rec *rec; + struct ocfs2_extent_block *eb; + + if (ocfs2_is_empty_extent(&el->l_recs[0]) && index > 0) { + ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + + index--; + } + + if (index == (le16_to_cpu(el->l_next_free_rec) - 1) && + path->p_tree_depth) { + /* + * Check whether this is the rightmost tree record. If + * we remove all of this record or part of its right + * edge then an update of the record lengths above it + * will be required. + */ + eb = (struct ocfs2_extent_block *)path_leaf_bh(path)->b_data; + if (eb->h_next_leaf_blk == 0) + is_rightmost_tree_rec = 1; + } + + rec = &el->l_recs[index]; + if (index == 0 && path->p_tree_depth && + le32_to_cpu(rec->e_cpos) == cpos) { + /* + * Changing the leftmost offset (via partial or whole + * record truncate) of an interior (or rightmost) path + * means we have to update the subtree that is formed + * by this leaf and the one to it's left. + * + * There are two cases we can skip: + * 1) Path is the leftmost one in our inode tree. + * 2) The leaf is rightmost and will be empty after + * we remove the extent record - the rotate code + * knows how to update the newly formed edge. + */ + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, + &left_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (left_cpos && le16_to_cpu(el->l_next_free_rec) > 1) { + left_path = ocfs2_new_path(path_root_bh(path), + path_root_el(path)); + if (!left_path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_path(inode, left_path, left_cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + } + } + + ret = ocfs2_extend_rotate_transaction(handle, 0, + handle->h_buffer_credits, + path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access_path(inode, handle, path); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access_path(inode, handle, left_path); + if (ret) { + mlog_errno(ret); + goto out; + } + + rec_range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + trunc_range = cpos + len; + + if (le32_to_cpu(rec->e_cpos) == cpos && rec_range == trunc_range) { + int next_free; + + memset(rec, 0, sizeof(*rec)); + ocfs2_cleanup_merge(el, index); + wants_rotate = 1; + + next_free = le16_to_cpu(el->l_next_free_rec); + if (is_rightmost_tree_rec && next_free > 1) { + /* + * We skip the edge update if this path will + * be deleted by the rotate code. + */ + rec = &el->l_recs[next_free - 1]; + ocfs2_adjust_rightmost_records(inode, handle, path, + rec); + } + } else if (le32_to_cpu(rec->e_cpos) == cpos) { + /* Remove leftmost portion of the record. */ + le32_add_cpu(&rec->e_cpos, len); + le64_add_cpu(&rec->e_blkno, ocfs2_clusters_to_blocks(sb, len)); + le16_add_cpu(&rec->e_leaf_clusters, -len); + } else if (rec_range == trunc_range) { + /* Remove rightmost portion of the record */ + le16_add_cpu(&rec->e_leaf_clusters, -len); + if (is_rightmost_tree_rec) + ocfs2_adjust_rightmost_records(inode, handle, path, rec); + } else { + /* Caller should have trapped this. */ + mlog(ML_ERROR, "Inode %llu: Invalid record truncate: (%u, %u) " + "(%u, %u)\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, + le32_to_cpu(rec->e_cpos), + le16_to_cpu(rec->e_leaf_clusters), cpos, len); + BUG(); + } + + if (left_path) { + int subtree_index; + + subtree_index = ocfs2_find_subtree_root(inode, left_path, path); + ocfs2_complete_edge_insert(inode, handle, left_path, path, + subtree_index); + } + + ocfs2_journal_dirty(handle, path_leaf_bh(path)); + + ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + +out: + ocfs2_free_path(left_path); + return ret; +} + +int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, + u32 cpos, u32 len, handle_t *handle, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret, index; + u32 rec_range, trunc_range; + struct ocfs2_extent_rec *rec; + struct ocfs2_extent_list *el; + struct ocfs2_path *path; + + ocfs2_extent_map_trunc(inode, 0); + + path = ocfs2_new_inode_path(di_bh); + if (!path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_find_path(inode, path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + el = path_leaf_el(path); + index = ocfs2_search_extent_list(el, cpos); + if (index == -1 || index >= le16_to_cpu(el->l_next_free_rec)) { + ocfs2_error(inode->i_sb, + "Inode %llu has an extent at cpos %u which can no " + "longer be found.\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos); + ret = -EROFS; + goto out; + } + + /* + * We have 3 cases of extent removal: + * 1) Range covers the entire extent rec + * 2) Range begins or ends on one edge of the extent rec + * 3) Range is in the middle of the extent rec (no shared edges) + * + * For case 1 we remove the extent rec and left rotate to + * fill the hole. + * + * For case 2 we just shrink the existing extent rec, with a + * tree update if the shrinking edge is also the edge of an + * extent block. + * + * For case 3 we do a right split to turn the extent rec into + * something case 2 can handle. + */ + rec = &el->l_recs[index]; + rec_range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + trunc_range = cpos + len; + + BUG_ON(cpos < le32_to_cpu(rec->e_cpos) || trunc_range > rec_range); + + mlog(0, "Inode %llu, remove (cpos %u, len %u). Existing index %d " + "(cpos %u, len %u)\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos, len, index, + le32_to_cpu(rec->e_cpos), ocfs2_rec_clusters(el, rec)); + + if (le32_to_cpu(rec->e_cpos) == cpos || rec_range == trunc_range) { + ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc, + cpos, len); + if (ret) { + mlog_errno(ret); + goto out; + } + } else { + ret = ocfs2_split_tree(inode, di_bh, handle, path, index, + trunc_range, meta_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * The split could have manipulated the tree enough to + * move the record location, so we have to look for it again. + */ + ocfs2_reinit_path(path, 1); + + ret = ocfs2_find_path(inode, path, cpos); + if (ret) { + mlog_errno(ret); + goto out; + } + + el = path_leaf_el(path); + index = ocfs2_search_extent_list(el, cpos); + if (index == -1 || index >= le16_to_cpu(el->l_next_free_rec)) { + ocfs2_error(inode->i_sb, + "Inode %llu: split at cpos %u lost record.", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + cpos); + ret = -EROFS; + goto out; + } + + /* + * Double check our values here. If anything is fishy, + * it's easier to catch it at the top level. + */ + rec = &el->l_recs[index]; + rec_range = le32_to_cpu(rec->e_cpos) + + ocfs2_rec_clusters(el, rec); + if (rec_range != trunc_range) { + ocfs2_error(inode->i_sb, + "Inode %llu: error after split at cpos %u" + "trunc len %u, existing record is (%u,%u)", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + cpos, len, le32_to_cpu(rec->e_cpos), + ocfs2_rec_clusters(el, rec)); + ret = -EROFS; + goto out; + } + + ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc, + cpos, len); + if (ret) { + mlog_errno(ret); + goto out; + } + } + +out: + ocfs2_free_path(path); + return ret; +} + +int ocfs2_truncate_log_needs_flush(struct ocfs2_super *osb) { struct buffer_head *tl_bh = osb->osb_tl_bh; struct ocfs2_dinode *di; @@ -2464,10 +4539,10 @@ static int ocfs2_truncate_log_can_coalesce(struct ocfs2_truncate_log *tl, return current_tail == new_start; } -static int ocfs2_truncate_log_append(struct ocfs2_super *osb, - handle_t *handle, - u64 start_blk, - unsigned int num_clusters) +int ocfs2_truncate_log_append(struct ocfs2_super *osb, + handle_t *handle, + u64 start_blk, + unsigned int num_clusters) { int status, index; unsigned int start_cluster, tl_count; @@ -2623,7 +4698,7 @@ bail: } /* Expects you to already be holding tl_inode->i_mutex */ -static int __ocfs2_flush_truncate_log(struct ocfs2_super *osb) +int __ocfs2_flush_truncate_log(struct ocfs2_super *osb) { int status; unsigned int num_to_flush; @@ -2957,6 +5032,219 @@ int ocfs2_truncate_log_init(struct ocfs2_super *osb) return status; } +/* + * Delayed de-allocation of suballocator blocks. + * + * Some sets of block de-allocations might involve multiple suballocator inodes. + * + * The locking for this can get extremely complicated, especially when + * the suballocator inodes to delete from aren't known until deep + * within an unrelated codepath. + * + * ocfs2_extent_block structures are a good example of this - an inode + * btree could have been grown by any number of nodes each allocating + * out of their own suballoc inode. + * + * These structures allow the delay of block de-allocation until a + * later time, when locking of multiple cluster inodes won't cause + * deadlock. + */ + +/* + * Describes a single block free from a suballocator + */ +struct ocfs2_cached_block_free { + struct ocfs2_cached_block_free *free_next; + u64 free_blk; + unsigned int free_bit; +}; + +struct ocfs2_per_slot_free_list { + struct ocfs2_per_slot_free_list *f_next_suballocator; + int f_inode_type; + int f_slot; + struct ocfs2_cached_block_free *f_first; +}; + +static int ocfs2_free_cached_items(struct ocfs2_super *osb, + int sysfile_type, + int slot, + struct ocfs2_cached_block_free *head) +{ + int ret; + u64 bg_blkno; + handle_t *handle; + struct inode *inode; + struct buffer_head *di_bh = NULL; + struct ocfs2_cached_block_free *tmp; + + inode = ocfs2_get_system_file_inode(osb, sysfile_type, slot); + if (!inode) { + ret = -EINVAL; + mlog_errno(ret); + goto out; + } + + mutex_lock(&inode->i_mutex); + + ret = ocfs2_meta_lock(inode, &di_bh, 1); + if (ret) { + mlog_errno(ret); + goto out_mutex; + } + + handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_unlock; + } + + while (head) { + bg_blkno = ocfs2_which_suballoc_group(head->free_blk, + head->free_bit); + mlog(0, "Free bit: (bit %u, blkno %llu)\n", + head->free_bit, (unsigned long long)head->free_blk); + + ret = ocfs2_free_suballoc_bits(handle, inode, di_bh, + head->free_bit, bg_blkno, 1); + if (ret) { + mlog_errno(ret); + goto out_journal; + } + + ret = ocfs2_extend_trans(handle, OCFS2_SUBALLOC_FREE); + if (ret) { + mlog_errno(ret); + goto out_journal; + } + + tmp = head; + head = head->free_next; + kfree(tmp); + } + +out_journal: + ocfs2_commit_trans(osb, handle); + +out_unlock: + ocfs2_meta_unlock(inode, 1); + brelse(di_bh); +out_mutex: + mutex_unlock(&inode->i_mutex); + iput(inode); +out: + while(head) { + /* Premature exit may have left some dangling items. */ + tmp = head; + head = head->free_next; + kfree(tmp); + } + + return ret; +} + +int ocfs2_run_deallocs(struct ocfs2_super *osb, + struct ocfs2_cached_dealloc_ctxt *ctxt) +{ + int ret = 0, ret2; + struct ocfs2_per_slot_free_list *fl; + + if (!ctxt) + return 0; + + while (ctxt->c_first_suballocator) { + fl = ctxt->c_first_suballocator; + + if (fl->f_first) { + mlog(0, "Free items: (type %u, slot %d)\n", + fl->f_inode_type, fl->f_slot); + ret2 = ocfs2_free_cached_items(osb, fl->f_inode_type, + fl->f_slot, fl->f_first); + if (ret2) + mlog_errno(ret2); + if (!ret) + ret = ret2; + } + + ctxt->c_first_suballocator = fl->f_next_suballocator; + kfree(fl); + } + + return ret; +} + +static struct ocfs2_per_slot_free_list * +ocfs2_find_per_slot_free_list(int type, + int slot, + struct ocfs2_cached_dealloc_ctxt *ctxt) +{ + struct ocfs2_per_slot_free_list *fl = ctxt->c_first_suballocator; + + while (fl) { + if (fl->f_inode_type == type && fl->f_slot == slot) + return fl; + + fl = fl->f_next_suballocator; + } + + fl = kmalloc(sizeof(*fl), GFP_NOFS); + if (fl) { + fl->f_inode_type = type; + fl->f_slot = slot; + fl->f_first = NULL; + fl->f_next_suballocator = ctxt->c_first_suballocator; + + ctxt->c_first_suballocator = fl; + } + return fl; +} + +static int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, + int type, int slot, u64 blkno, + unsigned int bit) +{ + int ret; + struct ocfs2_per_slot_free_list *fl; + struct ocfs2_cached_block_free *item; + + fl = ocfs2_find_per_slot_free_list(type, slot, ctxt); + if (fl == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + item = kmalloc(sizeof(*item), GFP_NOFS); + if (item == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + mlog(0, "Insert: (type %d, slot %u, bit %u, blk %llu)\n", + type, slot, bit, (unsigned long long)blkno); + + item->free_blk = blkno; + item->free_bit = bit; + item->free_next = fl->f_first; + + fl->f_first = item; + + ret = 0; +out: + return ret; +} + +static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt, + struct ocfs2_extent_block *eb) +{ + return ocfs2_cache_block_dealloc(ctxt, EXTENT_ALLOC_SYSTEM_INODE, + le16_to_cpu(eb->h_suballoc_slot), + le64_to_cpu(eb->h_blkno), + le16_to_cpu(eb->h_suballoc_bit)); +} + /* This function will figure out whether the currently last extent * block will be deleted, and if it will, what the new last extent * block will be so we can update his h_next_leaf_blk field, as well @@ -3238,27 +5526,10 @@ delete: BUG_ON(le32_to_cpu(el->l_recs[0].e_cpos)); BUG_ON(le64_to_cpu(el->l_recs[0].e_blkno)); - if (le16_to_cpu(eb->h_suballoc_slot) == 0) { - /* - * This code only understands how to - * lock the suballocator in slot 0, - * which is fine because allocation is - * only ever done out of that - * suballocator too. A future version - * might change that however, so avoid - * a free if we don't know how to - * handle it. This way an fs incompat - * bit will not be necessary. - */ - ret = ocfs2_free_extent_block(handle, - tc->tc_ext_alloc_inode, - tc->tc_ext_alloc_bh, - eb); - - /* An error here is not fatal. */ - if (ret < 0) - mlog_errno(ret); - } + ret = ocfs2_cache_extent_block_free(&tc->tc_dealloc, eb); + /* An error here is not fatal. */ + if (ret < 0) + mlog_errno(ret); } else { deleted_eb = 0; } @@ -3397,9 +5668,9 @@ static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh) return ocfs2_journal_dirty_data(handle, bh); } -static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t isize, - struct page **pages, int numpages, - u64 phys, handle_t *handle) +static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t start, + loff_t end, struct page **pages, + int numpages, u64 phys, handle_t *handle) { int i, ret, partial = 0; void *kaddr; @@ -3412,26 +5683,14 @@ static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t isize, if (numpages == 0) goto out; - from = isize & (PAGE_CACHE_SIZE - 1); /* 1st page offset */ - if (PAGE_CACHE_SHIFT > OCFS2_SB(sb)->s_clustersize_bits) { - /* - * Since 'from' has been capped to a value below page - * size, this calculation won't be able to overflow - * 'to' - */ - to = ocfs2_align_bytes_to_clusters(sb, from); - - /* - * The truncate tail in this case should never contain - * more than one page at maximum. The loop below also - * assumes this. - */ - BUG_ON(numpages != 1); - } - + to = PAGE_CACHE_SIZE; for(i = 0; i < numpages; i++) { page = pages[i]; + from = start & (PAGE_CACHE_SIZE - 1); + if ((end >> PAGE_CACHE_SHIFT) == page->index) + to = end & (PAGE_CACHE_SIZE - 1); + BUG_ON(from > PAGE_CACHE_SIZE); BUG_ON(to > PAGE_CACHE_SIZE); @@ -3468,10 +5727,7 @@ static void ocfs2_zero_cluster_pages(struct inode *inode, loff_t isize, flush_dcache_page(page); - /* - * Every page after the 1st one should be completely zero'd. - */ - from = 0; + start = (page->index + 1) << PAGE_CACHE_SHIFT; } out: if (pages) { @@ -3484,24 +5740,26 @@ out: } } -static int ocfs2_grab_eof_pages(struct inode *inode, loff_t isize, struct page **pages, - int *num, u64 *phys) +static int ocfs2_grab_eof_pages(struct inode *inode, loff_t start, loff_t end, + struct page **pages, int *num, u64 *phys) { int i, numpages = 0, ret = 0; - unsigned int csize = OCFS2_SB(inode->i_sb)->s_clustersize; unsigned int ext_flags; struct super_block *sb = inode->i_sb; struct address_space *mapping = inode->i_mapping; unsigned long index; - u64 next_cluster_bytes; + loff_t last_page_bytes; BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(sb))); + BUG_ON(start > end); - /* Cluster boundary, so we don't need to grab any pages. */ - if ((isize & (csize - 1)) == 0) + if (start == end) goto out; - ret = ocfs2_extent_map_get_blocks(inode, isize >> sb->s_blocksize_bits, + BUG_ON(start >> OCFS2_SB(sb)->s_clustersize_bits != + (end - 1) >> OCFS2_SB(sb)->s_clustersize_bits); + + ret = ocfs2_extent_map_get_blocks(inode, start >> sb->s_blocksize_bits, phys, NULL, &ext_flags); if (ret) { mlog_errno(ret); @@ -3517,8 +5775,8 @@ static int ocfs2_grab_eof_pages(struct inode *inode, loff_t isize, struct page * if (ext_flags & OCFS2_EXT_UNWRITTEN) goto out; - next_cluster_bytes = ocfs2_align_bytes_to_clusters(inode->i_sb, isize); - index = isize >> PAGE_CACHE_SHIFT; + last_page_bytes = PAGE_ALIGN(end); + index = start >> PAGE_CACHE_SHIFT; do { pages[numpages] = grab_cache_page(mapping, index); if (!pages[numpages]) { @@ -3529,7 +5787,7 @@ static int ocfs2_grab_eof_pages(struct inode *inode, loff_t isize, struct page * numpages++; index++; - } while (index < (next_cluster_bytes >> PAGE_CACHE_SHIFT)); + } while (index < (last_page_bytes >> PAGE_CACHE_SHIFT)); out: if (ret != 0) { @@ -3558,11 +5816,10 @@ out: * otherwise block_write_full_page() will skip writeout of pages past * i_size. The new_i_size parameter is passed for this reason. */ -int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, - u64 new_i_size) +int ocfs2_zero_range_for_truncate(struct inode *inode, handle_t *handle, + u64 range_start, u64 range_end) { int ret, numpages; - loff_t endbyte; struct page **pages = NULL; u64 phys; @@ -3581,7 +5838,8 @@ int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, goto out; } - ret = ocfs2_grab_eof_pages(inode, new_i_size, pages, &numpages, &phys); + ret = ocfs2_grab_eof_pages(inode, range_start, range_end, pages, + &numpages, &phys); if (ret) { mlog_errno(ret); goto out; @@ -3590,17 +5848,16 @@ int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, if (numpages == 0) goto out; - ocfs2_zero_cluster_pages(inode, new_i_size, pages, numpages, phys, - handle); + ocfs2_zero_cluster_pages(inode, range_start, range_end, pages, + numpages, phys, handle); /* * Initiate writeout of the pages we zero'd here. We don't * wait on them - the truncate_inode_pages() call later will * do that for us. */ - endbyte = ocfs2_align_bytes_to_clusters(inode->i_sb, new_i_size); - ret = do_sync_mapping_range(inode->i_mapping, new_i_size, - endbyte - 1, SYNC_FILE_RANGE_WRITE); + ret = do_sync_mapping_range(inode->i_mapping, range_start, + range_end - 1, SYNC_FILE_RANGE_WRITE); if (ret) mlog_errno(ret); @@ -3631,8 +5888,6 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb, mlog_entry_void(); - down_write(&OCFS2_I(inode)->ip_alloc_sem); - new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb, i_size_read(inode)); @@ -3754,7 +6009,6 @@ start: goto start; bail: - up_write(&OCFS2_I(inode)->ip_alloc_sem); ocfs2_schedule_truncate_log_flush(osb, 1); @@ -3764,6 +6018,8 @@ bail: if (handle) ocfs2_commit_trans(osb, handle); + ocfs2_run_deallocs(osb, &tc->tc_dealloc); + ocfs2_free_path(path); /* This will drop the ext_alloc cluster lock for us */ @@ -3774,23 +6030,18 @@ bail: } /* - * Expects the inode to already be locked. This will figure out which - * inodes need to be locked and will put them on the returned truncate - * context. + * Expects the inode to already be locked. */ int ocfs2_prepare_truncate(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh, struct ocfs2_truncate_context **tc) { - int status, metadata_delete, i; + int status; unsigned int new_i_clusters; struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; - struct ocfs2_extent_list *el; struct buffer_head *last_eb_bh = NULL; - struct inode *ext_alloc_inode = NULL; - struct buffer_head *ext_alloc_bh = NULL; mlog_entry_void(); @@ -3810,12 +6061,9 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb, mlog_errno(status); goto bail; } + ocfs2_init_dealloc_ctxt(&(*tc)->tc_dealloc); - metadata_delete = 0; if (fe->id2.i_list.l_tree_depth) { - /* If we have a tree, then the truncate may result in - * metadata deletes. Figure this out from the - * rightmost leaf block.*/ status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk), &last_eb_bh, OCFS2_BH_CACHED, inode); if (status < 0) { @@ -3830,43 +6078,10 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb, status = -EIO; goto bail; } - el = &(eb->h_list); - - i = 0; - if (ocfs2_is_empty_extent(&el->l_recs[0])) - i = 1; - /* - * XXX: Should we check that next_free_rec contains - * the extent? - */ - if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_i_clusters) - metadata_delete = 1; } (*tc)->tc_last_eb_bh = last_eb_bh; - if (metadata_delete) { - mlog(0, "Will have to delete metadata for this trunc. " - "locking allocator.\n"); - ext_alloc_inode = ocfs2_get_system_file_inode(osb, EXTENT_ALLOC_SYSTEM_INODE, 0); - if (!ext_alloc_inode) { - status = -ENOMEM; - mlog_errno(status); - goto bail; - } - - mutex_lock(&ext_alloc_inode->i_mutex); - (*tc)->tc_ext_alloc_inode = ext_alloc_inode; - - status = ocfs2_meta_lock(ext_alloc_inode, &ext_alloc_bh, 1); - if (status < 0) { - mlog_errno(status); - goto bail; - } - (*tc)->tc_ext_alloc_bh = ext_alloc_bh; - (*tc)->tc_ext_alloc_locked = 1; - } - status = 0; bail: if (status < 0) { @@ -3880,16 +6095,13 @@ bail: static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc) { - if (tc->tc_ext_alloc_inode) { - if (tc->tc_ext_alloc_locked) - ocfs2_meta_unlock(tc->tc_ext_alloc_inode, 1); - - mutex_unlock(&tc->tc_ext_alloc_inode->i_mutex); - iput(tc->tc_ext_alloc_inode); - } - - if (tc->tc_ext_alloc_bh) - brelse(tc->tc_ext_alloc_bh); + /* + * The caller is responsible for completing deallocation + * before freeing the context. + */ + if (tc->tc_dealloc.c_first_suballocator != NULL) + mlog(ML_NOTICE, + "Truncate completion has non-empty dealloc context\n"); if (tc->tc_last_eb_bh) brelse(tc->tc_last_eb_bh); diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index fbcb593..990df48 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -34,7 +34,17 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, u32 cpos, u64 start_blk, u32 new_clusters, + u8 flags, struct ocfs2_alloc_context *meta_ac); +struct ocfs2_cached_dealloc_ctxt; +int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, + handle_t *handle, u32 cpos, u32 len, u32 phys, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_cached_dealloc_ctxt *dealloc); +int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, + u32 cpos, u32 len, handle_t *handle, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_cached_dealloc_ctxt *dealloc); int ocfs2_num_free_extents(struct ocfs2_super *osb, struct inode *inode, struct ocfs2_dinode *fe); @@ -62,17 +72,41 @@ int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb, struct ocfs2_dinode **tl_copy); int ocfs2_complete_truncate_log_recovery(struct ocfs2_super *osb, struct ocfs2_dinode *tl_copy); +int ocfs2_truncate_log_needs_flush(struct ocfs2_super *osb); +int ocfs2_truncate_log_append(struct ocfs2_super *osb, + handle_t *handle, + u64 start_blk, + unsigned int num_clusters); +int __ocfs2_flush_truncate_log(struct ocfs2_super *osb); + +/* + * Process local structure which describes the block unlinks done + * during an operation. This is populated via + * ocfs2_cache_block_dealloc(). + * + * ocfs2_run_deallocs() should be called after the potentially + * de-allocating routines. No journal handles should be open, and most + * locks should have been dropped. + */ +struct ocfs2_cached_dealloc_ctxt { + struct ocfs2_per_slot_free_list *c_first_suballocator; +}; +static inline void ocfs2_init_dealloc_ctxt(struct ocfs2_cached_dealloc_ctxt *c) +{ + c->c_first_suballocator = NULL; +} +int ocfs2_run_deallocs(struct ocfs2_super *osb, + struct ocfs2_cached_dealloc_ctxt *ctxt); struct ocfs2_truncate_context { - struct inode *tc_ext_alloc_inode; - struct buffer_head *tc_ext_alloc_bh; + struct ocfs2_cached_dealloc_ctxt tc_dealloc; int tc_ext_alloc_locked; /* is it cluster locked? */ /* these get destroyed once it's passed to ocfs2_commit_truncate. */ struct buffer_head *tc_last_eb_bh; }; -int ocfs2_zero_tail_for_truncate(struct inode *inode, handle_t *handle, - u64 new_i_size); +int ocfs2_zero_range_for_truncate(struct inode *inode, handle_t *handle, + u64 range_start, u64 range_end); int ocfs2_prepare_truncate(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh, @@ -84,6 +118,7 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb, int ocfs2_find_leaf(struct inode *inode, struct ocfs2_extent_list *root_el, u32 cpos, struct buffer_head **leaf_bh); +int ocfs2_search_extent_list(struct ocfs2_extent_list *el, u32 v_cluster); /* * Helper function to look at the # of clusters in an extent record. diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index a480b09..460d440 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -232,7 +232,7 @@ static int ocfs2_readpage(struct file *file, struct page *page) * might now be discovering a truncate that hit on another node. * block_read_full_page->get_block freaks out if it is asked to read * beyond the end of a file, so we check here. Callers - * (generic_file_read, fault->nopage) are clever enough to check i_size + * (generic_file_read, vm_ops->fault) are clever enough to check i_size * and notice that the page they just read isn't needed. * * XXX sys_readahead() seems to get that wrong? @@ -684,6 +684,8 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, bh = bh->b_this_page, block_start += bsize) { block_end = block_start + bsize; + clear_buffer_new(bh); + /* * Ignore blocks outside of our i/o range - * they may belong to unallocated clusters. @@ -698,9 +700,8 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, * For an allocating write with cluster size >= page * size, we always write the entire page. */ - - if (buffer_new(bh)) - clear_buffer_new(bh); + if (new) + set_buffer_new(bh); if (!buffer_mapped(bh)) { map_bh(bh, inode->i_sb, *p_blkno); @@ -711,7 +712,8 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, if (!buffer_uptodate(bh)) set_buffer_uptodate(bh); } else if (!buffer_uptodate(bh) && !buffer_delay(bh) && - (block_start < from || block_end > to)) { + !buffer_new(bh) && + (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); *wait_bh++=bh; } @@ -738,18 +740,13 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, bh = head; block_start = 0; do { - void *kaddr; - block_end = block_start + bsize; if (block_end <= from) goto next_bh; if (block_start >= to) break; - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr+block_start, 0, bh->b_size); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, block_start, bh->b_size, KM_USER0); set_buffer_uptodate(bh); mark_buffer_dirty(bh); @@ -761,217 +758,240 @@ next_bh: return ret; } +#if (PAGE_CACHE_SIZE >= OCFS2_MAX_CLUSTERSIZE) +#define OCFS2_MAX_CTXT_PAGES 1 +#else +#define OCFS2_MAX_CTXT_PAGES (OCFS2_MAX_CLUSTERSIZE / PAGE_CACHE_SIZE) +#endif + +#define OCFS2_MAX_CLUSTERS_PER_PAGE (PAGE_CACHE_SIZE / OCFS2_MIN_CLUSTERSIZE) + /* - * This will copy user data from the buffer page in the splice - * context. - * - * For now, we ignore SPLICE_F_MOVE as that would require some extra - * communication out all the way to ocfs2_write(). + * Describe the state of a single cluster to be written to. */ -int ocfs2_map_and_write_splice_data(struct inode *inode, - struct ocfs2_write_ctxt *wc, u64 *p_blkno, - unsigned int *ret_from, unsigned int *ret_to) +struct ocfs2_write_cluster_desc { + u32 c_cpos; + u32 c_phys; + /* + * Give this a unique field because c_phys eventually gets + * filled. + */ + unsigned c_new; + unsigned c_unwritten; +}; + +static inline int ocfs2_should_zero_cluster(struct ocfs2_write_cluster_desc *d) { - int ret; - unsigned int to, from, cluster_start, cluster_end; - char *src, *dst; - struct ocfs2_splice_write_priv *sp = wc->w_private; - struct pipe_buffer *buf = sp->s_buf; - unsigned long bytes, src_from; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + return d->c_new || d->c_unwritten; +} - ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start, - &cluster_end); +struct ocfs2_write_ctxt { + /* Logical cluster position / len of write */ + u32 w_cpos; + u32 w_clen; - from = sp->s_offset; - src_from = sp->s_buf_offset; - bytes = wc->w_count; + struct ocfs2_write_cluster_desc w_desc[OCFS2_MAX_CLUSTERS_PER_PAGE]; - if (wc->w_large_pages) { - /* - * For cluster size < page size, we have to - * calculate pos within the cluster and obey - * the rightmost boundary. - */ - bytes = min(bytes, (unsigned long)(osb->s_clustersize - - (wc->w_pos & (osb->s_clustersize - 1)))); - } - to = from + bytes; + /* + * This is true if page_size > cluster_size. + * + * It triggers a set of special cases during write which might + * have to deal with allocating writes to partial pages. + */ + unsigned int w_large_pages; + + /* + * Pages involved in this write. + * + * w_target_page is the page being written to by the user. + * + * w_pages is an array of pages which always contains + * w_target_page, and in the case of an allocating write with + * page_size < cluster size, it will contain zero'd and mapped + * pages adjacent to w_target_page which need to be written + * out in so that future reads from that region will get + * zero's. + */ + struct page *w_pages[OCFS2_MAX_CTXT_PAGES]; + unsigned int w_num_pages; + struct page *w_target_page; - BUG_ON(from > PAGE_CACHE_SIZE); - BUG_ON(to > PAGE_CACHE_SIZE); - BUG_ON(from < cluster_start); - BUG_ON(to > cluster_end); + /* + * ocfs2_write_end() uses this to know what the real range to + * write in the target should be. + */ + unsigned int w_target_from; + unsigned int w_target_to; - if (wc->w_this_page_new) - ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, - cluster_start, cluster_end, 1); - else - ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, - from, to, 0); - if (ret) { - mlog_errno(ret); - goto out; + /* + * We could use journal_current_handle() but this is cleaner, + * IMHO -Mark + */ + handle_t *w_handle; + + struct buffer_head *w_di_bh; + + struct ocfs2_cached_dealloc_ctxt w_dealloc; +}; + +static void ocfs2_free_write_ctxt(struct ocfs2_write_ctxt *wc) +{ + int i; + + for(i = 0; i < wc->w_num_pages; i++) { + if (wc->w_pages[i] == NULL) + continue; + + unlock_page(wc->w_pages[i]); + mark_page_accessed(wc->w_pages[i]); + page_cache_release(wc->w_pages[i]); } - src = buf->ops->map(sp->s_pipe, buf, 1); - dst = kmap_atomic(wc->w_this_page, KM_USER1); - memcpy(dst + from, src + src_from, bytes); - kunmap_atomic(wc->w_this_page, KM_USER1); - buf->ops->unmap(sp->s_pipe, buf, src); + brelse(wc->w_di_bh); + kfree(wc); +} + +static int ocfs2_alloc_write_ctxt(struct ocfs2_write_ctxt **wcp, + struct ocfs2_super *osb, loff_t pos, + unsigned len, struct buffer_head *di_bh) +{ + struct ocfs2_write_ctxt *wc; + + wc = kzalloc(sizeof(struct ocfs2_write_ctxt), GFP_NOFS); + if (!wc) + return -ENOMEM; - wc->w_finished_copy = 1; + wc->w_cpos = pos >> osb->s_clustersize_bits; + wc->w_clen = ocfs2_clusters_for_bytes(osb->sb, len); + get_bh(di_bh); + wc->w_di_bh = di_bh; - *ret_from = from; - *ret_to = to; -out: + if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits)) + wc->w_large_pages = 1; + else + wc->w_large_pages = 0; + + ocfs2_init_dealloc_ctxt(&wc->w_dealloc); + + *wcp = wc; - return bytes ? (unsigned int)bytes : ret; + return 0; } /* - * This will copy user data from the iovec in the buffered write - * context. + * If a page has any new buffers, zero them out here, and mark them uptodate + * and dirty so they'll be written out (in order to prevent uninitialised + * block data from leaking). And clear the new bit. */ -int ocfs2_map_and_write_user_data(struct inode *inode, - struct ocfs2_write_ctxt *wc, u64 *p_blkno, - unsigned int *ret_from, unsigned int *ret_to) +static void ocfs2_zero_new_buffers(struct page *page, unsigned from, unsigned to) { - int ret; - unsigned int to, from, cluster_start, cluster_end; - unsigned long bytes, src_from; - char *dst; - struct ocfs2_buffered_write_priv *bp = wc->w_private; - const struct iovec *cur_iov = bp->b_cur_iov; - char __user *buf; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + unsigned int block_start, block_end; + struct buffer_head *head, *bh; - ocfs2_figure_cluster_boundaries(osb, wc->w_cpos, &cluster_start, - &cluster_end); + BUG_ON(!PageLocked(page)); + if (!page_has_buffers(page)) + return; - buf = cur_iov->iov_base + bp->b_cur_off; - src_from = (unsigned long)buf & ~PAGE_CACHE_MASK; + bh = head = page_buffers(page); + block_start = 0; + do { + block_end = block_start + bh->b_size; - from = wc->w_pos & (PAGE_CACHE_SIZE - 1); + if (buffer_new(bh)) { + if (block_end > from && block_start < to) { + if (!PageUptodate(page)) { + unsigned start, end; - /* - * This is a lot of comparisons, but it reads quite - * easily, which is important here. - */ - /* Stay within the src page */ - bytes = PAGE_SIZE - src_from; - /* Stay within the vector */ - bytes = min(bytes, - (unsigned long)(cur_iov->iov_len - bp->b_cur_off)); - /* Stay within count */ - bytes = min(bytes, (unsigned long)wc->w_count); - /* - * For clustersize > page size, just stay within - * target page, otherwise we have to calculate pos - * within the cluster and obey the rightmost - * boundary. - */ - if (wc->w_large_pages) { - /* - * For cluster size < page size, we have to - * calculate pos within the cluster and obey - * the rightmost boundary. - */ - bytes = min(bytes, (unsigned long)(osb->s_clustersize - - (wc->w_pos & (osb->s_clustersize - 1)))); - } else { - /* - * cluster size > page size is the most common - * case - we just stay within the target page - * boundary. - */ - bytes = min(bytes, PAGE_CACHE_SIZE - from); - } + start = max(from, block_start); + end = min(to, block_end); - to = from + bytes; + zero_user_page(page, start, end - start, KM_USER0); + set_buffer_uptodate(bh); + } - BUG_ON(from > PAGE_CACHE_SIZE); - BUG_ON(to > PAGE_CACHE_SIZE); - BUG_ON(from < cluster_start); - BUG_ON(to > cluster_end); + clear_buffer_new(bh); + mark_buffer_dirty(bh); + } + } - if (wc->w_this_page_new) - ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, - cluster_start, cluster_end, 1); - else - ret = ocfs2_map_page_blocks(wc->w_this_page, p_blkno, inode, - from, to, 0); - if (ret) { - mlog_errno(ret); - goto out; - } + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); +} - dst = kmap(wc->w_this_page); - memcpy(dst + from, bp->b_src_buf + src_from, bytes); - kunmap(wc->w_this_page); +/* + * Only called when we have a failure during allocating write to write + * zero's to the newly allocated region. + */ +static void ocfs2_write_failure(struct inode *inode, + struct ocfs2_write_ctxt *wc, + loff_t user_pos, unsigned user_len) +{ + int i; + unsigned from, to; + struct page *tmppage; - /* - * XXX: This is slow, but simple. The caller of - * ocfs2_buffered_write_cluster() is responsible for - * passing through the iovecs, so it's difficult to - * predict what our next step is in here after our - * initial write. A future version should be pushing - * that iovec manipulation further down. - * - * By setting this, we indicate that a copy from user - * data was done, and subsequent calls for this - * cluster will skip copying more data. - */ - wc->w_finished_copy = 1; + ocfs2_zero_new_buffers(wc->w_target_page, user_pos, user_len); - *ret_from = from; - *ret_to = to; -out: + if (wc->w_large_pages) { + from = wc->w_target_from; + to = wc->w_target_to; + } else { + from = 0; + to = PAGE_CACHE_SIZE; + } + + for(i = 0; i < wc->w_num_pages; i++) { + tmppage = wc->w_pages[i]; - return bytes ? (unsigned int)bytes : ret; + if (ocfs2_should_order_data(inode)) + walk_page_buffers(wc->w_handle, page_buffers(tmppage), + from, to, NULL, + ocfs2_journal_dirty_data); + + block_commit_write(tmppage, from, to); + } } -/* - * Map, fill and write a page to disk. - * - * The work of copying data is done via callback. Newly allocated - * pages which don't take user data will be zero'd (set 'new' to - * indicate an allocating write) - * - * Returns a negative error code or the number of bytes copied into - * the page. - */ -static int ocfs2_write_data_page(struct inode *inode, handle_t *handle, - u64 *p_blkno, struct page *page, - struct ocfs2_write_ctxt *wc, int new) +static int ocfs2_prepare_page_for_write(struct inode *inode, u64 *p_blkno, + struct ocfs2_write_ctxt *wc, + struct page *page, u32 cpos, + loff_t user_pos, unsigned user_len, + int new) { - int ret, copied = 0; - unsigned int from = 0, to = 0; + int ret; + unsigned int map_from = 0, map_to = 0; unsigned int cluster_start, cluster_end; - unsigned int zero_from = 0, zero_to = 0; + unsigned int user_data_from = 0, user_data_to = 0; - ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), wc->w_cpos, + ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), cpos, &cluster_start, &cluster_end); - if ((wc->w_pos >> PAGE_CACHE_SHIFT) == page->index - && !wc->w_finished_copy) { - - wc->w_this_page = page; - wc->w_this_page_new = new; - ret = wc->w_write_data_page(inode, wc, p_blkno, &from, &to); - if (ret < 0) { + if (page == wc->w_target_page) { + map_from = user_pos & (PAGE_CACHE_SIZE - 1); + map_to = map_from + user_len; + + if (new) + ret = ocfs2_map_page_blocks(page, p_blkno, inode, + cluster_start, cluster_end, + new); + else + ret = ocfs2_map_page_blocks(page, p_blkno, inode, + map_from, map_to, new); + if (ret) { mlog_errno(ret); goto out; } - copied = ret; - - zero_from = from; - zero_to = to; + user_data_from = map_from; + user_data_to = map_to; if (new) { - from = cluster_start; - to = cluster_end; + map_from = cluster_start; + map_to = cluster_end; } + + wc->w_target_from = map_from; + wc->w_target_to = map_to; } else { /* * If we haven't allocated the new page yet, we @@ -980,11 +1000,11 @@ static int ocfs2_write_data_page(struct inode *inode, handle_t *handle, */ BUG_ON(!new); - from = cluster_start; - to = cluster_end; + map_from = cluster_start; + map_to = cluster_end; ret = ocfs2_map_page_blocks(page, p_blkno, inode, - cluster_start, cluster_end, 1); + cluster_start, cluster_end, new); if (ret) { mlog_errno(ret); goto out; @@ -1003,108 +1023,113 @@ static int ocfs2_write_data_page(struct inode *inode, handle_t *handle, */ if (new && !PageUptodate(page)) ocfs2_clear_page_regions(page, OCFS2_SB(inode->i_sb), - wc->w_cpos, zero_from, zero_to); + cpos, user_data_from, user_data_to); flush_dcache_page(page); - if (ocfs2_should_order_data(inode)) { - ret = walk_page_buffers(handle, - page_buffers(page), - from, to, NULL, - ocfs2_journal_dirty_data); - if (ret < 0) - mlog_errno(ret); - } - - /* - * We don't use generic_commit_write() because we need to - * handle our own i_size update. - */ - ret = block_commit_write(page, from, to); - if (ret) - mlog_errno(ret); out: - - return copied ? copied : ret; + return ret; } /* - * Do the actual write of some data into an inode. Optionally allocate - * in order to fulfill the write. - * - * cpos is the logical cluster offset within the file to write at - * - * 'phys' is the physical mapping of that offset. a 'phys' value of - * zero indicates that allocation is required. In this case, data_ac - * and meta_ac should be valid (meta_ac can be null if metadata - * allocation isn't required). + * This function will only grab one clusters worth of pages. */ -static ssize_t ocfs2_write(struct file *file, u32 phys, handle_t *handle, - struct buffer_head *di_bh, - struct ocfs2_alloc_context *data_ac, - struct ocfs2_alloc_context *meta_ac, - struct ocfs2_write_ctxt *wc) +static int ocfs2_grab_pages_for_write(struct address_space *mapping, + struct ocfs2_write_ctxt *wc, + u32 cpos, loff_t user_pos, int new, + struct page *mmap_page) { - int ret, i, numpages = 1, new; - unsigned int copied = 0; - u32 tmp_pos; - u64 v_blkno, p_blkno; - struct address_space *mapping = file->f_mapping; + int ret = 0, i; + unsigned long start, target_index, index; struct inode *inode = mapping->host; - unsigned long index, start; - struct page **cpages; - new = phys == 0 ? 1 : 0; + target_index = user_pos >> PAGE_CACHE_SHIFT; /* * Figure out how many pages we'll be manipulating here. For * non allocating write, we just change the one * page. Otherwise, we'll need a whole clusters worth. */ - if (new) - numpages = ocfs2_pages_per_cluster(inode->i_sb); - - cpages = kzalloc(sizeof(*cpages) * numpages, GFP_NOFS); - if (!cpages) { - ret = -ENOMEM; - mlog_errno(ret); - return ret; - } - - /* - * Fill our page array first. That way we've grabbed enough so - * that we can zero and flush if we error after adding the - * extent. - */ if (new) { - start = ocfs2_align_clusters_to_page_index(inode->i_sb, - wc->w_cpos); - v_blkno = ocfs2_clusters_to_blocks(inode->i_sb, wc->w_cpos); + wc->w_num_pages = ocfs2_pages_per_cluster(inode->i_sb); + start = ocfs2_align_clusters_to_page_index(inode->i_sb, cpos); } else { - start = wc->w_pos >> PAGE_CACHE_SHIFT; - v_blkno = wc->w_pos >> inode->i_sb->s_blocksize_bits; + wc->w_num_pages = 1; + start = target_index; } - for(i = 0; i < numpages; i++) { + for(i = 0; i < wc->w_num_pages; i++) { index = start + i; - cpages[i] = find_or_create_page(mapping, index, GFP_NOFS); - if (!cpages[i]) { - ret = -ENOMEM; - mlog_errno(ret); - goto out; + if (index == target_index && mmap_page) { + /* + * ocfs2_pagemkwrite() is a little different + * and wants us to directly use the page + * passed in. + */ + lock_page(mmap_page); + + if (mmap_page->mapping != mapping) { + unlock_page(mmap_page); + /* + * Sanity check - the locking in + * ocfs2_pagemkwrite() should ensure + * that this code doesn't trigger. + */ + ret = -EINVAL; + mlog_errno(ret); + goto out; + } + + page_cache_get(mmap_page); + wc->w_pages[i] = mmap_page; + } else { + wc->w_pages[i] = find_or_create_page(mapping, index, + GFP_NOFS); + if (!wc->w_pages[i]) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } } + + if (index == target_index) + wc->w_target_page = wc->w_pages[i]; } +out: + return ret; +} + +/* + * Prepare a single cluster for write one cluster into the file. + */ +static int ocfs2_write_cluster(struct address_space *mapping, + u32 phys, unsigned int unwritten, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_write_ctxt *wc, u32 cpos, + loff_t user_pos, unsigned user_len) +{ + int ret, i, new, should_zero = 0; + u64 v_blkno, p_blkno; + struct inode *inode = mapping->host; + + new = phys == 0 ? 1 : 0; + if (new || unwritten) + should_zero = 1; if (new) { + u32 tmp_pos; + /* * This is safe to call with the page locks - it won't take * any additional semaphores or cluster locks. */ - tmp_pos = wc->w_cpos; + tmp_pos = cpos; ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode, - &tmp_pos, 1, di_bh, handle, - data_ac, meta_ac, NULL); + &tmp_pos, 1, 0, wc->w_di_bh, + wc->w_handle, data_ac, + meta_ac, NULL); /* * This shouldn't happen because we must have already * calculated the correct meta data allocation required. The @@ -1121,159 +1146,433 @@ static ssize_t ocfs2_write(struct file *file, u32 phys, handle_t *handle, mlog_errno(ret); goto out; } + } else if (unwritten) { + ret = ocfs2_mark_extent_written(inode, wc->w_di_bh, + wc->w_handle, cpos, 1, phys, + meta_ac, &wc->w_dealloc); + if (ret < 0) { + mlog_errno(ret); + goto out; + } } + if (should_zero) + v_blkno = ocfs2_clusters_to_blocks(inode->i_sb, cpos); + else + v_blkno = user_pos >> inode->i_sb->s_blocksize_bits; + + /* + * The only reason this should fail is due to an inability to + * find the extent added. + */ ret = ocfs2_extent_map_get_blocks(inode, v_blkno, &p_blkno, NULL, NULL); if (ret < 0) { - - /* - * XXX: Should we go readonly here? - */ - - mlog_errno(ret); + ocfs2_error(inode->i_sb, "Corrupting extend for inode %llu, " + "at logical block %llu", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)v_blkno); goto out; } BUG_ON(p_blkno == 0); - for(i = 0; i < numpages; i++) { - ret = ocfs2_write_data_page(inode, handle, &p_blkno, cpages[i], - wc, new); - if (ret < 0) { - mlog_errno(ret); - goto out; + for(i = 0; i < wc->w_num_pages; i++) { + int tmpret; + + tmpret = ocfs2_prepare_page_for_write(inode, &p_blkno, wc, + wc->w_pages[i], cpos, + user_pos, user_len, + should_zero); + if (tmpret) { + mlog_errno(tmpret); + if (ret == 0) + tmpret = ret; } - - copied += ret; } + /* + * We only have cleanup to do in case of allocating write. + */ + if (ret && new) + ocfs2_write_failure(inode, wc, user_pos, user_len); + out: - for(i = 0; i < numpages; i++) { - unlock_page(cpages[i]); - mark_page_accessed(cpages[i]); - page_cache_release(cpages[i]); + + return ret; +} + +static int ocfs2_write_cluster_by_desc(struct address_space *mapping, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_write_ctxt *wc, + loff_t pos, unsigned len) +{ + int ret, i; + struct ocfs2_write_cluster_desc *desc; + + for (i = 0; i < wc->w_clen; i++) { + desc = &wc->w_desc[i]; + + ret = ocfs2_write_cluster(mapping, desc->c_phys, + desc->c_unwritten, data_ac, meta_ac, + wc, desc->c_cpos, pos, len); + if (ret) { + mlog_errno(ret); + goto out; + } } - kfree(cpages); - return copied ? copied : ret; + ret = 0; +out: + return ret; } -static void ocfs2_write_ctxt_init(struct ocfs2_write_ctxt *wc, - struct ocfs2_super *osb, loff_t pos, - size_t count, ocfs2_page_writer *cb, - void *cb_priv) +/* + * ocfs2_write_end() wants to know which parts of the target page it + * should complete the write on. It's easiest to compute them ahead of + * time when a more complete view of the write is available. + */ +static void ocfs2_set_target_boundaries(struct ocfs2_super *osb, + struct ocfs2_write_ctxt *wc, + loff_t pos, unsigned len, int alloc) { - wc->w_count = count; - wc->w_pos = pos; - wc->w_cpos = wc->w_pos >> osb->s_clustersize_bits; - wc->w_finished_copy = 0; + struct ocfs2_write_cluster_desc *desc; - if (unlikely(PAGE_CACHE_SHIFT > osb->s_clustersize_bits)) - wc->w_large_pages = 1; - else - wc->w_large_pages = 0; + wc->w_target_from = pos & (PAGE_CACHE_SIZE - 1); + wc->w_target_to = wc->w_target_from + len; - wc->w_write_data_page = cb; - wc->w_private = cb_priv; + if (alloc == 0) + return; + + /* + * Allocating write - we may have different boundaries based + * on page size and cluster size. + * + * NOTE: We can no longer compute one value from the other as + * the actual write length and user provided length may be + * different. + */ + + if (wc->w_large_pages) { + /* + * We only care about the 1st and last cluster within + * our range and whether they should be zero'd or not. Either + * value may be extended out to the start/end of a + * newly allocated cluster. + */ + desc = &wc->w_desc[0]; + if (ocfs2_should_zero_cluster(desc)) + ocfs2_figure_cluster_boundaries(osb, + desc->c_cpos, + &wc->w_target_from, + NULL); + + desc = &wc->w_desc[wc->w_clen - 1]; + if (ocfs2_should_zero_cluster(desc)) + ocfs2_figure_cluster_boundaries(osb, + desc->c_cpos, + NULL, + &wc->w_target_to); + } else { + wc->w_target_from = 0; + wc->w_target_to = PAGE_CACHE_SIZE; + } } /* - * Write a cluster to an inode. The cluster may not be allocated yet, - * in which case it will be. This only exists for buffered writes - - * O_DIRECT takes a more "traditional" path through the kernel. - * - * The caller is responsible for incrementing pos, written counts, etc + * Populate each single-cluster write descriptor in the write context + * with information about the i/o to be done. * - * For file systems that don't support sparse files, pre-allocation - * and page zeroing up until cpos should be done prior to this - * function call. - * - * Callers should be holding i_sem, and the rw cluster lock. - * - * Returns the number of user bytes written, or less than zero for - * error. + * Returns the number of clusters that will have to be allocated, as + * well as a worst case estimate of the number of extent records that + * would have to be created during a write to an unwritten region. */ -ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos, - size_t count, ocfs2_page_writer *actor, - void *priv) +static int ocfs2_populate_write_desc(struct inode *inode, + struct ocfs2_write_ctxt *wc, + unsigned int *clusters_to_alloc, + unsigned int *extents_to_split) +{ + int ret; + struct ocfs2_write_cluster_desc *desc; + unsigned int num_clusters = 0; + unsigned int ext_flags = 0; + u32 phys = 0; + int i; + + *clusters_to_alloc = 0; + *extents_to_split = 0; + + for (i = 0; i < wc->w_clen; i++) { + desc = &wc->w_desc[i]; + desc->c_cpos = wc->w_cpos + i; + + if (num_clusters == 0) { + /* + * Need to look up the next extent record. + */ + ret = ocfs2_get_clusters(inode, desc->c_cpos, &phys, + &num_clusters, &ext_flags); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * Assume worst case - that we're writing in + * the middle of the extent. + * + * We can assume that the write proceeds from + * left to right, in which case the extent + * insert code is smart enough to coalesce the + * next splits into the previous records created. + */ + if (ext_flags & OCFS2_EXT_UNWRITTEN) + *extents_to_split = *extents_to_split + 2; + } else if (phys) { + /* + * Only increment phys if it doesn't describe + * a hole. + */ + phys++; + } + + desc->c_phys = phys; + if (phys == 0) { + desc->c_new = 1; + *clusters_to_alloc = *clusters_to_alloc + 1; + } + if (ext_flags & OCFS2_EXT_UNWRITTEN) + desc->c_unwritten = 1; + + num_clusters--; + } + + ret = 0; +out: + return ret; +} + +int ocfs2_write_begin_nolock(struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata, + struct buffer_head *di_bh, struct page *mmap_page) { int ret, credits = OCFS2_INODE_UPDATE_CREDITS; - ssize_t written = 0; - u32 phys; - struct inode *inode = file->f_mapping->host; + unsigned int clusters_to_alloc, extents_to_split; + struct ocfs2_write_ctxt *wc; + struct inode *inode = mapping->host; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - struct buffer_head *di_bh = NULL; struct ocfs2_dinode *di; struct ocfs2_alloc_context *data_ac = NULL; struct ocfs2_alloc_context *meta_ac = NULL; handle_t *handle; - struct ocfs2_write_ctxt wc; - - ocfs2_write_ctxt_init(&wc, osb, pos, count, actor, priv); - ret = ocfs2_meta_lock(inode, &di_bh, 1); + ret = ocfs2_alloc_write_ctxt(&wc, osb, pos, len, di_bh); if (ret) { mlog_errno(ret); - goto out; + return ret; } - di = (struct ocfs2_dinode *)di_bh->b_data; - - /* - * Take alloc sem here to prevent concurrent lookups. That way - * the mapping, zeroing and tree manipulation within - * ocfs2_write() will be safe against ->readpage(). This - * should also serve to lock out allocation from a shared - * writeable region. - */ - down_write(&OCFS2_I(inode)->ip_alloc_sem); - ret = ocfs2_get_clusters(inode, wc.w_cpos, &phys, NULL, NULL); + ret = ocfs2_populate_write_desc(inode, wc, &clusters_to_alloc, + &extents_to_split); if (ret) { mlog_errno(ret); - goto out_meta; + goto out; } - /* phys == 0 means that allocation is required. */ - if (phys == 0) { - ret = ocfs2_lock_allocators(inode, di, 1, &data_ac, &meta_ac); + di = (struct ocfs2_dinode *)wc->w_di_bh->b_data; + + /* + * We set w_target_from, w_target_to here so that + * ocfs2_write_end() knows which range in the target page to + * write out. An allocation requires that we write the entire + * cluster range. + */ + if (clusters_to_alloc || extents_to_split) { + /* + * XXX: We are stretching the limits of + * ocfs2_lock_allocators(). It greatly over-estimates + * the work to be done. + */ + ret = ocfs2_lock_allocators(inode, di, clusters_to_alloc, + extents_to_split, &data_ac, &meta_ac); if (ret) { mlog_errno(ret); - goto out_meta; + goto out; } - credits = ocfs2_calc_extend_credits(inode->i_sb, di, 1); - } + credits = ocfs2_calc_extend_credits(inode->i_sb, di, + clusters_to_alloc); - ret = ocfs2_data_lock(inode, 1); - if (ret) { - mlog_errno(ret); - goto out_meta; } + ocfs2_set_target_boundaries(osb, wc, pos, len, + clusters_to_alloc + extents_to_split); + handle = ocfs2_start_trans(osb, credits); if (IS_ERR(handle)) { ret = PTR_ERR(handle); mlog_errno(ret); - goto out_data; + goto out; } - written = ocfs2_write(file, phys, handle, di_bh, data_ac, - meta_ac, &wc); - if (written < 0) { - ret = written; + wc->w_handle = handle; + + /* + * We don't want this to fail in ocfs2_write_end(), so do it + * here. + */ + ret = ocfs2_journal_access(handle, inode, wc->w_di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { mlog_errno(ret); goto out_commit; } - ret = ocfs2_journal_access(handle, inode, di_bh, - OCFS2_JOURNAL_ACCESS_WRITE); + /* + * Fill our page array first. That way we've grabbed enough so + * that we can zero and flush if we error after adding the + * extent. + */ + ret = ocfs2_grab_pages_for_write(mapping, wc, wc->w_cpos, pos, + clusters_to_alloc + extents_to_split, + mmap_page); if (ret) { mlog_errno(ret); goto out_commit; } - pos += written; + ret = ocfs2_write_cluster_by_desc(mapping, data_ac, meta_ac, wc, pos, + len); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + if (data_ac) + ocfs2_free_alloc_context(data_ac); + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + *pagep = wc->w_target_page; + *fsdata = wc; + return 0; +out_commit: + ocfs2_commit_trans(osb, handle); + +out: + ocfs2_free_write_ctxt(wc); + + if (data_ac) + ocfs2_free_alloc_context(data_ac); + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + return ret; +} + +int ocfs2_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + int ret; + struct buffer_head *di_bh = NULL; + struct inode *inode = mapping->host; + + ret = ocfs2_meta_lock(inode, &di_bh, 1); + if (ret) { + mlog_errno(ret); + return ret; + } + + /* + * Take alloc sem here to prevent concurrent lookups. That way + * the mapping, zeroing and tree manipulation within + * ocfs2_write() will be safe against ->readpage(). This + * should also serve to lock out allocation from a shared + * writeable region. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + + ret = ocfs2_data_lock(inode, 1); + if (ret) { + mlog_errno(ret); + goto out_fail; + } + + ret = ocfs2_write_begin_nolock(mapping, pos, len, flags, pagep, + fsdata, di_bh, NULL); + if (ret) { + mlog_errno(ret); + goto out_fail_data; + } + + brelse(di_bh); + + return 0; + +out_fail_data: + ocfs2_data_unlock(inode, 1); +out_fail: + up_write(&OCFS2_I(inode)->ip_alloc_sem); + + brelse(di_bh); + ocfs2_meta_unlock(inode, 1); + + return ret; +} + +int ocfs2_write_end_nolock(struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int i; + unsigned from, to, start = pos & (PAGE_CACHE_SIZE - 1); + struct inode *inode = mapping->host; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_write_ctxt *wc = fsdata; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data; + handle_t *handle = wc->w_handle; + struct page *tmppage; + + if (unlikely(copied < len)) { + if (!PageUptodate(wc->w_target_page)) + copied = 0; + + ocfs2_zero_new_buffers(wc->w_target_page, start+copied, + start+len); + } + flush_dcache_page(wc->w_target_page); + + for(i = 0; i < wc->w_num_pages; i++) { + tmppage = wc->w_pages[i]; + + if (tmppage == wc->w_target_page) { + from = wc->w_target_from; + to = wc->w_target_to; + + BUG_ON(from > PAGE_CACHE_SIZE || + to > PAGE_CACHE_SIZE || + to < from); + } else { + /* + * Pages adjacent to the target (if any) imply + * a hole-filling write in which case we want + * to flush their entire range. + */ + from = 0; + to = PAGE_CACHE_SIZE; + } + + if (ocfs2_should_order_data(inode)) + walk_page_buffers(wc->w_handle, page_buffers(tmppage), + from, to, NULL, + ocfs2_journal_dirty_data); + + block_commit_write(tmppage, from, to); + } + + pos += copied; if (pos > inode->i_size) { i_size_write(inode, pos); mark_inode_dirty(inode); @@ -1283,29 +1582,31 @@ ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos, inode->i_mtime = inode->i_ctime = CURRENT_TIME; di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec); di->i_mtime_nsec = di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); + ocfs2_journal_dirty(handle, wc->w_di_bh); - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret) - mlog_errno(ret); - -out_commit: ocfs2_commit_trans(osb, handle); -out_data: - ocfs2_data_unlock(inode, 1); + ocfs2_run_deallocs(osb, &wc->w_dealloc); + + ocfs2_free_write_ctxt(wc); + + return copied; +} + +int ocfs2_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int ret; + struct inode *inode = mapping->host; -out_meta: + ret = ocfs2_write_end_nolock(mapping, pos, len, copied, page, fsdata); + + ocfs2_data_unlock(inode, 1); up_write(&OCFS2_I(inode)->ip_alloc_sem); ocfs2_meta_unlock(inode, 1); -out: - brelse(di_bh); - if (data_ac) - ocfs2_free_alloc_context(data_ac); - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); - - return written ? written : ret; + return ret; } const struct address_space_operations ocfs2_aops = { diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index 45821d4..389579b 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -42,57 +42,22 @@ int walk_page_buffers( handle_t *handle, int (*fn)( handle_t *handle, struct buffer_head *bh)); -struct ocfs2_write_ctxt; -typedef int (ocfs2_page_writer)(struct inode *, struct ocfs2_write_ctxt *, - u64 *, unsigned int *, unsigned int *); +int ocfs2_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata); -ssize_t ocfs2_buffered_write_cluster(struct file *file, loff_t pos, - size_t count, ocfs2_page_writer *actor, - void *priv); +int ocfs2_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata); -struct ocfs2_write_ctxt { - size_t w_count; - loff_t w_pos; - u32 w_cpos; - unsigned int w_finished_copy; +int ocfs2_write_end_nolock(struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata); - /* This is true if page_size > cluster_size */ - unsigned int w_large_pages; - - /* Filler callback and private data */ - ocfs2_page_writer *w_write_data_page; - void *w_private; - - /* Only valid for the filler callback */ - struct page *w_this_page; - unsigned int w_this_page_new; -}; - -struct ocfs2_buffered_write_priv { - char *b_src_buf; - const struct iovec *b_cur_iov; /* Current iovec */ - size_t b_cur_off; /* Offset in the - * current iovec */ -}; -int ocfs2_map_and_write_user_data(struct inode *inode, - struct ocfs2_write_ctxt *wc, - u64 *p_blkno, - unsigned int *ret_from, - unsigned int *ret_to); - -struct ocfs2_splice_write_priv { - struct splice_desc *s_sd; - struct pipe_buffer *s_buf; - struct pipe_inode_info *s_pipe; - /* Neither offset value is ever larger than one page */ - unsigned int s_offset; - unsigned int s_buf_offset; -}; -int ocfs2_map_and_write_splice_data(struct inode *inode, - struct ocfs2_write_ctxt *wc, - u64 *p_blkno, - unsigned int *ret_from, - unsigned int *ret_to); +int ocfs2_write_begin_nolock(struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata, + struct buffer_head *di_bh, struct page *mmap_page); /* all ocfs2_dio_end_io()'s fault */ #define ocfs2_iocb_is_rw_locked(iocb) \ diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 9791134..2bd7f78 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1335,6 +1335,7 @@ static ssize_t o2hb_region_dev_write(struct o2hb_region *reg, ret = wait_event_interruptible(o2hb_steady_queue, atomic_read(®->hr_steady_iterations) == 0); if (ret) { + /* We got interrupted (hello ptrace!). Clean up */ spin_lock(&o2hb_live_lock); hb_task = reg->hr_task; reg->hr_task = NULL; @@ -1345,7 +1346,16 @@ static ssize_t o2hb_region_dev_write(struct o2hb_region *reg, goto out; } - ret = count; + /* Ok, we were woken. Make sure it wasn't by drop_item() */ + spin_lock(&o2hb_live_lock); + hb_task = reg->hr_task; + spin_unlock(&o2hb_live_lock); + + if (hb_task) + ret = count; + else + ret = -EIO; + out: if (filp) fput(filp); @@ -1523,6 +1533,15 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, if (hb_task) kthread_stop(hb_task); + /* + * If we're racing a dev_write(), we need to wake them. They will + * check reg->hr_task + */ + if (atomic_read(®->hr_steady_iterations) != 0) { + atomic_set(®->hr_steady_iterations, 0); + wake_up(&o2hb_steady_queue); + } + config_item_put(item); } @@ -1665,7 +1684,67 @@ void o2hb_setup_callback(struct o2hb_callback_func *hc, } EXPORT_SYMBOL_GPL(o2hb_setup_callback); -int o2hb_register_callback(struct o2hb_callback_func *hc) +static struct o2hb_region *o2hb_find_region(const char *region_uuid) +{ + struct o2hb_region *p, *reg = NULL; + + assert_spin_locked(&o2hb_live_lock); + + list_for_each_entry(p, &o2hb_all_regions, hr_all_item) { + if (!strcmp(region_uuid, config_item_name(&p->hr_item))) { + reg = p; + break; + } + } + + return reg; +} + +static int o2hb_region_get(const char *region_uuid) +{ + int ret = 0; + struct o2hb_region *reg; + + spin_lock(&o2hb_live_lock); + + reg = o2hb_find_region(region_uuid); + if (!reg) + ret = -ENOENT; + spin_unlock(&o2hb_live_lock); + + if (ret) + goto out; + + ret = o2nm_depend_this_node(); + if (ret) + goto out; + + ret = o2nm_depend_item(®->hr_item); + if (ret) + o2nm_undepend_this_node(); + +out: + return ret; +} + +static void o2hb_region_put(const char *region_uuid) +{ + struct o2hb_region *reg; + + spin_lock(&o2hb_live_lock); + + reg = o2hb_find_region(region_uuid); + + spin_unlock(&o2hb_live_lock); + + if (reg) { + o2nm_undepend_item(®->hr_item); + o2nm_undepend_this_node(); + } +} + +int o2hb_register_callback(const char *region_uuid, + struct o2hb_callback_func *hc) { struct o2hb_callback_func *tmp; struct list_head *iter; @@ -1681,6 +1760,12 @@ int o2hb_register_callback(struct o2hb_callback_func *hc) goto out; } + if (region_uuid) { + ret = o2hb_region_get(region_uuid); + if (ret) + goto out; + } + down_write(&o2hb_callback_sem); list_for_each(iter, &hbcall->list) { @@ -1702,16 +1787,21 @@ out: } EXPORT_SYMBOL_GPL(o2hb_register_callback); -void o2hb_unregister_callback(struct o2hb_callback_func *hc) +void o2hb_unregister_callback(const char *region_uuid, + struct o2hb_callback_func *hc) { BUG_ON(hc->hc_magic != O2HB_CB_MAGIC); mlog(ML_HEARTBEAT, "on behalf of %p for funcs %p\n", __builtin_return_address(0), hc); + /* XXX Can this happen _with_ a region reference? */ if (list_empty(&hc->hc_item)) return; + if (region_uuid) + o2hb_region_put(region_uuid); + down_write(&o2hb_callback_sem); list_del_init(&hc->hc_item); diff --git a/fs/ocfs2/cluster/heartbeat.h b/fs/ocfs2/cluster/heartbeat.h index cc6d40b..35397dd 100644 --- a/fs/ocfs2/cluster/heartbeat.h +++ b/fs/ocfs2/cluster/heartbeat.h @@ -69,8 +69,10 @@ void o2hb_setup_callback(struct o2hb_callback_func *hc, o2hb_cb_func *func, void *data, int priority); -int o2hb_register_callback(struct o2hb_callback_func *hc); -void o2hb_unregister_callback(struct o2hb_callback_func *hc); +int o2hb_register_callback(const char *region_uuid, + struct o2hb_callback_func *hc); +void o2hb_unregister_callback(const char *region_uuid, + struct o2hb_callback_func *hc); void o2hb_fill_node_map(unsigned long *map, unsigned bytes); void o2hb_init(void); diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c index 2b205f5..e9e042b 100644 --- a/fs/ocfs2/cluster/masklog.c +++ b/fs/ocfs2/cluster/masklog.c @@ -74,7 +74,6 @@ struct mlog_attribute { #define define_mask(_name) { \ .attr = { \ .name = #_name, \ - .owner = THIS_MODULE, \ .mode = S_IRUGO | S_IWUSR, \ }, \ .mask = ML_##_name, \ diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index 9f5ad0f..af2070d 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -900,6 +900,46 @@ static struct o2nm_cluster_group o2nm_cluster_group = { }, }; +int o2nm_depend_item(struct config_item *item) +{ + return configfs_depend_item(&o2nm_cluster_group.cs_subsys, item); +} + +void o2nm_undepend_item(struct config_item *item) +{ + configfs_undepend_item(&o2nm_cluster_group.cs_subsys, item); +} + +int o2nm_depend_this_node(void) +{ + int ret = 0; + struct o2nm_node *local_node; + + local_node = o2nm_get_node_by_num(o2nm_this_node()); + if (!local_node) { + ret = -EINVAL; + goto out; + } + + ret = o2nm_depend_item(&local_node->nd_item); + o2nm_node_put(local_node); + +out: + return ret; +} + +void o2nm_undepend_this_node(void) +{ + struct o2nm_node *local_node; + + local_node = o2nm_get_node_by_num(o2nm_this_node()); + BUG_ON(!local_node); + + o2nm_undepend_item(&local_node->nd_item); + o2nm_node_put(local_node); +} + + static void __exit exit_o2nm(void) { if (ocfs2_table_header) @@ -934,7 +974,7 @@ static int __init init_o2nm(void) goto out_sysctl; config_group_init(&o2nm_cluster_group.cs_subsys.su_group); - init_MUTEX(&o2nm_cluster_group.cs_subsys.su_sem); + mutex_init(&o2nm_cluster_group.cs_subsys.su_mutex); ret = configfs_register_subsystem(&o2nm_cluster_group.cs_subsys); if (ret) { printk(KERN_ERR "nodemanager: Registration returned %d\n", ret); diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h index 0705221..7c86036 100644 --- a/fs/ocfs2/cluster/nodemanager.h +++ b/fs/ocfs2/cluster/nodemanager.h @@ -77,4 +77,9 @@ struct o2nm_node *o2nm_get_node_by_ip(__be32 addr); void o2nm_node_get(struct o2nm_node *node); void o2nm_node_put(struct o2nm_node *node); +int o2nm_depend_item(struct config_item *item); +void o2nm_undepend_item(struct config_item *item); +int o2nm_depend_this_node(void); +void o2nm_undepend_this_node(void); + #endif /* O2CLUSTER_NODEMANAGER_H */ diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 0b229a9..f0bdfd9 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -261,14 +261,12 @@ out: static void o2net_complete_nodes_nsw(struct o2net_node *nn) { - struct list_head *iter, *tmp; + struct o2net_status_wait *nsw, *tmp; unsigned int num_kills = 0; - struct o2net_status_wait *nsw; assert_spin_locked(&nn->nn_lock); - list_for_each_safe(iter, tmp, &nn->nn_status_list) { - nsw = list_entry(iter, struct o2net_status_wait, ns_node_item); + list_for_each_entry_safe(nsw, tmp, &nn->nn_status_list, ns_node_item) { o2net_complete_nsw_locked(nn, nsw, O2NET_ERR_DIED, 0); num_kills++; } @@ -764,13 +762,10 @@ EXPORT_SYMBOL_GPL(o2net_register_handler); void o2net_unregister_handler_list(struct list_head *list) { - struct list_head *pos, *n; - struct o2net_msg_handler *nmh; + struct o2net_msg_handler *nmh, *n; write_lock(&o2net_handler_lock); - list_for_each_safe(pos, n, list) { - nmh = list_entry(pos, struct o2net_msg_handler, - nh_unregister_item); + list_for_each_entry_safe(nmh, n, list, nh_unregister_item) { mlog(ML_TCP, "unregistering handler func %p type %u key %08x\n", nmh->nh_func, nmh->nh_msg_type, nmh->nh_key); rb_erase(&nmh->nh_node, &o2net_handler_tree); @@ -1638,8 +1633,8 @@ static void o2net_hb_node_up_cb(struct o2nm_node *node, int node_num, void o2net_unregister_hb_callbacks(void) { - o2hb_unregister_callback(&o2net_hb_up); - o2hb_unregister_callback(&o2net_hb_down); + o2hb_unregister_callback(NULL, &o2net_hb_up); + o2hb_unregister_callback(NULL, &o2net_hb_down); } int o2net_register_hb_callbacks(void) @@ -1651,9 +1646,9 @@ int o2net_register_hb_callbacks(void) o2hb_setup_callback(&o2net_hb_up, O2HB_NODE_UP_CB, o2net_hb_node_up_cb, NULL, O2NET_HB_PRI); - ret = o2hb_register_callback(&o2net_hb_up); + ret = o2hb_register_callback(NULL, &o2net_hb_up); if (ret == 0) - ret = o2hb_register_callback(&o2net_hb_down); + ret = o2hb_register_callback(NULL, &o2net_hb_down); if (ret) o2net_unregister_hb_callbacks(); diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index c441ef1..0d5fdde 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -368,7 +368,7 @@ int ocfs2_do_extend_dir(struct super_block *sb, u32 offset = OCFS2_I(dir)->ip_clusters; status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset, - 1, parent_fe_bh, handle, + 1, 0, parent_fe_bh, handle, data_ac, meta_ac, NULL); BUG_ON(status == -EAGAIN); if (status < 0) { diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index d836b98..6954565 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -1128,8 +1128,8 @@ bail: static void dlm_unregister_domain_handlers(struct dlm_ctxt *dlm) { - o2hb_unregister_callback(&dlm->dlm_hb_up); - o2hb_unregister_callback(&dlm->dlm_hb_down); + o2hb_unregister_callback(NULL, &dlm->dlm_hb_up); + o2hb_unregister_callback(NULL, &dlm->dlm_hb_down); o2net_unregister_handler_list(&dlm->dlm_domain_handlers); } @@ -1141,13 +1141,13 @@ static int dlm_register_domain_handlers(struct dlm_ctxt *dlm) o2hb_setup_callback(&dlm->dlm_hb_down, O2HB_NODE_DOWN_CB, dlm_hb_node_down_cb, dlm, DLM_HB_NODE_DOWN_PRI); - status = o2hb_register_callback(&dlm->dlm_hb_down); + status = o2hb_register_callback(NULL, &dlm->dlm_hb_down); if (status) goto bail; o2hb_setup_callback(&dlm->dlm_hb_up, O2HB_NODE_UP_CB, dlm_hb_node_up_cb, dlm, DLM_HB_NODE_UP_PRI); - status = o2hb_register_callback(&dlm->dlm_hb_up); + status = o2hb_register_callback(NULL, &dlm->dlm_hb_up); if (status) goto bail; diff --git a/fs/ocfs2/dlm/dlmfs.c b/fs/ocfs2/dlm/dlmfs.c index fd8cb1b..7418dc8 100644 --- a/fs/ocfs2/dlm/dlmfs.c +++ b/fs/ocfs2/dlm/dlmfs.c @@ -592,7 +592,7 @@ static int __init init_dlmfs_fs(void) sizeof(struct dlmfs_inode_private), 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - dlmfs_init_once, NULL); + dlmfs_init_once); if (!dlmfs_inode_cache) return -ENOMEM; cleanup_inode = 1; diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 6edffca..62e4a7d 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -192,25 +192,20 @@ static void dlm_print_one_mle(struct dlm_master_list_entry *mle) static void dlm_dump_mles(struct dlm_ctxt *dlm) { struct dlm_master_list_entry *mle; - struct list_head *iter; mlog(ML_NOTICE, "dumping all mles for domain %s:\n", dlm->name); spin_lock(&dlm->master_lock); - list_for_each(iter, &dlm->master_list) { - mle = list_entry(iter, struct dlm_master_list_entry, list); + list_for_each_entry(mle, &dlm->master_list, list) dlm_print_one_mle(mle); - } spin_unlock(&dlm->master_lock); } int dlm_dump_all_mles(const char __user *data, unsigned int len) { - struct list_head *iter; struct dlm_ctxt *dlm; spin_lock(&dlm_domain_lock); - list_for_each(iter, &dlm_domains) { - dlm = list_entry (iter, struct dlm_ctxt, list); + list_for_each_entry(dlm, &dlm_domains, list) { mlog(ML_NOTICE, "found dlm: %p, name=%s\n", dlm, dlm->name); dlm_dump_mles(dlm); } @@ -454,12 +449,10 @@ static int dlm_find_mle(struct dlm_ctxt *dlm, char *name, unsigned int namelen) { struct dlm_master_list_entry *tmpmle; - struct list_head *iter; assert_spin_locked(&dlm->master_lock); - list_for_each(iter, &dlm->master_list) { - tmpmle = list_entry(iter, struct dlm_master_list_entry, list); + list_for_each_entry(tmpmle, &dlm->master_list, list) { if (!dlm_mle_equal(dlm, tmpmle, name, namelen)) continue; dlm_get_mle(tmpmle); @@ -472,13 +465,10 @@ static int dlm_find_mle(struct dlm_ctxt *dlm, void dlm_hb_event_notify_attached(struct dlm_ctxt *dlm, int idx, int node_up) { struct dlm_master_list_entry *mle; - struct list_head *iter; assert_spin_locked(&dlm->spinlock); - list_for_each(iter, &dlm->mle_hb_events) { - mle = list_entry(iter, struct dlm_master_list_entry, - hb_events); + list_for_each_entry(mle, &dlm->mle_hb_events, hb_events) { if (node_up) dlm_mle_node_up(dlm, mle, NULL, idx); else @@ -520,7 +510,7 @@ int dlm_init_mle_cache(void) dlm_mle_cache = kmem_cache_create("dlm_mle_cache", sizeof(struct dlm_master_list_entry), 0, SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (dlm_mle_cache == NULL) return -ENOMEM; return 0; @@ -2434,7 +2424,7 @@ static int dlm_is_lockres_migrateable(struct dlm_ctxt *dlm, int ret; int i; int count = 0; - struct list_head *queue, *iter; + struct list_head *queue; struct dlm_lock *lock; assert_spin_locked(&res->spinlock); @@ -2453,8 +2443,7 @@ static int dlm_is_lockres_migrateable(struct dlm_ctxt *dlm, ret = 0; queue = &res->granted; for (i = 0; i < 3; i++) { - list_for_each(iter, queue) { - lock = list_entry(iter, struct dlm_lock, list); + list_for_each_entry(lock, queue, list) { ++count; if (lock->ml.node == dlm->node_num) { mlog(0, "found a lock owned by this node still " @@ -2923,18 +2912,16 @@ again: static void dlm_remove_nonlocal_locks(struct dlm_ctxt *dlm, struct dlm_lock_resource *res) { - struct list_head *iter, *iter2; struct list_head *queue = &res->granted; int i, bit; - struct dlm_lock *lock; + struct dlm_lock *lock, *next; assert_spin_locked(&res->spinlock); BUG_ON(res->owner == dlm->node_num); for (i=0; i<3; i++) { - list_for_each_safe(iter, iter2, queue) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry_safe(lock, next, queue, list) { if (lock->ml.node != dlm->node_num) { mlog(0, "putting lock for node %u\n", lock->ml.node); @@ -2976,7 +2963,6 @@ static u8 dlm_pick_migration_target(struct dlm_ctxt *dlm, { int i; struct list_head *queue = &res->granted; - struct list_head *iter; struct dlm_lock *lock; int nodenum; @@ -2984,10 +2970,9 @@ static u8 dlm_pick_migration_target(struct dlm_ctxt *dlm, spin_lock(&res->spinlock); for (i=0; i<3; i++) { - list_for_each(iter, queue) { + list_for_each_entry(lock, queue, list) { /* up to the caller to make sure this node * is alive */ - lock = list_entry (iter, struct dlm_lock, list); if (lock->ml.node != dlm->node_num) { spin_unlock(&res->spinlock); return lock->ml.node; @@ -3234,8 +3219,7 @@ static int dlm_add_migration_mle(struct dlm_ctxt *dlm, void dlm_clean_master_list(struct dlm_ctxt *dlm, u8 dead_node) { - struct list_head *iter, *iter2; - struct dlm_master_list_entry *mle; + struct dlm_master_list_entry *mle, *next; struct dlm_lock_resource *res; unsigned int hash; @@ -3245,9 +3229,7 @@ top: /* clean the master list */ spin_lock(&dlm->master_lock); - list_for_each_safe(iter, iter2, &dlm->master_list) { - mle = list_entry(iter, struct dlm_master_list_entry, list); - + list_for_each_entry_safe(mle, next, &dlm->master_list, list) { BUG_ON(mle->type != DLM_MLE_BLOCK && mle->type != DLM_MLE_MASTER && mle->type != DLM_MLE_MIGRATION); diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 671c4ed..a2c3316 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -158,8 +158,7 @@ void dlm_dispatch_work(struct work_struct *work) struct dlm_ctxt *dlm = container_of(work, struct dlm_ctxt, dispatched_work); LIST_HEAD(tmp_list); - struct list_head *iter, *iter2; - struct dlm_work_item *item; + struct dlm_work_item *item, *next; dlm_workfunc_t *workfunc; int tot=0; @@ -167,13 +166,12 @@ void dlm_dispatch_work(struct work_struct *work) list_splice_init(&dlm->work_list, &tmp_list); spin_unlock(&dlm->work_lock); - list_for_each_safe(iter, iter2, &tmp_list) { + list_for_each_entry(item, &tmp_list, list) { tot++; } mlog(0, "%s: work thread has %d work items\n", dlm->name, tot); - list_for_each_safe(iter, iter2, &tmp_list) { - item = list_entry(iter, struct dlm_work_item, list); + list_for_each_entry_safe(item, next, &tmp_list, list) { workfunc = item->func; list_del_init(&item->list); @@ -549,7 +547,6 @@ static int dlm_remaster_locks(struct dlm_ctxt *dlm, u8 dead_node) { int status = 0; struct dlm_reco_node_data *ndata; - struct list_head *iter; int all_nodes_done; int destroy = 0; int pass = 0; @@ -567,8 +564,7 @@ static int dlm_remaster_locks(struct dlm_ctxt *dlm, u8 dead_node) /* safe to access the node data list without a lock, since this * process is the only one to change the list */ - list_for_each(iter, &dlm->reco.node_data) { - ndata = list_entry (iter, struct dlm_reco_node_data, list); + list_for_each_entry(ndata, &dlm->reco.node_data, list) { BUG_ON(ndata->state != DLM_RECO_NODE_DATA_INIT); ndata->state = DLM_RECO_NODE_DATA_REQUESTING; @@ -655,9 +651,7 @@ static int dlm_remaster_locks(struct dlm_ctxt *dlm, u8 dead_node) * done, or if anyone died */ all_nodes_done = 1; spin_lock(&dlm_reco_state_lock); - list_for_each(iter, &dlm->reco.node_data) { - ndata = list_entry (iter, struct dlm_reco_node_data, list); - + list_for_each_entry(ndata, &dlm->reco.node_data, list) { mlog(0, "checking recovery state of node %u\n", ndata->node_num); switch (ndata->state) { @@ -774,16 +768,14 @@ static int dlm_init_recovery_area(struct dlm_ctxt *dlm, u8 dead_node) static void dlm_destroy_recovery_area(struct dlm_ctxt *dlm, u8 dead_node) { - struct list_head *iter, *iter2; - struct dlm_reco_node_data *ndata; + struct dlm_reco_node_data *ndata, *next; LIST_HEAD(tmplist); spin_lock(&dlm_reco_state_lock); list_splice_init(&dlm->reco.node_data, &tmplist); spin_unlock(&dlm_reco_state_lock); - list_for_each_safe(iter, iter2, &tmplist) { - ndata = list_entry (iter, struct dlm_reco_node_data, list); + list_for_each_entry_safe(ndata, next, &tmplist, list) { list_del_init(&ndata->list); kfree(ndata); } @@ -876,7 +868,6 @@ static void dlm_request_all_locks_worker(struct dlm_work_item *item, void *data) struct dlm_lock_resource *res; struct dlm_ctxt *dlm; LIST_HEAD(resources); - struct list_head *iter; int ret; u8 dead_node, reco_master; int skip_all_done = 0; @@ -920,8 +911,7 @@ static void dlm_request_all_locks_worker(struct dlm_work_item *item, void *data) /* any errors returned will be due to the new_master dying, * the dlm_reco_thread should detect this */ - list_for_each(iter, &resources) { - res = list_entry (iter, struct dlm_lock_resource, recovering); + list_for_each_entry(res, &resources, recovering) { ret = dlm_send_one_lockres(dlm, res, mres, reco_master, DLM_MRES_RECOVERY); if (ret < 0) { @@ -983,7 +973,6 @@ int dlm_reco_data_done_handler(struct o2net_msg *msg, u32 len, void *data, { struct dlm_ctxt *dlm = data; struct dlm_reco_data_done *done = (struct dlm_reco_data_done *)msg->buf; - struct list_head *iter; struct dlm_reco_node_data *ndata = NULL; int ret = -EINVAL; @@ -1000,8 +989,7 @@ int dlm_reco_data_done_handler(struct o2net_msg *msg, u32 len, void *data, dlm->reco.dead_node, done->node_idx, dlm->node_num); spin_lock(&dlm_reco_state_lock); - list_for_each(iter, &dlm->reco.node_data) { - ndata = list_entry (iter, struct dlm_reco_node_data, list); + list_for_each_entry(ndata, &dlm->reco.node_data, list) { if (ndata->node_num != done->node_idx) continue; @@ -1049,13 +1037,11 @@ static void dlm_move_reco_locks_to_list(struct dlm_ctxt *dlm, struct list_head *list, u8 dead_node) { - struct dlm_lock_resource *res; - struct list_head *iter, *iter2; + struct dlm_lock_resource *res, *next; struct dlm_lock *lock; spin_lock(&dlm->spinlock); - list_for_each_safe(iter, iter2, &dlm->reco.resources) { - res = list_entry (iter, struct dlm_lock_resource, recovering); + list_for_each_entry_safe(res, next, &dlm->reco.resources, recovering) { /* always prune any $RECOVERY entries for dead nodes, * otherwise hangs can occur during later recovery */ if (dlm_is_recovery_lock(res->lockname.name, @@ -1169,7 +1155,7 @@ static void dlm_init_migratable_lockres(struct dlm_migratable_lockres *mres, u8 flags, u8 master) { /* mres here is one full page */ - memset(mres, 0, PAGE_SIZE); + clear_page(mres); mres->lockname_len = namelen; memcpy(mres->lockname, lockname, namelen); mres->num_locks = 0; @@ -1252,7 +1238,7 @@ int dlm_send_one_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, struct dlm_migratable_lockres *mres, u8 send_to, u8 flags) { - struct list_head *queue, *iter; + struct list_head *queue; int total_locks, i; u64 mig_cookie = 0; struct dlm_lock *lock; @@ -1278,9 +1264,7 @@ int dlm_send_one_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, total_locks = 0; for (i=DLM_GRANTED_LIST; i<=DLM_BLOCKED_LIST; i++) { queue = dlm_list_idx_to_ptr(res, i); - list_for_each(iter, queue) { - lock = list_entry (iter, struct dlm_lock, list); - + list_for_each_entry(lock, queue, list) { /* add another lock. */ total_locks++; if (!dlm_add_lock_to_array(lock, mres, i)) @@ -1717,7 +1701,6 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm, struct dlm_lockstatus *lksb = NULL; int ret = 0; int i, j, bad; - struct list_head *iter; struct dlm_lock *lock = NULL; u8 from = O2NM_MAX_NODES; unsigned int added = 0; @@ -1755,8 +1738,7 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm, spin_lock(&res->spinlock); for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) { tmpq = dlm_list_idx_to_ptr(res, j); - list_for_each(iter, tmpq) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry(lock, tmpq, list) { if (lock->ml.cookie != ml->cookie) lock = NULL; else @@ -1930,8 +1912,8 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm, struct dlm_lock_resource *res) { int i; - struct list_head *queue, *iter, *iter2; - struct dlm_lock *lock; + struct list_head *queue; + struct dlm_lock *lock, *next; res->state |= DLM_LOCK_RES_RECOVERING; if (!list_empty(&res->recovering)) { @@ -1947,8 +1929,7 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm, /* find any pending locks and put them back on proper list */ for (i=DLM_BLOCKED_LIST; i>=DLM_GRANTED_LIST; i--) { queue = dlm_list_idx_to_ptr(res, i); - list_for_each_safe(iter, iter2, queue) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry_safe(lock, next, queue, list) { dlm_lock_get(lock); if (lock->convert_pending) { /* move converting lock back to granted */ @@ -2013,18 +1994,15 @@ static void dlm_finish_local_lockres_recovery(struct dlm_ctxt *dlm, u8 dead_node, u8 new_master) { int i; - struct list_head *iter, *iter2; struct hlist_node *hash_iter; struct hlist_head *bucket; - - struct dlm_lock_resource *res; + struct dlm_lock_resource *res, *next; mlog_entry_void(); assert_spin_locked(&dlm->spinlock); - list_for_each_safe(iter, iter2, &dlm->reco.resources) { - res = list_entry (iter, struct dlm_lock_resource, recovering); + list_for_each_entry_safe(res, next, &dlm->reco.resources, recovering) { if (res->owner == dead_node) { list_del_init(&res->recovering); spin_lock(&res->spinlock); @@ -2099,7 +2077,7 @@ static inline int dlm_lvb_needs_invalidation(struct dlm_lock *lock, int local) static void dlm_revalidate_lvb(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, u8 dead_node) { - struct list_head *iter, *queue; + struct list_head *queue; struct dlm_lock *lock; int blank_lvb = 0, local = 0; int i; @@ -2121,8 +2099,7 @@ static void dlm_revalidate_lvb(struct dlm_ctxt *dlm, for (i=DLM_GRANTED_LIST; i<=DLM_CONVERTING_LIST; i++) { queue = dlm_list_idx_to_ptr(res, i); - list_for_each(iter, queue) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry(lock, queue, list) { if (lock->ml.node == search_node) { if (dlm_lvb_needs_invalidation(lock, local)) { /* zero the lksb lvb and lockres lvb */ @@ -2143,8 +2120,7 @@ static void dlm_revalidate_lvb(struct dlm_ctxt *dlm, static void dlm_free_dead_locks(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, u8 dead_node) { - struct list_head *iter, *tmpiter; - struct dlm_lock *lock; + struct dlm_lock *lock, *next; unsigned int freed = 0; /* this node is the lockres master: @@ -2155,24 +2131,21 @@ static void dlm_free_dead_locks(struct dlm_ctxt *dlm, assert_spin_locked(&res->spinlock); /* TODO: check pending_asts, pending_basts here */ - list_for_each_safe(iter, tmpiter, &res->granted) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry_safe(lock, next, &res->granted, list) { if (lock->ml.node == dead_node) { list_del_init(&lock->list); dlm_lock_put(lock); freed++; } } - list_for_each_safe(iter, tmpiter, &res->converting) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry_safe(lock, next, &res->converting, list) { if (lock->ml.node == dead_node) { list_del_init(&lock->list); dlm_lock_put(lock); freed++; } } - list_for_each_safe(iter, tmpiter, &res->blocked) { - lock = list_entry (iter, struct dlm_lock, list); + list_for_each_entry_safe(lock, next, &res->blocked, list) { if (lock->ml.node == dead_node) { list_del_init(&lock->list); dlm_lock_put(lock); diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index d1bd305..f71250e 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -600,15 +600,13 @@ static inline int ocfs2_highest_compat_lock_level(int level) static void lockres_set_flags(struct ocfs2_lock_res *lockres, unsigned long newflags) { - struct list_head *pos, *tmp; - struct ocfs2_mask_waiter *mw; + struct ocfs2_mask_waiter *mw, *tmp; assert_spin_locked(&lockres->l_lock); lockres->l_flags = newflags; - list_for_each_safe(pos, tmp, &lockres->l_mask_waiters) { - mw = list_entry(pos, struct ocfs2_mask_waiter, mw_item); + list_for_each_entry_safe(mw, tmp, &lockres->l_mask_waiters, mw_item) { if ((lockres->l_flags & mw->mw_mask) != mw->mw_goal) continue; diff --git a/fs/ocfs2/endian.h b/fs/ocfs2/endian.h index f226b22..ff25762 100644 --- a/fs/ocfs2/endian.h +++ b/fs/ocfs2/endian.h @@ -32,6 +32,11 @@ static inline void le32_add_cpu(__le32 *var, u32 val) *var = cpu_to_le32(le32_to_cpu(*var) + val); } +static inline void le64_add_cpu(__le64 *var, u64 val) +{ + *var = cpu_to_le64(le64_to_cpu(*var) + val); +} + static inline void le32_and_cpu(__le32 *var, u32 val) { *var = cpu_to_le32(le32_to_cpu(*var) & val); diff --git a/fs/ocfs2/export.h b/fs/ocfs2/export.h index 5b77ee7..e08bed9 100644 --- a/fs/ocfs2/export.h +++ b/fs/ocfs2/export.h @@ -26,6 +26,8 @@ #ifndef OCFS2_EXPORT_H #define OCFS2_EXPORT_H +#include <linux/exportfs.h> + extern struct export_operations ocfs2_export_ops; #endif /* OCFS2_EXPORT_H */ diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index ba2b2ab..03c1d365 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -109,17 +109,14 @@ static int ocfs2_extent_map_lookup(struct inode *inode, unsigned int cpos, */ void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cpos) { - struct list_head *p, *n; - struct ocfs2_extent_map_item *emi; + struct ocfs2_extent_map_item *emi, *n; struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_extent_map *em = &oi->ip_extent_map; LIST_HEAD(tmp_list); unsigned int range; spin_lock(&oi->ip_lock); - list_for_each_safe(p, n, &em->em_list) { - emi = list_entry(p, struct ocfs2_extent_map_item, ei_list); - + list_for_each_entry_safe(emi, n, &em->em_list, ei_list) { if (emi->ei_cpos >= cpos) { /* Full truncate of this record. */ list_move(&emi->ei_list, &tmp_list); @@ -136,8 +133,7 @@ void ocfs2_extent_map_trunc(struct inode *inode, unsigned int cpos) } spin_unlock(&oi->ip_lock); - list_for_each_safe(p, n, &tmp_list) { - emi = list_entry(p, struct ocfs2_extent_map_item, ei_list); + list_for_each_entry_safe(emi, n, &tmp_list, ei_list) { list_del(&emi->ei_list); kfree(emi); } @@ -377,37 +373,6 @@ out: return ret; } -/* - * Return the index of the extent record which contains cluster #v_cluster. - * -1 is returned if it was not found. - * - * Should work fine on interior and exterior nodes. - */ -static int ocfs2_search_extent_list(struct ocfs2_extent_list *el, - u32 v_cluster) -{ - int ret = -1; - int i; - struct ocfs2_extent_rec *rec; - u32 rec_end, rec_start, clusters; - - for(i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { - rec = &el->l_recs[i]; - - rec_start = le32_to_cpu(rec->e_cpos); - clusters = ocfs2_rec_clusters(el, rec); - - rec_end = rec_start + clusters; - - if (v_cluster >= rec_start && v_cluster < rec_end) { - ret = i; - break; - } - } - - return ret; -} - int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster, u32 *num_clusters, unsigned int *extent_flags) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4979b66..5727cd1 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -34,6 +34,7 @@ #include <linux/splice.h> #include <linux/mount.h> #include <linux/writeback.h> +#include <linux/falloc.h> #define MLOG_MASK_PREFIX ML_INODE #include <cluster/masklog.h> @@ -263,6 +264,7 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, int status; handle_t *handle; struct ocfs2_dinode *di; + u64 cluster_bytes; mlog_entry_void(); @@ -286,7 +288,9 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, /* * Do this before setting i_size. */ - status = ocfs2_zero_tail_for_truncate(inode, handle, new_i_size); + cluster_bytes = ocfs2_align_bytes_to_clusters(inode->i_sb, new_i_size); + status = ocfs2_zero_range_for_truncate(inode, handle, new_i_size, + cluster_bytes); if (status) { mlog_errno(status); goto out_commit; @@ -326,9 +330,6 @@ static int ocfs2_truncate_file(struct inode *inode, (unsigned long long)OCFS2_I(inode)->ip_blkno, (unsigned long long)new_i_size); - unmap_mapping_range(inode->i_mapping, new_i_size + PAGE_SIZE - 1, 0, 1); - truncate_inode_pages(inode->i_mapping, new_i_size); - fe = (struct ocfs2_dinode *) di_bh->b_data; if (!OCFS2_IS_VALID_DINODE(fe)) { OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe); @@ -363,16 +364,23 @@ static int ocfs2_truncate_file(struct inode *inode, if (new_i_size == le64_to_cpu(fe->i_size)) goto bail; + down_write(&OCFS2_I(inode)->ip_alloc_sem); + /* This forces other nodes to sync and drop their pages. Do * this even if we have a truncate without allocation change - * ocfs2 cluster sizes can be much greater than page size, so * we have to truncate them anyway. */ status = ocfs2_data_lock(inode, 1); if (status < 0) { + up_write(&OCFS2_I(inode)->ip_alloc_sem); + mlog_errno(status); goto bail; } + unmap_mapping_range(inode->i_mapping, new_i_size + PAGE_SIZE - 1, 0, 1); + truncate_inode_pages(inode->i_mapping, new_i_size); + /* alright, we're going to need to do a full blown alloc size * change. Orphan the inode so that recovery can complete the * truncate if necessary. This does the task of marking @@ -399,6 +407,8 @@ static int ocfs2_truncate_file(struct inode *inode, bail_unlock_data: ocfs2_data_unlock(inode, 1); + up_write(&OCFS2_I(inode)->ip_alloc_sem); + bail: mlog_exit(status); @@ -419,6 +429,7 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, struct inode *inode, u32 *logical_offset, u32 clusters_to_add, + int mark_unwritten, struct buffer_head *fe_bh, handle_t *handle, struct ocfs2_alloc_context *data_ac, @@ -431,9 +442,13 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, enum ocfs2_alloc_restarted reason = RESTART_NONE; u32 bit_off, num_bits; u64 block; + u8 flags = 0; BUG_ON(!clusters_to_add); + if (mark_unwritten) + flags = OCFS2_EXT_UNWRITTEN; + free_extents = ocfs2_num_free_extents(osb, inode, fe); if (free_extents < 0) { status = free_extents; @@ -483,7 +498,7 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb, num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno); status = ocfs2_insert_extent(osb, handle, inode, fe_bh, *logical_offset, block, num_bits, - meta_ac); + flags, meta_ac); if (status < 0) { mlog_errno(status); goto leave; @@ -516,25 +531,31 @@ leave: * For a given allocation, determine which allocators will need to be * accessed, and lock them, reserving the appropriate number of bits. * - * Called from ocfs2_extend_allocation() for file systems which don't - * support holes, and from ocfs2_write() for file systems which - * understand sparse inodes. + * Sparse file systems call this from ocfs2_write_begin_nolock() + * and ocfs2_allocate_unwritten_extents(). + * + * File systems which don't support holes call this from + * ocfs2_extend_allocation(). */ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, - u32 clusters_to_add, + u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, struct ocfs2_alloc_context **meta_ac) { - int ret, num_free_extents; + int ret = 0, num_free_extents; + unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); *meta_ac = NULL; - *data_ac = NULL; + if (data_ac) + *data_ac = NULL; + + BUG_ON(clusters_to_add != 0 && data_ac == NULL); mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, " - "clusters_to_add = %u\n", + "clusters_to_add = %u, extents_to_split = %u\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, i_size_read(inode), - le32_to_cpu(di->i_clusters), clusters_to_add); + le32_to_cpu(di->i_clusters), clusters_to_add, extents_to_split); num_free_extents = ocfs2_num_free_extents(osb, inode, di); if (num_free_extents < 0) { @@ -552,9 +573,12 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, * * Most of the time we'll only be seeing this 1 cluster at a time * anyway. + * + * Always lock for any unwritten extents - we might want to + * add blocks during a split. */ if (!num_free_extents || - (ocfs2_sparse_alloc(osb) && num_free_extents < clusters_to_add)) { + (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) { ret = ocfs2_reserve_new_metadata(osb, di, meta_ac); if (ret < 0) { if (ret != -ENOSPC) @@ -563,6 +587,9 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, } } + if (clusters_to_add == 0) + goto out; + ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac); if (ret < 0) { if (ret != -ENOSPC) @@ -585,14 +612,13 @@ out: return ret; } -static int ocfs2_extend_allocation(struct inode *inode, - u32 clusters_to_add) +static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, + u32 clusters_to_add, int mark_unwritten) { int status = 0; int restart_func = 0; - int drop_alloc_sem = 0; int credits; - u32 prev_clusters, logical_start; + u32 prev_clusters; struct buffer_head *bh = NULL; struct ocfs2_dinode *fe = NULL; handle_t *handle = NULL; @@ -607,7 +633,7 @@ static int ocfs2_extend_allocation(struct inode *inode, * This function only exists for file systems which don't * support holes. */ - BUG_ON(ocfs2_sparse_alloc(osb)); + BUG_ON(mark_unwritten && !ocfs2_sparse_alloc(osb)); status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh, OCFS2_BH_CACHED, inode); @@ -623,19 +649,10 @@ static int ocfs2_extend_allocation(struct inode *inode, goto leave; } - logical_start = OCFS2_I(inode)->ip_clusters; - restart_all: BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters); - /* blocks peope in read/write from reading our allocation - * until we're done changing it. We depend on i_mutex to block - * other extend/truncate calls while we're here. Ordering wrt - * start_trans is important here -- always do it before! */ - down_write(&OCFS2_I(inode)->ip_alloc_sem); - drop_alloc_sem = 1; - - status = ocfs2_lock_allocators(inode, fe, clusters_to_add, &data_ac, + status = ocfs2_lock_allocators(inode, fe, clusters_to_add, 0, &data_ac, &meta_ac); if (status) { mlog_errno(status); @@ -668,6 +685,7 @@ restarted_transaction: inode, &logical_start, clusters_to_add, + mark_unwritten, bh, handle, data_ac, @@ -720,10 +738,6 @@ restarted_transaction: OCFS2_I(inode)->ip_clusters, i_size_read(inode)); leave: - if (drop_alloc_sem) { - up_write(&OCFS2_I(inode)->ip_alloc_sem); - drop_alloc_sem = 0; - } if (handle) { ocfs2_commit_trans(osb, handle); handle = NULL; @@ -749,6 +763,25 @@ leave: return status; } +static int ocfs2_extend_allocation(struct inode *inode, u32 logical_start, + u32 clusters_to_add, int mark_unwritten) +{ + int ret; + + /* + * The alloc sem blocks peope in read/write from reading our + * allocation until we're done changing it. We depend on + * i_mutex to block other extend/truncate calls while we're + * here. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + ret = __ocfs2_extend_allocation(inode, logical_start, clusters_to_add, + mark_unwritten); + up_write(&OCFS2_I(inode)->ip_alloc_sem); + + return ret; +} + /* Some parts of this taken from generic_cont_expand, which turned out * to be too fragile to do exactly what we need without us having to * worry about recursive locking in ->prepare_write() and @@ -890,7 +923,9 @@ static int ocfs2_extend_file(struct inode *inode, } if (clusters_to_add) { - ret = ocfs2_extend_allocation(inode, clusters_to_add); + ret = ocfs2_extend_allocation(inode, + OCFS2_I(inode)->ip_clusters, + clusters_to_add, 0); if (ret < 0) { mlog_errno(ret); goto out_unlock; @@ -995,6 +1030,13 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) goto bail_unlock; } + /* + * This will intentionally not wind up calling vmtruncate(), + * since all the work for a size change has been done above. + * Otherwise, we could get into problems with truncate as + * ip_alloc_sem is used there to protect against i_size + * changes. + */ status = inode_setattr(inode, attr); if (status < 0) { mlog_errno(status); @@ -1070,17 +1112,16 @@ out: return ret; } -static int ocfs2_write_remove_suid(struct inode *inode) +static int __ocfs2_write_remove_suid(struct inode *inode, + struct buffer_head *bh) { int ret; - struct buffer_head *bh = NULL; - struct ocfs2_inode_info *oi = OCFS2_I(inode); handle_t *handle; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct ocfs2_dinode *di; mlog_entry("(Inode %llu, mode 0%o)\n", - (unsigned long long)oi->ip_blkno, inode->i_mode); + (unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_mode); handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); if (handle == NULL) { @@ -1089,17 +1130,11 @@ static int ocfs2_write_remove_suid(struct inode *inode) goto out; } - ret = ocfs2_read_block(osb, oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode); - if (ret < 0) { - mlog_errno(ret); - goto out_trans; - } - ret = ocfs2_journal_access(handle, inode, bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret < 0) { mlog_errno(ret); - goto out_bh; + goto out_trans; } inode->i_mode &= ~S_ISUID; @@ -1112,8 +1147,7 @@ static int ocfs2_write_remove_suid(struct inode *inode) ret = ocfs2_journal_dirty(handle, bh); if (ret < 0) mlog_errno(ret); -out_bh: - brelse(bh); + out_trans: ocfs2_commit_trans(osb, handle); out: @@ -1159,6 +1193,499 @@ out: return ret; } +static int ocfs2_write_remove_suid(struct inode *inode) +{ + int ret; + struct buffer_head *bh = NULL; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + ret = __ocfs2_write_remove_suid(inode, bh); +out: + brelse(bh); + return ret; +} + +/* + * Allocate enough extents to cover the region starting at byte offset + * start for len bytes. Existing extents are skipped, any extents + * added are marked as "unwritten". + */ +static int ocfs2_allocate_unwritten_extents(struct inode *inode, + u64 start, u64 len) +{ + int ret; + u32 cpos, phys_cpos, clusters, alloc_size; + + /* + * We consider both start and len to be inclusive. + */ + cpos = start >> OCFS2_SB(inode->i_sb)->s_clustersize_bits; + clusters = ocfs2_clusters_for_bytes(inode->i_sb, start + len); + clusters -= cpos; + + while (clusters) { + ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, + &alloc_size, NULL); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * Hole or existing extent len can be arbitrary, so + * cap it to our own allocation request. + */ + if (alloc_size > clusters) + alloc_size = clusters; + + if (phys_cpos) { + /* + * We already have an allocation at this + * region so we can safely skip it. + */ + goto next; + } + + ret = __ocfs2_extend_allocation(inode, cpos, alloc_size, 1); + if (ret) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + +next: + cpos += alloc_size; + clusters -= alloc_size; + } + + ret = 0; +out: + return ret; +} + +static int __ocfs2_remove_inode_range(struct inode *inode, + struct buffer_head *di_bh, + u32 cpos, u32 phys_cpos, u32 len, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret; + u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct inode *tl_inode = osb->osb_tl_inode; + handle_t *handle; + struct ocfs2_alloc_context *meta_ac = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + + ret = ocfs2_lock_allocators(inode, di, 0, 1, NULL, &meta_ac); + if (ret) { + mlog_errno(ret); + return ret; + } + + mutex_lock(&tl_inode->i_mutex); + + if (ocfs2_truncate_log_needs_flush(osb)) { + ret = __ocfs2_flush_truncate_log(osb); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS); + if (handle == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_remove_extent(inode, di_bh, cpos, len, handle, meta_ac, + dealloc); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + OCFS2_I(inode)->ip_clusters -= len; + di->i_clusters = cpu_to_le32(OCFS2_I(inode)->ip_clusters); + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_truncate_log_append(osb, handle, phys_blkno, len); + if (ret) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(osb, handle); +out: + mutex_unlock(&tl_inode->i_mutex); + + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + return ret; +} + +/* + * Truncate a byte range, avoiding pages within partial clusters. This + * preserves those pages for the zeroing code to write to. + */ +static void ocfs2_truncate_cluster_pages(struct inode *inode, u64 byte_start, + u64 byte_len) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + loff_t start, end; + struct address_space *mapping = inode->i_mapping; + + start = (loff_t)ocfs2_align_bytes_to_clusters(inode->i_sb, byte_start); + end = byte_start + byte_len; + end = end & ~(osb->s_clustersize - 1); + + if (start < end) { + unmap_mapping_range(mapping, start, end - start, 0); + truncate_inode_pages_range(mapping, start, end - 1); + } +} + +static int ocfs2_zero_partial_clusters(struct inode *inode, + u64 start, u64 len) +{ + int ret = 0; + u64 tmpend, end = start + len; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + unsigned int csize = osb->s_clustersize; + handle_t *handle; + + /* + * The "start" and "end" values are NOT necessarily part of + * the range whose allocation is being deleted. Rather, this + * is what the user passed in with the request. We must zero + * partial clusters here. There's no need to worry about + * physical allocation - the zeroing code knows to skip holes. + */ + mlog(0, "byte start: %llu, end: %llu\n", + (unsigned long long)start, (unsigned long long)end); + + /* + * If both edges are on a cluster boundary then there's no + * zeroing required as the region is part of the allocation to + * be truncated. + */ + if ((start & (csize - 1)) == 0 && (end & (csize - 1)) == 0) + goto out; + + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (handle == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + /* + * We want to get the byte offset of the end of the 1st cluster. + */ + tmpend = (u64)osb->s_clustersize + (start & ~(osb->s_clustersize - 1)); + if (tmpend > end) + tmpend = end; + + mlog(0, "1st range: start: %llu, tmpend: %llu\n", + (unsigned long long)start, (unsigned long long)tmpend); + + ret = ocfs2_zero_range_for_truncate(inode, handle, start, tmpend); + if (ret) + mlog_errno(ret); + + if (tmpend < end) { + /* + * This may make start and end equal, but the zeroing + * code will skip any work in that case so there's no + * need to catch it up here. + */ + start = end & ~(osb->s_clustersize - 1); + + mlog(0, "2nd range: start: %llu, end: %llu\n", + (unsigned long long)start, (unsigned long long)end); + + ret = ocfs2_zero_range_for_truncate(inode, handle, start, end); + if (ret) + mlog_errno(ret); + } + + ocfs2_commit_trans(osb, handle); +out: + return ret; +} + +static int ocfs2_remove_inode_range(struct inode *inode, + struct buffer_head *di_bh, u64 byte_start, + u64 byte_len) +{ + int ret = 0; + u32 trunc_start, trunc_len, cpos, phys_cpos, alloc_size; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_cached_dealloc_ctxt dealloc; + + ocfs2_init_dealloc_ctxt(&dealloc); + + if (byte_len == 0) + return 0; + + trunc_start = ocfs2_clusters_for_bytes(osb->sb, byte_start); + trunc_len = (byte_start + byte_len) >> osb->s_clustersize_bits; + if (trunc_len >= trunc_start) + trunc_len -= trunc_start; + else + trunc_len = 0; + + mlog(0, "Inode: %llu, start: %llu, len: %llu, cstart: %u, clen: %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (unsigned long long)byte_start, + (unsigned long long)byte_len, trunc_start, trunc_len); + + ret = ocfs2_zero_partial_clusters(inode, byte_start, byte_len); + if (ret) { + mlog_errno(ret); + goto out; + } + + cpos = trunc_start; + while (trunc_len) { + ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, + &alloc_size, NULL); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (alloc_size > trunc_len) + alloc_size = trunc_len; + + /* Only do work for non-holes */ + if (phys_cpos != 0) { + ret = __ocfs2_remove_inode_range(inode, di_bh, cpos, + phys_cpos, alloc_size, + &dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + cpos += alloc_size; + trunc_len -= alloc_size; + } + + ocfs2_truncate_cluster_pages(inode, byte_start, byte_len); + +out: + ocfs2_schedule_truncate_log_flush(osb, 1); + ocfs2_run_deallocs(osb, &dealloc); + + return ret; +} + +/* + * Parts of this function taken from xfs_change_file_space() + */ +static int __ocfs2_change_file_space(struct file *file, struct inode *inode, + loff_t f_pos, unsigned int cmd, + struct ocfs2_space_resv *sr, + int change_size) +{ + int ret; + s64 llen; + loff_t size; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct buffer_head *di_bh = NULL; + handle_t *handle; + unsigned long long max_off = ocfs2_max_file_offset(inode->i_sb->s_blocksize_bits); + + if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb)) + return -EROFS; + + mutex_lock(&inode->i_mutex); + + /* + * This prevents concurrent writes on other nodes + */ + ret = ocfs2_rw_lock(inode, 1); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_meta_lock(inode, &di_bh, 1); + if (ret) { + mlog_errno(ret); + goto out_rw_unlock; + } + + if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) { + ret = -EPERM; + goto out_meta_unlock; + } + + switch (sr->l_whence) { + case 0: /*SEEK_SET*/ + break; + case 1: /*SEEK_CUR*/ + sr->l_start += f_pos; + break; + case 2: /*SEEK_END*/ + sr->l_start += i_size_read(inode); + break; + default: + ret = -EINVAL; + goto out_meta_unlock; + } + sr->l_whence = 0; + + llen = sr->l_len > 0 ? sr->l_len - 1 : sr->l_len; + + if (sr->l_start < 0 + || sr->l_start > max_off + || (sr->l_start + llen) < 0 + || (sr->l_start + llen) > max_off) { + ret = -EINVAL; + goto out_meta_unlock; + } + size = sr->l_start + sr->l_len; + + if (cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) { + if (sr->l_len <= 0) { + ret = -EINVAL; + goto out_meta_unlock; + } + } + + if (file && should_remove_suid(file->f_path.dentry)) { + ret = __ocfs2_write_remove_suid(inode, di_bh); + if (ret) { + mlog_errno(ret); + goto out_meta_unlock; + } + } + + down_write(&OCFS2_I(inode)->ip_alloc_sem); + switch (cmd) { + case OCFS2_IOC_RESVSP: + case OCFS2_IOC_RESVSP64: + /* + * This takes unsigned offsets, but the signed ones we + * pass have been checked against overflow above. + */ + ret = ocfs2_allocate_unwritten_extents(inode, sr->l_start, + sr->l_len); + break; + case OCFS2_IOC_UNRESVSP: + case OCFS2_IOC_UNRESVSP64: + ret = ocfs2_remove_inode_range(inode, di_bh, sr->l_start, + sr->l_len); + break; + default: + ret = -EINVAL; + } + up_write(&OCFS2_I(inode)->ip_alloc_sem); + if (ret) { + mlog_errno(ret); + goto out_meta_unlock; + } + + /* + * We update c/mtime for these changes + */ + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_meta_unlock; + } + + if (change_size && i_size_read(inode) < size) + i_size_write(inode, size); + + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + ret = ocfs2_mark_inode_dirty(handle, inode, di_bh); + if (ret < 0) + mlog_errno(ret); + + ocfs2_commit_trans(osb, handle); + +out_meta_unlock: + brelse(di_bh); + ocfs2_meta_unlock(inode, 1); +out_rw_unlock: + ocfs2_rw_unlock(inode, 1); + + mutex_unlock(&inode->i_mutex); +out: + return ret; +} + +int ocfs2_change_file_space(struct file *file, unsigned int cmd, + struct ocfs2_space_resv *sr) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);; + + if ((cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) && + !ocfs2_writes_unwritten_extents(osb)) + return -ENOTTY; + else if ((cmd == OCFS2_IOC_UNRESVSP || cmd == OCFS2_IOC_UNRESVSP64) && + !ocfs2_sparse_alloc(osb)) + return -ENOTTY; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (!(file->f_mode & FMODE_WRITE)) + return -EBADF; + + return __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0); +} + +static long ocfs2_fallocate(struct inode *inode, int mode, loff_t offset, + loff_t len) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_space_resv sr; + int change_size = 1; + + if (!ocfs2_writes_unwritten_extents(osb)) + return -EOPNOTSUPP; + + if (S_ISDIR(inode->i_mode)) + return -ENODEV; + + if (mode & FALLOC_FL_KEEP_SIZE) + change_size = 0; + + sr.l_whence = 0; + sr.l_start = (s64)offset; + sr.l_len = (s64)len; + + return __ocfs2_change_file_space(NULL, inode, offset, + OCFS2_IOC_RESVSP64, &sr, change_size); +} + static int ocfs2_prepare_inode_for_write(struct dentry *dentry, loff_t *ppos, size_t count, @@ -1329,15 +1856,16 @@ ocfs2_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) *basep = base; } -static struct page * ocfs2_get_write_source(struct ocfs2_buffered_write_priv *bp, +static struct page * ocfs2_get_write_source(char **ret_src_buf, const struct iovec *cur_iov, size_t iov_offset) { int ret; - char *buf; + char *buf = cur_iov->iov_base + iov_offset; struct page *src_page = NULL; + unsigned long off; - buf = cur_iov->iov_base + iov_offset; + off = (unsigned long)(buf) & ~PAGE_CACHE_MASK; if (!segment_eq(get_fs(), KERNEL_DS)) { /* @@ -1349,18 +1877,17 @@ static struct page * ocfs2_get_write_source(struct ocfs2_buffered_write_priv *bp (unsigned long)buf & PAGE_CACHE_MASK, 1, 0, 0, &src_page, NULL); if (ret == 1) - bp->b_src_buf = kmap(src_page); + *ret_src_buf = kmap(src_page) + off; else src_page = ERR_PTR(-EFAULT); } else { - bp->b_src_buf = buf; + *ret_src_buf = buf; } return src_page; } -static void ocfs2_put_write_source(struct ocfs2_buffered_write_priv *bp, - struct page *page) +static void ocfs2_put_write_source(struct page *page) { if (page) { kunmap(page); @@ -1376,10 +1903,13 @@ static ssize_t ocfs2_file_buffered_write(struct file *file, loff_t *ppos, { int ret = 0; ssize_t copied, total = 0; - size_t iov_offset = 0; + size_t iov_offset = 0, bytes; + loff_t pos; const struct iovec *cur_iov = iov; - struct ocfs2_buffered_write_priv bp; - struct page *page; + struct page *user_page, *page; + char * uninitialized_var(buf); + char *dst; + void *fsdata; /* * handle partial DIO write. Adjust cur_iov if needed. @@ -1387,21 +1917,38 @@ static ssize_t ocfs2_file_buffered_write(struct file *file, loff_t *ppos, ocfs2_set_next_iovec(&cur_iov, &iov_offset, o_direct_written); do { - bp.b_cur_off = iov_offset; - bp.b_cur_iov = cur_iov; + pos = *ppos; - page = ocfs2_get_write_source(&bp, cur_iov, iov_offset); - if (IS_ERR(page)) { - ret = PTR_ERR(page); + user_page = ocfs2_get_write_source(&buf, cur_iov, iov_offset); + if (IS_ERR(user_page)) { + ret = PTR_ERR(user_page); goto out; } - copied = ocfs2_buffered_write_cluster(file, *ppos, count, - ocfs2_map_and_write_user_data, - &bp); + /* Stay within our page boundaries */ + bytes = min((PAGE_CACHE_SIZE - ((unsigned long)pos & ~PAGE_CACHE_MASK)), + (PAGE_CACHE_SIZE - ((unsigned long)buf & ~PAGE_CACHE_MASK))); + /* Stay within the vector boundary */ + bytes = min_t(size_t, bytes, cur_iov->iov_len - iov_offset); + /* Stay within count */ + bytes = min(bytes, count); + + page = NULL; + ret = ocfs2_write_begin(file, file->f_mapping, pos, bytes, 0, + &page, &fsdata); + if (ret) { + mlog_errno(ret); + goto out; + } - ocfs2_put_write_source(&bp, page); + dst = kmap_atomic(page, KM_USER0); + memcpy(dst + (pos & (PAGE_CACHE_SIZE - 1)), buf, bytes); + kunmap_atomic(dst, KM_USER0); + flush_dcache_page(page); + ocfs2_put_write_source(user_page); + copied = ocfs2_write_end(file, file->f_mapping, pos, bytes, + bytes, page, fsdata); if (copied < 0) { mlog_errno(copied); ret = copied; @@ -1409,7 +1956,7 @@ static ssize_t ocfs2_file_buffered_write(struct file *file, loff_t *ppos, } total += copied; - *ppos = *ppos + copied; + *ppos = pos + copied; count -= copied; ocfs2_set_next_iovec(&cur_iov, &iov_offset, copied); @@ -1579,52 +2126,46 @@ static int ocfs2_splice_write_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { - int ret, count, total = 0; + int ret, count; ssize_t copied = 0; - struct ocfs2_splice_write_priv sp; + struct file *file = sd->u.file; + unsigned int offset; + struct page *page = NULL; + void *fsdata; + char *src, *dst; ret = buf->ops->confirm(pipe, buf); if (ret) goto out; - sp.s_sd = sd; - sp.s_buf = buf; - sp.s_pipe = pipe; - sp.s_offset = sd->pos & ~PAGE_CACHE_MASK; - sp.s_buf_offset = buf->offset; - + offset = sd->pos & ~PAGE_CACHE_MASK; count = sd->len; - if (count + sp.s_offset > PAGE_CACHE_SIZE) - count = PAGE_CACHE_SIZE - sp.s_offset; + if (count + offset > PAGE_CACHE_SIZE) + count = PAGE_CACHE_SIZE - offset; - do { - /* - * splice wants us to copy up to one page at a - * time. For pagesize > cluster size, this means we - * might enter ocfs2_buffered_write_cluster() more - * than once, so keep track of our progress here. - */ - copied = ocfs2_buffered_write_cluster(sd->u.file, - (loff_t)sd->pos + total, - count, - ocfs2_map_and_write_splice_data, - &sp); - if (copied < 0) { - mlog_errno(copied); - ret = copied; - goto out; - } + ret = ocfs2_write_begin(file, file->f_mapping, sd->pos, count, 0, + &page, &fsdata); + if (ret) { + mlog_errno(ret); + goto out; + } - count -= copied; - sp.s_offset += copied; - sp.s_buf_offset += copied; - total += copied; - } while (count); + src = buf->ops->map(pipe, buf, 1); + dst = kmap_atomic(page, KM_USER1); + memcpy(dst + offset, src + buf->offset, count); + kunmap_atomic(page, KM_USER1); + buf->ops->unmap(pipe, buf, src); - ret = 0; + copied = ocfs2_write_end(file, file->f_mapping, sd->pos, count, count, + page, fsdata); + if (copied < 0) { + mlog_errno(copied); + ret = copied; + goto out; + } out: - return total ? total : ret; + return copied ? copied : ret; } static ssize_t __ocfs2_file_splice_write(struct pipe_inode_info *pipe, @@ -1811,6 +2352,7 @@ const struct inode_operations ocfs2_file_iops = { .setattr = ocfs2_setattr, .getattr = ocfs2_getattr, .permission = ocfs2_permission, + .fallocate = ocfs2_fallocate, }; const struct inode_operations ocfs2_special_file_iops = { diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index a4dd1fa..36fe27f 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -39,15 +39,16 @@ enum ocfs2_alloc_restarted { }; int ocfs2_do_extend_allocation(struct ocfs2_super *osb, struct inode *inode, - u32 *cluster_start, + u32 *logical_offset, u32 clusters_to_add, + int mark_unwritten, struct buffer_head *fe_bh, handle_t *handle, struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, - enum ocfs2_alloc_restarted *reason); + enum ocfs2_alloc_restarted *reason_ret); int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, - u32 clusters_to_add, + u32 clusters_to_add, u32 extents_to_split, struct ocfs2_alloc_context **data_ac, struct ocfs2_alloc_context **meta_ac); int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); @@ -61,4 +62,7 @@ int ocfs2_should_update_atime(struct inode *inode, int ocfs2_update_inode_atime(struct inode *inode, struct buffer_head *bh); +int ocfs2_change_file_space(struct file *file, unsigned int cmd, + struct ocfs2_space_resv *sr); + #endif /* OCFS2_FILE_H */ diff --git a/fs/ocfs2/heartbeat.c b/fs/ocfs2/heartbeat.c index b25ef63..c4c3617 100644 --- a/fs/ocfs2/heartbeat.c +++ b/fs/ocfs2/heartbeat.c @@ -157,16 +157,16 @@ int ocfs2_register_hb_callbacks(struct ocfs2_super *osb) if (ocfs2_mount_local(osb)) return 0; - status = o2hb_register_callback(&osb->osb_hb_down); + status = o2hb_register_callback(osb->uuid_str, &osb->osb_hb_down); if (status < 0) { mlog_errno(status); goto bail; } - status = o2hb_register_callback(&osb->osb_hb_up); + status = o2hb_register_callback(osb->uuid_str, &osb->osb_hb_up); if (status < 0) { mlog_errno(status); - o2hb_unregister_callback(&osb->osb_hb_down); + o2hb_unregister_callback(osb->uuid_str, &osb->osb_hb_down); } bail: @@ -178,8 +178,8 @@ void ocfs2_clear_hb_callbacks(struct ocfs2_super *osb) if (ocfs2_mount_local(osb)) return; - o2hb_unregister_callback(&osb->osb_hb_down); - o2hb_unregister_callback(&osb->osb_hb_up); + o2hb_unregister_callback(osb->uuid_str, &osb->osb_hb_down); + o2hb_unregister_callback(osb->uuid_str, &osb->osb_hb_up); } void ocfs2_stop_heartbeat(struct ocfs2_super *osb) @@ -209,7 +209,7 @@ void ocfs2_stop_heartbeat(struct ocfs2_super *osb) envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[2] = NULL; - ret = call_usermodehelper(argv[0], argv, envp, 1); + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); if (ret < 0) mlog_errno(ret); } diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index f3ad21a..87dcece 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -14,6 +14,7 @@ #include "ocfs2.h" #include "alloc.h" #include "dlmglue.h" +#include "file.h" #include "inode.h" #include "journal.h" @@ -62,7 +63,7 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags, goto bail_unlock; status = -EACCES; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) goto bail_unlock; if (!S_ISDIR(inode->i_mode)) @@ -115,6 +116,7 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp, { unsigned int flags; int status; + struct ocfs2_space_resv sr; switch (cmd) { case OCFS2_IOC_GETFLAGS: @@ -130,6 +132,14 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp, return ocfs2_set_inode_attr(inode, flags, OCFS2_FL_MODIFIABLE); + case OCFS2_IOC_RESVSP: + case OCFS2_IOC_RESVSP64: + case OCFS2_IOC_UNRESVSP: + case OCFS2_IOC_UNRESVSP64: + if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) + return -EFAULT; + + return ocfs2_change_file_space(filp, cmd, &sr); default: return -ENOTTY; } @@ -148,6 +158,11 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) case OCFS2_IOC32_SETFLAGS: cmd = OCFS2_IOC_SETFLAGS; break; + case OCFS2_IOC_RESVSP: + case OCFS2_IOC_RESVSP64: + case OCFS2_IOC_UNRESVSP: + case OCFS2_IOC_UNRESVSP64: + break; default: return -ENOIOCTLCMD; } diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index dc11880..dbfb20b 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -722,8 +722,7 @@ void ocfs2_complete_recovery(struct work_struct *work) container_of(work, struct ocfs2_journal, j_recovery_work); struct ocfs2_super *osb = journal->j_osb; struct ocfs2_dinode *la_dinode, *tl_dinode; - struct ocfs2_la_recovery_item *item; - struct list_head *p, *n; + struct ocfs2_la_recovery_item *item, *n; LIST_HEAD(tmp_la_list); mlog_entry_void(); @@ -734,8 +733,7 @@ void ocfs2_complete_recovery(struct work_struct *work) list_splice_init(&journal->j_la_cleanups, &tmp_la_list); spin_unlock(&journal->j_lock); - list_for_each_safe(p, n, &tmp_la_list) { - item = list_entry(p, struct ocfs2_la_recovery_item, lri_list); + list_for_each_entry_safe(item, n, &tmp_la_list, lri_list) { list_del_init(&item->lri_list); mlog(0, "Complete recovery for slot %d\n", item->lri_slot); diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 3db5de4..ce60aab 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -289,6 +289,8 @@ int ocfs2_journal_dirty_data(handle_t *handle, #define OCFS2_TRUNCATE_LOG_FLUSH_ONE_REC (OCFS2_SUBALLOC_FREE \ + OCFS2_TRUNCATE_LOG_UPDATE) +#define OCFS2_REMOVE_EXTENT_CREDITS (OCFS2_TRUNCATE_LOG_UPDATE + OCFS2_INODE_UPDATE_CREDITS) + /* data block for new dir/symlink, 2 for bitmap updates (bitmap fe + * bitmap block for the new bit) */ #define OCFS2_DIR_LINK_ADDITIONAL_CREDITS (1 + 2) diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index af01158..9875615 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -37,66 +37,182 @@ #include "ocfs2.h" +#include "aops.h" #include "dlmglue.h" #include "file.h" #include "inode.h" #include "mmap.h" -static struct page *ocfs2_nopage(struct vm_area_struct * area, - unsigned long address, - int *type) +static inline int ocfs2_vm_op_block_sigs(sigset_t *blocked, sigset_t *oldset) +{ + /* The best way to deal with signals in the vm path is + * to block them upfront, rather than allowing the + * locking paths to return -ERESTARTSYS. */ + sigfillset(blocked); + + /* We should technically never get a bad return value + * from sigprocmask */ + return sigprocmask(SIG_BLOCK, blocked, oldset); +} + +static inline int ocfs2_vm_op_unblock_sigs(sigset_t *oldset) +{ + return sigprocmask(SIG_SETMASK, oldset, NULL); +} + +static int ocfs2_fault(struct vm_area_struct *area, struct vm_fault *vmf) { - struct page *page = NOPAGE_SIGBUS; sigset_t blocked, oldset; + int error, ret; + + mlog_entry("(area=%p, page offset=%lu)\n", area, vmf->pgoff); + + error = ocfs2_vm_op_block_sigs(&blocked, &oldset); + if (error < 0) { + mlog_errno(error); + ret = VM_FAULT_SIGBUS; + goto out; + } + + ret = filemap_fault(area, vmf); + + error = ocfs2_vm_op_unblock_sigs(&oldset); + if (error < 0) + mlog_errno(error); +out: + mlog_exit_ptr(vmf->page); + return ret; +} + +static int __ocfs2_page_mkwrite(struct inode *inode, struct buffer_head *di_bh, + struct page *page) +{ int ret; + struct address_space *mapping = inode->i_mapping; + loff_t pos = page_offset(page); + unsigned int len = PAGE_CACHE_SIZE; + pgoff_t last_index; + struct page *locked_page = NULL; + void *fsdata; + loff_t size = i_size_read(inode); - mlog_entry("(area=%p, address=%lu, type=%p)\n", area, address, - type); + /* + * Another node might have truncated while we were waiting on + * cluster locks. + */ + last_index = size >> PAGE_CACHE_SHIFT; + if (page->index > last_index) { + ret = -EINVAL; + goto out; + } - /* The best way to deal with signals in this path is - * to block them upfront, rather than allowing the - * locking paths to return -ERESTARTSYS. */ - sigfillset(&blocked); + /* + * The i_size check above doesn't catch the case where nodes + * truncated and then re-extended the file. We'll re-check the + * page mapping after taking the page lock inside of + * ocfs2_write_begin_nolock(). + */ + if (!PageUptodate(page) || page->mapping != inode->i_mapping) { + ret = -EINVAL; + goto out; + } - /* We should technically never get a bad ret return - * from sigprocmask */ - ret = sigprocmask(SIG_BLOCK, &blocked, &oldset); + /* + * Call ocfs2_write_begin() and ocfs2_write_end() to take + * advantage of the allocation code there. We pass a write + * length of the whole page (chopped to i_size) to make sure + * the whole thing is allocated. + * + * Since we know the page is up to date, we don't have to + * worry about ocfs2_write_begin() skipping some buffer reads + * because the "write" would invalidate their data. + */ + if (page->index == last_index) + len = size & ~PAGE_CACHE_MASK; + + ret = ocfs2_write_begin_nolock(mapping, pos, len, 0, &locked_page, + &fsdata, di_bh, page); + if (ret) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + + ret = ocfs2_write_end_nolock(mapping, pos, len, len, locked_page, + fsdata); if (ret < 0) { mlog_errno(ret); goto out; } + BUG_ON(ret != len); + ret = 0; +out: + return ret; +} - page = filemap_nopage(area, address, type); +static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) +{ + struct inode *inode = vma->vm_file->f_path.dentry->d_inode; + struct buffer_head *di_bh = NULL; + sigset_t blocked, oldset; + int ret, ret2; - ret = sigprocmask(SIG_SETMASK, &oldset, NULL); - if (ret < 0) + ret = ocfs2_vm_op_block_sigs(&blocked, &oldset); + if (ret < 0) { mlog_errno(ret); + return ret; + } + + /* + * The cluster locks taken will block a truncate from another + * node. Taking the data lock will also ensure that we don't + * attempt page truncation as part of a downconvert. + */ + ret = ocfs2_meta_lock(inode, &di_bh, 1); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + /* + * The alloc sem should be enough to serialize with + * ocfs2_truncate_file() changing i_size as well as any thread + * modifying the inode btree. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + + ret = ocfs2_data_lock(inode, 1); + if (ret < 0) { + mlog_errno(ret); + goto out_meta_unlock; + } + + ret = __ocfs2_page_mkwrite(inode, di_bh, page); + + ocfs2_data_unlock(inode, 1); + +out_meta_unlock: + up_write(&OCFS2_I(inode)->ip_alloc_sem); + + brelse(di_bh); + ocfs2_meta_unlock(inode, 1); + out: - mlog_exit_ptr(page); - return page; + ret2 = ocfs2_vm_op_unblock_sigs(&oldset); + if (ret2 < 0) + mlog_errno(ret2); + + return ret; } static struct vm_operations_struct ocfs2_file_vm_ops = { - .nopage = ocfs2_nopage, + .fault = ocfs2_fault, + .page_mkwrite = ocfs2_page_mkwrite, }; int ocfs2_mmap(struct file *file, struct vm_area_struct *vma) { int ret = 0, lock_level = 0; - struct ocfs2_super *osb = OCFS2_SB(file->f_dentry->d_inode->i_sb); - - /* - * Only support shared writeable mmap for local mounts which - * don't know about holes. - */ - if ((!ocfs2_mount_local(osb) || ocfs2_sparse_alloc(osb)) && - ((vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_MAYSHARE)) && - ((vma->vm_flags & VM_WRITE) || (vma->vm_flags & VM_MAYWRITE))) { - mlog(0, "disallow shared writable mmaps %lx\n", vma->vm_flags); - /* This is -EINVAL because generic_file_readonly_mmap - * returns it in a similar situation. */ - return -EINVAL; - } ret = ocfs2_meta_lock_atime(file->f_dentry->d_inode, file->f_vfsmnt, &lock_level); @@ -107,6 +223,7 @@ int ocfs2_mmap(struct file *file, struct vm_area_struct *vma) ocfs2_meta_unlock(file->f_dentry->d_inode, lock_level); out: vma->vm_ops = &ocfs2_file_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; return 0; } diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 36289e6..d430fda 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1674,7 +1674,7 @@ static int ocfs2_symlink(struct inode *dir, u32 offset = 0; inode->i_op = &ocfs2_symlink_inode_operations; - status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, + status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, 0, new_fe_bh, handle, data_ac, NULL, NULL); diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index a860633..5cc90a4 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -219,6 +219,7 @@ struct ocfs2_super u16 max_slots; s16 node_num; s16 slot_num; + s16 preferred_slot; int s_sectsize_bits; int s_clustersize; int s_clustersize_bits; @@ -305,6 +306,19 @@ static inline int ocfs2_sparse_alloc(struct ocfs2_super *osb) return 0; } +static inline int ocfs2_writes_unwritten_extents(struct ocfs2_super *osb) +{ + /* + * Support for sparse files is a pre-requisite + */ + if (!ocfs2_sparse_alloc(osb)) + return 0; + + if (osb->s_feature_ro_compat & OCFS2_FEATURE_RO_COMPAT_UNWRITTEN) + return 1; + return 0; +} + /* set / clear functions because cluster events can make these happen * in parallel so we want the transitions to be atomic. this also * means that any future flags osb_flags must be protected by spinlock diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index f0d9eb0..82f8a75 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -88,7 +88,7 @@ #define OCFS2_FEATURE_COMPAT_SUPP OCFS2_FEATURE_COMPAT_BACKUP_SB #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) -#define OCFS2_FEATURE_RO_COMPAT_SUPP 0 +#define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN /* * Heartbeat-only devices are missing journals and other files. The @@ -116,6 +116,11 @@ */ #define OCFS2_FEATURE_COMPAT_BACKUP_SB 0x0001 +/* + * Unwritten extents support. + */ +#define OCFS2_FEATURE_RO_COMPAT_UNWRITTEN 0x0001 + /* The byte offset of the first backup block will be 1G. * The following will be 4G, 16G, 64G, 256G and 1T. */ @@ -170,6 +175,32 @@ #define OCFS2_IOC32_SETFLAGS _IOW('f', 2, int) /* + * Space reservation / allocation / free ioctls and argument structure + * are designed to be compatible with XFS. + * + * ALLOCSP* and FREESP* are not and will never be supported, but are + * included here for completeness. + */ +struct ocfs2_space_resv { + __s16 l_type; + __s16 l_whence; + __s64 l_start; + __s64 l_len; /* len == 0 means until end of file */ + __s32 l_sysid; + __u32 l_pid; + __s32 l_pad[4]; /* reserve area */ +}; + +#define OCFS2_IOC_ALLOCSP _IOW ('X', 10, struct ocfs2_space_resv) +#define OCFS2_IOC_FREESP _IOW ('X', 11, struct ocfs2_space_resv) +#define OCFS2_IOC_RESVSP _IOW ('X', 40, struct ocfs2_space_resv) +#define OCFS2_IOC_UNRESVSP _IOW ('X', 41, struct ocfs2_space_resv) +#define OCFS2_IOC_ALLOCSP64 _IOW ('X', 36, struct ocfs2_space_resv) +#define OCFS2_IOC_FREESP64 _IOW ('X', 37, struct ocfs2_space_resv) +#define OCFS2_IOC_RESVSP64 _IOW ('X', 42, struct ocfs2_space_resv) +#define OCFS2_IOC_UNRESVSP64 _IOW ('X', 43, struct ocfs2_space_resv) + +/* * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) */ #define OCFS2_JOURNAL_DIRTY_FL (0x00000001) /* Journal needs recovery */ diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index d8b7906..af4882b 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -121,17 +121,25 @@ static s16 __ocfs2_node_num_to_slot(struct ocfs2_slot_info *si, return ret; } -static s16 __ocfs2_find_empty_slot(struct ocfs2_slot_info *si) +static s16 __ocfs2_find_empty_slot(struct ocfs2_slot_info *si, s16 preferred) { int i; s16 ret = OCFS2_INVALID_SLOT; + if (preferred >= 0 && preferred < si->si_num_slots) { + if (OCFS2_INVALID_SLOT == si->si_global_node_nums[preferred]) { + ret = preferred; + goto out; + } + } + for(i = 0; i < si->si_num_slots; i++) { if (OCFS2_INVALID_SLOT == si->si_global_node_nums[i]) { ret = (s16) i; break; } } +out: return ret; } @@ -248,7 +256,7 @@ int ocfs2_find_slot(struct ocfs2_super *osb) if (slot == OCFS2_INVALID_SLOT) { /* if no slot yet, then just take 1st available * one. */ - slot = __ocfs2_find_empty_slot(si); + slot = __ocfs2_find_empty_slot(si, osb->preferred_slot); if (slot == OCFS2_INVALID_SLOT) { spin_unlock(&si->si_lock); mlog(ML_ERROR, "no free slots available!\n"); diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index e343762..d9c5c9f 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -98,14 +98,6 @@ static int ocfs2_relink_block_group(handle_t *handle, u16 chain); static inline int ocfs2_block_group_reasonably_empty(struct ocfs2_group_desc *bg, u32 wanted); -static int ocfs2_free_suballoc_bits(handle_t *handle, - struct inode *alloc_inode, - struct buffer_head *alloc_bh, - unsigned int start_bit, - u64 bg_blkno, - unsigned int count); -static inline u64 ocfs2_which_suballoc_group(u64 block, - unsigned int bit); static inline u32 ocfs2_desc_bitmap_to_cluster_off(struct inode *inode, u64 bg_blkno, u16 bg_bit_off); @@ -496,13 +488,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, (*ac)->ac_bits_wanted = ocfs2_extend_meta_needed(fe); (*ac)->ac_which = OCFS2_AC_USE_META; - -#ifndef OCFS2_USE_ALL_METADATA_SUBALLOCATORS - slot = 0; -#else slot = osb->slot_num; -#endif - (*ac)->ac_group_search = ocfs2_block_group_search; status = ocfs2_reserve_suballoc_bits(osb, (*ac), @@ -1626,12 +1612,12 @@ bail: /* * expects the suballoc inode to already be locked. */ -static int ocfs2_free_suballoc_bits(handle_t *handle, - struct inode *alloc_inode, - struct buffer_head *alloc_bh, - unsigned int start_bit, - u64 bg_blkno, - unsigned int count) +int ocfs2_free_suballoc_bits(handle_t *handle, + struct inode *alloc_inode, + struct buffer_head *alloc_bh, + unsigned int start_bit, + u64 bg_blkno, + unsigned int count) { int status = 0; u32 tmp_used; @@ -1703,13 +1689,6 @@ bail: return status; } -static inline u64 ocfs2_which_suballoc_group(u64 block, unsigned int bit) -{ - u64 group = block - (u64) bit; - - return group; -} - int ocfs2_free_dinode(handle_t *handle, struct inode *inode_alloc_inode, struct buffer_head *inode_alloc_bh, @@ -1723,19 +1702,6 @@ int ocfs2_free_dinode(handle_t *handle, inode_alloc_bh, bit, bg_blkno, 1); } -int ocfs2_free_extent_block(handle_t *handle, - struct inode *eb_alloc_inode, - struct buffer_head *eb_alloc_bh, - struct ocfs2_extent_block *eb) -{ - u64 blk = le64_to_cpu(eb->h_blkno); - u16 bit = le16_to_cpu(eb->h_suballoc_bit); - u64 bg_blkno = ocfs2_which_suballoc_group(blk, bit); - - return ocfs2_free_suballoc_bits(handle, eb_alloc_inode, eb_alloc_bh, - bit, bg_blkno, 1); -} - int ocfs2_free_clusters(handle_t *handle, struct inode *bitmap_inode, struct buffer_head *bitmap_bh, diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index 1a3c94c..f212dc0 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -86,20 +86,29 @@ int ocfs2_claim_clusters(struct ocfs2_super *osb, u32 *cluster_start, u32 *num_clusters); +int ocfs2_free_suballoc_bits(handle_t *handle, + struct inode *alloc_inode, + struct buffer_head *alloc_bh, + unsigned int start_bit, + u64 bg_blkno, + unsigned int count); int ocfs2_free_dinode(handle_t *handle, struct inode *inode_alloc_inode, struct buffer_head *inode_alloc_bh, struct ocfs2_dinode *di); -int ocfs2_free_extent_block(handle_t *handle, - struct inode *eb_alloc_inode, - struct buffer_head *eb_alloc_bh, - struct ocfs2_extent_block *eb); int ocfs2_free_clusters(handle_t *handle, struct inode *bitmap_inode, struct buffer_head *bitmap_bh, u64 start_blk, unsigned int num_clusters); +static inline u64 ocfs2_which_suballoc_group(u64 block, unsigned int bit) +{ + u64 group = block - (u64) bit; + + return group; +} + static inline u32 ocfs2_cluster_from_desc(struct ocfs2_super *osb, u64 bg_blkno) { diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 86b559c..200c7d4 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -82,7 +82,8 @@ MODULE_AUTHOR("Oracle"); MODULE_LICENSE("GPL"); static int ocfs2_parse_options(struct super_block *sb, char *options, - unsigned long *mount_opt, int is_remount); + unsigned long *mount_opt, s16 *slot, + int is_remount); static void ocfs2_put_super(struct super_block *sb); static int ocfs2_mount_volume(struct super_block *sb); static int ocfs2_remount(struct super_block *sb, int *flags, char *data); @@ -114,8 +115,6 @@ static void ocfs2_write_super(struct super_block *sb); static struct inode *ocfs2_alloc_inode(struct super_block *sb); static void ocfs2_destroy_inode(struct inode *inode); -static unsigned long long ocfs2_max_file_offset(unsigned int blockshift); - static const struct super_operations ocfs2_sops = { .statfs = ocfs2_statfs, .alloc_inode = ocfs2_alloc_inode, @@ -140,6 +139,7 @@ enum { Opt_data_ordered, Opt_data_writeback, Opt_atime_quantum, + Opt_slot, Opt_err, }; @@ -154,6 +154,7 @@ static match_table_t tokens = { {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, {Opt_atime_quantum, "atime_quantum=%u"}, + {Opt_slot, "preferred_slot=%u"}, {Opt_err, NULL} }; @@ -318,7 +319,7 @@ static void ocfs2_destroy_inode(struct inode *inode) /* From xfs_super.c:xfs_max_file_offset * Copyright (c) 2000-2004 Silicon Graphics, Inc. */ -static unsigned long long ocfs2_max_file_offset(unsigned int blockshift) +unsigned long long ocfs2_max_file_offset(unsigned int blockshift) { unsigned int pagefactor = 1; unsigned int bitshift = BITS_PER_LONG - 1; @@ -355,9 +356,10 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) int incompat_features; int ret = 0; unsigned long parsed_options; + s16 slot; struct ocfs2_super *osb = OCFS2_SB(sb); - if (!ocfs2_parse_options(sb, data, &parsed_options, 1)) { + if (!ocfs2_parse_options(sb, data, &parsed_options, &slot, 1)) { ret = -EINVAL; goto out; } @@ -534,6 +536,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) struct dentry *root; int status, sector_size; unsigned long parsed_opt; + s16 slot; struct inode *inode = NULL; struct ocfs2_super *osb = NULL; struct buffer_head *bh = NULL; @@ -541,7 +544,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) mlog_entry("%p, %p, %i", sb, data, silent); - if (!ocfs2_parse_options(sb, data, &parsed_opt, 0)) { + if (!ocfs2_parse_options(sb, data, &parsed_opt, &slot, 0)) { status = -EINVAL; goto read_super_error; } @@ -571,6 +574,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) brelse(bh); bh = NULL; osb->s_mount_opt = parsed_opt; + osb->preferred_slot = slot; sb->s_magic = OCFS2_SUPER_MAGIC; @@ -713,6 +717,7 @@ static struct file_system_type ocfs2_fs_type = { static int ocfs2_parse_options(struct super_block *sb, char *options, unsigned long *mount_opt, + s16 *slot, int is_remount) { int status; @@ -722,6 +727,7 @@ static int ocfs2_parse_options(struct super_block *sb, options ? options : "(none)"); *mount_opt = 0; + *slot = OCFS2_INVALID_SLOT; if (!options) { status = 1; @@ -782,6 +788,15 @@ static int ocfs2_parse_options(struct super_block *sb, else osb->s_atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; break; + case Opt_slot: + option = 0; + if (match_int(&args[0], &option)) { + status = 0; + goto bail; + } + if (option) + *slot = (s16)option; + break; default: mlog(ML_ERROR, "Unrecognized mount option \"%s\" " @@ -969,7 +984,7 @@ static int ocfs2_initialize_mem_caches(void) 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - ocfs2_inode_init_once, NULL); + ocfs2_inode_init_once); if (!ocfs2_inode_cachep) return -ENOMEM; diff --git a/fs/ocfs2/super.h b/fs/ocfs2/super.h index 783f527..3b9cb3d 100644 --- a/fs/ocfs2/super.h +++ b/fs/ocfs2/super.h @@ -45,4 +45,6 @@ void __ocfs2_abort(struct super_block *sb, #define ocfs2_abort(sb, fmt, args...) __ocfs2_abort(sb, __PRETTY_FUNCTION__, fmt, ##args) +unsigned long long ocfs2_max_file_offset(unsigned int blockshift); + #endif /* OCFS2_SUPER_H */ diff --git a/fs/ocfs2/uptodate.c b/fs/ocfs2/uptodate.c index 39814b9..4da8851 100644 --- a/fs/ocfs2/uptodate.c +++ b/fs/ocfs2/uptodate.c @@ -548,7 +548,7 @@ int __init init_ocfs2_uptodate_cache(void) { ocfs2_uptodate_cachep = kmem_cache_create("ocfs2_uptodate", sizeof(struct ocfs2_meta_cache_item), - 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + 0, SLAB_HWCACHE_ALIGN, NULL); if (!ocfs2_uptodate_cachep) return -ENOMEM; @@ -26,6 +26,7 @@ #include <linux/syscalls.h> #include <linux/rcupdate.h> #include <linux/audit.h> +#include <linux/falloc.h> int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -352,6 +353,64 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length) } #endif +asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len) +{ + struct file *file; + struct inode *inode; + long ret = -EINVAL; + + if (offset < 0 || len <= 0) + goto out; + + /* Return error if mode is not supported */ + ret = -EOPNOTSUPP; + if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) + goto out; + + ret = -EBADF; + file = fget(fd); + if (!file) + goto out; + if (!(file->f_mode & FMODE_WRITE)) + goto out_fput; + /* + * Revalidate the write permissions, in case security policy has + * changed since the files were opened. + */ + ret = security_file_permission(file, MAY_WRITE); + if (ret) + goto out_fput; + + inode = file->f_path.dentry->d_inode; + + ret = -ESPIPE; + if (S_ISFIFO(inode->i_mode)) + goto out_fput; + + ret = -ENODEV; + /* + * Let individual file system decide if it supports preallocation + * for directories or not. + */ + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) + goto out_fput; + + ret = -EFBIG; + /* Check for wrap through zero too */ + if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) + goto out_fput; + + if (inode->i_op && inode->i_op->fallocate) + ret = inode->i_op->fallocate(inode, mode, offset, len); + else + ret = -ENOSYS; + +out_fput: + fput(file); +out: + return ret; +} + /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and @@ -855,7 +914,7 @@ EXPORT_SYMBOL(dentry_open); /* * Find an empty file descriptor entry, and mark it busy. */ -int get_unused_fd(void) +int get_unused_fd_flags(int flags) { struct files_struct * files = current->files; int fd, error; @@ -891,7 +950,10 @@ repeat: } FD_SET(fd, fdt->open_fds); - FD_CLR(fd, fdt->close_on_exec); + if (flags & O_CLOEXEC) + FD_SET(fd, fdt->close_on_exec); + else + FD_CLR(fd, fdt->close_on_exec); files->next_fd = fd + 1; #if 1 /* Sanity check */ @@ -907,6 +969,11 @@ out: return error; } +int get_unused_fd(void) +{ + return get_unused_fd_flags(0); +} + EXPORT_SYMBOL(get_unused_fd); static void __put_unused_fd(struct files_struct *files, unsigned int fd) @@ -959,7 +1026,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) int fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - fd = get_unused_fd(); + fd = get_unused_fd_flags(flags); if (fd >= 0) { struct file *f = do_filp_open(dfd, tmp, flags, mode); if (IS_ERR(f)) { diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index e623973..dd86be2 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -431,7 +431,7 @@ static int __init init_openprom_fs(void) 0, (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD), - op_inode_init_once, NULL); + op_inode_init_once); if (!op_inode_cachep) return -ENOMEM; diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index e349132..3d3e166 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -25,6 +25,8 @@ #define PARTITION_RISCIX_SCSI 2 #define PARTITION_LINUX 9 +#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \ + defined(CONFIG_ACORN_PARTITION_ADFS) static struct adfs_discrecord * adfs_partition(struct parsed_partitions *state, char *name, char *data, unsigned long first_sector, int slot) @@ -48,6 +50,7 @@ adfs_partition(struct parsed_partitions *state, char *name, char *data, put_partition(state, slot, first_sector, nr_sects); return dr; } +#endif #ifdef CONFIG_ACORN_PARTITION_RISCIX @@ -65,6 +68,8 @@ struct riscix_record { struct riscix_part part[8]; }; +#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \ + defined(CONFIG_ACORN_PARTITION_ADFS) static int riscix_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -105,6 +110,7 @@ riscix_partition(struct parsed_partitions *state, struct block_device *bdev, return slot; } #endif +#endif #define LINUX_NATIVE_MAGIC 0xdeafa1de #define LINUX_SWAP_MAGIC 0xdeafab1e @@ -115,6 +121,8 @@ struct linux_part { __le32 nr_sects; }; +#if defined(CONFIG_ACORN_PARTITION_CUMANA) || \ + defined(CONFIG_ACORN_PARTITION_ADFS) static int linux_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -146,6 +154,7 @@ linux_partition(struct parsed_partitions *state, struct block_device *bdev, put_dev_sector(sect); return slot; } +#endif #ifdef CONFIG_ACORN_PARTITION_CUMANA int diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 9a3a058..783c57e 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -372,11 +372,10 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, { struct hd_struct *p; - p = kmalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return; - memset(p, 0, sizeof(*p)); p->start_sect = start; p->nr_sects = len; p->partno = part; @@ -397,7 +396,6 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, static struct attribute addpartattr = { .name = "whole_disk", .mode = S_IRUSR | S_IRGRP | S_IROTH, - .owner = THIS_MODULE, }; sysfs_create_file(&p->kobj, &addpartattr); diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index 99873a2..e7dd1d4 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -677,15 +677,24 @@ static bool ldm_create_data_partitions (struct parsed_partitions *pp, * Return: -1 Error, the calculated offset exceeded the size of the buffer * n OK, a range-checked offset into buffer */ -static int ldm_relative (const u8 *buffer, int buflen, int base, int offset) +static int ldm_relative(const u8 *buffer, int buflen, int base, int offset) { base += offset; - if ((!buffer) || (offset < 0) || (base > buflen)) + if (!buffer || offset < 0 || base > buflen) { + if (!buffer) + ldm_error("!buffer"); + if (offset < 0) + ldm_error("offset (%d) < 0", offset); + if (base > buflen) + ldm_error("base (%d) > buflen (%d)", base, buflen); return -1; - if ((base + buffer[base]) >= buflen) + } + if (base + buffer[base] >= buflen) { + ldm_error("base (%d) + buffer[base] (%d) >= buflen (%d)", base, + buffer[base], buflen); return -1; - + } return buffer[base] + offset + 1; } @@ -1054,60 +1063,98 @@ static bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb) * Return: 'true' @vb contains a Volume VBLK * 'false' @vb contents are not defined */ -static bool ldm_parse_vol5 (const u8 *buffer, int buflen, struct vblk *vb) +static bool ldm_parse_vol5(const u8 *buffer, int buflen, struct vblk *vb) { - int r_objid, r_name, r_vtype, r_child, r_size, r_id1, r_id2, r_size2; - int r_drive, len; + int r_objid, r_name, r_vtype, r_disable_drive_letter, r_child, r_size; + int r_id1, r_id2, r_size2, r_drive, len; struct vblk_volu *volu; - BUG_ON (!buffer || !vb); - - r_objid = ldm_relative (buffer, buflen, 0x18, 0); - r_name = ldm_relative (buffer, buflen, 0x18, r_objid); - r_vtype = ldm_relative (buffer, buflen, 0x18, r_name); - r_child = ldm_relative (buffer, buflen, 0x2E, r_vtype); - r_size = ldm_relative (buffer, buflen, 0x3E, r_child); - - if (buffer[0x12] & VBLK_FLAG_VOLU_ID1) - r_id1 = ldm_relative (buffer, buflen, 0x53, r_size); - else + BUG_ON(!buffer || !vb); + r_objid = ldm_relative(buffer, buflen, 0x18, 0); + if (r_objid < 0) { + ldm_error("r_objid %d < 0", r_objid); + return false; + } + r_name = ldm_relative(buffer, buflen, 0x18, r_objid); + if (r_name < 0) { + ldm_error("r_name %d < 0", r_name); + return false; + } + r_vtype = ldm_relative(buffer, buflen, 0x18, r_name); + if (r_vtype < 0) { + ldm_error("r_vtype %d < 0", r_vtype); + return false; + } + r_disable_drive_letter = ldm_relative(buffer, buflen, 0x18, r_vtype); + if (r_disable_drive_letter < 0) { + ldm_error("r_disable_drive_letter %d < 0", + r_disable_drive_letter); + return false; + } + r_child = ldm_relative(buffer, buflen, 0x2D, r_disable_drive_letter); + if (r_child < 0) { + ldm_error("r_child %d < 0", r_child); + return false; + } + r_size = ldm_relative(buffer, buflen, 0x3D, r_child); + if (r_size < 0) { + ldm_error("r_size %d < 0", r_size); + return false; + } + if (buffer[0x12] & VBLK_FLAG_VOLU_ID1) { + r_id1 = ldm_relative(buffer, buflen, 0x52, r_size); + if (r_id1 < 0) { + ldm_error("r_id1 %d < 0", r_id1); + return false; + } + } else r_id1 = r_size; - - if (buffer[0x12] & VBLK_FLAG_VOLU_ID2) - r_id2 = ldm_relative (buffer, buflen, 0x53, r_id1); - else + if (buffer[0x12] & VBLK_FLAG_VOLU_ID2) { + r_id2 = ldm_relative(buffer, buflen, 0x52, r_id1); + if (r_id2 < 0) { + ldm_error("r_id2 %d < 0", r_id2); + return false; + } + } else r_id2 = r_id1; - - if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE) - r_size2 = ldm_relative (buffer, buflen, 0x53, r_id2); - else + if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE) { + r_size2 = ldm_relative(buffer, buflen, 0x52, r_id2); + if (r_size2 < 0) { + ldm_error("r_size2 %d < 0", r_size2); + return false; + } + } else r_size2 = r_id2; - - if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) - r_drive = ldm_relative (buffer, buflen, 0x53, r_size2); - else + if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) { + r_drive = ldm_relative(buffer, buflen, 0x52, r_size2); + if (r_drive < 0) { + ldm_error("r_drive %d < 0", r_drive); + return false; + } + } else r_drive = r_size2; - len = r_drive; - if (len < 0) + if (len < 0) { + ldm_error("len %d < 0", len); return false; - + } len += VBLK_SIZE_VOL5; - if (len != BE32 (buffer + 0x14)) + if (len > BE32(buffer + 0x14)) { + ldm_error("len %d > BE32(buffer + 0x14) %d", len, + BE32(buffer + 0x14)); return false; - + } volu = &vb->vblk.volu; - - ldm_get_vstr (buffer + 0x18 + r_name, volu->volume_type, - sizeof (volu->volume_type)); - memcpy (volu->volume_state, buffer + 0x19 + r_vtype, - sizeof (volu->volume_state)); - volu->size = ldm_get_vnum (buffer + 0x3E + r_child); - volu->partition_type = buffer[0x42 + r_size]; - memcpy (volu->guid, buffer + 0x43 + r_size, sizeof (volu->guid)); + ldm_get_vstr(buffer + 0x18 + r_name, volu->volume_type, + sizeof(volu->volume_type)); + memcpy(volu->volume_state, buffer + 0x18 + r_disable_drive_letter, + sizeof(volu->volume_state)); + volu->size = ldm_get_vnum(buffer + 0x3D + r_child); + volu->partition_type = buffer[0x41 + r_size]; + memcpy(volu->guid, buffer + 0x42 + r_size, sizeof(volu->guid)); if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) { - ldm_get_vstr (buffer + 0x53 + r_size, volu->drive_hint, - sizeof (volu->drive_hint)); + ldm_get_vstr(buffer + 0x52 + r_size, volu->drive_hint, + sizeof(volu->drive_hint)); } return true; } diff --git a/fs/partitions/ldm.h b/fs/partitions/ldm.h index d2e6a30..80f63b5 100644 --- a/fs/partitions/ldm.h +++ b/fs/partitions/ldm.h @@ -68,7 +68,7 @@ struct parsed_partitions; #define VBLK_SIZE_DSK3 12 #define VBLK_SIZE_DSK4 45 #define VBLK_SIZE_PRT3 28 -#define VBLK_SIZE_VOL5 59 +#define VBLK_SIZE_VOL5 58 /* component types */ #define COMP_STRIPE 0x01 /* Stripe-set */ diff --git a/fs/proc/array.c b/fs/proc/array.c index 98e78e2..965625a 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -62,6 +62,8 @@ #include <linux/mman.h> #include <linux/proc_fs.h> #include <linux/ioport.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <linux/mm.h> #include <linux/hugetlb.h> #include <linux/pagemap.h> @@ -76,9 +78,7 @@ #include <linux/rcupdate.h> #include <linux/delayacct.h> -#include <asm/uaccess.h> #include <asm/pgtable.h> -#include <asm/io.h> #include <asm/processor.h> #include "internal.h" @@ -87,10 +87,10 @@ do { memcpy(buffer, string, strlen(string)); \ buffer += strlen(string); } while (0) -static inline char * task_name(struct task_struct *p, char * buf) +static inline char *task_name(struct task_struct *p, char *buf) { int i; - char * name; + char *name; char tcomm[sizeof(p->comm)]; get_task_comm(tcomm, p); @@ -138,7 +138,7 @@ static const char *task_state_array[] = { "X (dead)" /* 32 */ }; -static inline const char * get_task_state(struct task_struct *tsk) +static inline const char *get_task_state(struct task_struct *tsk) { unsigned int state = (tsk->state & (TASK_RUNNING | TASK_INTERRUPTIBLE | @@ -156,7 +156,7 @@ static inline const char * get_task_state(struct task_struct *tsk) return *p; } -static inline char * task_state(struct task_struct *p, char *buffer) +static inline char *task_state(struct task_struct *p, char *buffer) { struct group_info *group_info; int g; @@ -172,8 +172,8 @@ static inline char * task_state(struct task_struct *p, char *buffer) "Uid:\t%d\t%d\t%d\t%d\n" "Gid:\t%d\t%d\t%d\t%d\n", get_task_state(p), - p->tgid, p->pid, - pid_alive(p) ? rcu_dereference(p->real_parent)->tgid : 0, + p->tgid, p->pid, + pid_alive(p) ? rcu_dereference(p->real_parent)->tgid : 0, pid_alive(p) && p->ptrace ? rcu_dereference(p->parent)->pid : 0, p->uid, p->euid, p->suid, p->fsuid, p->gid, p->egid, p->sgid, p->fsgid); @@ -191,15 +191,15 @@ static inline char * task_state(struct task_struct *p, char *buffer) get_group_info(group_info); task_unlock(p); - for (g = 0; g < min(group_info->ngroups,NGROUPS_SMALL); g++) - buffer += sprintf(buffer, "%d ", GROUP_AT(group_info,g)); + for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++) + buffer += sprintf(buffer, "%d ", GROUP_AT(group_info, g)); put_group_info(group_info); buffer += sprintf(buffer, "\n"); return buffer; } -static char * render_sigset_t(const char *header, sigset_t *set, char *buffer) +static char *render_sigset_t(const char *header, sigset_t *set, char *buffer) { int i, len; @@ -239,7 +239,7 @@ static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, } } -static inline char * task_sig(struct task_struct *p, char *buffer) +static inline char *task_sig(struct task_struct *p, char *buffer) { unsigned long flags; sigset_t pending, shpending, blocked, ignored, caught; @@ -289,14 +289,23 @@ static inline char *task_cap(struct task_struct *p, char *buffer) cap_t(p->cap_effective)); } -int proc_pid_status(struct task_struct *task, char * buffer) +static inline char *task_context_switch_counts(struct task_struct *p, + char *buffer) { - char * orig = buffer; + return buffer + sprintf(buffer, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); +} + +int proc_pid_status(struct task_struct *task, char *buffer) +{ + char *orig = buffer; struct mm_struct *mm = get_task_mm(task); buffer = task_name(task, buffer); buffer = task_state(task, buffer); - + if (mm) { buffer = task_mem(mm, buffer); mmput(mm); @@ -307,6 +316,7 @@ int proc_pid_status(struct task_struct *task, char * buffer) #if defined(CONFIG_S390) buffer = task_show_regs(task, buffer); #endif + buffer = task_context_switch_counts(task, buffer); return buffer - orig; } @@ -332,7 +342,7 @@ static clock_t task_utime(struct task_struct *p) static clock_t task_stime(struct task_struct *p) { - clock_t stime = cputime_to_clock_t(p->stime); + clock_t stime; /* * Use CFS's precise accounting. (we subtract utime from @@ -344,8 +354,7 @@ static clock_t task_stime(struct task_struct *p) return stime; } - -static int do_task_stat(struct task_struct *task, char * buffer, int whole) +static int do_task_stat(struct task_struct *task, char *buffer, int whole) { unsigned long vsize, eip, esp, wchan = ~0UL; long priority, nice; @@ -353,7 +362,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) sigset_t sigign, sigcatch; char state; int res; - pid_t ppid = 0, pgid = -1, sid = -1; + pid_t ppid = 0, pgid = -1, sid = -1; int num_threads = 0; struct mm_struct *mm; unsigned long long start_time; @@ -424,7 +433,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) } rcu_read_unlock(); - if (!whole || num_threads<2) + if (!whole || num_threads < 2) wchan = get_wchan(task); if (!whole) { min_flt = task->min_flt; @@ -440,12 +449,13 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) /* Temporary variable needed for gcc-2.96 */ /* convert timespec -> nsec*/ - start_time = (unsigned long long)task->start_time.tv_sec * NSEC_PER_SEC - + task->start_time.tv_nsec; + start_time = + (unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC + + task->real_start_time.tv_nsec; /* convert nsec -> ticks */ start_time = nsec_to_clock_t(start_time); - res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %u %lu \ + res = sprintf(buffer, "%d (%s) %c %d %d %d %d %d %u %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \ %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu\n", task->pid, @@ -471,7 +481,7 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) start_time, vsize, mm ? get_mm_rss(mm) : 0, - rsslim, + rsslim, mm ? mm->start_code : 0, mm ? mm->end_code : 0, mm ? mm->start_stack : 0, @@ -493,17 +503,17 @@ static int do_task_stat(struct task_struct *task, char * buffer, int whole) task->rt_priority, task->policy, (unsigned long long)delayacct_blkio_ticks(task)); - if(mm) + if (mm) mmput(mm); return res; } -int proc_tid_stat(struct task_struct *task, char * buffer) +int proc_tid_stat(struct task_struct *task, char *buffer) { return do_task_stat(task, buffer, 0); } -int proc_tgid_stat(struct task_struct *task, char * buffer) +int proc_tgid_stat(struct task_struct *task, char *buffer) { return do_task_stat(task, buffer, 1); } @@ -512,12 +522,12 @@ int proc_pid_statm(struct task_struct *task, char *buffer) { int size = 0, resident = 0, shared = 0, text = 0, lib = 0, data = 0; struct mm_struct *mm = get_task_mm(task); - + if (mm) { size = task_statm(mm, &shared, &text, &data, &resident); mmput(mm); } - return sprintf(buffer,"%d %d %d %d %d %d %d\n", + return sprintf(buffer, "%d %d %d %d %d %d %d\n", size, resident, shared, text, lib, data, 0); } diff --git a/fs/proc/base.c b/fs/proc/base.c index 46ea5d5..3c77d5a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -67,12 +67,12 @@ #include <linux/mount.h> #include <linux/security.h> #include <linux/ptrace.h> -#include <linux/seccomp.h> #include <linux/cpuset.h> #include <linux/audit.h> #include <linux/poll.h> #include <linux/nsproxy.h> #include <linux/oom.h> +#include <linux/elf.h> #include "internal.h" /* NOTE: @@ -204,12 +204,17 @@ static int proc_pid_environ(struct task_struct *task, char * buffer) int res = 0; struct mm_struct *mm = get_task_mm(task); if (mm) { - unsigned int len = mm->env_end - mm->env_start; + unsigned int len; + + res = -ESRCH; + if (!ptrace_may_attach(task)) + goto out; + + len = mm->env_end - mm->env_start; if (len > PAGE_SIZE) len = PAGE_SIZE; res = access_process_vm(task, mm->env_start, buffer, len, 0); - if (!ptrace_may_attach(task)) - res = -ESRCH; +out: mmput(mm); } return res; @@ -279,7 +284,7 @@ static int proc_pid_auxv(struct task_struct *task, char *buffer) static int proc_pid_wchan(struct task_struct *task, char *buffer) { unsigned long wchan; - char symname[KSYM_NAME_LEN+1]; + char symname[KSYM_NAME_LEN]; wchan = get_wchan(task); @@ -812,71 +817,6 @@ static const struct file_operations proc_loginuid_operations = { }; #endif -#ifdef CONFIG_SECCOMP -static ssize_t seccomp_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); - char __buf[20]; - size_t len; - - if (!tsk) - return -ESRCH; - /* no need to print the trailing zero, so use only len */ - len = sprintf(__buf, "%u\n", tsk->seccomp.mode); - put_task_struct(tsk); - - return simple_read_from_buffer(buf, count, ppos, __buf, len); -} - -static ssize_t seccomp_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); - char __buf[20], *end; - unsigned int seccomp_mode; - ssize_t result; - - result = -ESRCH; - if (!tsk) - goto out_no_task; - - /* can set it only once to be even more secure */ - result = -EPERM; - if (unlikely(tsk->seccomp.mode)) - goto out; - - result = -EFAULT; - memset(__buf, 0, sizeof(__buf)); - count = min(count, sizeof(__buf) - 1); - if (copy_from_user(__buf, buf, count)) - goto out; - - seccomp_mode = simple_strtoul(__buf, &end, 0); - if (*end == '\n') - end++; - result = -EINVAL; - if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) { - tsk->seccomp.mode = seccomp_mode; - set_tsk_thread_flag(tsk, TIF_SECCOMP); - } else - goto out; - result = -EIO; - if (unlikely(!(end - __buf))) - goto out; - result = end - __buf; -out: - put_task_struct(tsk); -out_no_task: - return result; -} - -static const struct file_operations proc_seccomp_operations = { - .read = seccomp_read, - .write = seccomp_write, -}; -#endif /* CONFIG_SECCOMP */ - #ifdef CONFIG_FAULT_INJECTION static ssize_t proc_fault_inject_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) @@ -1075,7 +1015,7 @@ static int task_dumpable(struct task_struct *task) task_lock(task); mm = task->mm; if (mm) - dumpable = mm->dumpable; + dumpable = get_dumpable(mm); task_unlock(task); if(dumpable == 1) return 1; @@ -1846,6 +1786,91 @@ static const struct inode_operations proc_attr_dir_inode_operations = { #endif +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) +static ssize_t proc_coredump_filter_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task = get_proc_task(file->f_dentry->d_inode); + struct mm_struct *mm; + char buffer[PROC_NUMBUF]; + size_t len; + int ret; + + if (!task) + return -ESRCH; + + ret = 0; + mm = get_task_mm(task); + if (mm) { + len = snprintf(buffer, sizeof(buffer), "%08lx\n", + ((mm->flags & MMF_DUMP_FILTER_MASK) >> + MMF_DUMP_FILTER_SHIFT)); + mmput(mm); + ret = simple_read_from_buffer(buf, count, ppos, buffer, len); + } + + put_task_struct(task); + + return ret; +} + +static ssize_t proc_coredump_filter_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + struct task_struct *task; + struct mm_struct *mm; + char buffer[PROC_NUMBUF], *end; + unsigned int val; + int ret; + int i; + unsigned long mask; + + ret = -EFAULT; + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) + goto out_no_task; + + ret = -EINVAL; + val = (unsigned int)simple_strtoul(buffer, &end, 0); + if (*end == '\n') + end++; + if (end - buffer == 0) + goto out_no_task; + + ret = -ESRCH; + task = get_proc_task(file->f_dentry->d_inode); + if (!task) + goto out_no_task; + + ret = end - buffer; + mm = get_task_mm(task); + if (!mm) + goto out_no_mm; + + for (i = 0, mask = 1; i < MMF_DUMP_FILTER_BITS; i++, mask <<= 1) { + if (val & mask) + set_bit(i + MMF_DUMP_FILTER_SHIFT, &mm->flags); + else + clear_bit(i + MMF_DUMP_FILTER_SHIFT, &mm->flags); + } + + mmput(mm); + out_no_mm: + put_task_struct(task); + out_no_task: + return ret; +} + +static const struct file_operations proc_coredump_filter_operations = { + .read = proc_coredump_filter_read, + .write = proc_coredump_filter_write, +}; +#endif + /* * /proc/self: */ @@ -2037,9 +2062,6 @@ static const struct pid_entry tgid_base_stuff[] = { REG("numa_maps", S_IRUGO, numa_maps), #endif REG("mem", S_IRUSR|S_IWUSR, mem), -#ifdef CONFIG_SECCOMP - REG("seccomp", S_IRUSR|S_IWUSR, seccomp), -#endif LNK("cwd", cwd), LNK("root", root), LNK("exe", exe), @@ -2069,6 +2091,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject), #endif +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) + REG("coredump_filter", S_IRUGO|S_IWUSR, coredump_filter), +#endif #ifdef CONFIG_TASK_IO_ACCOUNTING INF("io", S_IRUGO, pid_io_accounting), #endif @@ -2324,9 +2349,6 @@ static const struct pid_entry tid_base_stuff[] = { REG("numa_maps", S_IRUGO, numa_maps), #endif REG("mem", S_IRUSR|S_IWUSR, mem), -#ifdef CONFIG_SECCOMP - REG("seccomp", S_IRUSR|S_IWUSR, seccomp), -#endif LNK("cwd", cwd), LNK("root", root), LNK("exe", exe), diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8a40e15..b5e7155 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -20,6 +20,7 @@ #include <linux/namei.h> #include <linux/bitops.h> #include <linux/spinlock.h> +#include <linux/completion.h> #include <asm/uaccess.h> #include "internal.h" @@ -529,12 +530,6 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp return -EAGAIN; dp->low_ino = i; - spin_lock(&proc_subdir_lock); - dp->next = dir->subdir; - dp->parent = dir; - dir->subdir = dp; - spin_unlock(&proc_subdir_lock); - if (S_ISDIR(dp->mode)) { if (dp->proc_iops == NULL) { dp->proc_fops = &proc_dir_operations; @@ -550,6 +545,13 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp if (dp->proc_iops == NULL) dp->proc_iops = &proc_file_inode_operations; } + + spin_lock(&proc_subdir_lock); + dp->next = dir->subdir; + dp->parent = dir; + dir->subdir = dp; + spin_unlock(&proc_subdir_lock); + return 0; } @@ -613,6 +615,9 @@ static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent, ent->namelen = len; ent->mode = mode; ent->nlink = nlink; + ent->pde_users = 0; + spin_lock_init(&ent->pde_unload_lock); + ent->pde_unload_completion = NULL; out: return ent; } @@ -649,9 +654,6 @@ struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode, ent = proc_create(&parent, name, S_IFDIR | mode, 2); if (ent) { - ent->proc_fops = &proc_dir_operations; - ent->proc_iops = &proc_dir_inode_operations; - if (proc_register(parent, ent) < 0) { kfree(ent); ent = NULL; @@ -686,10 +688,6 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, ent = proc_create(&parent,name,mode,nlink); if (ent) { - if (S_ISDIR(mode)) { - ent->proc_fops = &proc_dir_operations; - ent->proc_iops = &proc_dir_inode_operations; - } if (proc_register(parent, ent) < 0) { kfree(ent); ent = NULL; @@ -734,9 +732,35 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) de = *p; *p = de->next; de->next = NULL; + + spin_lock(&de->pde_unload_lock); + /* + * Stop accepting new callers into module. If you're + * dynamically allocating ->proc_fops, save a pointer somewhere. + */ + de->proc_fops = NULL; + /* Wait until all existing callers into module are done. */ + if (de->pde_users > 0) { + DECLARE_COMPLETION_ONSTACK(c); + + if (!de->pde_unload_completion) + de->pde_unload_completion = &c; + + spin_unlock(&de->pde_unload_lock); + spin_unlock(&proc_subdir_lock); + + wait_for_completion(de->pde_unload_completion); + + spin_lock(&proc_subdir_lock); + goto continue_removing; + } + spin_unlock(&de->pde_unload_lock); + +continue_removing: if (S_ISDIR(de->mode)) parent->nlink--; - proc_kill_inodes(de); + if (!S_ISREG(de->mode)) + proc_kill_inodes(de); de->nlink = 0; WARN_ON(de->subdir); if (!atomic_read(&de->count)) diff --git a/fs/proc/inode.c b/fs/proc/inode.c index d5ce65c..94e2c1a 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/stat.h> +#include <linux/completion.h> #include <linux/file.h> #include <linux/limits.h> #include <linux/init.h> @@ -111,14 +112,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + int __init proc_init_inodecache(void) { proc_inode_cachep = kmem_cache_create("proc_inode_cache", sizeof(struct proc_inode), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (proc_inode_cachep == NULL) return -ENOMEM; return 0; @@ -140,6 +141,251 @@ static const struct super_operations proc_sops = { .remount_fs = proc_remount, }; +static void pde_users_dec(struct proc_dir_entry *pde) +{ + spin_lock(&pde->pde_unload_lock); + pde->pde_users--; + if (pde->pde_unload_completion && pde->pde_users == 0) + complete(pde->pde_unload_completion); + spin_unlock(&pde->pde_unload_lock); +} + +static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + loff_t rv = -EINVAL; + loff_t (*llseek)(struct file *, loff_t, int); + + spin_lock(&pde->pde_unload_lock); + /* + * remove_proc_entry() is going to delete PDE (as part of module + * cleanup sequence). No new callers into module allowed. + */ + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + /* + * Bump refcount so that remove_proc_entry will wail for ->llseek to + * complete. + */ + pde->pde_users++; + /* + * Save function pointer under lock, to protect against ->proc_fops + * NULL'ifying right after ->pde_unload_lock is dropped. + */ + llseek = pde->proc_fops->llseek; + spin_unlock(&pde->pde_unload_lock); + + if (!llseek) + llseek = default_llseek; + rv = llseek(file, offset, whence); + + pde_users_dec(pde); + return rv; +} + +static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + ssize_t rv = -EIO; + ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + read = pde->proc_fops->read; + spin_unlock(&pde->pde_unload_lock); + + if (read) + rv = read(file, buf, count, ppos); + + pde_users_dec(pde); + return rv; +} + +static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + ssize_t rv = -EIO; + ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + write = pde->proc_fops->write; + spin_unlock(&pde->pde_unload_lock); + + if (write) + rv = write(file, buf, count, ppos); + + pde_users_dec(pde); + return rv; +} + +static unsigned int proc_reg_poll(struct file *file, struct poll_table_struct *pts) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + unsigned int rv = 0; + unsigned int (*poll)(struct file *, struct poll_table_struct *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + poll = pde->proc_fops->poll; + spin_unlock(&pde->pde_unload_lock); + + if (poll) + rv = poll(file, pts); + + pde_users_dec(pde); + return rv; +} + +static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + long rv = -ENOTTY; + long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long); + int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + unlocked_ioctl = pde->proc_fops->unlocked_ioctl; + ioctl = pde->proc_fops->ioctl; + spin_unlock(&pde->pde_unload_lock); + + if (unlocked_ioctl) { + rv = unlocked_ioctl(file, cmd, arg); + if (rv == -ENOIOCTLCMD) + rv = -EINVAL; + } else if (ioctl) { + lock_kernel(); + rv = ioctl(file->f_path.dentry->d_inode, file, cmd, arg); + unlock_kernel(); + } + + pde_users_dec(pde); + return rv; +} + +#ifdef CONFIG_COMPAT +static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + long rv = -ENOTTY; + long (*compat_ioctl)(struct file *, unsigned int, unsigned long); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + compat_ioctl = pde->proc_fops->compat_ioctl; + spin_unlock(&pde->pde_unload_lock); + + if (compat_ioctl) + rv = compat_ioctl(file, cmd, arg); + + pde_users_dec(pde); + return rv; +} +#endif + +static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); + int rv = -EIO; + int (*mmap)(struct file *, struct vm_area_struct *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + mmap = pde->proc_fops->mmap; + spin_unlock(&pde->pde_unload_lock); + + if (mmap) + rv = mmap(file, vma); + + pde_users_dec(pde); + return rv; +} + +static int proc_reg_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + int rv = 0; + int (*open)(struct inode *, struct file *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + open = pde->proc_fops->open; + spin_unlock(&pde->pde_unload_lock); + + if (open) + rv = open(inode, file); + + pde_users_dec(pde); + return rv; +} + +static int proc_reg_release(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + int rv = 0; + int (*release)(struct inode *, struct file *); + + spin_lock(&pde->pde_unload_lock); + if (!pde->proc_fops) { + spin_unlock(&pde->pde_unload_lock); + return rv; + } + pde->pde_users++; + release = pde->proc_fops->release; + spin_unlock(&pde->pde_unload_lock); + + if (release) + rv = release(inode, file); + + pde_users_dec(pde); + return rv; +} + +static const struct file_operations proc_reg_file_ops = { + .llseek = proc_reg_llseek, + .read = proc_reg_read, + .write = proc_reg_write, + .poll = proc_reg_poll, + .unlocked_ioctl = proc_reg_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = proc_reg_compat_ioctl, +#endif + .mmap = proc_reg_mmap, + .open = proc_reg_open, + .release = proc_reg_release, +}; + struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, struct proc_dir_entry *de) { @@ -166,8 +412,12 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, inode->i_nlink = de->nlink; if (de->proc_iops) inode->i_op = de->proc_iops; - if (de->proc_fops) - inode->i_fop = de->proc_fops; + if (de->proc_fops) { + if (S_ISREG(inode->i_mode)) + inode->i_fop = &proc_reg_file_ops; + else + inode->i_fop = de->proc_fops; + } } return inode; diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 5fd49e4..bee251c 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -105,6 +105,7 @@ static int uptime_read_proc(char *page, char **start, off_t off, cputime_t idletime = cputime_add(init_task.utime, init_task.stime); do_posix_clock_monotonic_gettime(&uptime); + monotonic_to_bootbased(&uptime); cputime_to_timespec(idletime, &idle); len = sprintf(page,"%lu.%02lu %lu.%02lu\n", (unsigned long) uptime.tv_sec, @@ -443,12 +444,17 @@ static int show_stat(struct seq_file *p, void *v) unsigned long jif; cputime64_t user, nice, system, idle, iowait, irq, softirq, steal; u64 sum = 0; + struct timespec boottime; + unsigned int *per_irq_sum; + + per_irq_sum = kzalloc(sizeof(unsigned int)*NR_IRQS, GFP_KERNEL); + if (!per_irq_sum) + return -ENOMEM; user = nice = system = idle = iowait = irq = softirq = steal = cputime64_zero; - jif = - wall_to_monotonic.tv_sec; - if (wall_to_monotonic.tv_nsec) - --jif; + getboottime(&boottime); + jif = boottime.tv_sec; for_each_possible_cpu(i) { int j; @@ -461,8 +467,11 @@ static int show_stat(struct seq_file *p, void *v) irq = cputime64_add(irq, kstat_cpu(i).cpustat.irq); softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq); steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal); - for (j = 0 ; j < NR_IRQS ; j++) - sum += kstat_cpu(i).irqs[j]; + for (j = 0; j < NR_IRQS; j++) { + unsigned int temp = kstat_cpu(i).irqs[j]; + sum += temp; + per_irq_sum[j] += temp; + } } seq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu\n", @@ -498,9 +507,10 @@ static int show_stat(struct seq_file *p, void *v) } seq_printf(p, "intr %llu", (unsigned long long)sum); -#if !defined(CONFIG_PPC64) && !defined(CONFIG_ALPHA) && !defined(CONFIG_IA64) +#ifndef CONFIG_SMP + /* Touches too many cache lines on SMP setups */ for (i = 0; i < NR_IRQS; i++) - seq_printf(p, " %u", kstat_irqs(i)); + seq_printf(p, " %u", per_irq_sum[i]); #endif seq_printf(p, @@ -515,6 +525,7 @@ static int show_stat(struct seq_file *p, void *v) nr_running(), nr_iowait()); + kfree(per_irq_sum); return 0; } diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index b3a473b..2284622 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -69,7 +69,7 @@ static void show_tty_range(struct seq_file *m, struct tty_driver *p, static int show_tty_driver(struct seq_file *m, void *v) { - struct tty_driver *p = v; + struct tty_driver *p = list_entry(v, struct tty_driver, tty_drivers); dev_t from = MKDEV(p->major, p->minor_start); dev_t to = from + p->num; @@ -106,22 +106,13 @@ static int show_tty_driver(struct seq_file *m, void *v) /* iterator */ static void *t_start(struct seq_file *m, loff_t *pos) { - struct list_head *p; - loff_t l = *pos; - mutex_lock(&tty_mutex); - list_for_each(p, &tty_drivers) - if (!l--) - return list_entry(p, struct tty_driver, tty_drivers); - return NULL; + return seq_list_start(&tty_drivers, *pos); } static void *t_next(struct seq_file *m, void *v, loff_t *pos) { - struct list_head *p = ((struct tty_driver *)v)->tty_drivers.next; - (*pos)++; - return p==&tty_drivers ? NULL : - list_entry(p, struct tty_driver, tty_drivers); + return seq_list_next(v, &tty_drivers, pos); } static void t_stop(struct seq_file *m, void *v) diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 8d256eb..1bc8d87 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -545,7 +545,7 @@ static int init_inodecache(void) sizeof(struct qnx4_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (qnx4_inode_cachep == NULL) return -ENOMEM; return 0; @@ -10,12 +10,14 @@ #include <linux/slab.h> #include <asm/current.h> #include <asm/uaccess.h> +#include <linux/compat.h> #include <linux/kernel.h> #include <linux/security.h> #include <linux/syscalls.h> #include <linux/buffer_head.h> #include <linux/capability.h> #include <linux/quotaops.h> +#include <linux/types.h> /* Check validity of generic quotactl commands */ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) @@ -384,3 +386,119 @@ asmlinkage long sys_quotactl(unsigned int cmd, const char __user *special, qid_t return ret; } + +#if defined(CONFIG_X86_64) || defined(CONFIG_IA64) +/* + * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64) + * and is necessary due to alignment problems. + */ +struct compat_if_dqblk { + compat_u64 dqb_bhardlimit; + compat_u64 dqb_bsoftlimit; + compat_u64 dqb_curspace; + compat_u64 dqb_ihardlimit; + compat_u64 dqb_isoftlimit; + compat_u64 dqb_curinodes; + compat_u64 dqb_btime; + compat_u64 dqb_itime; + compat_uint_t dqb_valid; +}; + +/* XFS structures */ +struct compat_fs_qfilestat { + compat_u64 dqb_bhardlimit; + compat_u64 qfs_nblks; + compat_uint_t qfs_nextents; +}; + +struct compat_fs_quota_stat { + __s8 qs_version; + __u16 qs_flags; + __s8 qs_pad; + struct compat_fs_qfilestat qs_uquota; + struct compat_fs_qfilestat qs_gquota; + compat_uint_t qs_incoredqs; + compat_int_t qs_btimelimit; + compat_int_t qs_itimelimit; + compat_int_t qs_rtbtimelimit; + __u16 qs_bwarnlimit; + __u16 qs_iwarnlimit; +}; + +asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, + qid_t id, void __user *addr) +{ + unsigned int cmds; + struct if_dqblk __user *dqblk; + struct compat_if_dqblk __user *compat_dqblk; + struct fs_quota_stat __user *fsqstat; + struct compat_fs_quota_stat __user *compat_fsqstat; + compat_uint_t data; + u16 xdata; + long ret; + + cmds = cmd >> SUBCMDSHIFT; + + switch (cmds) { + case Q_GETQUOTA: + dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); + compat_dqblk = addr; + ret = sys_quotactl(cmd, special, id, dqblk); + if (ret) + break; + if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) || + get_user(data, &dqblk->dqb_valid) || + put_user(data, &compat_dqblk->dqb_valid)) + ret = -EFAULT; + break; + case Q_SETQUOTA: + dqblk = compat_alloc_user_space(sizeof(struct if_dqblk)); + compat_dqblk = addr; + ret = -EFAULT; + if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) || + get_user(data, &compat_dqblk->dqb_valid) || + put_user(data, &dqblk->dqb_valid)) + break; + ret = sys_quotactl(cmd, special, id, dqblk); + break; + case Q_XGETQSTAT: + fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat)); + compat_fsqstat = addr; + ret = sys_quotactl(cmd, special, id, fsqstat); + if (ret) + break; + ret = -EFAULT; + /* Copying qs_version, qs_flags, qs_pad */ + if (copy_in_user(compat_fsqstat, fsqstat, + offsetof(struct compat_fs_quota_stat, qs_uquota))) + break; + /* Copying qs_uquota */ + if (copy_in_user(&compat_fsqstat->qs_uquota, + &fsqstat->qs_uquota, + sizeof(compat_fsqstat->qs_uquota)) || + get_user(data, &fsqstat->qs_uquota.qfs_nextents) || + put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents)) + break; + /* Copying qs_gquota */ + if (copy_in_user(&compat_fsqstat->qs_gquota, + &fsqstat->qs_gquota, + sizeof(compat_fsqstat->qs_gquota)) || + get_user(data, &fsqstat->qs_gquota.qfs_nextents) || + put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents)) + break; + /* Copying the rest */ + if (copy_in_user(&compat_fsqstat->qs_incoredqs, + &fsqstat->qs_incoredqs, + sizeof(struct compat_fs_quota_stat) - + offsetof(struct compat_fs_quota_stat, qs_incoredqs)) || + get_user(xdata, &fsqstat->qs_iwarnlimit) || + put_user(xdata, &compat_fsqstat->qs_iwarnlimit)) + break; + ret = 0; + break; + default: + ret = sys_quotactl(cmd, special, id, addr); + } + return ret; +} +#endif diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index d40d22b..ef2b46d 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -60,6 +60,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev) inode->i_blocks = 0; inode->i_mapping->a_ops = &ramfs_aops; inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info; + mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 30eebfb..2070aee 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1305,7 +1305,6 @@ static ssize_t reiserfs_file_write(struct file *file, /* the file we are going t if (get_inode_item_key_version (inode) == KEY_FORMAT_3_5 && *ppos + count > MAX_NON_LFS) { if (*ppos >= MAX_NON_LFS) { - send_sig(SIGXFSZ, current, 0); return -EFBIG; } if (count > MAX_NON_LFS - (unsigned long)*ppos) diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 1272d11..ddde489 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -7,6 +7,7 @@ #include <linux/reiserfs_fs.h> #include <linux/reiserfs_acl.h> #include <linux/reiserfs_xattr.h> +#include <linux/exportfs.h> #include <linux/smp_lock.h> #include <linux/pagemap.h> #include <linux/highmem.h> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c index b484d29..11a0fcc 100644 --- a/fs/reiserfs/ioctl.c +++ b/fs/reiserfs/ioctl.c @@ -51,8 +51,7 @@ int reiserfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, if (IS_RDONLY(inode)) return -EROFS; - if ((current->fsuid != inode->i_uid) - && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (get_user(flags, (int __user *)arg)) @@ -81,7 +80,7 @@ int reiserfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, case REISERFS_IOC_GETVERSION: return put_user(inode->i_generation, (int __user *)arg); case REISERFS_IOC_SETVERSION: - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index b4ac911..5b68dd3 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/blkdev.h> #include <linux/buffer_head.h> +#include <linux/exportfs.h> #include <linux/vfs.h> #include <linux/mnt_namespace.h> #include <linux/mount.h> @@ -526,7 +527,7 @@ static int init_inodecache(void) reiserfs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (reiserfs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c index 5296a29..b7e4fa4 100644 --- a/fs/reiserfs/xattr_acl.c +++ b/fs/reiserfs/xattr_acl.c @@ -21,7 +21,7 @@ xattr_set_acl(struct inode *inode, int type, const void *value, size_t size) if (!reiserfs_posixacl(inode->i_sb)) return -EOPNOTSUPP; - if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + if (!is_owner_or_cap(inode)) return -EPERM; if (value) { diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 2284e03..dae7945 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -572,14 +572,14 @@ static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { romfs_inode_cachep = kmem_cache_create("romfs_inode_cache", sizeof(struct romfs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (romfs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/seq_file.c b/fs/seq_file.c index 49194a4..bbb19be 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -177,21 +177,23 @@ EXPORT_SYMBOL(seq_read); static int traverse(struct seq_file *m, loff_t offset) { - loff_t pos = 0; + loff_t pos = 0, index; int error = 0; void *p; m->version = 0; - m->index = 0; + index = 0; m->count = m->from = 0; - if (!offset) + if (!offset) { + m->index = index; return 0; + } if (!m->buf) { m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); if (!m->buf) return -ENOMEM; } - p = m->op->start(m, &m->index); + p = m->op->start(m, &index); while (p) { error = PTR_ERR(p); if (IS_ERR(p)) @@ -204,15 +206,17 @@ static int traverse(struct seq_file *m, loff_t offset) if (pos + m->count > offset) { m->from = offset - pos; m->count -= m->from; + m->index = index; break; } pos += m->count; m->count = 0; if (pos == offset) { - m->index++; + index++; + m->index = index; break; } - p = m->op->next(m, p, &m->index); + p = m->op->next(m, p, &index); } m->op->stop(m, p); return error; @@ -260,8 +264,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin) } } } - mutex_unlock(&m->lock); file->f_version = m->version; + mutex_unlock(&m->lock); return retval; } EXPORT_SYMBOL(seq_lseek); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 6724a6c..73d1450 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -73,14 +73,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { smb_inode_cachep = kmem_cache_create("smb_inode_cache", sizeof(struct smb_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (smb_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/smbfs/request.c b/fs/smbfs/request.c index 3f54a0f..ca4b2d5 100644 --- a/fs/smbfs/request.c +++ b/fs/smbfs/request.c @@ -40,7 +40,7 @@ int smb_init_request_cache(void) req_cachep = kmem_cache_create("smb_request", sizeof(struct smb_request), 0, SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, - NULL, NULL); + NULL); if (req_cachep == NULL) return -ENOMEM; diff --git a/fs/splice.c b/fs/splice.c index ed2ce99..0a09732 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/syscalls.h> #include <linux/uio.h> +#include <linux/security.h> /* * Attempt to steal a page from a pipe buffer. This should perhaps go into @@ -264,7 +265,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, unsigned int flags) { struct address_space *mapping = in->f_mapping; - unsigned int loff, nr_pages; + unsigned int loff, nr_pages, req_pages; struct page *pages[PIPE_BUFFERS]; struct partial_page partial[PIPE_BUFFERS]; struct page *page; @@ -280,28 +281,24 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, index = *ppos >> PAGE_CACHE_SHIFT; loff = *ppos & ~PAGE_CACHE_MASK; - nr_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - if (nr_pages > PIPE_BUFFERS) - nr_pages = PIPE_BUFFERS; - - /* - * Don't try to 2nd guess the read-ahead logic, call into - * page_cache_readahead() like the page cache reads would do. - */ - page_cache_readahead(mapping, &in->f_ra, in, index, nr_pages); + req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + nr_pages = min(req_pages, (unsigned)PIPE_BUFFERS); /* * Lookup the (hopefully) full range of pages we need. */ spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, pages); + index += spd.nr_pages; /* * If find_get_pages_contig() returned fewer pages than we needed, - * allocate the rest and fill in the holes. + * readahead/allocate the rest and fill in the holes. */ + if (spd.nr_pages < nr_pages) + page_cache_sync_readahead(mapping, &in->f_ra, in, + index, req_pages - spd.nr_pages); + error = 0; - index += spd.nr_pages; while (spd.nr_pages < nr_pages) { /* * Page could be there, find_get_pages_contig() breaks on @@ -310,12 +307,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, page = find_get_page(mapping, index); if (!page) { /* - * Make sure the read-ahead engine is notified - * about this failure. - */ - handle_ra_miss(mapping, &in->f_ra, index); - - /* * page didn't exist, allocate one. */ page = page_cache_alloc_cold(mapping); @@ -360,6 +351,10 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, this_len = min_t(unsigned long, len, PAGE_CACHE_SIZE - loff); page = pages[page_nr]; + if (PageReadahead(page)) + page_cache_async_readahead(mapping, &in->f_ra, in, + page, index, req_pages - page_nr); + /* * If the page isn't uptodate, we may need to start io on it */ @@ -452,6 +447,7 @@ fill_it: */ while (page_nr < nr_pages) page_cache_release(pages[page_nr++]); + in->f_ra.prev_index = index; if (spd.nr_pages) return splice_to_pipe(pipe, &spd); @@ -491,7 +487,7 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, ret = 0; spliced = 0; - while (len) { + while (len && !spliced) { ret = __generic_file_splice_read(in, ppos, pipe, len, flags); if (ret < 0) @@ -598,7 +594,7 @@ find_page: ret = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL); if (unlikely(ret)) - goto out; + goto out_release; } ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len); @@ -654,8 +650,9 @@ find_page: */ mark_page_accessed(page); out: - page_cache_release(page); unlock_page(page); +out_release: + page_cache_release(page); out_ret: return ret; } @@ -961,6 +958,10 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, if (unlikely(ret < 0)) return ret; + ret = security_file_permission(out, MAY_WRITE); + if (unlikely(ret < 0)) + return ret; + return out->f_op->splice_write(pipe, out, ppos, len, flags); } @@ -983,6 +984,10 @@ static long do_splice_to(struct file *in, loff_t *ppos, if (unlikely(ret < 0)) return ret; + ret = security_file_permission(in, MAY_READ); + if (unlikely(ret < 0)) + return ret; + return in->f_op->splice_read(in, ppos, pipe, len, flags); } @@ -1051,15 +1056,11 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, sd->flags &= ~SPLICE_F_NONBLOCK; while (len) { - size_t read_len, max_read_len; + size_t read_len; + loff_t pos = sd->pos; - /* - * Do at most PIPE_BUFFERS pages worth of transfer: - */ - max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE)); - - ret = do_splice_to(in, &sd->pos, pipe, max_read_len, flags); - if (unlikely(ret < 0)) + ret = do_splice_to(in, &pos, pipe, len, flags); + if (unlikely(ret <= 0)) goto out_release; read_len = ret; @@ -1071,26 +1072,18 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, * could get stuck data in the internal pipe: */ ret = actor(pipe, sd); - if (unlikely(ret < 0)) + if (unlikely(ret <= 0)) goto out_release; bytes += ret; len -= ret; + sd->pos = pos; - /* - * In nonblocking mode, if we got back a short read then - * that was due to either an IO error or due to the - * pagecache entry not being there. In the IO error case - * the _next_ splice attempt will produce a clean IO error - * return value (not a short read), so in both cases it's - * correct to break out of the loop here: - */ - if ((flags & SPLICE_F_NONBLOCK) && (read_len < max_read_len)) - break; + if (ret < read_len) + goto out_release; } pipe->nrbufs = pipe->curbuf = 0; - return bytes; out_release: @@ -1152,10 +1145,12 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, .pos = *ppos, .u.file = out, }; - size_t ret; + long ret; ret = splice_direct_to_actor(in, &sd, direct_splice_actor); - *ppos = sd.pos; + if (ret > 0) + *ppos += ret; + return ret; } @@ -884,6 +884,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void error = type->get_sb(type, flags, name, data, mnt); if (error < 0) goto out_free_secdata; + BUG_ON(!mnt->mnt_sb); error = security_sb_kern_mount(mnt->mnt_sb, secdata); if (error) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index d3b9f5f..135353f 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -20,29 +20,41 @@ #include "sysfs.h" +struct bin_buffer { + struct mutex mutex; + void *buffer; + int mmapped; +}; + static int fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) { - struct bin_attribute * attr = to_bin_attr(dentry); - struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + int rc; + + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - if (!attr->read) - return -EIO; + rc = -EIO; + if (attr->read) + rc = attr->read(kobj, attr, buffer, off, count); - return attr->read(kobj, buffer, off, count); + sysfs_put_active_two(attr_sd); + + return rc; } static ssize_t -read(struct file * file, char __user * userbuf, size_t count, loff_t * off) +read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { - char *buffer = file->private_data; + struct bin_buffer *bb = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; - int ret; - - if (count > PAGE_SIZE) - count = PAGE_SIZE; + int count = min_t(size_t, bytes, PAGE_SIZE); if (size) { if (offs > size) @@ -51,43 +63,56 @@ read(struct file * file, char __user * userbuf, size_t count, loff_t * off) count = size - offs; } - ret = fill_read(dentry, buffer, offs, count); - if (ret < 0) - return ret; - count = ret; + mutex_lock(&bb->mutex); + + count = fill_read(dentry, bb->buffer, offs, count); + if (count < 0) + goto out_unlock; - if (copy_to_user(userbuf, buffer, count)) - return -EFAULT; + if (copy_to_user(userbuf, bb->buffer, count)) { + count = -EFAULT; + goto out_unlock; + } - pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); + pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); *off = offs + count; + out_unlock: + mutex_unlock(&bb->mutex); return count; } static int flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) { - struct bin_attribute *attr = to_bin_attr(dentry); - struct kobject *kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + int rc; + + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + rc = -EIO; + if (attr->write) + rc = attr->write(kobj, attr, buffer, offset, count); - if (!attr->write) - return -EIO; + sysfs_put_active_two(attr_sd); - return attr->write(kobj, buffer, offset, count); + return rc; } -static ssize_t write(struct file * file, const char __user * userbuf, - size_t count, loff_t * off) +static ssize_t write(struct file *file, const char __user *userbuf, + size_t bytes, loff_t *off) { - char *buffer = file->private_data; + struct bin_buffer *bb = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; + int count = min_t(size_t, bytes, PAGE_SIZE); - if (count > PAGE_SIZE) - count = PAGE_SIZE; if (size) { if (offs > size) return 0; @@ -95,72 +120,100 @@ static ssize_t write(struct file * file, const char __user * userbuf, count = size - offs; } - if (copy_from_user(buffer, userbuf, count)) - return -EFAULT; + mutex_lock(&bb->mutex); - count = flush_write(dentry, buffer, offs, count); + if (copy_from_user(bb->buffer, userbuf, count)) { + count = -EFAULT; + goto out_unlock; + } + + count = flush_write(dentry, bb->buffer, offs, count); if (count > 0) *off = offs + count; + + out_unlock: + mutex_unlock(&bb->mutex); return count; } static int mmap(struct file *file, struct vm_area_struct *vma) { - struct dentry *dentry = file->f_path.dentry; - struct bin_attribute *attr = to_bin_attr(dentry); - struct kobject *kobj = to_kobj(dentry->d_parent); + struct bin_buffer *bb = file->private_data; + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + int rc; + + mutex_lock(&bb->mutex); + + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - if (!attr->mmap) - return -EINVAL; + rc = -EINVAL; + if (attr->mmap) + rc = attr->mmap(kobj, attr, vma); - return attr->mmap(kobj, attr, vma); + if (rc == 0 && !bb->mmapped) + bb->mmapped = 1; + else + sysfs_put_active_two(attr_sd); + + mutex_unlock(&bb->mutex); + + return rc; } static int open(struct inode * inode, struct file * file) { - struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); - struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); - int error = -EINVAL; - - if (!kobj || !attr) - goto Done; + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct bin_buffer *bb = NULL; + int error; - /* Grab the module reference for this attribute if we have one */ - error = -ENODEV; - if (!try_module_get(attr->attr.owner)) - goto Done; + /* need attr_sd for attr */ + if (!sysfs_get_active(attr_sd)) + return -ENODEV; error = -EACCES; if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) - goto Error; + goto err_out; if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) - goto Error; + goto err_out; error = -ENOMEM; - file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!file->private_data) - goto Error; - - error = 0; - goto Done; - - Error: - module_put(attr->attr.owner); - Done: - if (error) - kobject_put(kobj); + bb = kzalloc(sizeof(*bb), GFP_KERNEL); + if (!bb) + goto err_out; + + bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!bb->buffer) + goto err_out; + + mutex_init(&bb->mutex); + file->private_data = bb; + + /* open succeeded, put active reference and pin attr_sd */ + sysfs_put_active(attr_sd); + sysfs_get(attr_sd); + return 0; + + err_out: + sysfs_put_active(attr_sd); + kfree(bb); return error; } static int release(struct inode * inode, struct file * file) { - struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); - struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); - u8 * buffer = file->private_data; - - kobject_put(kobj); - module_put(attr->attr.owner); - kfree(buffer); + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_buffer *bb = file->private_data; + + if (bb->mmapped) + sysfs_put_active_two(attr_sd); + sysfs_put(attr_sd); + kfree(bb->buffer); + kfree(bb); return 0; } @@ -181,9 +234,9 @@ const struct file_operations bin_fops = { int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) { - BUG_ON(!kobj || !kobj->dentry || !attr); + BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR); + return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); } @@ -195,7 +248,7 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) { - if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) { + if (sysfs_hash_and_remove(kobj->sd, attr->attr.name) < 0) { printk(KERN_ERR "%s: " "bad dentry or inode or no such file: \"%s\"\n", __FUNCTION__, attr->attr.name); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index c4342a0..048e605 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -9,21 +9,337 @@ #include <linux/module.h> #include <linux/kobject.h> #include <linux/namei.h> +#include <linux/idr.h> +#include <linux/completion.h> #include <asm/semaphore.h> #include "sysfs.h" -DECLARE_RWSEM(sysfs_rename_sem); -spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; +DEFINE_MUTEX(sysfs_mutex); +spinlock_t sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; + +static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_IDA(sysfs_ino_ida); + +/** + * sysfs_link_sibling - link sysfs_dirent into sibling list + * @sd: sysfs_dirent of interest + * + * Link @sd into its sibling list which starts from + * sd->s_parent->s_children. + * + * Locking: + * mutex_lock(sysfs_mutex) + */ +void sysfs_link_sibling(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *parent_sd = sd->s_parent; + + BUG_ON(sd->s_sibling); + sd->s_sibling = parent_sd->s_children; + parent_sd->s_children = sd; +} + +/** + * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list + * @sd: sysfs_dirent of interest + * + * Unlink @sd from its sibling list which starts from + * sd->s_parent->s_children. + * + * Locking: + * mutex_lock(sysfs_mutex) + */ +void sysfs_unlink_sibling(struct sysfs_dirent *sd) +{ + struct sysfs_dirent **pos; + + for (pos = &sd->s_parent->s_children; *pos; pos = &(*pos)->s_sibling) { + if (*pos == sd) { + *pos = sd->s_sibling; + sd->s_sibling = NULL; + break; + } + } +} + +/** + * sysfs_get_dentry - get dentry for the given sysfs_dirent + * @sd: sysfs_dirent of interest + * + * Get dentry for @sd. Dentry is looked up if currently not + * present. This function climbs sysfs_dirent tree till it + * reaches a sysfs_dirent with valid dentry attached and descends + * down from there looking up dentry for each step. + * + * LOCKING: + * Kernel thread context (may sleep) + * + * RETURNS: + * Pointer to found dentry on success, ERR_PTR() value on error. + */ +struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *cur; + struct dentry *parent_dentry, *dentry; + int i, depth; + + /* Find the first parent which has valid s_dentry and get the + * dentry. + */ + mutex_lock(&sysfs_mutex); + restart0: + spin_lock(&sysfs_assoc_lock); + restart1: + spin_lock(&dcache_lock); + + dentry = NULL; + depth = 0; + cur = sd; + while (!cur->s_dentry || !cur->s_dentry->d_inode) { + if (cur->s_flags & SYSFS_FLAG_REMOVED) { + dentry = ERR_PTR(-ENOENT); + depth = 0; + break; + } + cur = cur->s_parent; + depth++; + } + if (!IS_ERR(dentry)) + dentry = dget_locked(cur->s_dentry); + + spin_unlock(&dcache_lock); + spin_unlock(&sysfs_assoc_lock); + + /* from the found dentry, look up depth times */ + while (depth--) { + /* find and get depth'th ancestor */ + for (cur = sd, i = 0; cur && i < depth; i++) + cur = cur->s_parent; + + /* This can happen if tree structure was modified due + * to move/rename. Restart. + */ + if (i != depth) { + dput(dentry); + goto restart0; + } + + sysfs_get(cur); + + mutex_unlock(&sysfs_mutex); + + /* look it up */ + parent_dentry = dentry; + dentry = lookup_one_len_kern(cur->s_name, parent_dentry, + strlen(cur->s_name)); + dput(parent_dentry); + + if (IS_ERR(dentry)) { + sysfs_put(cur); + return dentry; + } + + mutex_lock(&sysfs_mutex); + spin_lock(&sysfs_assoc_lock); + + /* This, again, can happen if tree structure has + * changed and we looked up the wrong thing. Restart. + */ + if (cur->s_dentry != dentry) { + dput(dentry); + sysfs_put(cur); + goto restart1; + } + + spin_unlock(&sysfs_assoc_lock); + + sysfs_put(cur); + } + + mutex_unlock(&sysfs_mutex); + return dentry; +} + +/** + * sysfs_get_active - get an active reference to sysfs_dirent + * @sd: sysfs_dirent to get an active reference to + * + * Get an active reference of @sd. This function is noop if @sd + * is NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) +{ + if (unlikely(!sd)) + return NULL; + + while (1) { + int v, t; + + v = atomic_read(&sd->s_active); + if (unlikely(v < 0)) + return NULL; + + t = atomic_cmpxchg(&sd->s_active, v, v + 1); + if (likely(t == v)) + return sd; + if (t < 0) + return NULL; + + cpu_relax(); + } +} + +/** + * sysfs_put_active - put an active reference to sysfs_dirent + * @sd: sysfs_dirent to put an active reference to + * + * Put an active reference to @sd. This function is noop if @sd + * is NULL. + */ +void sysfs_put_active(struct sysfs_dirent *sd) +{ + struct completion *cmpl; + int v; + + if (unlikely(!sd)) + return; + + v = atomic_dec_return(&sd->s_active); + if (likely(v != SD_DEACTIVATED_BIAS)) + return; + + /* atomic_dec_return() is a mb(), we'll always see the updated + * sd->s_sibling. + */ + cmpl = (void *)sd->s_sibling; + complete(cmpl); +} + +/** + * sysfs_get_active_two - get active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Get active reference to @sd and its parent. Parent's active + * reference is grabbed first. This function is noop if @sd is + * NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) + return NULL; + if (unlikely(!sysfs_get_active(sd))) { + sysfs_put_active(sd->s_parent); + return NULL; + } + } + return sd; +} + +/** + * sysfs_put_active_two - put active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Put active references to @sd and its parent. This function is + * noop if @sd is NULL. + */ +void sysfs_put_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + sysfs_put_active(sd); + sysfs_put_active(sd->s_parent); + } +} + +/** + * sysfs_deactivate - deactivate sysfs_dirent + * @sd: sysfs_dirent to deactivate + * + * Deny new active references and drain existing ones. + */ +static void sysfs_deactivate(struct sysfs_dirent *sd) +{ + DECLARE_COMPLETION_ONSTACK(wait); + int v; + + BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED)); + sd->s_sibling = (void *)&wait; + + /* atomic_add_return() is a mb(), put_active() will always see + * the updated sd->s_sibling. + */ + v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); + + if (v != SD_DEACTIVATED_BIAS) + wait_for_completion(&wait); + + sd->s_sibling = NULL; +} + +static int sysfs_alloc_ino(ino_t *pino) +{ + int ino, rc; + + retry: + spin_lock(&sysfs_ino_lock); + rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino); + spin_unlock(&sysfs_ino_lock); + + if (rc == -EAGAIN) { + if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL)) + goto retry; + rc = -ENOMEM; + } + + *pino = ino; + return rc; +} + +static void sysfs_free_ino(ino_t ino) +{ + spin_lock(&sysfs_ino_lock); + ida_remove(&sysfs_ino_ida, ino); + spin_unlock(&sysfs_ino_lock); +} + +void release_sysfs_dirent(struct sysfs_dirent * sd) +{ + struct sysfs_dirent *parent_sd; + + repeat: + /* Moving/renaming is always done while holding reference. + * sd->s_parent won't change beneath us. + */ + parent_sd = sd->s_parent; + + if (sysfs_type(sd) == SYSFS_KOBJ_LINK) + sysfs_put(sd->s_elem.symlink.target_sd); + if (sysfs_type(sd) & SYSFS_COPY_NAME) + kfree(sd->s_name); + kfree(sd->s_iattr); + sysfs_free_ino(sd->s_ino); + kmem_cache_free(sysfs_dir_cachep, sd); + + sd = parent_sd; + if (sd && atomic_dec_and_test(&sd->s_count)) + goto repeat; +} static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) { struct sysfs_dirent * sd = dentry->d_fsdata; if (sd) { - /* sd->s_dentry is protected with sysfs_lock. This - * allows sysfs_drop_dentry() to dereference it. + /* sd->s_dentry is protected with sysfs_assoc_lock. + * This allows sysfs_drop_dentry() to dereference it. */ - spin_lock(&sysfs_lock); + spin_lock(&sysfs_assoc_lock); /* The dentry might have been deleted or another * lookup could have happened updating sd->s_dentry to @@ -32,7 +348,7 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) */ if (sd->s_dentry == dentry) sd->s_dentry = NULL; - spin_unlock(&sysfs_lock); + spin_unlock(&sysfs_assoc_lock); sysfs_put(sd); } iput(inode); @@ -42,260 +358,405 @@ static struct dentry_operations sysfs_dentry_ops = { .d_iput = sysfs_d_iput, }; -static unsigned int sysfs_inode_counter; -ino_t sysfs_get_inum(void) +struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) { - if (unlikely(sysfs_inode_counter < 3)) - sysfs_inode_counter = 3; - return sysfs_inode_counter++; -} + char *dup_name = NULL; + struct sysfs_dirent *sd; -/* - * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent - */ -static struct sysfs_dirent * __sysfs_new_dirent(void * element) -{ - struct sysfs_dirent * sd; + if (type & SYSFS_COPY_NAME) { + name = dup_name = kstrdup(name, GFP_KERNEL); + if (!name) + return NULL; + } sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); if (!sd) - return NULL; + goto err_out1; + + if (sysfs_alloc_ino(&sd->s_ino)) + goto err_out2; - sd->s_ino = sysfs_get_inum(); atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_active, 0); atomic_set(&sd->s_event, 1); - INIT_LIST_HEAD(&sd->s_children); - INIT_LIST_HEAD(&sd->s_sibling); - sd->s_element = element; + + sd->s_name = name; + sd->s_mode = mode; + sd->s_flags = type; return sd; + + err_out2: + kmem_cache_free(sysfs_dir_cachep, sd); + err_out1: + kfree(dup_name); + return NULL; } -static void __sysfs_list_dirent(struct sysfs_dirent *parent_sd, - struct sysfs_dirent *sd) +/** + * sysfs_attach_dentry - associate sysfs_dirent with dentry + * @sd: target sysfs_dirent + * @dentry: dentry to associate + * + * Associate @sd with @dentry. This is protected by + * sysfs_assoc_lock to avoid race with sysfs_d_iput(). + * + * LOCKING: + * mutex_lock(sysfs_mutex) + */ +static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) { - if (sd) - list_add(&sd->s_sibling, &parent_sd->s_children); + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + + /* protect sd->s_dentry against sysfs_d_iput */ + spin_lock(&sysfs_assoc_lock); + sd->s_dentry = dentry; + spin_unlock(&sysfs_assoc_lock); + + d_rehash(dentry); } -static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent *parent_sd, - void * element) +static int sysfs_ilookup_test(struct inode *inode, void *arg) { - struct sysfs_dirent *sd; - sd = __sysfs_new_dirent(element); - __sysfs_list_dirent(parent_sd, sd); - return sd; + struct sysfs_dirent *sd = arg; + return inode->i_ino == sd->s_ino; } -/* +/** + * sysfs_addrm_start - prepare for sysfs_dirent add/remove + * @acxt: pointer to sysfs_addrm_cxt to be used + * @parent_sd: parent sysfs_dirent * - * Return -EEXIST if there is already a sysfs element with the same name for - * the same parent. + * This function is called when the caller is about to add or + * remove sysfs_dirent under @parent_sd. This function acquires + * sysfs_mutex, grabs inode for @parent_sd if available and lock + * i_mutex of it. @acxt is used to keep and pass context to + * other addrm functions. * - * called with parent inode's i_mutex held + * LOCKING: + * Kernel thread context (may sleep). sysfs_mutex is locked on + * return. i_mutex of parent inode is locked on return if + * available. */ -int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, - const unsigned char *new) +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *parent_sd) { - struct sysfs_dirent * sd; + struct inode *inode; - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (sd->s_element) { - const unsigned char *existing = sysfs_get_name(sd); - if (strcmp(existing, new)) - continue; - else - return -EEXIST; - } - } + memset(acxt, 0, sizeof(*acxt)); + acxt->parent_sd = parent_sd; - return 0; + /* Lookup parent inode. inode initialization and I_NEW + * clearing are protected by sysfs_mutex. By grabbing it and + * looking up with _nowait variant, inode state can be + * determined reliably. + */ + mutex_lock(&sysfs_mutex); + + inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test, + parent_sd); + + if (inode && !(inode->i_state & I_NEW)) { + /* parent inode available */ + acxt->parent_inode = inode; + + /* sysfs_mutex is below i_mutex in lock hierarchy. + * First, trylock i_mutex. If fails, unlock + * sysfs_mutex and lock them in order. + */ + if (!mutex_trylock(&inode->i_mutex)) { + mutex_unlock(&sysfs_mutex); + mutex_lock(&inode->i_mutex); + mutex_lock(&sysfs_mutex); + } + } else + iput(inode); } +/** + * sysfs_add_one - add sysfs_dirent to parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Get @acxt->parent_sd and set sd->s_parent to it and increment + * nlink of parent inode if @sd is a directory. @sd is NOT + * linked into the children list of the parent. The caller + * should invoke sysfs_link_sibling() after this function + * completes if @sd needs to be on the children list. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + */ +void sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +{ + sd->s_parent = sysfs_get(acxt->parent_sd); + + if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) + inc_nlink(acxt->parent_inode); -static struct sysfs_dirent * -__sysfs_make_dirent(struct dentry *dentry, void *element, mode_t mode, int type) + acxt->cnt++; +} + +/** + * sysfs_remove_one - remove sysfs_dirent from parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Mark @sd removed and drop nlink of parent inode if @sd is a + * directory. @sd is NOT unlinked from the children list of the + * parent. The caller is repsonsible for removing @sd from the + * children list before calling this function. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + */ +void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { - struct sysfs_dirent * sd; + BUG_ON(sd->s_sibling || (sd->s_flags & SYSFS_FLAG_REMOVED)); - sd = __sysfs_new_dirent(element); - if (!sd) - goto out; + sd->s_flags |= SYSFS_FLAG_REMOVED; + sd->s_sibling = acxt->removed; + acxt->removed = sd; - sd->s_mode = mode; - sd->s_type = type; - sd->s_dentry = dentry; - if (dentry) { - dentry->d_fsdata = sysfs_get(sd); - dentry->d_op = &sysfs_dentry_ops; - } + if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) + drop_nlink(acxt->parent_inode); -out: - return sd; + acxt->cnt++; } -int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, - void * element, umode_t mode, int type) +/** + * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent + * @sd: target sysfs_dirent + * + * Drop dentry for @sd. @sd must have been unlinked from its + * parent on entry to this function such that it can't be looked + * up anymore. + * + * @sd->s_dentry which is protected with sysfs_assoc_lock points + * to the currently associated dentry but we're not holding a + * reference to it and racing with dput(). Grab dcache_lock and + * verify dentry before dropping it. If @sd->s_dentry is NULL or + * dput() beats us, no need to bother. + */ +static void sysfs_drop_dentry(struct sysfs_dirent *sd) { - struct sysfs_dirent *sd; + struct dentry *dentry = NULL; + struct inode *inode; - sd = __sysfs_make_dirent(dentry, element, mode, type); - __sysfs_list_dirent(parent_sd, sd); + /* We're not holding a reference to ->s_dentry dentry but the + * field will stay valid as long as sysfs_assoc_lock is held. + */ + spin_lock(&sysfs_assoc_lock); + spin_lock(&dcache_lock); + + /* drop dentry if it's there and dput() didn't kill it yet */ + if (sd->s_dentry && sd->s_dentry->d_inode) { + dentry = dget_locked(sd->s_dentry); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); + } - return sd ? 0 : -ENOMEM; + spin_unlock(&dcache_lock); + spin_unlock(&sysfs_assoc_lock); + + /* dentries for shadowed inodes are pinned, unpin */ + if (dentry && sysfs_is_shadowed_inode(dentry->d_inode)) + dput(dentry); + dput(dentry); + + /* adjust nlink and update timestamp */ + inode = ilookup(sysfs_sb, sd->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); + + inode->i_ctime = CURRENT_TIME; + drop_nlink(inode); + if (sysfs_type(sd) == SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); + } } -static int init_dir(struct inode * inode) +/** + * sysfs_addrm_finish - finish up sysfs_dirent add/remove + * @acxt: addrm context to finish up + * + * Finish up sysfs_dirent add/remove. Resources acquired by + * sysfs_addrm_start() are released and removed sysfs_dirents are + * cleaned up. Timestamps on the parent inode are updated. + * + * LOCKING: + * All mutexes acquired by sysfs_addrm_start() are released. + * + * RETURNS: + * Number of added/removed sysfs_dirents since sysfs_addrm_start(). + */ +int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) { - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; + /* release resources acquired by sysfs_addrm_start() */ + mutex_unlock(&sysfs_mutex); + if (acxt->parent_inode) { + struct inode *inode = acxt->parent_inode; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - return 0; + /* if added/removed, update timestamps on the parent */ + if (acxt->cnt) + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + + mutex_unlock(&inode->i_mutex); + iput(inode); + } + + /* kill removed sysfs_dirents */ + while (acxt->removed) { + struct sysfs_dirent *sd = acxt->removed; + + acxt->removed = sd->s_sibling; + sd->s_sibling = NULL; + + sysfs_drop_dentry(sd); + sysfs_deactivate(sd); + sysfs_put(sd); + } + + return acxt->cnt; } -static int init_file(struct inode * inode) +/** + * sysfs_find_dirent - find sysfs_dirent with the given name + * @parent_sd: sysfs_dirent to search under + * @name: name to look for + * + * Look for sysfs_dirent with name @name under @parent_sd. + * + * LOCKING: + * mutex_lock(sysfs_mutex) + * + * RETURNS: + * Pointer to sysfs_dirent if found, NULL if not. + */ +struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name) { - inode->i_size = PAGE_SIZE; - inode->i_fop = &sysfs_file_operations; - return 0; + struct sysfs_dirent *sd; + + for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) + if (sysfs_type(sd) && !strcmp(sd->s_name, name)) + return sd; + return NULL; } -static int init_symlink(struct inode * inode) +/** + * sysfs_get_dirent - find and get sysfs_dirent with the given name + * @parent_sd: sysfs_dirent to search under + * @name: name to look for + * + * Look for sysfs_dirent with name @name under @parent_sd and get + * it if found. + * + * LOCKING: + * Kernel thread context (may sleep). Grabs sysfs_mutex. + * + * RETURNS: + * Pointer to sysfs_dirent if found, NULL if not. + */ +struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name) { - inode->i_op = &sysfs_symlink_inode_operations; - return 0; + struct sysfs_dirent *sd; + + mutex_lock(&sysfs_mutex); + sd = sysfs_find_dirent(parent_sd, name); + sysfs_get(sd); + mutex_unlock(&sysfs_mutex); + + return sd; } -static int create_dir(struct kobject * k, struct dentry * p, - const char * n, struct dentry ** d) +static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, + const char *name, struct sysfs_dirent **p_sd) { - int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; - mutex_lock(&p->d_inode->i_mutex); - *d = lookup_one_len(n, p, strlen(n)); - if (!IS_ERR(*d)) { - if (sysfs_dirent_exist(p->d_fsdata, n)) - error = -EEXIST; - else - error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, - SYSFS_DIR); - if (!error) { - error = sysfs_create(*d, mode, init_dir); - if (!error) { - inc_nlink(p->d_inode); - (*d)->d_op = &sysfs_dentry_ops; - d_rehash(*d); - } - } - if (error && (error != -EEXIST)) { - struct sysfs_dirent *sd = (*d)->d_fsdata; - if (sd) { - list_del_init(&sd->s_sibling); - sysfs_put(sd); - } - d_drop(*d); - } - dput(*d); - } else - error = PTR_ERR(*d); - mutex_unlock(&p->d_inode->i_mutex); - return error; -} + /* allocate */ + sd = sysfs_new_dirent(name, mode, SYSFS_DIR); + if (!sd) + return -ENOMEM; + sd->s_elem.dir.kobj = kobj; + /* link in */ + sysfs_addrm_start(&acxt, parent_sd); -int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) + if (!sysfs_find_dirent(parent_sd, name)) { + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); + } + + if (!sysfs_addrm_finish(&acxt)) { + sysfs_put(sd); + return -EEXIST; + } + + *p_sd = sd; + return 0; +} + +int sysfs_create_subdir(struct kobject *kobj, const char *name, + struct sysfs_dirent **p_sd) { - return create_dir(k,k->dentry,n,d); + return create_dir(kobj, kobj->sd, name, p_sd); } /** * sysfs_create_dir - create a directory for an object. * @kobj: object we're creating directory for. - * @shadow_parent: parent parent object. + * @shadow_parent: parent object. */ - -int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) +int sysfs_create_dir(struct kobject *kobj, + struct sysfs_dirent *shadow_parent_sd) { - struct dentry * dentry = NULL; - struct dentry * parent; + struct sysfs_dirent *parent_sd, *sd; int error = 0; BUG_ON(!kobj); - if (shadow_parent) - parent = shadow_parent; + if (shadow_parent_sd) + parent_sd = shadow_parent_sd; else if (kobj->parent) - parent = kobj->parent->dentry; + parent_sd = kobj->parent->sd; else if (sysfs_mount && sysfs_mount->mnt_sb) - parent = sysfs_mount->mnt_sb->s_root; + parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; else return -EFAULT; - error = create_dir(kobj,parent,kobject_name(kobj),&dentry); + error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); if (!error) - kobj->dentry = dentry; + kobj->sd = sd; return error; } -/* attaches attribute's sysfs_dirent to the dentry corresponding to the - * attribute file - */ -static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) +static int sysfs_count_nlink(struct sysfs_dirent *sd) { - struct attribute * attr = NULL; - struct bin_attribute * bin_attr = NULL; - int (* init) (struct inode *) = NULL; - int error = 0; - - if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { - bin_attr = sd->s_element; - attr = &bin_attr->attr; - } else { - attr = sd->s_element; - init = init_file; - } - - dentry->d_fsdata = sysfs_get(sd); - /* protect sd->s_dentry against sysfs_d_iput */ - spin_lock(&sysfs_lock); - sd->s_dentry = dentry; - spin_unlock(&sysfs_lock); - error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); - if (error) { - sysfs_put(sd); - return error; - } - - if (bin_attr) { - dentry->d_inode->i_size = bin_attr->size; - dentry->d_inode->i_fop = &bin_fops; - } - dentry->d_op = &sysfs_dentry_ops; - d_rehash(dentry); - - return 0; -} - -static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) -{ - int err = 0; - - dentry->d_fsdata = sysfs_get(sd); - /* protect sd->s_dentry against sysfs_d_iput */ - spin_lock(&sysfs_lock); - sd->s_dentry = dentry; - spin_unlock(&sysfs_lock); - err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink); - if (!err) { - dentry->d_op = &sysfs_dentry_ops; - d_rehash(dentry); - } else - sysfs_put(sd); + struct sysfs_dirent *child; + int nr = 0; - return err; + for (child = sd->s_children; child; child = child->s_sibling) + if (sysfs_type(child) == SYSFS_DIR) + nr++; + return nr + 2; } static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, @@ -303,24 +764,60 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, { struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; struct sysfs_dirent * sd; - int err = 0; + struct bin_attribute *bin_attr; + struct inode *inode; + int found = 0; - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (sd->s_type & SYSFS_NOT_PINNED) { - const unsigned char * name = sysfs_get_name(sd); + for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { + if (sysfs_type(sd) && + !strcmp(sd->s_name, dentry->d_name.name)) { + found = 1; + break; + } + } - if (strcmp(name, dentry->d_name.name)) - continue; + /* no such entry */ + if (!found) + return NULL; - if (sd->s_type & SYSFS_KOBJ_LINK) - err = sysfs_attach_link(sd, dentry); - else - err = sysfs_attach_attr(sd, dentry); + /* attach dentry and inode */ + inode = sysfs_get_inode(sd); + if (!inode) + return ERR_PTR(-ENOMEM); + + mutex_lock(&sysfs_mutex); + + if (inode->i_state & I_NEW) { + /* initialize inode according to type */ + switch (sysfs_type(sd)) { + case SYSFS_DIR: + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + inode->i_nlink = sysfs_count_nlink(sd); + break; + case SYSFS_KOBJ_ATTR: + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; break; + case SYSFS_KOBJ_BIN_ATTR: + bin_attr = sd->s_elem.bin_attr.bin_attr; + inode->i_size = bin_attr->size; + inode->i_fop = &bin_fops; + break; + case SYSFS_KOBJ_LINK: + inode->i_op = &sysfs_symlink_inode_operations; + break; + default: + BUG(); } } - return ERR_PTR(err); + sysfs_instantiate(dentry, inode); + sysfs_attach_dentry(sd, dentry); + + mutex_unlock(&sysfs_mutex); + + return NULL; } const struct inode_operations sysfs_dir_inode_operations = { @@ -328,58 +825,46 @@ const struct inode_operations sysfs_dir_inode_operations = { .setattr = sysfs_setattr, }; -static void remove_dir(struct dentry * d) +static void remove_dir(struct sysfs_dirent *sd) { - struct dentry * parent = dget(d->d_parent); - struct sysfs_dirent * sd; + struct sysfs_addrm_cxt acxt; - mutex_lock(&parent->d_inode->i_mutex); - d_delete(d); - sd = d->d_fsdata; - list_del_init(&sd->s_sibling); - sysfs_put(sd); - if (d->d_inode) - simple_rmdir(parent->d_inode,d); - - pr_debug(" o %s removing done (%d)\n",d->d_name.name, - atomic_read(&d->d_count)); - - mutex_unlock(&parent->d_inode->i_mutex); - dput(parent); + sysfs_addrm_start(&acxt, sd->s_parent); + sysfs_unlink_sibling(sd); + sysfs_remove_one(&acxt, sd); + sysfs_addrm_finish(&acxt); } -void sysfs_remove_subdir(struct dentry * d) +void sysfs_remove_subdir(struct sysfs_dirent *sd) { - remove_dir(d); + remove_dir(sd); } -static void __sysfs_remove_dir(struct dentry *dentry) +static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) { - struct sysfs_dirent * parent_sd; - struct sysfs_dirent * sd, * tmp; + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent **pos; - dget(dentry); - if (!dentry) + if (!dir_sd) return; - pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); - mutex_lock(&dentry->d_inode->i_mutex); - parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED)) - continue; - list_del_init(&sd->s_sibling); - sysfs_drop_dentry(sd, dentry); - sysfs_put(sd); + pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); + sysfs_addrm_start(&acxt, dir_sd); + pos = &dir_sd->s_children; + while (*pos) { + struct sysfs_dirent *sd = *pos; + + if (sysfs_type(sd) && sysfs_type(sd) != SYSFS_DIR) { + *pos = sd->s_sibling; + sd->s_sibling = NULL; + sysfs_remove_one(&acxt, sd); + } else + pos = &(*pos)->s_sibling; } - mutex_unlock(&dentry->d_inode->i_mutex); + sysfs_addrm_finish(&acxt); - remove_dir(dentry); - /** - * Drop reference from dget() on entrance. - */ - dput(dentry); + remove_dir(dir_sd); } /** @@ -393,102 +878,166 @@ static void __sysfs_remove_dir(struct dentry *dentry) void sysfs_remove_dir(struct kobject * kobj) { - __sysfs_remove_dir(kobj->dentry); - kobj->dentry = NULL; + struct sysfs_dirent *sd = kobj->sd; + + spin_lock(&sysfs_assoc_lock); + kobj->sd = NULL; + spin_unlock(&sysfs_assoc_lock); + + __sysfs_remove_dir(sd); } -int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, +int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, const char *new_name) { - int error = 0; - struct dentry * new_dentry; + struct sysfs_dirent *sd = kobj->sd; + struct dentry *new_parent = NULL; + struct dentry *old_dentry = NULL, *new_dentry = NULL; + const char *dup_name = NULL; + int error; - if (!new_parent) - return -EFAULT; + /* get dentries */ + old_dentry = sysfs_get_dentry(sd); + if (IS_ERR(old_dentry)) { + error = PTR_ERR(old_dentry); + goto out_dput; + } - down_write(&sysfs_rename_sem); + new_parent = sysfs_get_dentry(new_parent_sd); + if (IS_ERR(new_parent)) { + error = PTR_ERR(new_parent); + goto out_dput; + } + + /* lock new_parent and get dentry for new name */ mutex_lock(&new_parent->d_inode->i_mutex); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); - if (!IS_ERR(new_dentry)) { - /* By allowing two different directories with the - * same d_parent we allow this routine to move - * between different shadows of the same directory - */ - if (kobj->dentry->d_parent->d_inode != new_parent->d_inode) - return -EINVAL; - else if (new_dentry->d_parent->d_inode != new_parent->d_inode) - error = -EINVAL; - else if (new_dentry == kobj->dentry) - error = -EINVAL; - else if (!new_dentry->d_inode) { - error = kobject_set_name(kobj, "%s", new_name); - if (!error) { - struct sysfs_dirent *sd, *parent_sd; - - d_add(new_dentry, NULL); - d_move(kobj->dentry, new_dentry); - - sd = kobj->dentry->d_fsdata; - parent_sd = new_parent->d_fsdata; - - list_del_init(&sd->s_sibling); - list_add(&sd->s_sibling, &parent_sd->s_children); - } - else - d_drop(new_dentry); - } else - error = -EEXIST; - dput(new_dentry); + if (IS_ERR(new_dentry)) { + error = PTR_ERR(new_dentry); + goto out_unlock; } - mutex_unlock(&new_parent->d_inode->i_mutex); - up_write(&sysfs_rename_sem); + /* By allowing two different directories with the same + * d_parent we allow this routine to move between different + * shadows of the same directory + */ + error = -EINVAL; + if (old_dentry->d_parent->d_inode != new_parent->d_inode || + new_dentry->d_parent->d_inode != new_parent->d_inode || + old_dentry == new_dentry) + goto out_unlock; + + error = -EEXIST; + if (new_dentry->d_inode) + goto out_unlock; + + /* rename kobject and sysfs_dirent */ + error = -ENOMEM; + new_name = dup_name = kstrdup(new_name, GFP_KERNEL); + if (!new_name) + goto out_drop; + + error = kobject_set_name(kobj, "%s", new_name); + if (error) + goto out_drop; + + dup_name = sd->s_name; + sd->s_name = new_name; + + /* move under the new parent */ + d_add(new_dentry, NULL); + d_move(sd->s_dentry, new_dentry); + + mutex_lock(&sysfs_mutex); + + sysfs_unlink_sibling(sd); + sysfs_get(new_parent_sd); + sysfs_put(sd->s_parent); + sd->s_parent = new_parent_sd; + sysfs_link_sibling(sd); + + mutex_unlock(&sysfs_mutex); + + error = 0; + goto out_unlock; + + out_drop: + d_drop(new_dentry); + out_unlock: + mutex_unlock(&new_parent->d_inode->i_mutex); + out_dput: + kfree(dup_name); + dput(new_parent); + dput(old_dentry); + dput(new_dentry); return error; } -int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) +int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) { - struct dentry *old_parent_dentry, *new_parent_dentry, *new_dentry; - struct sysfs_dirent *new_parent_sd, *sd; + struct sysfs_dirent *sd = kobj->sd; + struct sysfs_dirent *new_parent_sd; + struct dentry *old_parent, *new_parent = NULL; + struct dentry *old_dentry = NULL, *new_dentry = NULL; int error; - old_parent_dentry = kobj->parent ? - kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; - new_parent_dentry = new_parent ? - new_parent->dentry : sysfs_mount->mnt_sb->s_root; + BUG_ON(!sd->s_parent); + new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; + + /* get dentries */ + old_dentry = sysfs_get_dentry(sd); + if (IS_ERR(old_dentry)) { + error = PTR_ERR(old_dentry); + goto out_dput; + } + old_parent = sd->s_parent->s_dentry; + + new_parent = sysfs_get_dentry(new_parent_sd); + if (IS_ERR(new_parent)) { + error = PTR_ERR(new_parent); + goto out_dput; + } - if (old_parent_dentry->d_inode == new_parent_dentry->d_inode) - return 0; /* nothing to move */ + if (old_parent->d_inode == new_parent->d_inode) { + error = 0; + goto out_dput; /* nothing to move */ + } again: - mutex_lock(&old_parent_dentry->d_inode->i_mutex); - if (!mutex_trylock(&new_parent_dentry->d_inode->i_mutex)) { - mutex_unlock(&old_parent_dentry->d_inode->i_mutex); + mutex_lock(&old_parent->d_inode->i_mutex); + if (!mutex_trylock(&new_parent->d_inode->i_mutex)) { + mutex_unlock(&old_parent->d_inode->i_mutex); goto again; } - new_parent_sd = new_parent_dentry->d_fsdata; - sd = kobj->dentry->d_fsdata; - - new_dentry = lookup_one_len(kobj->name, new_parent_dentry, - strlen(kobj->name)); + new_dentry = lookup_one_len(kobj->name, new_parent, strlen(kobj->name)); if (IS_ERR(new_dentry)) { error = PTR_ERR(new_dentry); - goto out; + goto out_unlock; } else error = 0; d_add(new_dentry, NULL); - d_move(kobj->dentry, new_dentry); + d_move(sd->s_dentry, new_dentry); dput(new_dentry); /* Remove from old parent's list and insert into new parent's list. */ - list_del_init(&sd->s_sibling); - list_add(&sd->s_sibling, &new_parent_sd->s_children); + mutex_lock(&sysfs_mutex); + + sysfs_unlink_sibling(sd); + sysfs_get(new_parent_sd); + sysfs_put(sd->s_parent); + sd->s_parent = new_parent_sd; + sysfs_link_sibling(sd); -out: - mutex_unlock(&new_parent_dentry->d_inode->i_mutex); - mutex_unlock(&old_parent_dentry->d_inode->i_mutex); + mutex_unlock(&sysfs_mutex); + out_unlock: + mutex_unlock(&new_parent->d_inode->i_mutex); + mutex_unlock(&old_parent->d_inode->i_mutex); + out_dput: + dput(new_parent); + dput(old_dentry); + dput(new_dentry); return error; } @@ -496,23 +1045,27 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) { struct dentry * dentry = file->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; + struct sysfs_dirent * sd; - mutex_lock(&dentry->d_inode->i_mutex); - file->private_data = sysfs_new_dirent(parent_sd, NULL); - mutex_unlock(&dentry->d_inode->i_mutex); - - return file->private_data ? 0 : -ENOMEM; + sd = sysfs_new_dirent("_DIR_", 0, 0); + if (sd) { + mutex_lock(&sysfs_mutex); + sd->s_parent = sysfs_get(parent_sd); + sysfs_link_sibling(sd); + mutex_unlock(&sysfs_mutex); + } + file->private_data = sd; + return sd ? 0 : -ENOMEM; } static int sysfs_dir_close(struct inode *inode, struct file *file) { - struct dentry * dentry = file->f_path.dentry; struct sysfs_dirent * cursor = file->private_data; - mutex_lock(&dentry->d_inode->i_mutex); - list_del_init(&cursor->s_sibling); - mutex_unlock(&dentry->d_inode->i_mutex); + mutex_lock(&sysfs_mutex); + sysfs_unlink_sibling(cursor); + mutex_unlock(&sysfs_mutex); release_sysfs_dirent(cursor); @@ -530,7 +1083,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) struct dentry *dentry = filp->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent *cursor = filp->private_data; - struct list_head *p, *q = &cursor->s_sibling; + struct sysfs_dirent **pos; ino_t ino; int i = filp->f_pos; @@ -543,38 +1096,52 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) i++; /* fallthrough */ case 1: - ino = parent_ino(dentry); + if (parent_sd->s_parent) + ino = parent_sd->s_parent->s_ino; + else + ino = parent_sd->s_ino; if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) break; filp->f_pos++; i++; /* fallthrough */ default: + mutex_lock(&sysfs_mutex); + + pos = &parent_sd->s_children; + while (*pos != cursor) + pos = &(*pos)->s_sibling; + + /* unlink cursor */ + *pos = cursor->s_sibling; + if (filp->f_pos == 2) - list_move(q, &parent_sd->s_children); + pos = &parent_sd->s_children; - for (p=q->next; p!= &parent_sd->s_children; p=p->next) { - struct sysfs_dirent *next; + for ( ; *pos; pos = &(*pos)->s_sibling) { + struct sysfs_dirent *next = *pos; const char * name; int len; - next = list_entry(p, struct sysfs_dirent, - s_sibling); - if (!next->s_element) + if (!sysfs_type(next)) continue; - name = sysfs_get_name(next); + name = next->s_name; len = strlen(name); ino = next->s_ino; if (filldir(dirent, name, len, filp->f_pos, ino, dt_type(next)) < 0) - return 0; + break; - list_move(q, p); - p = q; filp->f_pos++; } + + /* put cursor back in */ + cursor->s_sibling = *pos; + *pos = cursor; + + mutex_unlock(&sysfs_mutex); } return 0; } @@ -583,7 +1150,6 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) { struct dentry * dentry = file->f_path.dentry; - mutex_lock(&dentry->d_inode->i_mutex); switch (origin) { case 1: offset += file->f_pos; @@ -591,31 +1157,35 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) if (offset >= 0) break; default: - mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); return -EINVAL; } if (offset != file->f_pos) { + mutex_lock(&sysfs_mutex); + file->f_pos = offset; if (file->f_pos >= 2) { struct sysfs_dirent *sd = dentry->d_fsdata; struct sysfs_dirent *cursor = file->private_data; - struct list_head *p; + struct sysfs_dirent **pos; loff_t n = file->f_pos - 2; - list_del(&cursor->s_sibling); - p = sd->s_children.next; - while (n && p != &sd->s_children) { - struct sysfs_dirent *next; - next = list_entry(p, struct sysfs_dirent, - s_sibling); - if (next->s_element) + sysfs_unlink_sibling(cursor); + + pos = &sd->s_children; + while (n && *pos) { + struct sysfs_dirent *next = *pos; + if (sysfs_type(next)) n--; - p = p->next; + pos = &(*pos)->s_sibling; } - list_add_tail(&cursor->s_sibling, p); + + cursor->s_sibling = *pos; + *pos = cursor; } + + mutex_unlock(&sysfs_mutex); } - mutex_unlock(&dentry->d_inode->i_mutex); + return offset; } @@ -628,12 +1198,20 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) int sysfs_make_shadowed_dir(struct kobject *kobj, void * (*follow_link)(struct dentry *, struct nameidata *)) { + struct dentry *dentry; struct inode *inode; struct inode_operations *i_op; - inode = kobj->dentry->d_inode; - if (inode->i_op != &sysfs_dir_inode_operations) + /* get dentry for @kobj->sd, dentry of a shadowed dir is pinned */ + dentry = sysfs_get_dentry(kobj->sd); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + inode = dentry->d_inode; + if (inode->i_op != &sysfs_dir_inode_operations) { + dput(dentry); return -EINVAL; + } i_op = kmalloc(sizeof(*i_op), GFP_KERNEL); if (!i_op) @@ -658,54 +1236,72 @@ int sysfs_make_shadowed_dir(struct kobject *kobj, * directory. */ -struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) +struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) { - struct sysfs_dirent *sd; - struct dentry *parent, *dir, *shadow; + struct sysfs_dirent *parent_sd = kobj->sd->s_parent; + struct dentry *dir, *parent, *shadow; struct inode *inode; + struct sysfs_dirent *sd; + struct sysfs_addrm_cxt acxt; - dir = kobj->dentry; - inode = dir->d_inode; + dir = sysfs_get_dentry(kobj->sd); + if (IS_ERR(dir)) { + sd = (void *)dir; + goto out; + } parent = dir->d_parent; - shadow = ERR_PTR(-EINVAL); + + inode = dir->d_inode; + sd = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) - goto out; + goto out_dput; shadow = d_alloc(parent, &dir->d_name); if (!shadow) goto nomem; - sd = __sysfs_make_dirent(shadow, kobj, inode->i_mode, SYSFS_DIR); + sd = sysfs_new_dirent("_SHADOW_", inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; + sd->s_elem.dir.kobj = kobj; + sysfs_addrm_start(&acxt, parent_sd); + + /* add but don't link into children list */ + sysfs_add_one(&acxt, sd); + + /* attach and instantiate dentry */ + sysfs_attach_dentry(sd, shadow); d_instantiate(shadow, igrab(inode)); - inc_nlink(inode); - inc_nlink(parent->d_inode); - shadow->d_op = &sysfs_dentry_ops; + inc_nlink(inode); /* tj: synchronization? */ + + sysfs_addrm_finish(&acxt); dget(shadow); /* Extra count - pin the dentry in core */ -out: - return shadow; -nomem: + goto out_dput; + + nomem: dput(shadow); - shadow = ERR_PTR(-ENOMEM); - goto out; + sd = ERR_PTR(-ENOMEM); + out_dput: + dput(dir); + out: + return sd; } /** * sysfs_remove_shadow_dir - remove an object's directory. - * @shadow: dentry of shadow directory + * @shadow_sd: sysfs_dirent of shadow directory * * The only thing special about this is that we remove any files in * the directory before we remove the directory, and we've inlined * what used to be sysfs_rmdir() below, instead of calling separately. */ -void sysfs_remove_shadow_dir(struct dentry *shadow) +void sysfs_remove_shadow_dir(struct sysfs_dirent *shadow_sd) { - __sysfs_remove_dir(shadow); + __sysfs_remove_dir(shadow_sd); } const struct file_operations sysfs_dir_operations = { diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b502c71..3e1cc06 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -50,29 +50,15 @@ static struct sysfs_ops subsys_sysfs_ops = { .store = subsys_attr_store, }; -/** - * add_to_collection - add buffer to a collection - * @buffer: buffer to be added - * @node: inode of set to add to - */ - -static inline void -add_to_collection(struct sysfs_buffer *buffer, struct inode *node) -{ - struct sysfs_buffer_collection *set = node->i_private; - - mutex_lock(&node->i_mutex); - list_add(&buffer->associates, &set->associates); - mutex_unlock(&node->i_mutex); -} - -static inline void -remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) -{ - mutex_lock(&node->i_mutex); - list_del(&buffer->associates); - mutex_unlock(&node->i_mutex); -} +struct sysfs_buffer { + size_t count; + loff_t pos; + char * page; + struct sysfs_ops * ops; + struct semaphore sem; + int needs_read_fill; + int event; +}; /** * fill_read_buffer - allocate and fill buffer from object. @@ -87,9 +73,8 @@ remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { - struct sysfs_dirent * sd = dentry->d_fsdata; - struct attribute * attr = to_attr(dentry); - struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; @@ -99,8 +84,15 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer if (!buffer->page) return -ENOMEM; - buffer->event = atomic_read(&sd->s_event); - count = ops->show(kobj,attr,buffer->page); + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + buffer->event = atomic_read(&attr_sd->s_event); + count = ops->show(kobj, attr_sd->s_elem.attr.attr, buffer->page); + + sysfs_put_active_two(attr_sd); + BUG_ON(count > (ssize_t)PAGE_SIZE); if (count >= 0) { buffer->needs_read_fill = 0; @@ -138,10 +130,7 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) down(&buffer->sem); if (buffer->needs_read_fill) { - if (buffer->orphaned) - retval = -ENODEV; - else - retval = fill_read_buffer(file->f_path.dentry,buffer); + retval = fill_read_buffer(file->f_path.dentry,buffer); if (retval) goto out; } @@ -196,14 +185,23 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t * passing the buffer that we acquired in fill_write_buffer(). */ -static int +static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { - struct attribute * attr = to_attr(dentry); - struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; + int rc; + + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + rc = ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); + + sysfs_put_active_two(attr_sd); - return ops->store(kobj,attr,buffer->page,count); + return rc; } @@ -231,37 +229,26 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t ssize_t len; down(&buffer->sem); - if (buffer->orphaned) { - len = -ENODEV; - goto out; - } len = fill_write_buffer(buffer, buf, count); if (len > 0) len = flush_write_buffer(file->f_path.dentry, buffer, len); if (len > 0) *ppos += len; -out: up(&buffer->sem); return len; } static int sysfs_open_file(struct inode *inode, struct file *file) { - struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); - struct attribute * attr = to_attr(file->f_path.dentry); - struct sysfs_buffer_collection *set; + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; - int error = 0; - - if (!kobj || !attr) - goto Einval; + int error; - /* Grab the module reference for this attribute if we have one */ - if (!try_module_get(attr->owner)) { - error = -ENODEV; - goto Done; - } + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; /* if the kobject has no ktype, then we assume that it is a subsystem * itself, and use ops for it. @@ -273,33 +260,21 @@ static int sysfs_open_file(struct inode *inode, struct file *file) else ops = &subsys_sysfs_ops; + error = -EACCES; + /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ if (!ops) - goto Eaccess; - - /* make sure we have a collection to add our buffers to */ - mutex_lock(&inode->i_mutex); - if (!(set = inode->i_private)) { - if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) { - error = -ENOMEM; - goto Done; - } else { - INIT_LIST_HEAD(&set->associates); - } - } - mutex_unlock(&inode->i_mutex); + goto err_out; /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { - if (!(inode->i_mode & S_IWUGO) || !ops->store) - goto Eaccess; - + goto err_out; } /* File needs read support. @@ -308,48 +283,38 @@ static int sysfs_open_file(struct inode *inode, struct file *file) */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) - goto Eaccess; + goto err_out; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ + error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); - if (buffer) { - INIT_LIST_HEAD(&buffer->associates); - init_MUTEX(&buffer->sem); - buffer->needs_read_fill = 1; - buffer->ops = ops; - add_to_collection(buffer, inode); - file->private_data = buffer; - } else - error = -ENOMEM; - goto Done; - - Einval: - error = -EINVAL; - goto Done; - Eaccess: - error = -EACCES; - module_put(attr->owner); - Done: - if (error) - kobject_put(kobj); + if (!buffer) + goto err_out; + + init_MUTEX(&buffer->sem); + buffer->needs_read_fill = 1; + buffer->ops = ops; + file->private_data = buffer; + + /* open succeeded, put active references and pin attr_sd */ + sysfs_put_active_two(attr_sd); + sysfs_get(attr_sd); + return 0; + + err_out: + sysfs_put_active_two(attr_sd); return error; } static int sysfs_release(struct inode * inode, struct file * filp) { - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); - struct attribute * attr = to_attr(filp->f_path.dentry); - struct module * owner = attr->owner; - struct sysfs_buffer * buffer = filp->private_data; + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct sysfs_buffer *buffer = filp->private_data; - if (buffer) - remove_from_collection(buffer, inode); - kobject_put(kobj); - /* After this point, attr should not be accessed. */ - module_put(owner); + sysfs_put(attr_sd); if (buffer) { if (buffer->page) @@ -376,57 +341,43 @@ static int sysfs_release(struct inode * inode, struct file * filp) static unsigned int sysfs_poll(struct file *filp, poll_table *wait) { struct sysfs_buffer * buffer = filp->private_data; - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); - struct sysfs_dirent * sd = filp->f_path.dentry->d_fsdata; - int res = 0; + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + + /* need parent for the kobj, grab both */ + if (!sysfs_get_active_two(attr_sd)) + goto trigger; poll_wait(filp, &kobj->poll, wait); - if (buffer->event != atomic_read(&sd->s_event)) { - res = POLLERR|POLLPRI; - buffer->needs_read_fill = 1; - } + sysfs_put_active_two(attr_sd); - return res; -} + if (buffer->event != atomic_read(&attr_sd->s_event)) + goto trigger; + return 0; -static struct dentry *step_down(struct dentry *dir, const char * name) -{ - struct dentry * de; - - if (dir == NULL || dir->d_inode == NULL) - return NULL; - - mutex_lock(&dir->d_inode->i_mutex); - de = lookup_one_len(name, dir, strlen(name)); - mutex_unlock(&dir->d_inode->i_mutex); - dput(dir); - if (IS_ERR(de)) - return NULL; - if (de->d_inode == NULL) { - dput(de); - return NULL; - } - return de; + trigger: + buffer->needs_read_fill = 1; + return POLLERR|POLLPRI; } -void sysfs_notify(struct kobject * k, char *dir, char *attr) +void sysfs_notify(struct kobject *k, char *dir, char *attr) { - struct dentry *de = k->dentry; - if (de) - dget(de); - if (de && dir) - de = step_down(de, dir); - if (de && attr) - de = step_down(de, attr); - if (de) { - struct sysfs_dirent * sd = de->d_fsdata; - if (sd) - atomic_inc(&sd->s_event); + struct sysfs_dirent *sd = k->sd; + + mutex_lock(&sysfs_mutex); + + if (sd && dir) + sd = sysfs_find_dirent(sd, dir); + if (sd && attr) + sd = sysfs_find_dirent(sd, attr); + if (sd) { + atomic_inc(&sd->s_event); wake_up_interruptible(&k->poll); - dput(de); } + + mutex_unlock(&sysfs_mutex); } EXPORT_SYMBOL_GPL(sysfs_notify); @@ -440,19 +391,31 @@ const struct file_operations sysfs_file_operations = { }; -int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) +int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, + int type) { - struct sysfs_dirent * parent_sd = dir->d_fsdata; umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; - int error = -EEXIST; + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; + + sd = sysfs_new_dirent(attr->name, mode, type); + if (!sd) + return -ENOMEM; + sd->s_elem.attr.attr = (void *)attr; - mutex_lock(&dir->d_inode->i_mutex); - if (!sysfs_dirent_exist(parent_sd, attr->name)) - error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, - mode, type); - mutex_unlock(&dir->d_inode->i_mutex); + sysfs_addrm_start(&acxt, dir_sd); - return error; + if (!sysfs_find_dirent(dir_sd, attr->name)) { + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); + } + + if (!sysfs_addrm_finish(&acxt)) { + sysfs_put(sd); + return -EEXIST; + } + + return 0; } @@ -464,9 +427,9 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) { - BUG_ON(!kobj || !kobj->dentry || !attr); + BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR); + return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); } @@ -480,16 +443,16 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct dentry *dir; + struct sysfs_dirent *dir_sd; int error; - dir = lookup_one_len(group, kobj->dentry, strlen(group)); - if (IS_ERR(dir)) - error = PTR_ERR(dir); - else { - error = sysfs_add_file(dir, attr, SYSFS_KOBJ_ATTR); - dput(dir); - } + dir_sd = sysfs_get_dirent(kobj->sd, group); + if (!dir_sd) + return -ENOENT; + + error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); + sysfs_put(dir_sd); + return error; } EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); @@ -502,30 +465,31 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { - struct dentry * dir = kobj->dentry; - struct dentry * victim; - int res = -ENOENT; - - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - /* make sure dentry is really there */ - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - victim->d_inode->i_mtime = CURRENT_TIME; - fsnotify_modify(victim); - res = 0; - } else - d_drop(victim); - - /** - * Drop the reference acquired from lookup_one_len() above. - */ - dput(victim); + struct sysfs_dirent *victim_sd = NULL; + struct dentry *victim = NULL; + int rc; + + rc = -ENOENT; + victim_sd = sysfs_get_dirent(kobj->sd, attr->name); + if (!victim_sd) + goto out; + + victim = sysfs_get_dentry(victim_sd); + if (IS_ERR(victim)) { + rc = PTR_ERR(victim); + victim = NULL; + goto out; } - mutex_unlock(&dir->d_inode->i_mutex); - return res; + mutex_lock(&victim->d_inode->i_mutex); + victim->d_inode->i_mtime = CURRENT_TIME; + fsnotify_modify(victim); + mutex_unlock(&victim->d_inode->i_mutex); + rc = 0; + out: + dput(victim); + sysfs_put(victim_sd); + return rc; } @@ -538,30 +502,34 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { - struct dentry *dir = kobj->dentry; - struct dentry *victim; + struct sysfs_dirent *victim_sd = NULL; + struct dentry *victim = NULL; struct inode * inode; struct iattr newattrs; - int res = -ENOENT; - - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - inode = victim->d_inode; - mutex_lock(&inode->i_mutex); - newattrs.ia_mode = (mode & S_IALLUGO) | - (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - res = notify_change(victim, &newattrs); - mutex_unlock(&inode->i_mutex); - } - dput(victim); + int rc; + + rc = -ENOENT; + victim_sd = sysfs_get_dirent(kobj->sd, attr->name); + if (!victim_sd) + goto out; + + victim = sysfs_get_dentry(victim_sd); + if (IS_ERR(victim)) { + rc = PTR_ERR(victim); + victim = NULL; + goto out; } - mutex_unlock(&dir->d_inode->i_mutex); - return res; + inode = victim->d_inode; + mutex_lock(&inode->i_mutex); + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + rc = notify_change(victim, &newattrs); + mutex_unlock(&inode->i_mutex); + out: + dput(victim); + sysfs_put(victim_sd); + return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); @@ -576,7 +544,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) { - sysfs_hash_and_remove(kobj->dentry, attr->name); + sysfs_hash_and_remove(kobj->sd, attr->name); } @@ -589,12 +557,12 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct dentry *dir; + struct sysfs_dirent *dir_sd; - dir = lookup_one_len(group, kobj->dentry, strlen(group)); - if (!IS_ERR(dir)) { - sysfs_hash_and_remove(dir, attr->name); - dput(dir); + dir_sd = sysfs_get_dirent(kobj->sd, group); + if (dir_sd) { + sysfs_hash_and_remove(dir_sd, attr->name); + sysfs_put(dir_sd); } } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 52eed2a..f318b73 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -18,26 +18,25 @@ #include "sysfs.h" -static void remove_files(struct dentry * dir, - const struct attribute_group * grp) +static void remove_files(struct sysfs_dirent *dir_sd, + const struct attribute_group *grp) { struct attribute *const* attr; for (attr = grp->attrs; *attr; attr++) - sysfs_hash_and_remove(dir,(*attr)->name); + sysfs_hash_and_remove(dir_sd, (*attr)->name); } -static int create_files(struct dentry * dir, - const struct attribute_group * grp) +static int create_files(struct sysfs_dirent *dir_sd, + const struct attribute_group *grp) { struct attribute *const* attr; int error = 0; - for (attr = grp->attrs; *attr && !error; attr++) { - error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR); - } + for (attr = grp->attrs; *attr && !error; attr++) + error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); if (error) - remove_files(dir,grp); + remove_files(dir_sd, grp); return error; } @@ -45,44 +44,44 @@ static int create_files(struct dentry * dir, int sysfs_create_group(struct kobject * kobj, const struct attribute_group * grp) { - struct dentry * dir; + struct sysfs_dirent *sd; int error; - BUG_ON(!kobj || !kobj->dentry); + BUG_ON(!kobj || !kobj->sd); if (grp->name) { - error = sysfs_create_subdir(kobj,grp->name,&dir); + error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else - dir = kobj->dentry; - dir = dget(dir); - if ((error = create_files(dir,grp))) { + sd = kobj->sd; + sysfs_get(sd); + error = create_files(sd, grp); + if (error) { if (grp->name) - sysfs_remove_subdir(dir); + sysfs_remove_subdir(sd); } - dput(dir); + sysfs_put(sd); return error; } void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { - struct dentry * dir; + struct sysfs_dirent *dir_sd = kobj->sd; + struct sysfs_dirent *sd; if (grp->name) { - dir = lookup_one_len_kern(grp->name, kobj->dentry, - strlen(grp->name)); - BUG_ON(IS_ERR(dir)); - } - else - dir = dget(kobj->dentry); + sd = sysfs_get_dirent(dir_sd, grp->name); + BUG_ON(!sd); + } else + sd = sysfs_get(dir_sd); - remove_files(dir,grp); + remove_files(sd, grp); if (grp->name) - sysfs_remove_subdir(dir); - /* release the ref. taken in this routine */ - dput(dir); + sysfs_remove_subdir(sd); + + sysfs_put(sd); } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5266eec..10d1b52 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -133,187 +133,94 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) */ static struct lock_class_key sysfs_inode_imutex_key; -struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd) +static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { - struct inode * inode = new_inode(sysfs_sb); - if (inode) { - inode->i_blocks = 0; - inode->i_mapping->a_ops = &sysfs_aops; - inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; - inode->i_op = &sysfs_inode_operations; - inode->i_ino = sd->s_ino; - lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); - - if (sd->s_iattr) { - /* sysfs_dirent has non-default attributes - * get them for the new inode from persistent copy - * in sysfs_dirent - */ - set_inode_attr(inode, sd->s_iattr); - } else - set_default_inode_attr(inode, mode); - } - return inode; -} - -int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) -{ - int error = 0; - struct inode * inode = NULL; - if (dentry) { - if (!dentry->d_inode) { - struct sysfs_dirent * sd = dentry->d_fsdata; - if ((inode = sysfs_new_inode(mode, sd))) { - if (dentry->d_parent && dentry->d_parent->d_inode) { - struct inode *p_inode = dentry->d_parent->d_inode; - p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; - } - goto Proceed; - } - else - error = -ENOMEM; - } else - error = -EEXIST; - } else - error = -ENOENT; - goto Done; - - Proceed: - if (init) - error = init(inode); - if (!error) { - d_instantiate(dentry, inode); - if (S_ISDIR(mode)) - dget(dentry); /* pin only directory dentry in core */ + inode->i_blocks = 0; + inode->i_mapping->a_ops = &sysfs_aops; + inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; + inode->i_op = &sysfs_inode_operations; + inode->i_ino = sd->s_ino; + lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); + + if (sd->s_iattr) { + /* sysfs_dirent has non-default attributes + * get them for the new inode from persistent copy + * in sysfs_dirent + */ + set_inode_attr(inode, sd->s_iattr); } else - iput(inode); - Done: - return error; + set_default_inode_attr(inode, sd->s_mode); } -/* - * Get the name for corresponding element represented by the given sysfs_dirent +/** + * sysfs_get_inode - get inode for sysfs_dirent + * @sd: sysfs_dirent to allocate inode for + * + * Get inode for @sd. If such inode doesn't exist, a new inode + * is allocated and basics are initialized. New inode is + * returned locked. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * Pointer to allocated inode on success, NULL on failure. */ -const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) +struct inode * sysfs_get_inode(struct sysfs_dirent *sd) { - struct attribute * attr; - struct bin_attribute * bin_attr; - struct sysfs_symlink * sl; - - BUG_ON(!sd || !sd->s_element); - - switch (sd->s_type) { - case SYSFS_DIR: - /* Always have a dentry so use that */ - return sd->s_dentry->d_name.name; - - case SYSFS_KOBJ_ATTR: - attr = sd->s_element; - return attr->name; - - case SYSFS_KOBJ_BIN_ATTR: - bin_attr = sd->s_element; - return bin_attr->attr.name; + struct inode *inode; - case SYSFS_KOBJ_LINK: - sl = sd->s_element; - return sl->link_name; - } - return NULL; -} + inode = iget_locked(sysfs_sb, sd->s_ino); + if (inode && (inode->i_state & I_NEW)) + sysfs_init_inode(sd, inode); -static inline void orphan_all_buffers(struct inode *node) -{ - struct sysfs_buffer_collection *set; - struct sysfs_buffer *buf; - - mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); - set = node->i_private; - if (set) { - list_for_each_entry(buf, &set->associates, associates) { - down(&buf->sem); - buf->orphaned = 1; - up(&buf->sem); - } - } - mutex_unlock(&node->i_mutex); + return inode; } - -/* - * Unhashes the dentry corresponding to given sysfs_dirent - * Called with parent inode's i_mutex held. +/** + * sysfs_instantiate - instantiate dentry + * @dentry: dentry to be instantiated + * @inode: inode associated with @sd + * + * Unlock @inode if locked and instantiate @dentry with @inode. + * + * LOCKING: + * None. */ -void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) +void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { - struct dentry *dentry = NULL; - struct inode *inode; + BUG_ON(!dentry || dentry->d_inode); - /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_lock is held. - */ - spin_lock(&sysfs_lock); - spin_lock(&dcache_lock); - - /* dget dentry if it's still alive */ - if (sd->s_dentry && sd->s_dentry->d_inode) - dentry = dget_locked(sd->s_dentry); - - spin_unlock(&dcache_lock); - spin_unlock(&sysfs_lock); - - /* drop dentry */ - if (dentry) { - spin_lock(&dcache_lock); - spin_lock(&dentry->d_lock); - if (!d_unhashed(dentry) && dentry->d_inode) { - inode = dentry->d_inode; - spin_lock(&inode->i_lock); - __iget(inode); - spin_unlock(&inode->i_lock); - dget_locked(dentry); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - simple_unlink(parent->d_inode, dentry); - orphan_all_buffers(inode); - iput(inode); - } else { - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - } + if (inode->i_state & I_NEW) + unlock_new_inode(inode); - dput(dentry); - } + d_instantiate(dentry, inode); } -int sysfs_hash_and_remove(struct dentry * dir, const char * name) +int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { - struct sysfs_dirent * sd; - struct sysfs_dirent * parent_sd; - int found = 0; + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent **pos, *sd; - if (!dir) + if (!dir_sd) return -ENOENT; - if (dir->d_inode == NULL) - /* no inode means this hasn't been made visible yet */ - return -ENOENT; + sysfs_addrm_start(&acxt, dir_sd); + + for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { + sd = *pos; - parent_sd = dir->d_fsdata; - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (!sd->s_element) + if (!sysfs_type(sd)) continue; - if (!strcmp(sysfs_get_name(sd), name)) { - list_del_init(&sd->s_sibling); - sysfs_drop_dentry(sd, dir); - sysfs_put(sd); - found = 1; + if (!strcmp(sd->s_name, name)) { + *pos = sd->s_sibling; + sd->s_sibling = NULL; + sysfs_remove_one(&acxt, sd); break; } } - mutex_unlock(&dir->d_inode->i_mutex); - return found ? 0 : -ENOENT; + if (sysfs_addrm_finish(&acxt)) + return 0; + return -ENOENT; } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 00ab912..fbc7b65 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -19,28 +19,18 @@ struct vfsmount *sysfs_mount; struct super_block * sysfs_sb = NULL; struct kmem_cache *sysfs_dir_cachep; -static void sysfs_clear_inode(struct inode *inode); - static const struct super_operations sysfs_ops = { .statfs = simple_statfs, .drop_inode = sysfs_delete_inode, - .clear_inode = sysfs_clear_inode, }; -static struct sysfs_dirent sysfs_root = { - .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), - .s_children = LIST_HEAD_INIT(sysfs_root.s_children), - .s_element = NULL, - .s_type = SYSFS_ROOT, - .s_iattr = NULL, +struct sysfs_dirent sysfs_root = { + .s_count = ATOMIC_INIT(1), + .s_flags = SYSFS_ROOT, + .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, .s_ino = 1, }; -static void sysfs_clear_inode(struct inode *inode) -{ - kfree(inode->i_private); -} - static int sysfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; @@ -53,24 +43,26 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_gran = 1; sysfs_sb = sb; - inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - &sysfs_root); - if (inode) { - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - } else { + /* get root inode, initialize and unlock it */ + inode = sysfs_get_inode(&sysfs_root); + if (!inode) { pr_debug("sysfs: could not get root inode\n"); return -ENOMEM; } + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + inc_nlink(inode); /* directory, account for "." */ + unlock_new_inode(inode); + + /* instantiate and link root dentry */ root = d_alloc_root(inode); if (!root) { pr_debug("%s: could not get root dentry!\n",__FUNCTION__); iput(inode); return -ENOMEM; } + sysfs_root.s_dentry = root; root->d_fsdata = &sysfs_root; sb->s_root = root; return 0; @@ -94,7 +86,7 @@ int __init sysfs_init(void) sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", sizeof(struct sysfs_dirent), - 0, 0, NULL, NULL); + 0, 0, NULL); if (!sysfs_dir_cachep) goto out; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 7b9c5bf..4ce687f 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -11,71 +11,39 @@ #include "sysfs.h" -static int object_depth(struct kobject * kobj) +static int object_depth(struct sysfs_dirent *sd) { - struct kobject * p = kobj; int depth = 0; - do { depth++; } while ((p = p->parent)); + + for (; sd->s_parent; sd = sd->s_parent) + depth++; + return depth; } -static int object_path_length(struct kobject * kobj) +static int object_path_length(struct sysfs_dirent * sd) { - struct kobject * p = kobj; int length = 1; - do { - length += strlen(kobject_name(p)) + 1; - p = p->parent; - } while (p); + + for (; sd->s_parent; sd = sd->s_parent) + length += strlen(sd->s_name) + 1; + return length; } -static void fill_object_path(struct kobject * kobj, char * buffer, int length) +static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) { - struct kobject * p; - --length; - for (p = kobj; p; p = p->parent) { - int cur = strlen(kobject_name(p)); + for (; sd->s_parent; sd = sd->s_parent) { + int cur = strlen(sd->s_name); /* back up enough to print this bus id with '/' */ length -= cur; - strncpy(buffer + length,kobject_name(p),cur); + strncpy(buffer + length, sd->s_name, cur); *(buffer + --length) = '/'; } } -static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) -{ - struct sysfs_dirent * parent_sd = parent->d_fsdata; - struct sysfs_symlink * sl; - int error = 0; - - error = -ENOMEM; - sl = kmalloc(sizeof(*sl), GFP_KERNEL); - if (!sl) - goto exit1; - - sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); - if (!sl->link_name) - goto exit2; - - strcpy(sl->link_name, name); - sl->target_kobj = kobject_get(target); - - error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, - SYSFS_KOBJ_LINK); - if (!error) - return 0; - - kobject_put(target); - kfree(sl->link_name); -exit2: - kfree(sl); -exit1: - return error; -} - /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. @@ -84,24 +52,61 @@ exit1: */ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) { - struct dentry *dentry = NULL; - int error = -EEXIST; + struct sysfs_dirent *parent_sd = NULL; + struct sysfs_dirent *target_sd = NULL; + struct sysfs_dirent *sd = NULL; + struct sysfs_addrm_cxt acxt; + int error; BUG_ON(!name); if (!kobj) { if (sysfs_mount && sysfs_mount->mnt_sb) - dentry = sysfs_mount->mnt_sb->s_root; + parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; } else - dentry = kobj->dentry; + parent_sd = kobj->sd; + + error = -EFAULT; + if (!parent_sd) + goto out_put; + + /* target->sd can go away beneath us but is protected with + * sysfs_assoc_lock. Fetch target_sd from it. + */ + spin_lock(&sysfs_assoc_lock); + if (target->sd) + target_sd = sysfs_get(target->sd); + spin_unlock(&sysfs_assoc_lock); - if (!dentry) - return -EFAULT; + error = -ENOENT; + if (!target_sd) + goto out_put; - mutex_lock(&dentry->d_inode->i_mutex); - if (!sysfs_dirent_exist(dentry->d_fsdata, name)) - error = sysfs_add_link(dentry, name, target); - mutex_unlock(&dentry->d_inode->i_mutex); + error = -ENOMEM; + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) + goto out_put; + + sd->s_elem.symlink.target_sd = target_sd; + target_sd = NULL; /* reference is now owned by the symlink */ + + sysfs_addrm_start(&acxt, parent_sd); + + if (!sysfs_find_dirent(parent_sd, name)) { + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); + } + + if (!sysfs_addrm_finish(&acxt)) { + error = -EEXIST; + goto out_put; + } + + return 0; + + out_put: + sysfs_put(target_sd); + sysfs_put(sd); return error; } @@ -114,17 +119,17 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char void sysfs_remove_link(struct kobject * kobj, const char * name) { - sysfs_hash_and_remove(kobj->dentry,name); + sysfs_hash_and_remove(kobj->sd, name); } -static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, - char *path) +static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, + struct sysfs_dirent * target_sd, char *path) { char * s; int depth, size; - depth = object_depth(kobj); - size = object_path_length(target) + depth * 3 - 1; + depth = object_depth(parent_sd); + size = object_path_length(target_sd) + depth * 3 - 1; if (size > PATH_MAX) return -ENAMETOOLONG; @@ -133,7 +138,7 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, for (s = path; depth--; s += 3) strcpy(s,"../"); - fill_object_path(target, path, size); + fill_object_path(target_sd, path, size); pr_debug("%s: path = '%s'\n", __FUNCTION__, path); return 0; @@ -141,27 +146,16 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, static int sysfs_getlink(struct dentry *dentry, char * path) { - struct kobject *kobj, *target_kobj; - int error = 0; + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_dirent *parent_sd = sd->s_parent; + struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; + int error; - kobj = sysfs_get_kobject(dentry->d_parent); - if (!kobj) - return -EINVAL; + mutex_lock(&sysfs_mutex); + error = sysfs_get_target_path(parent_sd, target_sd, path); + mutex_unlock(&sysfs_mutex); - target_kobj = sysfs_get_kobject(dentry); - if (!target_kobj) { - kobject_put(kobj); - return -EINVAL; - } - - down_read(&sysfs_rename_sem); - error = sysfs_get_target_path(kobj, target_kobj, path); - up_read(&sysfs_rename_sem); - - kobject_put(kobj); - kobject_put(target_kobj); return error; - } static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 502c949..6b8c8d7 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -1,9 +1,40 @@ +struct sysfs_elem_dir { + struct kobject * kobj; +}; + +struct sysfs_elem_symlink { + struct sysfs_dirent * target_sd; +}; + +struct sysfs_elem_attr { + struct attribute * attr; +}; + +struct sysfs_elem_bin_attr { + struct bin_attribute * bin_attr; +}; + +/* + * As long as s_count reference is held, the sysfs_dirent itself is + * accessible. Dereferencing s_elem or any other outer entity + * requires s_active reference. + */ struct sysfs_dirent { atomic_t s_count; - struct list_head s_sibling; - struct list_head s_children; - void * s_element; - int s_type; + atomic_t s_active; + struct sysfs_dirent * s_parent; + struct sysfs_dirent * s_sibling; + struct sysfs_dirent * s_children; + const char * s_name; + + union { + struct sysfs_elem_dir dir; + struct sysfs_elem_symlink symlink; + struct sysfs_elem_attr attr; + struct sysfs_elem_bin_attr bin_attr; + } s_elem; + + unsigned int s_flags; umode_t s_mode; ino_t s_ino; struct dentry * s_dentry; @@ -11,30 +42,59 @@ struct sysfs_dirent { atomic_t s_event; }; +#define SD_DEACTIVATED_BIAS INT_MIN + +struct sysfs_addrm_cxt { + struct sysfs_dirent *parent_sd; + struct inode *parent_inode; + struct sysfs_dirent *removed; + int cnt; +}; + extern struct vfsmount * sysfs_mount; +extern struct sysfs_dirent sysfs_root; extern struct kmem_cache *sysfs_dir_cachep; -extern void sysfs_delete_inode(struct inode *inode); -extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); -extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); +extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd); +extern void sysfs_link_sibling(struct sysfs_dirent *sd); +extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); +extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); +extern void sysfs_put_active(struct sysfs_dirent *sd); +extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); +extern void sysfs_put_active_two(struct sysfs_dirent *sd); +extern void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *parent_sd); +extern void sysfs_add_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd); +extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd); +extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); -extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); -extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, - umode_t, int); - -extern int sysfs_add_file(struct dentry *, const struct attribute *, int); -extern int sysfs_hash_and_remove(struct dentry * dir, const char * name); +extern void sysfs_delete_inode(struct inode *inode); +extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd); +extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode); + +extern void release_sysfs_dirent(struct sysfs_dirent * sd); +extern struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name); +extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name); +extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, + int type); + +extern int sysfs_add_file(struct sysfs_dirent *dir_sd, + const struct attribute *attr, int type); +extern int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); -extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); -extern void sysfs_remove_subdir(struct dentry *); +extern int sysfs_create_subdir(struct kobject *kobj, const char *name, + struct sysfs_dirent **p_sd); +extern void sysfs_remove_subdir(struct sysfs_dirent *sd); -extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd); -extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); -extern spinlock_t sysfs_lock; -extern struct rw_semaphore sysfs_rename_sem; +extern spinlock_t sysfs_assoc_lock; +extern struct mutex sysfs_mutex; extern struct super_block * sysfs_sb; extern const struct file_operations sysfs_dir_operations; extern const struct file_operations sysfs_file_operations; @@ -42,73 +102,9 @@ extern const struct file_operations bin_fops; extern const struct inode_operations sysfs_dir_inode_operations; extern const struct inode_operations sysfs_symlink_inode_operations; -struct sysfs_symlink { - char * link_name; - struct kobject * target_kobj; -}; - -struct sysfs_buffer { - struct list_head associates; - size_t count; - loff_t pos; - char * page; - struct sysfs_ops * ops; - struct semaphore sem; - int orphaned; - int needs_read_fill; - int event; -}; - -struct sysfs_buffer_collection { - struct list_head associates; -}; - -static inline struct kobject * to_kobj(struct dentry * dentry) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct kobject *) sd->s_element); -} - -static inline struct attribute * to_attr(struct dentry * dentry) +static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { - struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct attribute *) sd->s_element); -} - -static inline struct bin_attribute * to_bin_attr(struct dentry * dentry) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct bin_attribute *) sd->s_element); -} - -static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) -{ - struct kobject * kobj = NULL; - - spin_lock(&dcache_lock); - if (!d_unhashed(dentry)) { - struct sysfs_dirent * sd = dentry->d_fsdata; - if (sd->s_type & SYSFS_KOBJ_LINK) { - struct sysfs_symlink * sl = sd->s_element; - kobj = kobject_get(sl->target_kobj); - } else - kobj = kobject_get(sd->s_element); - } - spin_unlock(&dcache_lock); - - return kobj; -} - -static inline void release_sysfs_dirent(struct sysfs_dirent * sd) -{ - if (sd->s_type & SYSFS_KOBJ_LINK) { - struct sysfs_symlink * sl = sd->s_element; - kfree(sl->link_name); - kobject_put(sl->target_kobj); - kfree(sl); - } - kfree(sd->s_iattr); - kmem_cache_free(sysfs_dir_cachep, sd); + return sd->s_flags & SYSFS_TYPE_MASK; } static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) @@ -122,7 +118,7 @@ static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) static inline void sysfs_put(struct sysfs_dirent * sd) { - if (atomic_dec_and_test(&sd->s_count)) + if (sd && atomic_dec_and_test(&sd->s_count)) release_sysfs_dirent(sd); } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 5644116..7c4e5d3 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -342,7 +342,7 @@ int __init sysv_init_icache(void) sysv_inode_cachep = kmem_cache_create("sysv_inode_cache", sizeof(struct sysv_inode_info), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - init_once, NULL); + init_once); if (!sysv_inode_cachep) return -ENOMEM; return 0; diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 4cec910..276f720 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -41,18 +41,17 @@ #define uint(x) xuint(x) #define xuint(x) __le ## x -static inline int find_next_one_bit (void * addr, int size, int offset) +static inline int find_next_one_bit(void *addr, int size, int offset) { - uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); - int result = offset & ~(BITS_PER_LONG-1); + uintBPL_t *p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); + int result = offset & ~(BITS_PER_LONG - 1); unsigned long tmp; if (offset >= size) return size; size -= result; - offset &= (BITS_PER_LONG-1); - if (offset) - { + offset &= (BITS_PER_LONG - 1); + if (offset) { tmp = leBPL_to_cpup(p++); tmp &= ~0UL << offset; if (size < BITS_PER_LONG) @@ -62,8 +61,7 @@ static inline int find_next_one_bit (void * addr, int size, int offset) size -= BITS_PER_LONG; result += BITS_PER_LONG; } - while (size & ~(BITS_PER_LONG-1)) - { + while (size & ~(BITS_PER_LONG - 1)) { if ((tmp = leBPL_to_cpup(p++))) goto found_middle; result += BITS_PER_LONG; @@ -73,7 +71,7 @@ static inline int find_next_one_bit (void * addr, int size, int offset) return result; tmp = leBPL_to_cpup(p); found_first: - tmp &= ~0UL >> (BITS_PER_LONG-size); + tmp &= ~0UL >> (BITS_PER_LONG - size); found_middle: return result + ffz(~tmp); } @@ -81,8 +79,9 @@ found_middle: #define find_first_one_bit(addr, size)\ find_next_one_bit((addr), (size), 0) -static int read_block_bitmap(struct super_block * sb, - struct udf_bitmap *bitmap, unsigned int block, unsigned long bitmap_nr) +static int read_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, unsigned int block, + unsigned long bitmap_nr) { struct buffer_head *bh = NULL; int retval = 0; @@ -92,38 +91,39 @@ static int read_block_bitmap(struct super_block * sb, loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block)); - if (!bh) - { + if (!bh) { retval = -EIO; } bitmap->s_block_bitmap[bitmap_nr] = bh; return retval; } -static int __load_block_bitmap(struct super_block * sb, - struct udf_bitmap *bitmap, unsigned int block_group) +static int __load_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, + unsigned int block_group) { int retval = 0; int nr_groups = bitmap->s_nr_groups; - if (block_group >= nr_groups) - { - udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, nr_groups); + if (block_group >= nr_groups) { + udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, + nr_groups); } - if (bitmap->s_block_bitmap[block_group]) + if (bitmap->s_block_bitmap[block_group]) { return block_group; - else - { - retval = read_block_bitmap(sb, bitmap, block_group, block_group); + } else { + retval = read_block_bitmap(sb, bitmap, block_group, + block_group); if (retval < 0) return retval; return block_group; } } -static inline int load_block_bitmap(struct super_block * sb, - struct udf_bitmap *bitmap, unsigned int block_group) +static inline int load_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, + unsigned int block_group) { int slot; @@ -138,13 +138,14 @@ static inline int load_block_bitmap(struct super_block * sb, return slot; } -static void udf_bitmap_free_blocks(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +static void udf_bitmap_free_blocks(struct super_block *sb, + struct inode *inode, + struct udf_bitmap *bitmap, + kernel_lb_addr bloc, uint32_t offset, + uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; unsigned long block; unsigned long block_group; unsigned long bit; @@ -154,11 +155,10 @@ static void udf_bitmap_free_blocks(struct super_block * sb, mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || - (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) - { + (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", - bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, - UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); + bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, + UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } @@ -172,8 +172,7 @@ do_more: /* * Check to see if we are freeing blocks across a group boundary. */ - if (bit + count > (sb->s_blocksize << 3)) - { + if (bit + count > (sb->s_blocksize << 3)) { overflow = bit + count - (sb->s_blocksize << 3); count -= overflow; } @@ -182,27 +181,21 @@ do_more: goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; - for (i=0; i < count; i++) - { - if (udf_set_bit(bit + i, bh->b_data)) - { + for (i = 0; i < count; i++) { + if (udf_set_bit(bit + i, bh->b_data)) { udf_debug("bit %ld already set\n", bit + i); udf_debug("byte=%2x\n", ((char *)bh->b_data)[(bit + i) >> 3]); - } - else - { + } else { if (inode) DQUOT_FREE_BLOCK(inode, 1); - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]) + 1); } } } mark_buffer_dirty(bh); - if (overflow) - { + if (overflow) { block += count; count = overflow; goto do_more; @@ -215,10 +208,11 @@ error_return: return; } -static int udf_bitmap_prealloc_blocks(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block, - uint32_t block_count) +static int udf_bitmap_prealloc_blocks(struct super_block *sb, + struct inode *inode, + struct udf_bitmap *bitmap, + uint16_t partition, uint32_t first_block, + uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; @@ -235,7 +229,8 @@ static int udf_bitmap_prealloc_blocks(struct super_block * sb, repeat: nr_groups = (UDF_SB_PARTLEN(sb, partition) + - (sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); + (sizeof(struct spaceBitmapDesc) << 3) + + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); block = first_block + (sizeof(struct spaceBitmapDesc) << 3); block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); @@ -247,31 +242,28 @@ repeat: bit = block % (sb->s_blocksize << 3); - while (bit < (sb->s_blocksize << 3) && block_count > 0) - { - if (!udf_test_bit(bit, bh->b_data)) + while (bit < (sb->s_blocksize << 3) && block_count > 0) { + if (!udf_test_bit(bit, bh->b_data)) { goto out; - else if (DQUOT_PREALLOC_BLOCK(inode, 1)) + } else if (DQUOT_PREALLOC_BLOCK(inode, 1)) { goto out; - else if (!udf_clear_bit(bit, bh->b_data)) - { + } else if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); DQUOT_FREE_BLOCK(inode, 1); goto out; } - block_count --; - alloc_count ++; - bit ++; - block ++; + block_count--; + alloc_count++; + bit++; + block++; } mark_buffer_dirty(bh); if (block_count > 0) goto repeat; out: - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition]) - alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; @@ -279,12 +271,13 @@ out: return alloc_count; } -static int udf_bitmap_new_block(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err) +static int udf_bitmap_new_block(struct super_block *sb, + struct inode *inode, + struct udf_bitmap *bitmap, uint16_t partition, + uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); - int newbit, bit=0, block, block_group, group_start; + int newbit, bit = 0, block, block_group, group_start; int end_goal, nr_groups, bitmap_nr, i; struct buffer_head *bh = NULL; char *ptr; @@ -306,38 +299,35 @@ repeat: if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; - ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); + ptr = memscan((char *)bh->b_data + group_start, 0xFF, + sb->s_blocksize - group_start); - if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) - { + if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = block % (sb->s_blocksize << 3); - if (udf_test_bit(bit, bh->b_data)) - { goto got_block; - } + end_goal = (bit + 63) & ~63; bit = udf_find_next_one_bit(bh->b_data, end_goal, bit); if (bit < end_goal) goto got_block; + ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3)); newbit = (ptr - ((char *)bh->b_data)) << 3; - if (newbit < sb->s_blocksize << 3) - { + if (newbit < sb->s_blocksize << 3) { bit = newbit; goto search_back; } + newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit); - if (newbit < sb->s_blocksize << 3) - { + if (newbit < sb->s_blocksize << 3) { bit = newbit; goto got_block; } } - for (i=0; i<(nr_groups*2); i++) - { - block_group ++; + for (i = 0; i < (nr_groups * 2); i++) { + block_group++; if (block_group >= nr_groups) block_group = 0; group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); @@ -346,24 +336,22 @@ repeat: if (bitmap_nr < 0) goto error_return; bh = bitmap->s_block_bitmap[bitmap_nr]; - if (i < nr_groups) - { - ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start); - if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) - { + if (i < nr_groups) { + ptr = memscan((char *)bh->b_data + group_start, 0xFF, + sb->s_blocksize - group_start); + if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) { bit = (ptr - ((char *)bh->b_data)) << 3; break; } - } - else - { - bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3); + } else { + bit = udf_find_next_one_bit((char *)bh->b_data, + sb->s_blocksize << 3, + group_start << 3); if (bit < sb->s_blocksize << 3) break; } } - if (i >= (nr_groups*2)) - { + if (i >= (nr_groups * 2)) { mutex_unlock(&sbi->s_alloc_mutex); return newblock; } @@ -371,22 +359,21 @@ repeat: goto search_back; else bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3); - if (bit >= sb->s_blocksize << 3) - { + if (bit >= sb->s_blocksize << 3) { mutex_unlock(&sbi->s_alloc_mutex); return 0; } search_back: - for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--); + for (i = 0; i < 7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--) + ; /* empty loop */ got_block: /* * Check quota for allocation of this block. */ - if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) - { + if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; return 0; @@ -395,18 +382,16 @@ got_block: newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - (sizeof(struct spaceBitmapDesc) << 3); - if (!udf_clear_bit(bit, bh->b_data)) - { + if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); goto repeat; } mark_buffer_dirty(bh); - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition]) - 1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; @@ -420,10 +405,11 @@ error_return: return 0; } -static void udf_table_free_blocks(struct super_block * sb, - struct inode * inode, - struct inode * table, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +static void udf_table_free_blocks(struct super_block *sb, + struct inode *inode, + struct inode *table, + kernel_lb_addr bloc, uint32_t offset, + uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t start, end; @@ -435,11 +421,10 @@ static void udf_table_free_blocks(struct super_block * sb, mutex_lock(&sbi->s_alloc_mutex); if (bloc.logicalBlockNum < 0 || - (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) - { + (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) { udf_debug("%d < %d || %d + %d > %d\n", - bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, - UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); + bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, + UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); goto error_return; } @@ -447,10 +432,9 @@ static void udf_table_free_blocks(struct super_block * sb, but.. oh well */ if (inode) DQUOT_FREE_BLOCK(inode, count); - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]) + count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } @@ -462,74 +446,59 @@ static void udf_table_free_blocks(struct super_block * sb, epos.block = oepos.block = UDF_I_LOCATION(table); epos.bh = oepos.bh = NULL; - while (count && (etype = - udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) - { - if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) == - start)) - { - if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) - { + while (count && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { + if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) == start)) { + if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); - } - else - { - elen = (etype << 30) | - (elen + (count << sb->s_blocksize_bits)); + } else { + elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); start += count; count = 0; } udf_write_aext(table, &oepos, eloc, elen, 1); - } - else if (eloc.logicalBlockNum == (end + 1)) - { - if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) - { + } else if (eloc.logicalBlockNum == (end + 1)) { + if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) { count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); end -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); - eloc.logicalBlockNum -= - ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); + eloc.logicalBlockNum -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); elen = (etype << 30) | (0x40000000 - sb->s_blocksize); - } - else - { + } else { eloc.logicalBlockNum = start; - elen = (etype << 30) | - (elen + (count << sb->s_blocksize_bits)); + elen = (etype << 30) | (elen + (count << sb->s_blocksize_bits)); end -= count; count = 0; } udf_write_aext(table, &oepos, eloc, elen, 1); } - if (epos.bh != oepos.bh) - { + if (epos.bh != oepos.bh) { i = -1; oepos.block = epos.block; brelse(oepos.bh); get_bh(epos.bh); oepos.bh = epos.bh; oepos.offset = 0; - } - else + } else { oepos.offset = epos.offset; + } } - if (count) - { - /* NOTE: we CANNOT use udf_add_aext here, as it can try to allocate - a new block, and since we hold the super block lock already - very bad things would happen :) - - We copy the behavior of udf_add_aext, but instead of - trying to allocate a new block close to the existing one, - we just steal a block from the extent we are trying to add. - - It would be nice if the blocks were close together, but it - isn't required. - */ + if (count) { + /* + * NOTE: we CANNOT use udf_add_aext here, as it can try to allocate + * a new block, and since we hold the super block lock already + * very bad things would happen :) + * + * We copy the behavior of udf_add_aext, but instead of + * trying to allocate a new block close to the existing one, + * we just steal a block from the extent we are trying to add. + * + * It would be nice if the blocks were close together, but it + * isn't required. + */ int adsize; short_ad *sad = NULL; @@ -540,40 +509,35 @@ static void udf_table_free_blocks(struct super_block * sb, elen = EXT_RECORDED_ALLOCATED | (count << sb->s_blocksize_bits); - if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) + if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) { adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) + } else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) { adsize = sizeof(long_ad); - else - { + } else { brelse(oepos.bh); brelse(epos.bh); goto error_return; } - if (epos.offset + (2 * adsize) > sb->s_blocksize) - { + if (epos.offset + (2 * adsize) > sb->s_blocksize) { char *sptr, *dptr; int loffset; - + brelse(oepos.bh); oepos = epos; /* Steal a block from the extent being free'd */ epos.block.logicalBlockNum = eloc.logicalBlockNum; - eloc.logicalBlockNum ++; + eloc.logicalBlockNum++; elen -= sb->s_blocksize; - if (!(epos.bh = udf_tread(sb, - udf_get_lb_pblock(sb, epos.block, 0)))) - { + if (!(epos.bh = udf_tread(sb, udf_get_lb_pblock(sb, epos.block, 0)))) { brelse(oepos.bh); goto error_return; } aed = (struct allocExtDesc *)(epos.bh->b_data); aed->previousAllocExtLocation = cpu_to_le32(oepos.block.logicalBlockNum); - if (epos.offset + adsize > sb->s_blocksize) - { + if (epos.offset + adsize > sb->s_blocksize) { loffset = epos.offset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = UDF_I_DATA(inode) + epos.offset - @@ -582,73 +546,59 @@ static void udf_table_free_blocks(struct super_block * sb, dptr = epos.bh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); epos.offset = sizeof(struct allocExtDesc) + adsize; - } - else - { + } else { loffset = epos.offset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = oepos.bh->b_data + epos.offset; epos.offset = sizeof(struct allocExtDesc); - if (oepos.bh) - { + if (oepos.bh) { aed = (struct allocExtDesc *)oepos.bh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); - } - else - { + } else { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); } } if (UDF_SB_UDFREV(sb) >= 0x0200) udf_new_tag(epos.bh->b_data, TAG_IDENT_AED, 3, 1, - epos.block.logicalBlockNum, sizeof(tag)); + epos.block.logicalBlockNum, sizeof(tag)); else udf_new_tag(epos.bh->b_data, TAG_IDENT_AED, 2, 1, - epos.block.logicalBlockNum, sizeof(tag)); - switch (UDF_I_ALLOCTYPE(table)) - { + epos.block.logicalBlockNum, sizeof(tag)); + + switch (UDF_I_ALLOCTYPE(table)) { case ICBTAG_FLAG_AD_SHORT: - { sad = (short_ad *)sptr; sad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); sad->extPosition = cpu_to_le32(epos.block.logicalBlockNum); break; - } case ICBTAG_FLAG_AD_LONG: - { lad = (long_ad *)sptr; lad->extLength = cpu_to_le32( EXT_NEXT_EXTENT_ALLOCDECS | sb->s_blocksize); lad->extLocation = cpu_to_lelb(epos.block); break; - } } - if (oepos.bh) - { + if (oepos.bh) { udf_update_tag(oepos.bh->b_data, loffset); mark_buffer_dirty(oepos.bh); - } - else + } else { mark_inode_dirty(table); + } } - if (elen) /* It's possible that stealing the block emptied the extent */ - { + if (elen) { /* It's possible that stealing the block emptied the extent */ udf_write_aext(table, &epos, eloc, elen, 1); - if (!epos.bh) - { + if (!epos.bh) { UDF_I_LENALLOC(table) += adsize; mark_inode_dirty(table); - } - else - { + } else { aed = (struct allocExtDesc *)epos.bh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); @@ -667,10 +617,10 @@ error_return: return; } -static int udf_table_prealloc_blocks(struct super_block * sb, - struct inode * inode, - struct inode *table, uint16_t partition, uint32_t first_block, - uint32_t block_count) +static int udf_table_prealloc_blocks(struct super_block *sb, + struct inode *inode, + struct inode *table, uint16_t partition, + uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; @@ -695,40 +645,36 @@ static int udf_table_prealloc_blocks(struct super_block * sb, epos.bh = NULL; eloc.logicalBlockNum = 0xFFFFFFFF; - while (first_block != eloc.logicalBlockNum && (etype = - udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) - { + while (first_block != eloc.logicalBlockNum && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { udf_debug("eloc=%d, elen=%d, first_block=%d\n", - eloc.logicalBlockNum, elen, first_block); + eloc.logicalBlockNum, elen, first_block); ; /* empty loop body */ } - if (first_block == eloc.logicalBlockNum) - { + if (first_block == eloc.logicalBlockNum) { epos.offset -= adsize; alloc_count = (elen >> sb->s_blocksize_bits); - if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count)) + if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count)) { alloc_count = 0; - else if (alloc_count > block_count) - { + } else if (alloc_count > block_count) { alloc_count = block_count; eloc.logicalBlockNum += alloc_count; elen -= (alloc_count << sb->s_blocksize_bits); udf_write_aext(table, &epos, eloc, (etype << 30) | elen, 1); - } - else + } else { udf_delete_aext(table, epos, eloc, (etype << 30) | elen); - } - else + } + } else { alloc_count = 0; + } brelse(epos.bh); - if (alloc_count && UDF_SB_LVIDBH(sb)) - { + if (alloc_count && UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition]) - alloc_count); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); sb->s_dirt = 1; } @@ -736,9 +682,10 @@ static int udf_table_prealloc_blocks(struct super_block * sb, return alloc_count; } -static int udf_table_new_block(struct super_block * sb, - struct inode * inode, - struct inode *table, uint16_t partition, uint32_t goal, int *err) +static int udf_table_new_block(struct super_block *sb, + struct inode *inode, + struct inode *table, uint16_t partition, + uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; @@ -765,30 +712,26 @@ static int udf_table_new_block(struct super_block * sb, we stop. Otherwise we keep going till we run out of extents. We store the buffer_head, bloc, and extoffset of the current closest match and use that when we are done. - */ + */ epos.offset = sizeof(struct unallocSpaceEntry); epos.block = UDF_I_LOCATION(table); epos.bh = goal_epos.bh = NULL; - while (spread && (etype = - udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) - { - if (goal >= eloc.logicalBlockNum) - { + while (spread && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { + if (goal >= eloc.logicalBlockNum) { if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) nspread = 0; else nspread = goal - eloc.logicalBlockNum - (elen >> sb->s_blocksize_bits); - } - else + } else { nspread = eloc.logicalBlockNum - goal; + } - if (nspread < spread) - { + if (nspread < spread) { spread = nspread; - if (goal_epos.bh != epos.bh) - { + if (goal_epos.bh != epos.bh) { brelse(goal_epos.bh); goal_epos.bh = epos.bh; get_bh(goal_epos.bh); @@ -802,8 +745,7 @@ static int udf_table_new_block(struct super_block * sb, brelse(epos.bh); - if (spread == 0xFFFFFFFF) - { + if (spread == 0xFFFFFFFF) { brelse(goal_epos.bh); mutex_unlock(&sbi->s_alloc_mutex); return 0; @@ -815,11 +757,10 @@ static int udf_table_new_block(struct super_block * sb, /* This works, but very poorly.... */ newblock = goal_eloc.logicalBlockNum; - goal_eloc.logicalBlockNum ++; + goal_eloc.logicalBlockNum++; goal_elen -= sb->s_blocksize; - if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) - { + if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) { brelse(goal_epos.bh); mutex_unlock(&sbi->s_alloc_mutex); *err = -EDQUOT; @@ -832,10 +773,9 @@ static int udf_table_new_block(struct super_block * sb, udf_delete_aext(table, goal_epos, goal_eloc, goal_elen); brelse(goal_epos.bh); - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); + cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition]) - 1); mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } @@ -845,105 +785,84 @@ static int udf_table_new_block(struct super_block * sb, return newblock; } -inline void udf_free_blocks(struct super_block * sb, - struct inode * inode, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +inline void udf_free_blocks(struct super_block *sb, + struct inode *inode, + kernel_lb_addr bloc, uint32_t offset, + uint32_t count) { uint16_t partition = bloc.partitionReferenceNum; - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { + if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, + bloc, offset, count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, + bloc, offset, count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, + bloc, offset, count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - bloc, offset, count); - } - else + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, + bloc, offset, count); + } else { return; + } } -inline int udf_prealloc_blocks(struct super_block * sb, - struct inode * inode, - uint16_t partition, uint32_t first_block, uint32_t block_count) +inline int udf_prealloc_blocks(struct super_block *sb, + struct inode *inode, + uint16_t partition, uint32_t first_block, + uint32_t block_count) { - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { + if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, + partition, first_block, block_count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, + partition, first_block, block_count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, + partition, first_block, block_count); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - partition, first_block, block_count); - } - else + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, + partition, first_block, block_count); + } else { return 0; + } } -inline int udf_new_block(struct super_block * sb, - struct inode * inode, - uint16_t partition, uint32_t goal, int *err) +inline int udf_new_block(struct super_block *sb, + struct inode *inode, + uint16_t partition, uint32_t goal, int *err) { int ret; - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { + if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) { ret = udf_bitmap_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - partition, goal, err); + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, + partition, goal, err); return ret; - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) { return udf_table_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - partition, goal, err); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { + UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, + partition, goal, err); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) { return udf_bitmap_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - partition, goal, err); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, + partition, goal, err); + } else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) { return udf_table_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - partition, goal, err); - } - else - { + UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, + partition, goal, err); + } else { *err = -EIO; return 0; } diff --git a/fs/udf/crc.c b/fs/udf/crc.c index 1b82a4a..85aaee5 100644 --- a/fs/udf/crc.c +++ b/fs/udf/crc.c @@ -79,8 +79,7 @@ static uint16_t crc_table[256] = { * July 21, 1997 - Andrew E. Mileski * Adapted from OSTA-UDF(tm) 1.50 standard. */ -uint16_t -udf_crc(uint8_t *data, uint32_t size, uint16_t crc) +uint16_t udf_crc(uint8_t * data, uint32_t size, uint16_t crc) { while (size--) crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8); @@ -106,8 +105,8 @@ int main(void) { unsigned short x; - x = udf_crc16(bytes, sizeof bytes); - printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); + x = udf_crc(bytes, sizeof bytes); + printf("udf_crc: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); return 0; } @@ -138,7 +137,7 @@ int main(int argc, char **argv) /* Get the polynomial */ sscanf(argv[1], "%lo", &poly); - if (poly & 0xffff0000U){ + if (poly & 0xffff0000U) { fprintf(stderr, "polynomial is too large\en"); exit(1); } @@ -147,22 +146,22 @@ int main(int argc, char **argv) /* Create a table */ printf("static unsigned short crc_table[256] = {\n"); - for (n = 0; n < 256; n++){ + for (n = 0; n < 256; n++) { if (n % 8 == 0) printf("\t"); crc = n << 8; - for (i = 0; i < 8; i++){ - if(crc & 0x8000U) + for (i = 0; i < 8; i++) { + if (crc & 0x8000U) crc = (crc << 1) ^ poly; else crc <<= 1; - crc &= 0xFFFFU; + crc &= 0xFFFFU; } if (n == 255) printf("0x%04xU ", crc); else printf("0x%04xU, ", crc); - if(n % 8 == 7) + if (n % 8 == 7) printf("\n"); } printf("};\n"); diff --git a/fs/udf/dir.c b/fs/udf/dir.c index e45f86b..9e3b9f9 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -82,14 +82,12 @@ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) lock_kernel(); - if ( filp->f_pos == 0 ) - { - if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) - { + if (filp->f_pos == 0) { + if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) { unlock_kernel(); return 0; } - filp->f_pos ++; + filp->f_pos++; } result = do_udf_readdir(dir, filp, filldir, dirent); @@ -97,11 +95,12 @@ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) return result; } -static int -do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent) +static int +do_udf_readdir(struct inode *dir, struct file *filp, filldir_t filldir, + void *dirent) { struct udf_fileident_bh fibh; - struct fileIdentDesc *fi=NULL; + struct fileIdentDesc *fi = NULL; struct fileIdentDesc cfi; int block, iblock; loff_t nf_pos = filp->f_pos - 1; @@ -117,7 +116,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d sector_t offset; int i, num; unsigned int dt_type; - struct extent_position epos = { NULL, 0, {0, 0}}; + struct extent_position epos = { NULL, 0, {0, 0} }; if (nf_pos >= size) return 0; @@ -126,64 +125,54 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d nf_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fibh.sbh = fibh.ebh = NULL; - else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), - &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) - { + } else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), + &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) epos.offset -= sizeof(long_ad); - } - else + } else { offset = 0; + } - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) - { + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { brelse(epos.bh); return -EIO; } - - if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) - { + + if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); - if (i+offset > (elen >> dir->i_sb->s_blocksize_bits)) - i = (elen >> dir->i_sb->s_blocksize_bits)-offset; - for (num=0; i>0; i--) - { - block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i); + if (i + offset > (elen >> dir->i_sb->s_blocksize_bits)) + i = (elen >> dir->i_sb->s_blocksize_bits) - offset; + for (num = 0; i > 0; i--) { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset + i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } - if (num) - { + if (num) { ll_rw_block(READA, num, bha); - for (i=0; i<num; i++) + for (i = 0; i < num; i++) brelse(bha[i]); } } - } - else - { + } else { brelse(epos.bh); return -ENOENT; } - while ( nf_pos < size ) - { + while (nf_pos < size) { filp->f_pos = nf_pos + 1; - fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc, &elen, &offset); - - if (!fi) - { + fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc, + &elen, &offset); + if (!fi) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); @@ -194,45 +183,40 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d liu = le16_to_cpu(cfi.lengthOfImpUse); lfi = cfi.lengthFileIdent; - if (fibh.sbh == fibh.ebh) + if (fibh.sbh == fibh.ebh) { nameptr = fi->fileIdent + liu; - else - { + } else { int poffset; /* Unpaded ending offset */ poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi; - if (poffset >= lfi) + if (poffset >= lfi) { nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); - else - { + } else { nameptr = fname; - memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); - memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); + memcpy(nameptr, fi->fileIdent + liu, + lfi - poffset); + memcpy(nameptr + lfi - poffset, + fibh.ebh->b_data, poffset); } } - if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) continue; } - - if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + + if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) continue; } - if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT ) - { + if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) { iblock = parent_ino(filp->f_path.dentry); flen = 2; memcpy(fname, "..", flen); dt_type = DT_DIR; - } - else - { + } else { kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation); iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0); @@ -240,10 +224,8 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d dt_type = DT_UNKNOWN; } - if (flen) - { - if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) - { + if (flen) { + if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); diff --git a/fs/udf/directory.c b/fs/udf/directory.c index 198caa3..ff8c08f 100644 --- a/fs/udf/directory.c +++ b/fs/udf/directory.c @@ -19,10 +19,10 @@ #include <linux/buffer_head.h> #if 0 -static uint8_t * -udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, - kernel_lb_addr fe_loc, int *pos, int *offset, - struct buffer_head **bh, int *error) +static uint8_t *udf_filead_read(struct inode *dir, uint8_t * tmpad, + uint8_t ad_size, kernel_lb_addr fe_loc, + int *pos, int *offset, struct buffer_head **bh, + int *error) { int loffset = *offset; int block; @@ -34,24 +34,20 @@ udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, ad = (uint8_t *)(*bh)->b_data + *offset; *offset += ad_size; - if (!ad) - { + if (!ad) { brelse(*bh); *error = 1; return NULL; } - if (*offset == dir->i_sb->s_blocksize) - { + if (*offset == dir->i_sb->s_blocksize) { brelse(*bh); block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); if (!block) return NULL; if (!(*bh = udf_tread(dir->i_sb, block))) return NULL; - } - else if (*offset > dir->i_sb->s_blocksize) - { + } else if (*offset > dir->i_sb->s_blocksize) { ad = tmpad; remainder = dir->i_sb->s_blocksize - loffset; @@ -67,53 +63,51 @@ udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder); *offset = ad_size - remainder; } + return ad; } #endif -struct fileIdentDesc * -udf_fileident_read(struct inode *dir, loff_t *nf_pos, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi, - struct extent_position *epos, - kernel_lb_addr *eloc, uint32_t *elen, - sector_t *offset) +struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t * nf_pos, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi, + struct extent_position *epos, + kernel_lb_addr * eloc, uint32_t * elen, + sector_t * offset) { struct fileIdentDesc *fi; int i, num, block; - struct buffer_head * tmp, * bha[16]; + struct buffer_head *tmp, *bha[16]; fibh->soffset = fibh->eoffset; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fi = udf_get_fileident(UDF_I_DATA(dir) - - (UDF_I_EFE(dir) ? - sizeof(struct extendedFileEntry) : - sizeof(struct fileEntry)), - dir->i_sb->s_blocksize, &(fibh->eoffset)); - + (UDF_I_EFE(dir) ? + sizeof(struct extendedFileEntry) : + sizeof(struct fileEntry)), + dir->i_sb->s_blocksize, &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); return fi; } - if (fibh->eoffset == dir->i_sb->s_blocksize) - { + if (fibh->eoffset == dir->i_sb->s_blocksize) { int lextoffset = epos->offset; if (udf_next_aext(dir, epos, eloc, elen, 1) != - (EXT_RECORDED_ALLOCATED >> 30)) + (EXT_RECORDED_ALLOCATED >> 30)) return NULL; block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); - (*offset) ++; + (*offset)++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; @@ -125,57 +119,50 @@ udf_fileident_read(struct inode *dir, loff_t *nf_pos, return NULL; fibh->soffset = fibh->eoffset = 0; - if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) - { + if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); - if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits)) + if (i + *offset > (*elen >> dir->i_sb->s_blocksize_bits)) i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset; - for (num=0; i>0; i--) - { - block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i); + for (num = 0; i > 0; i--) { + block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset + i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } - if (num) - { + if (num) { ll_rw_block(READA, num, bha); - for (i=0; i<num; i++) + for (i = 0; i < num; i++) brelse(bha[i]); } } - } - else if (fibh->sbh != fibh->ebh) - { + } else if (fibh->sbh != fibh->ebh) { brelse(fibh->sbh); fibh->sbh = fibh->ebh; } fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, - &(fibh->eoffset)); + &(fibh->eoffset)); if (!fi) return NULL; *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); - if (fibh->eoffset <= dir->i_sb->s_blocksize) - { - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); - } - else if (fibh->eoffset > dir->i_sb->s_blocksize) - { + if (fibh->eoffset <= dir->i_sb->s_blocksize) { + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); + } else if (fibh->eoffset > dir->i_sb->s_blocksize) { int lextoffset = epos->offset; if (udf_next_aext(dir, epos, eloc, elen, 1) != - (EXT_RECORDED_ALLOCATED >> 30)) + (EXT_RECORDED_ALLOCATED >> 30)) return NULL; block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); - (*offset) ++; + (*offset)++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; @@ -188,62 +175,59 @@ udf_fileident_read(struct inode *dir, loff_t *nf_pos, if (!(fibh->ebh = udf_tread(dir->i_sb, block))) return NULL; - if (sizeof(struct fileIdentDesc) > - fibh->soffset) - { + if (sizeof(struct fileIdentDesc) > -fibh->soffset) { int fi_len; - memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset); + memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset); memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data, - sizeof(struct fileIdentDesc) + fibh->soffset); + sizeof(struct fileIdentDesc) + fibh->soffset); fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent + - le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; + le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2); fibh->eoffset = fibh->soffset + fi_len; - } - else - { - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); + } else { + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); } } return fi; } -struct fileIdentDesc * -udf_get_fileident(void * buffer, int bufsize, int * offset) +struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset) { struct fileIdentDesc *fi; int lengthThisIdent; - uint8_t * ptr; + uint8_t *ptr; int padlen; - if ( (!buffer) || (!offset) ) { - udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); + if ((!buffer) || (!offset)) { + udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, + offset); return NULL; } ptr = buffer; - if ( (*offset > 0) && (*offset < bufsize) ) { + if ((*offset > 0) && (*offset < bufsize)) { ptr += *offset; } - fi=(struct fileIdentDesc *)ptr; - if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID) - { + fi = (struct fileIdentDesc *)ptr; + if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID) { udf_debug("0x%x != TAG_IDENT_FID\n", - le16_to_cpu(fi->descTag.tagIdent)); + le16_to_cpu(fi->descTag.tagIdent)); udf_debug("offset: %u sizeof: %lu bufsize: %u\n", - *offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize); + *offset, (unsigned long)sizeof(struct fileIdentDesc), + bufsize); return NULL; } - if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize ) - { + if ((*offset + sizeof(struct fileIdentDesc)) > bufsize) { lengthThisIdent = sizeof(struct fileIdentDesc); - } - else + } else { lengthThisIdent = sizeof(struct fileIdentDesc) + fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); + } /* we need to figure padding, too! */ padlen = lengthThisIdent % UDF_NAME_PAD; @@ -255,32 +239,28 @@ udf_get_fileident(void * buffer, int bufsize, int * offset) } #if 0 -static extent_ad * -udf_get_fileextent(void * buffer, int bufsize, int * offset) +static extent_ad *udf_get_fileextent(void *buffer, int bufsize, int *offset) { - extent_ad * ext; + extent_ad *ext; struct fileEntry *fe; - uint8_t * ptr; + uint8_t *ptr; - if ( (!buffer) || (!offset) ) - { + if ((!buffer) || (!offset)) { printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); return NULL; } fe = (struct fileEntry *)buffer; - if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE ) - { + if (le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE) { udf_debug("0x%x != TAG_IDENT_FE\n", - le16_to_cpu(fe->descTag.tagIdent)); + le16_to_cpu(fe->descTag.tagIdent)); return NULL; } - ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); + ptr = (uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); - if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) ) - { + if ((*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs))) { ptr += *offset; } @@ -291,18 +271,17 @@ udf_get_fileextent(void * buffer, int bufsize, int * offset) } #endif -short_ad * -udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc) +short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, + int inc) { short_ad *sa; - if ( (!ptr) || (!offset) ) - { + if ((!ptr) || (!offset)) { printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); return NULL; } - if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) ) + if ((*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset)) return NULL; else if ((sa = (short_ad *)ptr)->extLength == 0) return NULL; @@ -312,18 +291,16 @@ udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc) return sa; } -long_ad * -udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc) +long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, int *offset, int inc) { long_ad *la; - if ( (!ptr) || (!offset) ) - { + if ((!ptr) || (!offset)) { printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); return NULL; } - if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) ) + if ((*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset)) return NULL; else if ((la = (long_ad *)ptr)->extLength == 0) return NULL; diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h index f81f2eb..5638771 100644 --- a/fs/udf/ecma_167.h +++ b/fs/udf/ecma_167.h @@ -38,8 +38,7 @@ #define _ECMA_167_H 1 /* Character set specification (ECMA 167r3 1/7.2.1) */ -typedef struct -{ +typedef struct { uint8_t charSetType; uint8_t charSetInfo[63]; } __attribute__ ((packed)) charspec; @@ -58,8 +57,7 @@ typedef struct typedef uint8_t dstring; /* Timestamp (ECMA 167r3 1/7.3) */ -typedef struct -{ +typedef struct { __le16 typeAndTimezone; __le16 year; uint8_t month; @@ -72,8 +70,7 @@ typedef struct uint8_t microseconds; } __attribute__ ((packed)) timestamp; -typedef struct -{ +typedef struct { uint16_t typeAndTimezone; int16_t year; uint8_t month; @@ -94,8 +91,7 @@ typedef struct #define TIMESTAMP_TIMEZONE_MASK 0x0FFF /* Entity identifier (ECMA 167r3 1/7.4) */ -typedef struct -{ +typedef struct { uint8_t flags; uint8_t ident[23]; uint8_t identSuffix[8]; @@ -107,8 +103,7 @@ typedef struct /* Volume Structure Descriptor (ECMA 167r3 2/9.1) */ #define VSD_STD_ID_LEN 5 -struct volStructDesc -{ +struct volStructDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -127,8 +122,7 @@ struct volStructDesc #define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */ /* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */ -struct beginningExtendedAreaDesc -{ +struct beginningExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -136,8 +130,7 @@ struct beginningExtendedAreaDesc } __attribute__ ((packed)); /* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */ -struct terminatingExtendedAreaDesc -{ +struct terminatingExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -145,8 +138,7 @@ struct terminatingExtendedAreaDesc } __attribute__ ((packed)); /* Boot Descriptor (ECMA 167r3 2/9.4) */ -struct bootDesc -{ +struct bootDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -167,21 +159,18 @@ struct bootDesc #define BOOT_FLAGS_ERASE 0x01 /* Extent Descriptor (ECMA 167r3 3/7.1) */ -typedef struct -{ +typedef struct { __le32 extLength; __le32 extLocation; } __attribute__ ((packed)) extent_ad; -typedef struct -{ +typedef struct { uint32_t extLength; uint32_t extLocation; } kernel_extent_ad; /* Descriptor Tag (ECMA 167r3 3/7.2) */ -typedef struct -{ +typedef struct { __le16 tagIdent; __le16 descVersion; uint8_t tagChecksum; @@ -204,18 +193,16 @@ typedef struct #define TAG_IDENT_LVID 0x0009 /* NSR Descriptor (ECMA 167r3 3/9.1) */ -struct NSRDesc -{ +struct NSRDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved; uint8_t structData[2040]; } __attribute__ ((packed)); - + /* Primary Volume Descriptor (ECMA 167r3 3/10.1) */ -struct primaryVolDesc -{ +struct primaryVolDesc { tag descTag; __le32 volDescSeqNum; __le32 primaryVolDescNum; @@ -244,8 +231,7 @@ struct primaryVolDesc #define PVD_FLAGS_VSID_COMMON 0x0001 /* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */ -struct anchorVolDescPtr -{ +struct anchorVolDescPtr { tag descTag; extent_ad mainVolDescSeqExt; extent_ad reserveVolDescSeqExt; @@ -253,8 +239,7 @@ struct anchorVolDescPtr } __attribute__ ((packed)); /* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */ -struct volDescPtr -{ +struct volDescPtr { tag descTag; __le32 volDescSeqNum; extent_ad nextVolDescSeqExt; @@ -262,8 +247,7 @@ struct volDescPtr } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */ -struct impUseVolDesc -{ +struct impUseVolDesc { tag descTag; __le32 volDescSeqNum; regid impIdent; @@ -271,20 +255,19 @@ struct impUseVolDesc } __attribute__ ((packed)); /* Partition Descriptor (ECMA 167r3 3/10.5) */ -struct partitionDesc -{ - tag descTag; - __le32 volDescSeqNum; - __le16 partitionFlags; - __le16 partitionNumber; - regid partitionContents; - uint8_t partitionContentsUse[128]; - __le32 accessType; - __le32 partitionStartingLocation; - __le32 partitionLength; - regid impIdent; - uint8_t impUse[128]; - uint8_t reserved[156]; +struct partitionDesc { + tag descTag; + __le32 volDescSeqNum; + __le16 partitionFlags; + __le16 partitionNumber; + regid partitionContents; + uint8_t partitionContentsUse[128]; + __le32 accessType; + __le32 partitionStartingLocation; + __le32 partitionLength; + regid impIdent; + uint8_t impUse[128]; + uint8_t reserved[156]; } __attribute__ ((packed)); /* Partition Flags (ECMA 167r3 3/10.5.3) */ @@ -307,8 +290,7 @@ struct partitionDesc #define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004 /* Logical Volume Descriptor (ECMA 167r3 3/10.6) */ -struct logicalVolDesc -{ +struct logicalVolDesc { tag descTag; __le32 volDescSeqNum; charspec descCharSet; @@ -325,8 +307,7 @@ struct logicalVolDesc } __attribute__ ((packed)); /* Generic Partition Map (ECMA 167r3 3/10.7.1) */ -struct genericPartitionMap -{ +struct genericPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionMapping[0]; @@ -338,8 +319,7 @@ struct genericPartitionMap #define GP_PARTITION_MAP_TYPE_2 0x02 /* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */ -struct genericPartitionMap1 -{ +struct genericPartitionMap1 { uint8_t partitionMapType; uint8_t partitionMapLength; __le16 volSeqNum; @@ -347,16 +327,14 @@ struct genericPartitionMap1 } __attribute__ ((packed)); /* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */ -struct genericPartitionMap2 -{ +struct genericPartitionMap2 { uint8_t partitionMapType; - uint8_t partitionMapLength; + uint8_t partitionMapLength; uint8_t partitionIdent[62]; } __attribute__ ((packed)); /* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */ -struct unallocSpaceDesc -{ +struct unallocSpaceDesc { tag descTag; __le32 volDescSeqNum; __le32 numAllocDescs; @@ -364,15 +342,13 @@ struct unallocSpaceDesc } __attribute__ ((packed)); /* Terminating Descriptor (ECMA 167r3 3/10.9) */ -struct terminatingDesc -{ +struct terminatingDesc { tag descTag; uint8_t reserved[496]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */ -struct logicalVolIntegrityDesc -{ +struct logicalVolIntegrityDesc { tag descTag; timestamp recordingDateAndTime; __le32 integrityType; @@ -390,52 +366,45 @@ struct logicalVolIntegrityDesc #define LVID_INTEGRITY_TYPE_CLOSE 0x00000001 /* Recorded Address (ECMA 167r3 4/7.1) */ -typedef struct -{ +typedef struct { __le32 logicalBlockNum; __le16 partitionReferenceNum; } __attribute__ ((packed)) lb_addr; /* ... and its in-core analog */ -typedef struct -{ +typedef struct { uint32_t logicalBlockNum; uint16_t partitionReferenceNum; } kernel_lb_addr; /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ -typedef struct -{ +typedef struct { __le32 extLength; __le32 extPosition; } __attribute__ ((packed)) short_ad; /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ -typedef struct -{ +typedef struct { __le32 extLength; lb_addr extLocation; uint8_t impUse[6]; } __attribute__ ((packed)) long_ad; -typedef struct -{ +typedef struct { uint32_t extLength; kernel_lb_addr extLocation; uint8_t impUse[6]; } kernel_long_ad; /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ -typedef struct -{ +typedef struct { __le32 extLength; __le32 recordedLength; __le32 informationLength; lb_addr extLocation; } __attribute__ ((packed)) ext_ad; -typedef struct -{ +typedef struct { uint32_t extLength; uint32_t recordedLength; uint32_t informationLength; @@ -458,8 +427,7 @@ typedef struct #define TAG_IDENT_EFE 0x010A /* File Set Descriptor (ECMA 167r3 4/14.1) */ -struct fileSetDesc -{ +struct fileSetDesc { tag descTag; timestamp recordingDateAndTime; __le16 interchangeLvl; @@ -482,8 +450,7 @@ struct fileSetDesc } __attribute__ ((packed)); /* Partition Header Descriptor (ECMA 167r3 4/14.3) */ -struct partitionHeaderDesc -{ +struct partitionHeaderDesc { short_ad unallocSpaceTable; short_ad unallocSpaceBitmap; short_ad partitionIntegrityTable; @@ -493,8 +460,7 @@ struct partitionHeaderDesc } __attribute__ ((packed)); /* File Identifier Descriptor (ECMA 167r3 4/14.4) */ -struct fileIdentDesc -{ +struct fileIdentDesc { tag descTag; __le16 fileVersionNum; uint8_t fileCharacteristics; @@ -514,16 +480,14 @@ struct fileIdentDesc #define FID_FILE_CHAR_METADATA 0x10 /* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */ -struct allocExtDesc -{ +struct allocExtDesc { tag descTag; __le32 previousAllocExtLocation; __le32 lengthAllocDescs; } __attribute__ ((packed)); /* ICB Tag (ECMA 167r3 4/14.6) */ -typedef struct -{ +typedef struct { __le32 priorRecordedNumDirectEntries; __le16 strategyType; __le16 strategyParameter; @@ -576,23 +540,20 @@ typedef struct #define ICBTAG_FLAG_STREAM 0x2000 /* Indirect Entry (ECMA 167r3 4/14.7) */ -struct indirectEntry -{ +struct indirectEntry { tag descTag; icbtag icbTag; long_ad indirectICB; } __attribute__ ((packed)); /* Terminal Entry (ECMA 167r3 4/14.8) */ -struct terminalEntry -{ +struct terminalEntry { tag descTag; icbtag icbTag; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.9) */ -struct fileEntry -{ +struct fileEntry { tag descTag; icbtag icbTag; __le32 uid; @@ -655,16 +616,14 @@ struct fileEntry #define FE_RECORD_DISPLAY_ATTR_3 0x03 /* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */ -struct extendedAttrHeaderDesc -{ +struct extendedAttrHeaderDesc { tag descTag; __le32 impAttrLocation; __le32 appAttrLocation; } __attribute__ ((packed)); /* Generic Format (ECMA 167r3 4/14.10.2) */ -struct genericFormat -{ +struct genericFormat { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -673,8 +632,7 @@ struct genericFormat } __attribute__ ((packed)); /* Character Set Information (ECMA 167r3 4/14.10.3) */ -struct charSetInfo -{ +struct charSetInfo { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -685,8 +643,7 @@ struct charSetInfo } __attribute__ ((packed)); /* Alternate Permissions (ECMA 167r3 4/14.10.4) */ -struct altPerms -{ +struct altPerms { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -697,8 +654,7 @@ struct altPerms } __attribute__ ((packed)); /* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */ -struct fileTimesExtAttr -{ +struct fileTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -715,8 +671,7 @@ struct fileTimesExtAttr #define FTE_BACKUP 0x00000002 /* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */ -struct infoTimesExtAttr -{ +struct infoTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -727,8 +682,7 @@ struct infoTimesExtAttr } __attribute__ ((packed)); /* Device Specification (ECMA 167r3 4/14.10.7) */ -struct deviceSpec -{ +struct deviceSpec { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -740,8 +694,7 @@ struct deviceSpec } __attribute__ ((packed)); /* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ -struct impUseExtAttr -{ +struct impUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -752,8 +705,7 @@ struct impUseExtAttr } __attribute__ ((packed)); /* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ -struct appUseExtAttr -{ +struct appUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -771,10 +723,8 @@ struct appUseExtAttr #define EXTATTR_IMP_USE 2048 #define EXTATTR_APP_USE 65536 - /* Unallocated Space Entry (ECMA 167r3 4/14.11) */ -struct unallocSpaceEntry -{ +struct unallocSpaceEntry { tag descTag; icbtag icbTag; __le32 lengthAllocDescs; @@ -782,8 +732,7 @@ struct unallocSpaceEntry } __attribute__ ((packed)); /* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ -struct spaceBitmapDesc -{ +struct spaceBitmapDesc { tag descTag; __le32 numOfBits; __le32 numOfBytes; @@ -791,8 +740,7 @@ struct spaceBitmapDesc } __attribute__ ((packed)); /* Partition Integrity Entry (ECMA 167r3 4/14.13) */ -struct partitionIntegrityEntry -{ +struct partitionIntegrityEntry { tag descTag; icbtag icbTag; timestamp recordingDateAndTime; @@ -815,15 +763,13 @@ struct partitionIntegrityEntry /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ /* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */ -struct logicalVolHeaderDesc -{ +struct logicalVolHeaderDesc { __le64 uniqueID; uint8_t reserved[24]; } __attribute__ ((packed)); /* Path Component (ECMA 167r3 4/14.16.1) */ -struct pathComponent -{ +struct pathComponent { uint8_t componentType; uint8_t lengthComponentIdent; __le16 componentFileVersionNum; @@ -831,8 +777,7 @@ struct pathComponent } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.17) */ -struct extendedFileEntry -{ +struct extendedFileEntry { tag descTag; icbtag icbTag; __le32 uid; diff --git a/fs/udf/file.c b/fs/udf/file.c index df070be..5d7a4ea 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -41,7 +41,7 @@ #include "udf_i.h" #include "udf_sb.h" -static int udf_adinicb_readpage(struct file *file, struct page * page) +static int udf_adinicb_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; char *kaddr; @@ -55,6 +55,7 @@ static int udf_adinicb_readpage(struct file *file, struct page * page) SetPageUptodate(page); kunmap(page); unlock_page(page); + return 0; } @@ -71,22 +72,25 @@ static int udf_adinicb_writepage(struct page *page, struct writeback_control *wb SetPageUptodate(page); kunmap(page); unlock_page(page); + return 0; } -static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) +static int udf_adinicb_prepare_write(struct file *file, struct page *page, + unsigned offset, unsigned to) { kmap(page); return 0; } -static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) +static int udf_adinicb_commit_write(struct file *file, struct page *page, + unsigned offset, unsigned to) { struct inode *inode = page->mapping->host; char *kaddr = page_address(page); memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, - kaddr + offset, to - offset); + kaddr + offset, to - offset); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); @@ -97,15 +101,15 @@ static int udf_adinicb_commit_write(struct file *file, struct page *page, unsign } const struct address_space_operations udf_adinicb_aops = { - .readpage = udf_adinicb_readpage, - .writepage = udf_adinicb_writepage, - .sync_page = block_sync_page, - .prepare_write = udf_adinicb_prepare_write, - .commit_write = udf_adinicb_commit_write, + .readpage = udf_adinicb_readpage, + .writepage = udf_adinicb_writepage, + .sync_page = block_sync_page, + .prepare_write = udf_adinicb_prepare_write, + .commit_write = udf_adinicb_commit_write, }; static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t ppos) + unsigned long nr_segs, loff_t ppos) { ssize_t retval; struct file *file = iocb->ki_filp; @@ -113,25 +117,20 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, int err, pos; size_t count = iocb->ki_left; - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (file->f_flags & O_APPEND) pos = inode->i_size; else pos = ppos; if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + - pos + count)) - { + pos + count)) { udf_expand_file_adinicb(inode, pos + count, &err); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { udf_debug("udf_expand_adinicb: err=%d\n", err); return err; } - } - else - { + } else { if (pos + count > inode->i_size) UDF_I_LENALLOC(inode) = pos + count; else @@ -140,9 +139,9 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, } retval = generic_file_aio_write(iocb, iov, nr_segs, ppos); - if (retval > 0) mark_inode_dirty(inode); + return retval; } @@ -181,48 +180,42 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, * Written, tested, and released. */ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) + unsigned long arg) { + long old_block, new_block; int result = -EINVAL; - if ( file_permission(filp, MAY_READ) != 0 ) - { + if (file_permission(filp, MAY_READ) != 0) { udf_debug("no permission to access inode %lu\n", - inode->i_ino); + inode->i_ino); return -EPERM; } - if ( !arg ) - { + if (!arg) { udf_debug("invalid argument to udf_ioctl\n"); return -EINVAL; } - switch (cmd) - { - case UDF_GETVOLIDENT: - return copy_to_user((char __user *)arg, - UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0; - case UDF_RELOCATE_BLOCKS: - { - long old, new; - - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (get_user(old, (long __user *)arg)) return -EFAULT; - if ((result = udf_relocate_blocks(inode->i_sb, - old, &new)) == 0) - result = put_user(new, (long __user *)arg); - - return result; - } - case UDF_GETEASIZE: - result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg); - break; - - case UDF_GETEABLOCK: - result = copy_to_user((char __user *)arg, UDF_I_DATA(inode), - UDF_I_LENEATTR(inode)) ? -EFAULT : 0; - break; + switch (cmd) { + case UDF_GETVOLIDENT: + return copy_to_user((char __user *)arg, + UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0; + case UDF_RELOCATE_BLOCKS: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (get_user(old_block, (long __user *)arg)) + return -EFAULT; + if ((result = udf_relocate_blocks(inode->i_sb, + old_block, &new_block)) == 0) + result = put_user(new_block, (long __user *)arg); + return result; + case UDF_GETEASIZE: + result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg); + break; + case UDF_GETEABLOCK: + result = copy_to_user((char __user *)arg, UDF_I_DATA(inode), + UDF_I_LENEATTR(inode)) ? -EFAULT : 0; + break; } return result; @@ -240,10 +233,9 @@ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, * HISTORY * */ -static int udf_release_file(struct inode * inode, struct file * filp) +static int udf_release_file(struct inode *inode, struct file *filp) { - if (filp->f_mode & FMODE_WRITE) - { + if (filp->f_mode & FMODE_WRITE) { lock_kernel(); udf_discard_prealloc(inode); unlock_kernel(); @@ -265,5 +257,5 @@ const struct file_operations udf_file_operations = { }; const struct inode_operations udf_file_inode_operations = { - .truncate = udf_truncate, + .truncate = udf_truncate, }; diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c index 6ded93e..b2c472b 100644 --- a/fs/udf/fsync.c +++ b/fs/udf/fsync.c @@ -29,9 +29,10 @@ static int udf_fsync_inode(struct inode *, int); * even pass file to fsync ? */ -int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync) +int udf_fsync_file(struct file *file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; + return udf_fsync_inode(inode, datasync); } @@ -45,6 +46,7 @@ static int udf_fsync_inode(struct inode *inode, int datasync) if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) return err; - err |= udf_sync_inode (inode); + err |= udf_sync_inode(inode); + return err ? -EIO : 0; } diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 8206983..636d8f6 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -28,7 +28,7 @@ #include "udf_i.h" #include "udf_sb.h" -void udf_free_inode(struct inode * inode) +void udf_free_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); @@ -50,7 +50,7 @@ void udf_free_inode(struct inode * inode) else UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); - + mark_buffer_dirty(sbi->s_lvidbh); } mutex_unlock(&sbi->s_alloc_mutex); @@ -58,18 +58,17 @@ void udf_free_inode(struct inode * inode) udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1); } -struct inode * udf_new_inode (struct inode *dir, int mode, int * err) +struct inode *udf_new_inode(struct inode *dir, int mode, int *err) { struct super_block *sb = dir->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); - struct inode * inode; + struct inode *inode; int block; uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum; inode = new_inode(sb); - if (!inode) - { + if (!inode) { *err = -ENOMEM; return NULL; } @@ -82,16 +81,14 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err) UDF_I_STRAT4096(inode) = 0; block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum, - start, err); - if (*err) - { + start, err); + if (*err) { iput(inode); return NULL; } mutex_lock(&sbi->s_alloc_mutex); - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse); @@ -109,14 +106,13 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err) } inode->i_mode = mode; inode->i_uid = current->fsuid; - if (dir->i_mode & S_ISGID) - { + if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; - } - else + } else { inode->i_gid = current->fsgid; + } UDF_I_LOCATION(inode).logicalBlockNum = block; UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; @@ -125,17 +121,20 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err) UDF_I_LENEATTR(inode) = 0; UDF_I_LENALLOC(inode) = 0; UDF_I_USE(inode) = 0; - if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) - { + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) { UDF_I_EFE(inode) = 1; UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE); UDF_I_DATA(inode) = kzalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); - } - else - { + } else { UDF_I_EFE(inode) = 0; UDF_I_DATA(inode) = kzalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); } + if (!UDF_I_DATA(inode)) { + iput(inode); + *err = -ENOMEM; + mutex_unlock(&sbi->s_alloc_mutex); + return NULL; + } if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) @@ -148,8 +147,7 @@ struct inode * udf_new_inode (struct inode *dir, int mode, int * err) mark_inode_dirty(inode); mutex_unlock(&sbi->s_alloc_mutex); - if (DQUOT_ALLOC_INODE(inode)) - { + if (DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index bf7de0b..0d2c416 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -49,19 +49,20 @@ MODULE_LICENSE("GPL"); static mode_t udf_convert_permissions(struct fileEntry *); static int udf_update_inode(struct inode *, int); static void udf_fill_inode(struct inode *, struct buffer_head *); +static int udf_alloc_i_data(struct inode *inode, size_t size); static struct buffer_head *inode_getblk(struct inode *, sector_t, int *, - long *, int *); + long *, int *); static int8_t udf_insert_aext(struct inode *, struct extent_position, - kernel_lb_addr, uint32_t); + kernel_lb_addr, uint32_t); static void udf_split_extents(struct inode *, int *, int, int, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_prealloc_extents(struct inode *, int, int, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_merge_extents(struct inode *, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_update_extents(struct inode *, - kernel_long_ad [EXTENT_MERGE_SIZE], int, int, - struct extent_position *); + kernel_long_ad[EXTENT_MERGE_SIZE], int, int, + struct extent_position *); static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); /* @@ -80,7 +81,7 @@ static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); * * Called at the last iput() if i_nlink is zero. */ -void udf_delete_inode(struct inode * inode) +void udf_delete_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); @@ -96,6 +97,7 @@ void udf_delete_inode(struct inode * inode) unlock_kernel(); return; + no_delete: clear_inode(inode); } @@ -131,26 +133,27 @@ static int udf_readpage(struct file *file, struct page *page) return block_read_full_page(page, udf_get_block); } -static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +static int udf_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) { return block_prepare_write(page, from, to, udf_get_block); } static sector_t udf_bmap(struct address_space *mapping, sector_t block) { - return generic_block_bmap(mapping,block,udf_get_block); + return generic_block_bmap(mapping, block, udf_get_block); } const struct address_space_operations udf_aops = { - .readpage = udf_readpage, - .writepage = udf_writepage, - .sync_page = block_sync_page, - .prepare_write = udf_prepare_write, - .commit_write = generic_commit_write, - .bmap = udf_bmap, + .readpage = udf_readpage, + .writepage = udf_writepage, + .sync_page = block_sync_page, + .prepare_write = udf_prepare_write, + .commit_write = generic_commit_write, + .bmap = udf_bmap, }; -void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) +void udf_expand_file_adinicb(struct inode *inode, int newsize, int *err) { struct page *page; char *kaddr; @@ -162,8 +165,7 @@ void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) /* from now on we have normal address_space methods */ inode->i_data.a_ops = &udf_aops; - if (!UDF_I_LENALLOC(inode)) - { + if (!UDF_I_LENALLOC(inode)) { if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; else @@ -175,19 +177,18 @@ void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) page = grab_cache_page(inode->i_mapping, 0); BUG_ON(!PageLocked(page)); - if (!PageUptodate(page)) - { + if (!PageUptodate(page)) { kaddr = kmap(page); memset(kaddr + UDF_I_LENALLOC(inode), 0x00, - PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); + PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), - UDF_I_LENALLOC(inode)); + UDF_I_LENALLOC(inode)); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); } memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0x00, - UDF_I_LENALLOC(inode)); + UDF_I_LENALLOC(inode)); UDF_I_LENALLOC(inode) = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; @@ -200,7 +201,8 @@ void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) mark_inode_dirty(inode); } -struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) +struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block, + int *err) { int newblock; struct buffer_head *dbh = NULL; @@ -219,8 +221,7 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int else alloctype = ICBTAG_FLAG_AD_LONG; - if (!inode->i_size) - { + if (!inode->i_size) { UDF_I_ALLOCTYPE(inode) = alloctype; mark_inode_dirty(inode); return NULL; @@ -228,13 +229,12 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int /* alloc block, and copy data to it */ *block = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, - UDF_I_LOCATION(inode).logicalBlockNum, err); - + UDF_I_LOCATION(inode).partitionReferenceNum, + UDF_I_LOCATION(inode).logicalBlockNum, err); if (!(*block)) return NULL; newblock = udf_get_pblock(inode->i_sb, *block, - UDF_I_LOCATION(inode).partitionReferenceNum, 0); + UDF_I_LOCATION(inode).partitionReferenceNum, 0); if (!newblock) return NULL; dbh = udf_tgetblk(inode->i_sb, newblock); @@ -250,12 +250,10 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int sfibh.sbh = sfibh.ebh = NULL; dfibh.soffset = dfibh.eoffset = 0; dfibh.sbh = dfibh.ebh = dbh; - while ( (f_pos < size) ) - { + while ((f_pos < size)) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL); - if (!sfi) - { + if (!sfi) { brelse(dbh); return NULL; } @@ -265,8 +263,7 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, - sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse))) - { + sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse))) { UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; brelse(dbh); return NULL; @@ -291,14 +288,14 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int return dbh; } -static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) +static int udf_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) { int err, new; struct buffer_head *bh; unsigned long phys; - if (!create) - { + if (!create) { phys = udf_block_map(inode, block); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -314,10 +311,9 @@ static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head if (block < 0) goto abort_negative; - if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) - { - UDF_I_NEXT_ALLOC_BLOCK(inode) ++; - UDF_I_NEXT_ALLOC_GOAL(inode) ++; + if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) { + UDF_I_NEXT_ALLOC_BLOCK(inode)++; + UDF_I_NEXT_ALLOC_GOAL(inode)++; } err = 0; @@ -331,6 +327,7 @@ static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head if (new) set_buffer_new(bh_result); map_bh(bh_result, inode->i_sb, phys); + abort: unlock_kernel(); return err; @@ -340,20 +337,18 @@ abort_negative: goto abort; } -static struct buffer_head * -udf_getblk(struct inode *inode, long block, int create, int *err) +static struct buffer_head *udf_getblk(struct inode *inode, long block, + int create, int *err) { + struct buffer_head *bh; struct buffer_head dummy; dummy.b_state = 0; dummy.b_blocknr = -1000; *err = udf_get_block(inode, block, &dummy, create); - if (!*err && buffer_mapped(&dummy)) - { - struct buffer_head *bh; + if (!*err && buffer_mapped(&dummy)) { bh = sb_getblk(inode->i_sb, dummy.b_blocknr); - if (buffer_new(&dummy)) - { + if (buffer_new(&dummy)) { lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); @@ -362,33 +357,36 @@ udf_getblk(struct inode *inode, long block, int create, int *err) } return bh; } + return NULL; } /* Extend the file by 'blocks' blocks, return the number of extents added */ int udf_extend_file(struct inode *inode, struct extent_position *last_pos, - kernel_long_ad *last_ext, sector_t blocks) + kernel_long_ad * last_ext, sector_t blocks) { sector_t add; int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); struct super_block *sb = inode->i_sb; - kernel_lb_addr prealloc_loc = {0, 0}; + kernel_lb_addr prealloc_loc = {}; int prealloc_len = 0; /* The previous extent is fake and we should not extend by anything * - there's nothing to do... */ if (!blocks && fake) return 0; + /* Round the last extent up to a multiple of block size */ if (last_ext->extLength & (sb->s_blocksize - 1)) { last_ext->extLength = (last_ext->extLength & UDF_EXTENT_FLAG_MASK) | (((last_ext->extLength & UDF_EXTENT_LENGTH_MASK) + - sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); UDF_I_LENEXTENTS(inode) = (UDF_I_LENEXTENTS(inode) + sb->s_blocksize - 1) & - ~(sb->s_blocksize - 1); + ~(sb->s_blocksize - 1); } + /* Last extent are just preallocated blocks? */ if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_ALLOCATED) { /* Save the extent so that we can reattach it to the end */ @@ -400,10 +398,11 @@ int udf_extend_file(struct inode *inode, struct extent_position *last_pos, last_ext->extLocation.logicalBlockNum = 0; last_ext->extLocation.partitionReferenceNum = 0; } + /* Can we merge with the previous extent? */ if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED) { - add = ((1<<30) - sb->s_blocksize - (last_ext->extLength & - UDF_EXTENT_LENGTH_MASK)) >> sb->s_blocksize_bits; + add = ((1 << 30) - sb->s_blocksize - (last_ext->extLength & + UDF_EXTENT_LENGTH_MASK)) >> sb->s_blocksize_bits; if (add > blocks) add = blocks; blocks -= add; @@ -412,11 +411,12 @@ int udf_extend_file(struct inode *inode, struct extent_position *last_pos, if (fake) { udf_add_aext(inode, last_pos, last_ext->extLocation, - last_ext->extLength, 1); + last_ext->extLength, 1); count++; - } - else + } else { udf_write_aext(inode, last_pos, last_ext->extLocation, last_ext->extLength, 1); + } + /* Managed to do everything necessary? */ if (!blocks) goto out; @@ -426,11 +426,12 @@ int udf_extend_file(struct inode *inode, struct extent_position *last_pos, last_ext->extLocation.partitionReferenceNum = 0; add = (1 << (30-sb->s_blocksize_bits)) - 1; last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (add << sb->s_blocksize_bits); + /* Create enough extents to cover the whole hole */ while (blocks > add) { blocks -= add; if (udf_add_aext(inode, last_pos, last_ext->extLocation, - last_ext->extLength, 1) == -1) + last_ext->extLength, 1) == -1) return -1; count++; } @@ -438,10 +439,11 @@ int udf_extend_file(struct inode *inode, struct extent_position *last_pos, last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (blocks << sb->s_blocksize_bits); if (udf_add_aext(inode, last_pos, last_ext->extLocation, - last_ext->extLength, 1) == -1) + last_ext->extLength, 1) == -1) return -1; count++; } + out: /* Do we have some preallocated blocks saved? */ if (prealloc_len) { @@ -451,6 +453,7 @@ out: last_ext->extLength = prealloc_len; count++; } + /* last_pos should point to the last written extent... */ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) last_pos->offset -= sizeof(short_ad); @@ -458,11 +461,12 @@ out: last_pos->offset -= sizeof(long_ad); else return -1; + return count; } -static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, - int *err, long *phys, int *new) +static struct buffer_head *inode_getblk(struct inode *inode, sector_t block, + int *err, long *phys, int *new) { static sector_t last_block; struct buffer_head *result = NULL; @@ -486,18 +490,15 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, b_off = (loff_t)block << inode->i_sb->s_blocksize_bits; /* find the extent which contains the block we are looking for. - alternate between laarr[0] and laarr[1] for locations of the - current extent, and the previous extent */ - do - { - if (prev_epos.bh != cur_epos.bh) - { + alternate between laarr[0] and laarr[1] for locations of the + current extent, and the previous extent */ + do { + if (prev_epos.bh != cur_epos.bh) { brelse(prev_epos.bh); get_bh(cur_epos.bh); prev_epos.bh = cur_epos.bh; } - if (cur_epos.bh != next_epos.bh) - { + if (cur_epos.bh != next_epos.bh) { brelse(cur_epos.bh); get_bh(next_epos.bh); cur_epos.bh = next_epos.bh; @@ -522,9 +523,9 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) pgoal = eloc.logicalBlockNum + ((elen + inode->i_sb->s_blocksize - 1) >> - inode->i_sb->s_blocksize_bits); + inode->i_sb->s_blocksize_bits); - count ++; + count++; } while (lbcount + elen <= b_off); b_off -= lbcount; @@ -537,15 +538,13 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, udf_next_aext(inode, &cur_epos, &tmpeloc, &tmpelen, 0); /* if the extent is allocated and recorded, return the block - if the extent is not a multiple of the blocksize, round up */ + if the extent is not a multiple of the blocksize, round up */ - if (etype == (EXT_RECORDED_ALLOCATED >> 30)) - { - if (elen & (inode->i_sb->s_blocksize - 1)) - { + if (etype == (EXT_RECORDED_ALLOCATED >> 30)) { + if (elen & (inode->i_sb->s_blocksize - 1)) { elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1)); + ~(inode->i_sb->s_blocksize - 1)); etype = udf_write_aext(inode, &cur_epos, eloc, elen, 1); } brelse(prev_epos.bh); @@ -558,16 +557,14 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, last_block = block; /* Are we beyond EOF? */ - if (etype == -1) - { + if (etype == -1) { int ret; if (count) { if (c) laarr[0] = laarr[1]; startnum = 1; - } - else { + } else { /* Create a fake extent when there's not one */ memset(&laarr[0].extLocation, 0x00, sizeof(kernel_lb_addr)); laarr[0].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; @@ -597,18 +594,16 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | inode->i_sb->s_blocksize; memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr)); - count ++; - endnum ++; + count++; + endnum++; } - endnum = c+1; + endnum = c + 1; lastblock = 1; - } - else { + } else { endnum = startnum = ((count > 2) ? 2 : count); /* if the current extent is in position 0, swap it with the previous */ - if (!c && count != 1) - { + if (!c && count != 1) { laarr[2] = laarr[0]; laarr[0] = laarr[1]; laarr[1] = laarr[2]; @@ -616,37 +611,33 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, } /* if the current block is located in an extent, read the next extent */ - if ((etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 0)) != -1) - { - laarr[c+1].extLength = (etype << 30) | elen; - laarr[c+1].extLocation = eloc; - count ++; - startnum ++; - endnum ++; - } - else { + if ((etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 0)) != -1) { + laarr[c + 1].extLength = (etype << 30) | elen; + laarr[c + 1].extLocation = eloc; + count++; + startnum++; + endnum++; + } else { lastblock = 1; } } /* if the current extent is not recorded but allocated, get the - block in the extent corresponding to the requested block */ - if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) + * block in the extent corresponding to the requested block */ + if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { newblocknum = laarr[c].extLocation.logicalBlockNum + offset; - else /* otherwise, allocate a new block */ - { + } else { /* otherwise, allocate a new block */ if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block) goal = UDF_I_NEXT_ALLOC_GOAL(inode); - if (!goal) - { + if (!goal) { if (!(goal = pgoal)) goal = UDF_I_LOCATION(inode).logicalBlockNum + 1; } if (!(newblocknum = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) - { + UDF_I_LOCATION(inode).partitionReferenceNum, + goal, err))) { brelse(prev_epos.bh); *err = -ENOSPC; return NULL; @@ -655,8 +646,8 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, } /* if the extent the requsted block is located in contains multiple blocks, - split the extent into at most three extents. blocks prior to requested - block, requested block, and blocks after requested block */ + * split the extent into at most three extents. blocks prior to requested + * block, requested block, and blocks after requested block */ udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum); #ifdef UDF_PREALLOCATE @@ -668,15 +659,14 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, udf_merge_extents(inode, laarr, &endnum); /* write back the new extents, inserting new extents if the new number - of extents is greater than the old number, and deleting extents if - the new number of extents is less than the old number */ + * of extents is greater than the old number, and deleting extents if + * the new number of extents is less than the old number */ udf_update_extents(inode, laarr, startnum, endnum, &prev_epos); brelse(prev_epos.bh); if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum, - UDF_I_LOCATION(inode).partitionReferenceNum, 0))) - { + UDF_I_LOCATION(inode).partitionReferenceNum, 0))) { return NULL; } *phys = newblock; @@ -690,156 +680,139 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block, udf_sync_inode(inode); else mark_inode_dirty(inode); + return result; } -static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +static void udf_split_extents(struct inode *inode, int *c, int offset, + int newblocknum, + kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) || - (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - { + (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { int curr = *c; int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; int8_t etype = (laarr[curr].extLength >> 30); - if (blen == 1) + if (blen == 1) { ; - else if (!offset || blen == offset + 1) - { - laarr[curr+2] = laarr[curr+1]; - laarr[curr+1] = laarr[curr]; - } - else - { - laarr[curr+3] = laarr[curr+1]; - laarr[curr+2] = laarr[curr+1] = laarr[curr]; + } else if (!offset || blen == offset + 1) { + laarr[curr + 2] = laarr[curr + 1]; + laarr[curr + 1] = laarr[curr]; + } else { + laarr[curr + 3] = laarr[curr + 1]; + laarr[curr + 2] = laarr[curr + 1] = laarr[curr]; } - if (offset) - { - if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { + if (offset) { + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset); laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (offset << inode->i_sb->s_blocksize_bits); laarr[curr].extLocation.logicalBlockNum = 0; laarr[curr].extLocation.partitionReferenceNum = 0; - } - else + } else { laarr[curr].extLength = (etype << 30) | (offset << inode->i_sb->s_blocksize_bits); - curr ++; - (*c) ++; - (*endnum) ++; + } + curr++; + (*c)++; + (*endnum)++; } - + laarr[curr].extLocation.logicalBlockNum = newblocknum; if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) laarr[curr].extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; laarr[curr].extLength = EXT_RECORDED_ALLOCATED | inode->i_sb->s_blocksize; - curr ++; + curr++; - if (blen != offset + 1) - { + if (blen != offset + 1) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) laarr[curr].extLocation.logicalBlockNum += (offset + 1); laarr[curr].extLength = (etype << 30) | ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); - curr ++; - (*endnum) ++; + curr++; + (*endnum)++; } } } static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) + kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { int start, length = 0, currlength = 0, i; - if (*endnum >= (c+1)) - { + if (*endnum >= (c + 1)) { if (!lastblock) return; else start = c; - } - else - { - if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - start = c+1; - length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - } - else + } else { + if ((laarr[c + 1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + start = c + 1; + length = currlength = (((laarr[c + 1].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + } else { start = c; + } } - for (i=start+1; i<=*endnum; i++) - { - if (i == *endnum) - { + for (i = start + 1; i <= *endnum; i++) { + if (i == *endnum) { if (lastblock) length += UDF_DEFAULT_PREALLOC_BLOCKS; - } - else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) + } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - else + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + } else { break; + } } - if (length) - { + if (length) { int next = laarr[start].extLocation.logicalBlockNum + (((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); int numalloc = udf_prealloc_blocks(inode->i_sb, inode, - laarr[start].extLocation.partitionReferenceNum, - next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : - UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); - - if (numalloc) - { - if (start == (c+1)) + laarr[start].extLocation.partitionReferenceNum, + next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : + UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); + if (numalloc) { + if (start == (c + 1)) { laarr[start].extLength += (numalloc << inode->i_sb->s_blocksize_bits); - else - { - memmove(&laarr[c+2], &laarr[c+1], - sizeof(long_ad) * (*endnum - (c+1))); - (*endnum) ++; - laarr[c+1].extLocation.logicalBlockNum = next; - laarr[c+1].extLocation.partitionReferenceNum = + } else { + memmove(&laarr[c + 2], &laarr[c + 1], + sizeof(long_ad) * (*endnum - (c + 1))); + (*endnum)++; + laarr[c + 1].extLocation.logicalBlockNum = next; + laarr[c + 1].extLocation.partitionReferenceNum = laarr[c].extLocation.partitionReferenceNum; - laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED | + laarr[c + 1].extLength = EXT_NOT_RECORDED_ALLOCATED | (numalloc << inode->i_sb->s_blocksize_bits); - start = c+1; + start = c + 1; } - for (i=start+1; numalloc && i<*endnum; i++) - { + for (i = start + 1; numalloc && i < *endnum; i++) { int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; - if (elen > numalloc) - { + if (elen > numalloc) { laarr[i].extLength -= (numalloc << inode->i_sb->s_blocksize_bits); numalloc = 0; - } - else - { + } else { numalloc -= elen; - if (*endnum > (i+1)) - memmove(&laarr[i], &laarr[i+1], - sizeof(long_ad) * (*endnum - (i+1))); - i --; - (*endnum) --; + if (*endnum > (i + 1)) + memmove(&laarr[i], &laarr[i + 1], + sizeof(long_ad) * (*endnum - (i + 1))); + i--; + (*endnum)--; } } UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits; @@ -848,82 +821,70 @@ static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, } static void udf_merge_extents(struct inode *inode, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) + kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { int i; - for (i=0; i<(*endnum-1); i++) - { - if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30)) - { + for (i = 0; i < (*endnum - 1); i++) { + if ((laarr[i].extLength >> 30) == (laarr[i + 1].extLength >> 30)) { if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) || - ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == - (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) - { + ((laarr[i + 1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) { if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) - { - laarr[i+1].extLength = (laarr[i+1].extLength - - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); + (laarr[i + 1].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { + laarr[i + 1].extLength = (laarr[i + 1].extLength - + (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize - 1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; - laarr[i+1].extLocation.logicalBlockNum = + laarr[i + 1].extLocation.logicalBlockNum = laarr[i].extLocation.logicalBlockNum + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >> - inode->i_sb->s_blocksize_bits); - } - else - { - laarr[i].extLength = laarr[i+1].extLength + + inode->i_sb->s_blocksize_bits); + } else { + laarr[i].extLength = laarr[i + 1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); - if (*endnum > (i+2)) - memmove(&laarr[i+1], &laarr[i+2], - sizeof(long_ad) * (*endnum - (i+2))); - i --; - (*endnum) --; + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); + if (*endnum > (i + 2)) + memmove(&laarr[i + 1], &laarr[i + 2], + sizeof(long_ad) * (*endnum - (i + 2))); + i--; + (*endnum)--; } } - } - else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && - ((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) - { + } else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && + ((laarr[i + 1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, - ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) - { - laarr[i+1].extLength = (laarr[i+1].extLength - - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); + (laarr[i + 1].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { + laarr[i + 1].extLength = (laarr[i + 1].extLength - + (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize - 1); laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; - } - else - { - laarr[i].extLength = laarr[i+1].extLength + + } else { + laarr[i].extLength = laarr[i + 1].extLength + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); - if (*endnum > (i+2)) - memmove(&laarr[i+1], &laarr[i+2], - sizeof(long_ad) * (*endnum - (i+2))); - i --; - (*endnum) --; + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); + if (*endnum > (i + 2)) + memmove(&laarr[i + 1], &laarr[i + 2], + sizeof(long_ad) * (*endnum - (i + 2))); + i--; + (*endnum)--; } - } - else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { + } else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, - ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); laarr[i].extLocation.logicalBlockNum = 0; laarr[i].extLocation.partitionReferenceNum = 0; laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) | @@ -933,43 +894,39 @@ static void udf_merge_extents(struct inode *inode, } static void udf_update_extents(struct inode *inode, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, - struct extent_position *epos) + kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int startnum, int endnum, + struct extent_position *epos) { int start = 0, i; kernel_lb_addr tmploc; uint32_t tmplen; - if (startnum > endnum) - { - for (i=0; i<(startnum-endnum); i++) + if (startnum > endnum) { + for (i = 0; i < (startnum - endnum); i++) udf_delete_aext(inode, *epos, laarr[i].extLocation, - laarr[i].extLength); - } - else if (startnum < endnum) - { - for (i=0; i<(endnum-startnum); i++) - { + laarr[i].extLength); + } else if (startnum < endnum) { + for (i = 0; i < (endnum - startnum); i++) { udf_insert_aext(inode, *epos, laarr[i].extLocation, - laarr[i].extLength); + laarr[i].extLength); udf_next_aext(inode, epos, &laarr[i].extLocation, - &laarr[i].extLength, 1); - start ++; + &laarr[i].extLength, 1); + start++; } } - for (i=start; i<endnum; i++) - { + for (i = start; i < endnum; i++) { udf_next_aext(inode, epos, &tmploc, &tmplen, 0); udf_write_aext(inode, epos, laarr[i].extLocation, - laarr[i].extLength, 1); + laarr[i].extLength, 1); } } -struct buffer_head * udf_bread(struct inode * inode, int block, - int create, int * err) +struct buffer_head *udf_bread(struct inode *inode, int block, + int create, int *err) { - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; bh = udf_getblk(inode, block, create, err); if (!bh) @@ -977,65 +934,61 @@ struct buffer_head * udf_bread(struct inode * inode, int block, if (buffer_uptodate(bh)) return bh; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); if (buffer_uptodate(bh)) return bh; + brelse(bh); *err = -EIO; return NULL; } -void udf_truncate(struct inode * inode) +void udf_truncate(struct inode *inode) { int offset; int err; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) + S_ISLNK(inode->i_mode))) return; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; lock_kernel(); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + - inode->i_size)) - { + inode->i_size)) { udf_expand_file_adinicb(inode, inode->i_size, &err); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { inode->i_size = UDF_I_LENALLOC(inode); unlock_kernel(); return; - } - else + } else { udf_truncate_extents(inode); - } - else - { + } + } else { offset = inode->i_size & (inode->i_sb->s_blocksize - 1); - memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode)); + memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, + inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode)); UDF_I_LENALLOC(inode) = inode->i_size; } - } - else - { + } else { block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block); udf_truncate_extents(inode); - } + } inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) - udf_sync_inode (inode); + udf_sync_inode(inode); else mark_inode_dirty(inode); unlock_kernel(); } -static void -__udf_read_inode(struct inode *inode) +static void __udf_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; struct fileEntry *fe; @@ -1054,20 +1007,17 @@ __udf_read_inode(struct inode *inode) * i_op = NULL; */ bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); - - if (!bh) - { + if (!bh) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n", - inode->i_ino); + inode->i_ino); make_bad_inode(inode); return; } if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE && - ident != TAG_IDENT_USE) - { + ident != TAG_IDENT_USE) { printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n", - inode->i_ino, ident); + inode->i_ino, ident); brelse(bh); make_bad_inode(inode); return; @@ -1075,51 +1025,43 @@ __udf_read_inode(struct inode *inode) fe = (struct fileEntry *)bh->b_data; - if (le16_to_cpu(fe->icbTag.strategyType) == 4096) - { + if (le16_to_cpu(fe->icbTag.strategyType) == 4096) { struct buffer_head *ibh = NULL, *nbh = NULL; struct indirectEntry *ie; ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident); - if (ident == TAG_IDENT_IE) - { - if (ibh) - { + if (ident == TAG_IDENT_IE) { + if (ibh) { kernel_lb_addr loc; ie = (struct indirectEntry *)ibh->b_data; - + loc = lelb_to_cpu(ie->indirectICB.extLocation); - - if (ie->indirectICB.extLength && - (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) - { + + if (ie->indirectICB.extLength && + (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) { if (ident == TAG_IDENT_FE || - ident == TAG_IDENT_EFE) - { - memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(kernel_lb_addr)); + ident == TAG_IDENT_EFE) { + memcpy(&UDF_I_LOCATION(inode), &loc, + sizeof(kernel_lb_addr)); brelse(bh); brelse(ibh); brelse(nbh); __udf_read_inode(inode); return; - } - else - { + } else { brelse(nbh); brelse(ibh); } - } - else + } else { brelse(ibh); + } } - } - else + } else { brelse(ibh); - } - else if (le16_to_cpu(fe->icbTag.strategyType) != 4) - { + } + } else if (le16_to_cpu(fe->icbTag.strategyType) != 4) { printk(KERN_ERR "udf: unsupported strategy type: %d\n", - le16_to_cpu(fe->icbTag.strategyType)); + le16_to_cpu(fe->icbTag.strategyType)); brelse(bh); make_bad_inode(inode); return; @@ -1152,87 +1094,83 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) UDF_I_LENALLOC(inode) = 0; UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; - if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) - { + if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) { UDF_I_EFE(inode) = 1; UDF_I_USE(inode) = 0; - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); - } - else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) - { + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry))) { + make_bad_inode(inode); + return; + } + memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), + inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); + } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 0; - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); - } - else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) - { + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - sizeof(struct fileEntry))) { + make_bad_inode(inode); + return; + } + memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), + inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + } else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { UDF_I_EFE(inode) = 0; UDF_I_USE(inode) = 1; UDF_I_LENALLOC(inode) = - le32_to_cpu( - ((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs); - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); + le32_to_cpu(((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs); + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry))) { + make_bad_inode(inode); + return; + } + memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), + inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); return; } inode->i_uid = le32_to_cpu(fe->uid); if (inode->i_uid == -1 || UDF_QUERY_FLAG(inode->i_sb, - UDF_FLAG_UID_IGNORE)) + UDF_FLAG_UID_IGNORE)) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; inode->i_gid = le32_to_cpu(fe->gid); if (inode->i_gid == -1 || UDF_QUERY_FLAG(inode->i_sb, - UDF_FLAG_GID_IGNORE)) + UDF_FLAG_GID_IGNORE)) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; inode->i_nlink = le16_to_cpu(fe->fileLinkCount); if (!inode->i_nlink) inode->i_nlink = 1; - + inode->i_size = le64_to_cpu(fe->informationLength); UDF_I_LENEXTENTS(inode) = inode->i_size; inode->i_mode = udf_convert_permissions(fe); inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; - if (UDF_I_EFE(inode) == 0) - { + if (UDF_I_EFE(inode) == 0) { inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->accessTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(fe->accessTime))) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->modificationTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(fe->modificationTime))) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->attrTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(fe->attrTime))) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } @@ -1240,53 +1178,39 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode); - } - else - { - inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << - (inode->i_sb->s_blocksize_bits - 9); + } else { + inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << + (inode->i_sb->s_blocksize_bits - 9); - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->accessTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->accessTime))) { inode->i_atime.tv_sec = convtime; inode->i_atime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); } - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->modificationTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->modificationTime))) { inode->i_mtime.tv_sec = convtime; inode->i_mtime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); } - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->createTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->createTime))) { UDF_I_CRTIME(inode).tv_sec = convtime; UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000; - } - else - { + } else { UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb); } - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->attrTime)) ) - { + if (udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->attrTime))) { inode->i_ctime.tv_sec = convtime; inode->i_ctime.tv_nsec = convtime_usec * 1000; - } - else - { + } else { inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); } @@ -1296,86 +1220,74 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode); } - switch (fe->icbTag.fileType) - { - case ICBTAG_FILE_TYPE_DIRECTORY: - { - inode->i_op = &udf_dir_inode_operations; - inode->i_fop = &udf_dir_operations; - inode->i_mode |= S_IFDIR; - inc_nlink(inode); - break; - } - case ICBTAG_FILE_TYPE_REALTIME: - case ICBTAG_FILE_TYPE_REGULAR: - case ICBTAG_FILE_TYPE_UNDEF: - { - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - inode->i_data.a_ops = &udf_adinicb_aops; - else - inode->i_data.a_ops = &udf_aops; - inode->i_op = &udf_file_inode_operations; - inode->i_fop = &udf_file_operations; - inode->i_mode |= S_IFREG; - break; - } - case ICBTAG_FILE_TYPE_BLOCK: - { - inode->i_mode |= S_IFBLK; - break; - } - case ICBTAG_FILE_TYPE_CHAR: - { - inode->i_mode |= S_IFCHR; - break; - } - case ICBTAG_FILE_TYPE_FIFO: - { - init_special_inode(inode, inode->i_mode | S_IFIFO, 0); - break; - } - case ICBTAG_FILE_TYPE_SOCKET: - { - init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); - break; - } - case ICBTAG_FILE_TYPE_SYMLINK: - { - inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &page_symlink_inode_operations; - inode->i_mode = S_IFLNK|S_IRWXUGO; - break; - } - default: - { - printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", - inode->i_ino, fe->icbTag.fileType); - make_bad_inode(inode); - return; - } + switch (fe->icbTag.fileType) { + case ICBTAG_FILE_TYPE_DIRECTORY: + inode->i_op = &udf_dir_inode_operations; + inode->i_fop = &udf_dir_operations; + inode->i_mode |= S_IFDIR; + inc_nlink(inode); + break; + case ICBTAG_FILE_TYPE_REALTIME: + case ICBTAG_FILE_TYPE_REGULAR: + case ICBTAG_FILE_TYPE_UNDEF: + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) + inode->i_data.a_ops = &udf_adinicb_aops; + else + inode->i_data.a_ops = &udf_aops; + inode->i_op = &udf_file_inode_operations; + inode->i_fop = &udf_file_operations; + inode->i_mode |= S_IFREG; + break; + case ICBTAG_FILE_TYPE_BLOCK: + inode->i_mode |= S_IFBLK; + break; + case ICBTAG_FILE_TYPE_CHAR: + inode->i_mode |= S_IFCHR; + break; + case ICBTAG_FILE_TYPE_FIFO: + init_special_inode(inode, inode->i_mode | S_IFIFO, 0); + break; + case ICBTAG_FILE_TYPE_SOCKET: + init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); + break; + case ICBTAG_FILE_TYPE_SYMLINK: + inode->i_data.a_ops = &udf_symlink_aops; + inode->i_op = &page_symlink_inode_operations; + inode->i_mode = S_IFLNK | S_IRWXUGO; + break; + default: + printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", + inode->i_ino, fe->icbTag.fileType); + make_bad_inode(inode); + return; } - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - { - struct deviceSpec *dsea = - (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1); - - if (dsea) - { - init_special_inode(inode, inode->i_mode, MKDEV( - le32_to_cpu(dsea->majorDeviceIdent), - le32_to_cpu(dsea->minorDeviceIdent))); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + struct deviceSpec *dsea = (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); + if (dsea) { + init_special_inode(inode, inode->i_mode, + MKDEV(le32_to_cpu(dsea->majorDeviceIdent), + le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */ - } - else - { + } else { make_bad_inode(inode); } } } -static mode_t -udf_convert_permissions(struct fileEntry *fe) +static int udf_alloc_i_data(struct inode *inode, size_t size) +{ + UDF_I_DATA(inode) = kmalloc(size, GFP_KERNEL); + + if (!UDF_I_DATA(inode)) { + printk(KERN_ERR "udf:udf_alloc_i_data (ino %ld) no free memory\n", + inode->i_ino); + return -ENOMEM; + } + + return 0; +} + +static mode_t udf_convert_permissions(struct fileEntry *fe) { mode_t mode; uint32_t permissions; @@ -1409,22 +1321,23 @@ udf_convert_permissions(struct fileEntry *fe) * Written, tested, and released. */ -int udf_write_inode(struct inode * inode, int sync) +int udf_write_inode(struct inode *inode, int sync) { int ret; + lock_kernel(); ret = udf_update_inode(inode, sync); unlock_kernel(); + return ret; } -int udf_sync_inode(struct inode * inode) +int udf_sync_inode(struct inode *inode) { return udf_update_inode(inode, 1); } -static int -udf_update_inode(struct inode *inode, int do_sync) +static int udf_update_inode(struct inode *inode, int do_sync) { struct buffer_head *bh = NULL; struct fileEntry *fe; @@ -1436,11 +1349,8 @@ udf_update_inode(struct inode *inode, int do_sync) kernel_timestamp cpu_time; int err = 0; - bh = udf_tread(inode->i_sb, - udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0)); - - if (!bh) - { + bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0)); + if (!bh) { udf_debug("bread failure\n"); return -EIO; } @@ -1450,23 +1360,23 @@ udf_update_inode(struct inode *inode, int do_sync) fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; - if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) - { + if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) { struct unallocSpaceEntry *use = (struct unallocSpaceEntry *)bh->b_data; use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); - memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); - crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) - - sizeof(tag); + memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), + inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); + crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) - sizeof(tag); use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); use->descTag.descCRCLength = cpu_to_le16(crclen); use->descTag.descCRC = cpu_to_le16(udf_crc((char *)use + sizeof(tag), crclen, 0)); use->descTag.tagChecksum = 0; - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) { if (i != 4) use->descTag.tagChecksum += ((uint8_t *)&(use->descTag))[i]; + } mark_buffer_dirty(bh); brelse(bh); @@ -1475,11 +1385,13 @@ udf_update_inode(struct inode *inode, int do_sync) if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET)) fe->uid = cpu_to_le32(-1); - else fe->uid = cpu_to_le32(inode->i_uid); + else + fe->uid = cpu_to_le32(inode->i_uid); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_FORGET)) fe->gid = cpu_to_le32(-1); - else fe->gid = cpu_to_le32(inode->i_gid); + else + fe->gid = cpu_to_le32(inode->i_gid); udfperms = ((inode->i_mode & S_IRWXO) ) | ((inode->i_mode & S_IRWXG) << 2) | @@ -1498,23 +1410,19 @@ udf_update_inode(struct inode *inode, int do_sync) fe->informationLength = cpu_to_le64(inode->i_size); - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - { + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { regid *eid; struct deviceSpec *dsea = - (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1); - - if (!dsea) - { + (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); + if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, - sizeof(struct deviceSpec) + - sizeof(regid), 12, 0x3); + sizeof(struct deviceSpec) + + sizeof(regid), 12, 0x3); dsea->attrType = cpu_to_le32(12); dsea->attrSubtype = 1; dsea->attrLength = cpu_to_le32(sizeof(struct deviceSpec) + - sizeof(regid)); + sizeof(regid)); dsea->impUseLength = cpu_to_le32(sizeof(regid)); } eid = (regid *)dsea->impUse; @@ -1526,9 +1434,9 @@ udf_update_inode(struct inode *inode, int do_sync) dsea->minorDeviceIdent = cpu_to_le32(iminor(inode)); } - if (UDF_I_EFE(inode) == 0) - { - memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + if (UDF_I_EFE(inode) == 0) { + memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), + inode->i_sb->s_blocksize - sizeof(struct fileEntry)); fe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); @@ -1548,31 +1456,27 @@ udf_update_inode(struct inode *inode, int do_sync) fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE); crclen = sizeof(struct fileEntry); - } - else - { - memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); + } else { + memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), + inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); efe->objectSize = cpu_to_le64(inode->i_size); efe->logicalBlocksRecorded = cpu_to_le64( (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> (inode->i_sb->s_blocksize_bits - 9)); if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) - { + (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_atime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) - { + (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_mtime; } if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) - { + (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && + UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) { UDF_I_CRTIME(inode) = inode->i_ctime; } @@ -1595,14 +1499,11 @@ udf_update_inode(struct inode *inode, int do_sync) efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE); crclen = sizeof(struct extendedFileEntry); } - if (UDF_I_STRAT4096(inode)) - { + if (UDF_I_STRAT4096(inode)) { fe->icbTag.strategyType = cpu_to_le16(4096); fe->icbTag.strategyParameter = cpu_to_le16(1); fe->icbTag.numEntries = cpu_to_le16(2); - } - else - { + } else { fe->icbTag.strategyType = cpu_to_le16(4); fe->icbTag.numEntries = cpu_to_le16(1); } @@ -1642,28 +1543,27 @@ udf_update_inode(struct inode *inode, int do_sync) fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0)); fe->descTag.tagChecksum = 0; - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) { if (i != 4) fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i]; + } /* write the data blocks */ mark_buffer_dirty(bh); - if (do_sync) - { + if (do_sync) { sync_dirty_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) - { + if (buffer_req(bh) && !buffer_uptodate(bh)) { printk("IO error syncing udf inode [%s:%08lx]\n", - inode->i_sb->s_id, inode->i_ino); + inode->i_sb->s_id, inode->i_ino); err = -EIO; } } brelse(bh); + return err; } -struct inode * -udf_iget(struct super_block *sb, kernel_lb_addr ino) +struct inode *udf_iget(struct super_block *sb, kernel_lb_addr ino) { unsigned long block = udf_get_lb_pblock(sb, ino, 0); struct inode *inode = iget_locked(sb, block); @@ -1682,7 +1582,7 @@ udf_iget(struct super_block *sb, kernel_lb_addr ino) if (ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum)) { udf_debug("block=%d, partition=%d out of range\n", - ino.logicalBlockNum, ino.partitionReferenceNum); + ino.logicalBlockNum, ino.partitionReferenceNum); make_bad_inode(inode); goto out_iput; } @@ -1694,8 +1594,8 @@ udf_iget(struct super_block *sb, kernel_lb_addr ino) return NULL; } -int8_t udf_add_aext(struct inode *inode, struct extent_position *epos, - kernel_lb_addr eloc, uint32_t elen, int inc) +int8_t udf_add_aext(struct inode * inode, struct extent_position * epos, + kernel_lb_addr eloc, uint32_t elen, int inc) { int adsize; short_ad *sad = NULL; @@ -1716,21 +1616,19 @@ int8_t udf_add_aext(struct inode *inode, struct extent_position *epos, else return -1; - if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) - { + if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) { char *sptr, *dptr; struct buffer_head *nbh; int err, loffset; kernel_lb_addr obloc = epos->block; if (!(epos->block.logicalBlockNum = udf_new_block(inode->i_sb, NULL, - obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) - { + obloc.partitionReferenceNum, + obloc.logicalBlockNum, &err))) { return -1; } if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, - epos->block, 0)))) - { + epos->block, 0)))) { return -1; } lock_buffer(nbh); @@ -1742,85 +1640,69 @@ int8_t udf_add_aext(struct inode *inode, struct extent_position *epos, aed = (struct allocExtDesc *)(nbh->b_data); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); - if (epos->offset + adsize > inode->i_sb->s_blocksize) - { + if (epos->offset + adsize > inode->i_sb->s_blocksize) { loffset = epos->offset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = ptr - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); epos->offset = sizeof(struct allocExtDesc) + adsize; - } - else - { + } else { loffset = epos->offset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = ptr; epos->offset = sizeof(struct allocExtDesc); - if (epos->bh) - { + if (epos->bh) { aed = (struct allocExtDesc *)epos->bh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); - } - else - { + } else { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); } } if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, - epos->block.logicalBlockNum, sizeof(tag)); + epos->block.logicalBlockNum, sizeof(tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, - epos->block.logicalBlockNum, sizeof(tag)); - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - sad = (short_ad *)sptr; - sad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - inode->i_sb->s_blocksize); - sad->extPosition = cpu_to_le32(epos->block.logicalBlockNum); - break; - } - case ICBTAG_FLAG_AD_LONG: - { - lad = (long_ad *)sptr; - lad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - inode->i_sb->s_blocksize); - lad->extLocation = cpu_to_lelb(epos->block); - memset(lad->impUse, 0x00, sizeof(lad->impUse)); - break; - } + epos->block.logicalBlockNum, sizeof(tag)); + switch (UDF_I_ALLOCTYPE(inode)) { + case ICBTAG_FLAG_AD_SHORT: + sad = (short_ad *)sptr; + sad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS | + inode->i_sb->s_blocksize); + sad->extPosition = cpu_to_le32(epos->block.logicalBlockNum); + break; + case ICBTAG_FLAG_AD_LONG: + lad = (long_ad *)sptr; + lad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS | + inode->i_sb->s_blocksize); + lad->extLocation = cpu_to_lelb(epos->block); + memset(lad->impUse, 0x00, sizeof(lad->impUse)); + break; } - if (epos->bh) - { - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) + if (epos->bh) { + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(epos->bh->b_data, loffset); else udf_update_tag(epos->bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(epos->bh, inode); brelse(epos->bh); - } - else + } else { mark_inode_dirty(inode); + } epos->bh = nbh; } etype = udf_write_aext(inode, epos, eloc, elen, inc); - if (!epos->bh) - { + if (!epos->bh) { UDF_I_LENALLOC(inode) += adsize; mark_inode_dirty(inode); - } - else - { + } else { aed = (struct allocExtDesc *)epos->bh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); @@ -1834,73 +1716,68 @@ int8_t udf_add_aext(struct inode *inode, struct extent_position *epos, return etype; } -int8_t udf_write_aext(struct inode *inode, struct extent_position *epos, - kernel_lb_addr eloc, uint32_t elen, int inc) +int8_t udf_write_aext(struct inode * inode, struct extent_position * epos, + kernel_lb_addr eloc, uint32_t elen, int inc) { int adsize; uint8_t *ptr; + short_ad *sad; + long_ad *lad; if (!epos->bh) ptr = UDF_I_DATA(inode) + epos->offset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); else ptr = epos->bh->b_data + epos->offset; - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - short_ad *sad = (short_ad *)ptr; - sad->extLength = cpu_to_le32(elen); - sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); - adsize = sizeof(short_ad); - break; - } - case ICBTAG_FLAG_AD_LONG: - { - long_ad *lad = (long_ad *)ptr; - lad->extLength = cpu_to_le32(elen); - lad->extLocation = cpu_to_lelb(eloc); - memset(lad->impUse, 0x00, sizeof(lad->impUse)); - adsize = sizeof(long_ad); - break; - } - default: - return -1; + switch (UDF_I_ALLOCTYPE(inode)) { + case ICBTAG_FLAG_AD_SHORT: + sad = (short_ad *)ptr; + sad->extLength = cpu_to_le32(elen); + sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); + adsize = sizeof(short_ad); + break; + case ICBTAG_FLAG_AD_LONG: + lad = (long_ad *)ptr; + lad->extLength = cpu_to_le32(elen); + lad->extLocation = cpu_to_lelb(eloc); + memset(lad->impUse, 0x00, sizeof(lad->impUse)); + adsize = sizeof(long_ad); + break; + default: + return -1; } - if (epos->bh) - { - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - { + if (epos->bh) { + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(inode->i_sb) >= 0x0201) { struct allocExtDesc *aed = (struct allocExtDesc *)epos->bh->b_data; udf_update_tag(epos->bh->b_data, - le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc)); + le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc)); } mark_buffer_dirty_inode(epos->bh, inode); - } - else + } else { mark_inode_dirty(inode); + } if (inc) epos->offset += adsize; + return (elen >> 30); } -int8_t udf_next_aext(struct inode *inode, struct extent_position *epos, - kernel_lb_addr *eloc, uint32_t *elen, int inc) +int8_t udf_next_aext(struct inode * inode, struct extent_position * epos, + kernel_lb_addr * eloc, uint32_t * elen, int inc) { int8_t etype; while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) == - (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) - { + (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { epos->block = *eloc; epos->offset = sizeof(struct allocExtDesc); brelse(epos->bh); - if (!(epos->bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, epos->block, 0)))) - { + if (!(epos->bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, epos->block, 0)))) { udf_debug("reading block %d failed!\n", - udf_get_lb_pblock(inode->i_sb, epos->block, 0)); + udf_get_lb_pblock(inode->i_sb, epos->block, 0)); return -1; } } @@ -1908,68 +1785,55 @@ int8_t udf_next_aext(struct inode *inode, struct extent_position *epos, return etype; } -int8_t udf_current_aext(struct inode *inode, struct extent_position *epos, - kernel_lb_addr *eloc, uint32_t *elen, int inc) +int8_t udf_current_aext(struct inode * inode, struct extent_position * epos, + kernel_lb_addr * eloc, uint32_t * elen, int inc) { int alen; int8_t etype; uint8_t *ptr; + short_ad *sad; + long_ad *lad; - if (!epos->bh) - { + + if (!epos->bh) { if (!epos->offset) epos->offset = udf_file_entry_alloc_offset(inode); ptr = UDF_I_DATA(inode) + epos->offset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); alen = udf_file_entry_alloc_offset(inode) + UDF_I_LENALLOC(inode); - } - else - { + } else { if (!epos->offset) epos->offset = sizeof(struct allocExtDesc); ptr = epos->bh->b_data + epos->offset; - alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)epos->bh->b_data)->lengthAllocDescs); + alen = sizeof(struct allocExtDesc) + + le32_to_cpu(((struct allocExtDesc *)epos->bh->b_data)->lengthAllocDescs); } - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - short_ad *sad; - - if (!(sad = udf_get_fileshortad(ptr, alen, &epos->offset, inc))) - return -1; - - etype = le32_to_cpu(sad->extLength) >> 30; - eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); - eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; - break; - } - case ICBTAG_FLAG_AD_LONG: - { - long_ad *lad; - - if (!(lad = udf_get_filelongad(ptr, alen, &epos->offset, inc))) - return -1; - - etype = le32_to_cpu(lad->extLength) >> 30; - *eloc = lelb_to_cpu(lad->extLocation); - *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; - break; - } - default: - { - udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + switch (UDF_I_ALLOCTYPE(inode)) { + case ICBTAG_FLAG_AD_SHORT: + if (!(sad = udf_get_fileshortad(ptr, alen, &epos->offset, inc))) return -1; - } + etype = le32_to_cpu(sad->extLength) >> 30; + eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + case ICBTAG_FLAG_AD_LONG: + if (!(lad = udf_get_filelongad(ptr, alen, &epos->offset, inc))) + return -1; + etype = le32_to_cpu(lad->extLength) >> 30; + *eloc = lelb_to_cpu(lad->extLocation); + *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + default: + udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + return -1; } return etype; } -static int8_t -udf_insert_aext(struct inode *inode, struct extent_position epos, - kernel_lb_addr neloc, uint32_t nelen) +static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos, + kernel_lb_addr neloc, uint32_t nelen) { kernel_lb_addr oeloc; uint32_t oelen; @@ -1978,28 +1842,26 @@ udf_insert_aext(struct inode *inode, struct extent_position epos, if (epos.bh) get_bh(epos.bh); - while ((etype = udf_next_aext(inode, &epos, &oeloc, &oelen, 0)) != -1) - { + while ((etype = udf_next_aext(inode, &epos, &oeloc, &oelen, 0)) != -1) { udf_write_aext(inode, &epos, neloc, nelen, 1); - neloc = oeloc; nelen = (etype << 30) | oelen; } udf_add_aext(inode, &epos, neloc, nelen, 1); brelse(epos.bh); + return (nelen >> 30); } -int8_t udf_delete_aext(struct inode *inode, struct extent_position epos, - kernel_lb_addr eloc, uint32_t elen) +int8_t udf_delete_aext(struct inode * inode, struct extent_position epos, + kernel_lb_addr eloc, uint32_t elen) { struct extent_position oepos; int adsize; int8_t etype; struct allocExtDesc *aed; - if (epos.bh) - { + if (epos.bh) { get_bh(epos.bh); get_bh(epos.bh); } @@ -2015,11 +1877,9 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos, if (udf_next_aext(inode, &epos, &eloc, &elen, 1) == -1) return -1; - while ((etype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) - { + while ((etype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { udf_write_aext(inode, &oepos, eloc, (etype << 30) | elen, 1); - if (oepos.bh != epos.bh) - { + if (oepos.bh != epos.bh) { oepos.block = epos.block; brelse(oepos.bh); get_bh(epos.bh); @@ -2030,62 +1890,57 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos, memset(&eloc, 0x00, sizeof(kernel_lb_addr)); elen = 0; - if (epos.bh != oepos.bh) - { + if (epos.bh != oepos.bh) { udf_free_blocks(inode->i_sb, inode, epos.block, 0, 1); udf_write_aext(inode, &oepos, eloc, elen, 1); udf_write_aext(inode, &oepos, eloc, elen, 1); - if (!oepos.bh) - { + if (!oepos.bh) { UDF_I_LENALLOC(inode) -= (adsize * 2); mark_inode_dirty(inode); - } - else - { + } else { aed = (struct allocExtDesc *)oepos.bh->b_data; aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag(oepos.bh->b_data, oepos.offset - (2*adsize)); + cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2 * adsize)); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(inode->i_sb) >= 0x0201) + udf_update_tag(oepos.bh->b_data, oepos.offset - (2 * adsize)); else udf_update_tag(oepos.bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(oepos.bh, inode); } - } - else - { + } else { udf_write_aext(inode, &oepos, eloc, elen, 1); - if (!oepos.bh) - { + if (!oepos.bh) { UDF_I_LENALLOC(inode) -= adsize; mark_inode_dirty(inode); - } - else - { + } else { aed = (struct allocExtDesc *)oepos.bh->b_data; aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(oepos.bh->b_data, epos.offset - adsize); else udf_update_tag(oepos.bh->b_data, sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(oepos.bh, inode); } } - + brelse(epos.bh); brelse(oepos.bh); + return (elen >> 30); } -int8_t inode_bmap(struct inode *inode, sector_t block, struct extent_position *pos, - kernel_lb_addr *eloc, uint32_t *elen, sector_t *offset) +int8_t inode_bmap(struct inode * inode, sector_t block, + struct extent_position * pos, kernel_lb_addr * eloc, + uint32_t * elen, sector_t * offset) { - loff_t lbcount = 0, bcount = (loff_t)block << inode->i_sb->s_blocksize_bits; + loff_t lbcount = 0, bcount = + (loff_t) block << inode->i_sb->s_blocksize_bits; int8_t etype; - if (block < 0) - { + if (block < 0) { printk(KERN_ERR "udf: inode_bmap: block < 0\n"); return -1; } @@ -2095,10 +1950,8 @@ int8_t inode_bmap(struct inode *inode, sector_t block, struct extent_position *p pos->bh = NULL; *elen = 0; - do - { - if ((etype = udf_next_aext(inode, pos, eloc, elen, 1)) == -1) - { + do { + if ((etype = udf_next_aext(inode, pos, eloc, elen, 1)) == -1) { *offset = (bcount - lbcount) >> inode->i_sb->s_blocksize_bits; UDF_I_LENEXTENTS(inode) = lbcount; return -1; @@ -2116,7 +1969,7 @@ long udf_block_map(struct inode *inode, sector_t block) kernel_lb_addr eloc; uint32_t elen; sector_t offset; - struct extent_position epos = { NULL, 0, { 0, 0}}; + struct extent_position epos = {}; int ret; lock_kernel(); diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c index 0842161..579bae7 100644 --- a/fs/udf/lowlevel.c +++ b/fs/udf/lowlevel.c @@ -26,38 +26,33 @@ #include <linux/udf_fs.h> #include "udf_sb.h" -unsigned int -udf_get_last_session(struct super_block *sb) +unsigned int udf_get_last_session(struct super_block *sb) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct block_device *bdev = sb->s_bdev; int i; - vol_desc_start=0; - ms_info.addr_format=CDROM_LBA; - i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); + vol_desc_start = 0; + ms_info.addr_format = CDROM_LBA; + i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long)&ms_info); #define WE_OBEY_THE_WRITTEN_STANDARDS 1 - if (i == 0) - { + if (i == 0) { udf_debug("XA disk: %s, vol_desc_start=%d\n", - (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); + (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); #if WE_OBEY_THE_WRITTEN_STANDARDS if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif vol_desc_start = ms_info.addr.lba; - } - else - { + } else { udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); } return vol_desc_start; } -unsigned long -udf_get_last_block(struct super_block *sb) +unsigned long udf_get_last_block(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; unsigned long lblock = 0; diff --git a/fs/udf/misc.c b/fs/udf/misc.c index a2b2a98..15297de 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -29,8 +29,7 @@ #include "udf_i.h" #include "udf_sb.h" -struct buffer_head * -udf_tgetblk(struct super_block *sb, int block) +struct buffer_head *udf_tgetblk(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_getblk(sb, udf_fixed_to_variable(block)); @@ -38,8 +37,7 @@ udf_tgetblk(struct super_block *sb, int block) return sb_getblk(sb, block); } -struct buffer_head * -udf_tread(struct super_block *sb, int block) +struct buffer_head *udf_tread(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_bread(sb, udf_fixed_to_variable(block)); @@ -47,9 +45,8 @@ udf_tread(struct super_block *sb, int block) return sb_bread(sb, block); } -struct genericFormat * -udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, - uint8_t loc) +struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size, + uint32_t type, uint8_t loc) { uint8_t *ea = NULL, *ad = NULL; int offset; @@ -57,10 +54,9 @@ udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, int i; ea = UDF_I_DATA(inode); - if (UDF_I_LENEATTR(inode)) + if (UDF_I_LENEATTR(inode)) { ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); - else - { + } else { ad = ea; size += sizeof(struct extendedAttrHeaderDesc); } @@ -70,27 +66,21 @@ udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, /* TODO - Check for FreeEASpace */ - if (loc & 0x01 && offset >= size) - { + if (loc & 0x01 && offset >= size) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; - if (UDF_I_LENALLOC(inode)) - { + if (UDF_I_LENALLOC(inode)) { memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); } - if (UDF_I_LENEATTR(inode)) - { + if (UDF_I_LENEATTR(inode)) { /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || - le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) - { + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } - } - else - { + } else { size -= sizeof(struct extendedAttrHeaderDesc); UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); @@ -105,29 +95,23 @@ udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, } offset = UDF_I_LENEATTR(inode); - if (type < 2048) - { - if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) - { + if (type < 2048) { + if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; eahd->appAttrLocation = cpu_to_le32(aal + size); } - if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) - { + if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t ial = le32_to_cpu(eahd->impAttrLocation); memmove(&ea[offset - ial + size], &ea[ial], offset - ial); offset -= ial; eahd->impAttrLocation = cpu_to_le32(ial + size); } - } - else if (type < 65536) - { - if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) - { + } else if (type < 65536) { + if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) { uint32_t aal = le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); @@ -138,22 +122,23 @@ udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, /* rewrite CRC + checksum of eahd */ crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); eahd->descTag.descCRCLength = cpu_to_le16(crclen); - eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); + eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + + sizeof(tag), crclen, 0)); eahd->descTag.tagChecksum = 0; - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) if (i != 4) eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; UDF_I_LENEATTR(inode) += size; return (struct genericFormat *)&ea[offset]; } - if (loc & 0x02) - { + if (loc & 0x02) { } + return NULL; } -struct genericFormat * -udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) +struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, + uint8_t subtype) { struct genericFormat *gaf; uint8_t *ea = NULL; @@ -161,18 +146,16 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) ea = UDF_I_DATA(inode); - if (UDF_I_LENEATTR(inode)) - { + if (UDF_I_LENEATTR(inode)) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; /* check checksum/crc */ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || - le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) - { + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { return NULL; } - + if (type < 2048) offset = sizeof(struct extendedAttrHeaderDesc); else if (type < 65536) @@ -180,8 +163,7 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) else offset = le32_to_cpu(eahd->appAttrLocation); - while (offset < UDF_I_LENEATTR(inode)) - { + while (offset < UDF_I_LENEATTR(inode)) { gaf = (struct genericFormat *)&ea[offset]; if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) return gaf; @@ -189,6 +171,7 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) offset += le32_to_cpu(gaf->attrLength); } } + return NULL; } @@ -202,8 +185,8 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -struct buffer_head * -udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident) +struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block, + uint32_t location, uint16_t * ident) { tag *tag_p; struct buffer_head *bh = NULL; @@ -215,9 +198,9 @@ udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint1 return NULL; bh = udf_tread(sb, block + UDF_SB_SESSION(sb)); - if (!bh) - { - udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location); + if (!bh) { + udf_debug("block=%d, location=%d: read failed\n", + block + UDF_SB_SESSION(sb), location); return NULL; } @@ -225,13 +208,12 @@ udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint1 *ident = le16_to_cpu(tag_p->tagIdent); - if ( location != le32_to_cpu(tag_p->tagLocation) ) - { + if (location != le32_to_cpu(tag_p->tagLocation)) { udf_debug("location mismatch block %u, tag %u != %u\n", - block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); + block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); goto error_out; } - + /* Verify the tag checksum */ checksum = 0U; for (i = 0; i < 4; i++) @@ -245,33 +227,32 @@ udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint1 /* Verify the tag version */ if (le16_to_cpu(tag_p->descVersion) != 0x0002U && - le16_to_cpu(tag_p->descVersion) != 0x0003U) - { + le16_to_cpu(tag_p->descVersion) != 0x0003U) { udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n", - le16_to_cpu(tag_p->descVersion), block); + le16_to_cpu(tag_p->descVersion), block); goto error_out; } /* Verify the descriptor CRC */ if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || - le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), - le16_to_cpu(tag_p->descCRCLength), 0)) - { + le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), + le16_to_cpu(tag_p->descCRCLength), 0)) { return bh; } udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", - block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); + block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), + le16_to_cpu(tag_p->descCRCLength)); error_out: brelse(bh); return NULL; } -struct buffer_head * -udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident) +struct buffer_head *udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, + uint32_t offset, uint16_t * ident) { return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), - loc.logicalBlockNum + offset, ident); + loc.logicalBlockNum + offset, ident); } void udf_update_tag(char *data, int length) @@ -285,13 +266,13 @@ void udf_update_tag(char *data, int length) tptr->descCRCLength = cpu_to_le16(length); tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0)); - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) if (i != 4) tptr->tagChecksum += (uint8_t)(data[i]); } void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum, - uint32_t loc, int length) + uint32_t loc, int length) { tag *tptr = (tag *)data; tptr->tagIdent = cpu_to_le16(ident); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 51fe307..bec96a6 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -32,16 +32,18 @@ #include <linux/buffer_head.h> #include <linux/sched.h> -static inline int udf_match(int len1, const char *name1, int len2, const char *name2) +static inline int udf_match(int len1, const char *name1, int len2, + const char *name2) { if (len1 != len2) return 0; + return !memcmp(name1, name2, len1); } int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, - struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, - uint8_t *impuse, uint8_t *fileident) + struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, + uint8_t * impuse, uint8_t * fileident) { uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag); uint16_t crc; @@ -59,14 +61,12 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, offset = fibh->soffset + sizeof(struct fileIdentDesc); - if (impuse) - { - if (adinicb || (offset + liu < 0)) + if (impuse) { + if (adinicb || (offset + liu < 0)) { memcpy((uint8_t *)sfi->impUse, impuse, liu); - else if (offset >= 0) + } else if (offset >= 0) { memcpy(fibh->ebh->b_data + offset, impuse, liu); - else - { + } else { memcpy((uint8_t *)sfi->impUse, impuse, -offset); memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); } @@ -74,14 +74,12 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, offset += liu; - if (fileident) - { - if (adinicb || (offset + lfi < 0)) + if (fileident) { + if (adinicb || (offset + lfi < 0)) { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); - else if (offset >= 0) + } else if (offset >= 0) { memcpy(fibh->ebh->b_data + offset, fileident, lfi); - else - { + } else { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset); memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); } @@ -89,53 +87,50 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, offset += lfi; - if (adinicb || (offset + padlen < 0)) + if (adinicb || (offset + padlen < 0)) { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); - else if (offset >= 0) + } else if (offset >= 0) { memset(fibh->ebh->b_data + offset, 0x00, padlen); - else - { + } else { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } - crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) - - sizeof(tag), 0); + crc = udf_crc((uint8_t *)cfi + sizeof(tag), + sizeof(struct fileIdentDesc) - sizeof(tag), 0); - if (fibh->sbh == fibh->ebh) + if (fibh->sbh == fibh->ebh) { crc = udf_crc((uint8_t *)sfi->impUse, - crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); - else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) + crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); + } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) { crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset, - crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); - else - { + crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); + } else { crc = udf_crc((uint8_t *)sfi->impUse, - -fibh->soffset - sizeof(struct fileIdentDesc), crc); + -fibh->soffset - sizeof(struct fileIdentDesc), crc); crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); } cfi->descTag.descCRC = cpu_to_le16(crc); cfi->descTag.descCRCLength = cpu_to_le16(crclen); - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) { if (i != 4) checksum += ((uint8_t *)&cfi->descTag)[i]; + } cfi->descTag.tagChecksum = checksum; - if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) + if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) { memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc)); - else - { + } else { memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, - sizeof(struct fileIdentDesc) + fibh->soffset); + sizeof(struct fileIdentDesc) + fibh->soffset); } - if (adinicb) + if (adinicb) { mark_inode_dirty(inode); - else - { + } else { if (fibh->sbh != fibh->ebh) mark_buffer_dirty_inode(fibh->ebh, inode); mark_buffer_dirty_inode(fibh->sbh, inode); @@ -143,12 +138,12 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, return 0; } -static struct fileIdentDesc * -udf_find_entry(struct inode *dir, struct dentry *dentry, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi) +static struct fileIdentDesc *udf_find_entry(struct inode *dir, + struct dentry *dentry, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi) { - struct fileIdentDesc *fi=NULL; + struct fileIdentDesc *fi = NULL; loff_t f_pos; int block, flen; char fname[UDF_NAME_LEN]; @@ -159,46 +154,39 @@ udf_find_entry(struct inode *dir, struct dentry *dentry, kernel_lb_addr eloc; uint32_t elen; sector_t offset; - struct extent_position epos = { NULL, 0, { 0, 0}}; + struct extent_position epos = {}; size = (udf_ext0_offset(dir) + dir->i_size) >> 2; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fibh->sbh = fibh->ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) - { + } else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) epos.offset -= sizeof(long_ad); - } - else + } else { offset = 0; + } - if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) - { + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { brelse(epos.bh); return NULL; } - } - else - { + } else { brelse(epos.bh); return NULL; } - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, &elen, &offset); - - if (!fi) - { + while ((f_pos < size)) { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, + &elen, &offset); + if (!fi) { if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); brelse(fibh->sbh); @@ -209,54 +197,48 @@ udf_find_entry(struct inode *dir, struct dentry *dentry, liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; - if (fibh->sbh == fibh->ebh) - { + if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; - } - else - { + } else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; - if (poffset >= lfi) + if (poffset >= lfi) { nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi); - else - { + } else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) continue; } - - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + + if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) continue; } if (!lfi) continue; - if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) - { - if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) - { + if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) { + if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { brelse(epos.bh); return fi; } } } + if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); brelse(fibh->sbh); brelse(epos.bh); + return NULL; } @@ -293,25 +275,27 @@ udf_find_entry(struct inode *dir, struct dentry *dentry, * Written, tested, and released. */ -static struct dentry * -udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) { struct inode *inode = NULL; struct fileIdentDesc cfi; struct udf_fileident_bh fibh; - if (dentry->d_name.len > UDF_NAME_LEN-2) + if (dentry->d_name.len > UDF_NAME_LEN - 2) return ERR_PTR(-ENAMETOOLONG); lock_kernel(); #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ - if (!strncmp(dentry->d_name.name, ".B=", 3) ) - { - kernel_lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; + if (!strncmp(dentry->d_name.name, ".B=", 3)) { + kernel_lb_addr lb = { + .logicalBlockNum = 0, + .partitionReferenceNum = simple_strtoul(dentry->d_name.name + 3, + NULL, 0), + }; inode = udf_iget(dir->i_sb, lb); - if (!inode) - { + if (!inode) { unlock_kernel(); return ERR_PTR(-EACCES); } @@ -319,31 +303,30 @@ udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) else #endif /* UDF_RECOVERY */ - if (udf_find_entry(dir, dentry, &fibh, &cfi)) - { + if (udf_find_entry(dir, dentry, &fibh, &cfi)) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); - if ( !inode ) - { + if (!inode) { unlock_kernel(); return ERR_PTR(-EACCES); } } unlock_kernel(); d_add(dentry, inode); + return NULL; } -static struct fileIdentDesc * -udf_add_entry(struct inode *dir, struct dentry *dentry, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi, int *err) +static struct fileIdentDesc *udf_add_entry(struct inode *dir, + struct dentry *dentry, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi, int *err) { struct super_block *sb; - struct fileIdentDesc *fi=NULL; + struct fileIdentDesc *fi = NULL; char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; int namelen; loff_t f_pos; @@ -357,50 +340,44 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, kernel_lb_addr eloc; uint32_t elen; sector_t offset; - struct extent_position epos = { NULL, 0, { 0, 0 }}; + struct extent_position epos = {}; sb = dir->i_sb; - if (dentry) - { - if (!dentry->d_name.len) - { + if (dentry) { + if (!dentry->d_name.len) { *err = -EINVAL; return NULL; } - - if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len))) - { + if (!(namelen = udf_put_filename(sb, dentry->d_name.name, name, + dentry->d_name.len))) { *err = -ENAMETOOLONG; return NULL; } - } - else + } else { namelen = 0; + } nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3; f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fibh->sbh = fibh->ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) - { + } else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) epos.offset -= sizeof(long_ad); - } - else + } else { offset = 0; + } - if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) - { + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) { brelse(epos.bh); *err = -EIO; return NULL; @@ -408,21 +385,18 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, block = UDF_I_LOCATION(dir).logicalBlockNum; - } - else - { + } else { block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); fibh->sbh = fibh->ebh = NULL; fibh->soffset = fibh->eoffset = sb->s_blocksize; goto add; } - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, &elen, &offset); + while ((f_pos < size)) { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, + &elen, &offset); - if (!fi) - { + if (!fi) { if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); brelse(fibh->sbh); @@ -434,38 +408,33 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; - if (fibh->sbh == fibh->ebh) + if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; - else - { + } else { int poffset; /* Unpaded ending offset */ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; - if (poffset >= lfi) + if (poffset >= lfi) { nameptr = (char *)(fibh->ebh->b_data + poffset - lfi); - else - { + } else { nameptr = fname; memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); } } - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) - { + if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) { brelse(epos.bh); cfi->descTag.tagSerialNum = cpu_to_le16(1); cfi->fileVersionNum = cpu_to_le16(1); cfi->fileCharacteristics = 0; cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); - if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) + if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { return fi; - else - { + } else { *err = -EIO; return NULL; } @@ -476,8 +445,7 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, continue; if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) && - udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) - { + udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) { if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); brelse(fibh->sbh); @@ -491,8 +459,7 @@ add: f_pos += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB && - sb->s_blocksize - fibh->eoffset < nfidlen) - { + sb->s_blocksize - fibh->eoffset < nfidlen) { brelse(epos.bh); epos.bh = NULL; fibh->soffset -= udf_ext0_offset(dir); @@ -514,65 +481,54 @@ add: epos.offset += sizeof(long_ad); } - if (sb->s_blocksize - fibh->eoffset >= nfidlen) - { + if (sb->s_blocksize - fibh->eoffset >= nfidlen) { fibh->soffset = fibh->eoffset; fibh->eoffset += nfidlen; - if (fibh->sbh != fibh->ebh) - { + if (fibh->sbh != fibh->ebh) { brelse(fibh->sbh); fibh->sbh = fibh->ebh; } - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { block = UDF_I_LOCATION(dir).logicalBlockNum; - fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - udf_ext0_offset(dir) + UDF_I_LENEATTR(dir)); - } - else - { + fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - + udf_ext0_offset(dir) + + UDF_I_LENEATTR(dir)); + } else { block = eloc.logicalBlockNum + ((elen - 1) >> - dir->i_sb->s_blocksize_bits); + dir->i_sb->s_blocksize_bits); fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset); } - } - else - { + } else { fibh->soffset = fibh->eoffset - sb->s_blocksize; fibh->eoffset += nfidlen - sb->s_blocksize; - if (fibh->sbh != fibh->ebh) - { + if (fibh->sbh != fibh->ebh) { brelse(fibh->sbh); fibh->sbh = fibh->ebh; } block = eloc.logicalBlockNum + ((elen - 1) >> - dir->i_sb->s_blocksize_bits); - - if (!(fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err))) - { + dir->i_sb->s_blocksize_bits); + fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err); + if (!fibh->ebh) { brelse(epos.bh); brelse(fibh->sbh); return NULL; } - if (!(fibh->soffset)) - { + if (!fibh->soffset) { if (udf_next_aext(dir, &epos, &eloc, &elen, 1) == - (EXT_RECORDED_ALLOCATED >> 30)) - { + (EXT_RECORDED_ALLOCATED >> 30)) { block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); + } else { + block++; } - else - block ++; brelse(fibh->sbh); fibh->sbh = fibh->ebh; fi = (struct fileIdentDesc *)(fibh->sbh->b_data); - } - else - { + } else { fi = (struct fileIdentDesc *) (fibh->sbh->b_data + sb->s_blocksize + fibh->soffset); } @@ -586,17 +542,14 @@ add: cfi->fileVersionNum = cpu_to_le16(1); cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); - if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) - { + if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { brelse(epos.bh); dir->i_size += nfidlen; if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) UDF_I_LENALLOC(dir) += nfidlen; mark_inode_dirty(dir); return fi; - } - else - { + } else { brelse(epos.bh); if (fibh->sbh != fibh->ebh) brelse(fibh->ebh); @@ -607,15 +560,19 @@ add: } static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, - struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi) { cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) memset(&(cfi->icb), 0x00, sizeof(long_ad)); + return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); } -static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) +static int udf_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) { struct udf_fileident_bh fibh; struct inode *inode; @@ -624,8 +581,7 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct lock_kernel(); inode = udf_new_inode(dir, mode, &err); - if (!inode) - { + if (!inode) { unlock_kernel(); return err; } @@ -639,9 +595,8 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct inode->i_mode = mode; mark_inode_dirty(inode); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - inode->i_nlink --; + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { + inode->i_nlink--; mark_inode_dirty(inode); iput(inode); unlock_kernel(); @@ -652,8 +607,7 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) @@ -661,12 +615,14 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct brelse(fibh.sbh); unlock_kernel(); d_instantiate(dentry, inode); + return 0; } -static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) +static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) { - struct inode * inode; + struct inode *inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; @@ -682,9 +638,8 @@ static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t inode->i_uid = current->fsuid; init_special_inode(inode, mode, rdev); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - inode->i_nlink --; + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { + inode->i_nlink--; mark_inode_dirty(inode); iput(inode); unlock_kernel(); @@ -695,8 +650,7 @@ static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } mark_inode_dirty(inode); @@ -706,21 +660,22 @@ static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t brelse(fibh.sbh); d_instantiate(dentry, inode); err = 0; + out: unlock_kernel(); return err; } -static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - struct inode * inode; + struct inode *inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; lock_kernel(); err = -EMLINK; - if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1) + if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1) goto out; err = -EIO; @@ -730,8 +685,7 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; - if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) - { + if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) { inode->i_nlink--; mark_inode_dirty(inode); iput(inode); @@ -750,8 +704,7 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); @@ -770,6 +723,7 @@ static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) brelse(fibh.ebh); brelse(fibh.sbh); err = 0; + out: unlock_kernel(); return err; @@ -785,47 +739,39 @@ static int empty_dir(struct inode *dir) kernel_lb_addr eloc; uint32_t elen; sector_t offset; - struct extent_position epos = { NULL, 0, { 0, 0}}; + struct extent_position epos = {}; f_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { fibh.sbh = fibh.ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) - { + } else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &epos, &eloc, &elen, &offset) == (EXT_RECORDED_ALLOCATED >> 30)) { block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) epos.offset -= sizeof(long_ad); - } - else + } else { offset = 0; + } - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) - { + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { brelse(epos.bh); return 0; } - } - else - { + } else { brelse(epos.bh); return 0; } - - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc, &elen, &offset); - - if (!fi) - { + while ((f_pos < size)) { + fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc, + &elen, &offset); + if (!fi) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); @@ -833,8 +779,8 @@ static int empty_dir(struct inode *dir) return 0; } - if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) - { + if (cfi.lengthFileIdent && + (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); @@ -842,17 +788,19 @@ static int empty_dir(struct inode *dir) return 0; } } + if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); brelse(epos.bh); + return 1; } -static int udf_rmdir(struct inode * dir, struct dentry * dentry) +static int udf_rmdir(struct inode *dir, struct dentry *dentry) { int retval; - struct inode * inode = dentry->d_inode; + struct inode *inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi, cfi; kernel_lb_addr tloc; @@ -875,8 +823,8 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry) goto end_rmdir; if (inode->i_nlink != 2) udf_warning(inode->i_sb, "udf_rmdir", - "empty directory has nlink != 2 (%d)", - inode->i_nlink); + "empty directory has nlink != 2 (%d)", + inode->i_nlink); clear_nlink(inode); inode->i_size = 0; inode_dec_link_count(dir); @@ -887,15 +835,16 @@ end_rmdir: if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); + out: unlock_kernel(); return retval; } -static int udf_unlink(struct inode * dir, struct dentry * dentry) +static int udf_unlink(struct inode *dir, struct dentry *dentry) { int retval; - struct inode * inode = dentry->d_inode; + struct inode *inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi; struct fileIdentDesc cfi; @@ -912,10 +861,9 @@ static int udf_unlink(struct inode * dir, struct dentry * dentry) if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) goto end_unlink; - if (!inode->i_nlink) - { + if (!inode->i_nlink) { udf_debug("Deleting nonexistent file (%lu), %d\n", - inode->i_ino, inode->i_nlink); + inode->i_ino, inode->i_nlink); inode->i_nlink = 1; } retval = udf_delete_entry(dir, fi, &fibh, &cfi); @@ -931,18 +879,20 @@ end_unlink: if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); + out: unlock_kernel(); return retval; } -static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname) +static int udf_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) { - struct inode * inode; + struct inode *inode; struct pathComponent *pc; char *compstart; struct udf_fileident_bh fibh; - struct extent_position epos = { NULL, 0, {0, 0}}; + struct extent_position epos = {}; int eoffset, elen = 0; struct fileIdentDesc *fi; struct fileIdentDesc cfi; @@ -960,14 +910,13 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; - if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) { kernel_lb_addr eloc; uint32_t elen; block = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, - UDF_I_LOCATION(inode).logicalBlockNum, &err); + UDF_I_LOCATION(inode).partitionReferenceNum, + UDF_I_LOCATION(inode).logicalBlockNum, &err); if (!block) goto out_no_entry; epos.block = UDF_I_LOCATION(inode); @@ -981,7 +930,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * brelse(epos.bh); block = udf_get_pblock(inode->i_sb, block, - UDF_I_LOCATION(inode).partitionReferenceNum, 0); + UDF_I_LOCATION(inode).partitionReferenceNum, 0); epos.bh = udf_tread(inode->i_sb, block); lock_buffer(epos.bh); memset(epos.bh->b_data, 0x00, inode->i_sb->s_blocksize); @@ -989,17 +938,15 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * unlock_buffer(epos.bh); mark_buffer_dirty_inode(epos.bh, inode); ea = epos.bh->b_data + udf_ext0_offset(inode); - } - else + } else { ea = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); + } eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode); pc = (struct pathComponent *)ea; - if (*symname == '/') - { - do - { + if (*symname == '/') { + do { symname++; } while (*symname == '/'); @@ -1012,8 +959,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * err = -ENAMETOOLONG; - while (*symname) - { + while (*symname) { if (elen + sizeof(struct pathComponent) > eoffset) goto out_no_entry; @@ -1021,25 +967,24 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * compstart = (char *)symname; - do - { + do { symname++; } while (*symname && *symname != '/'); pc->componentType = 5; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; - if (compstart[0] == '.') - { - if ((symname-compstart) == 1) + if (compstart[0] == '.') { + if ((symname - compstart) == 1) pc->componentType = 4; - else if ((symname-compstart) == 2 && compstart[1] == '.') + else if ((symname - compstart) == 2 && compstart[1] == '.') pc->componentType = 3; } - if (pc->componentType == 5) - { - if ( !(namelen = udf_put_filename(inode->i_sb, compstart, name, symname-compstart))) + if (pc->componentType == 5) { + namelen = udf_put_filename(inode->i_sb, compstart, name, + symname - compstart); + if (!namelen) goto out_no_entry; if (elen + sizeof(struct pathComponent) + namelen > eoffset) @@ -1052,10 +997,8 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; - if (*symname) - { - do - { + if (*symname) { + do { symname++; } while (*symname == '/'); } @@ -1071,8 +1014,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * goto out_no_entry; cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); - if (UDF_SB_LVIDBH(inode->i_sb)) - { + if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); @@ -1085,8 +1027,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } if (fibh.sbh != fibh.ebh) @@ -1105,8 +1046,8 @@ out_no_entry: goto out; } -static int udf_link(struct dentry * old_dentry, struct inode * dir, - struct dentry *dentry) +static int udf_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; struct udf_fileident_bh fibh; @@ -1114,21 +1055,18 @@ static int udf_link(struct dentry * old_dentry, struct inode * dir, int err; lock_kernel(); - if (inode->i_nlink >= (256<<sizeof(inode->i_nlink))-1) - { + if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) { unlock_kernel(); return -EMLINK; } - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) { unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); - if (UDF_SB_LVIDBH(inode->i_sb)) - { + if (UDF_SB_LVIDBH(inode->i_sb)) { struct logicalVolHeaderDesc *lvhd; uint64_t uniqueID; lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); @@ -1141,10 +1079,10 @@ static int udf_link(struct dentry * old_dentry, struct inode * dir, mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(dir); } + if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); @@ -1154,17 +1092,18 @@ static int udf_link(struct dentry * old_dentry, struct inode * dir, atomic_inc(&inode->i_count); d_instantiate(dentry, inode); unlock_kernel(); + return 0; } /* Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, - struct inode * new_dir, struct dentry * new_dentry) +static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { - struct inode * old_inode = old_dentry->d_inode; - struct inode * new_inode = new_dentry->d_inode; + struct inode *old_inode = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; struct udf_fileident_bh ofibh, nfibh; struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi; struct buffer_head *dir_bh = NULL; @@ -1172,49 +1111,41 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, kernel_lb_addr tloc; lock_kernel(); - if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) - { + if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) { if (ofibh.sbh != ofibh.ebh) brelse(ofibh.ebh); brelse(ofibh.sbh); } tloc = lelb_to_cpu(ocfi.icb.extLocation); if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0) - != old_inode->i_ino) + != old_inode->i_ino) goto end_rename; nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); - if (nfi) - { - if (!new_inode) - { + if (nfi) { + if (!new_inode) { if (nfibh.sbh != nfibh.ebh) brelse(nfibh.ebh); brelse(nfibh.sbh); nfi = NULL; } } - if (S_ISDIR(old_inode->i_mode)) - { + if (S_ISDIR(old_inode->i_mode)) { uint32_t offset = udf_ext0_offset(old_inode); - if (new_inode) - { + if (new_inode) { retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; - if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { dir_fi = udf_get_fileident(UDF_I_DATA(old_inode) - - (UDF_I_EFE(old_inode) ? - sizeof(struct extendedFileEntry) : - sizeof(struct fileEntry)), - old_inode->i_sb->s_blocksize, &offset); - } - else - { + (UDF_I_EFE(old_inode) ? + sizeof(struct extendedFileEntry) : + sizeof(struct fileEntry)), + old_inode->i_sb->s_blocksize, &offset); + } else { dir_bh = udf_bread(old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; @@ -1223,16 +1154,14 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, if (!dir_fi) goto end_rename; tloc = lelb_to_cpu(dir_fi->icb.extLocation); - if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) - != old_dir->i_ino) + if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= (256<<sizeof(new_dir->i_nlink))-1) + if (!new_inode && new_dir->i_nlink >= (256 << sizeof(new_dir->i_nlink)) - 1) goto end_rename; } - if (!nfi) - { + if (!nfi) { nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); if (!nfi) goto end_rename; @@ -1257,39 +1186,32 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); - if (new_inode) - { + if (new_inode) { new_inode->i_ctime = current_fs_time(new_inode->i_sb); inode_dec_link_count(new_inode); } old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb); mark_inode_dirty(old_dir); - if (dir_fi) - { + if (dir_fi) { dir_fi->icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(new_dir)); udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) + - le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); - if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) - { + le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); + if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) { mark_inode_dirty(old_inode); - } - else + } else { mark_buffer_dirty_inode(dir_bh, old_inode); + } inode_dec_link_count(old_dir); - if (new_inode) - { + if (new_inode) { inode_dec_link_count(new_inode); - } - else - { + } else { inc_nlink(new_dir); mark_inode_dirty(new_dir); } } - if (ofi) - { + if (ofi) { if (ofibh.sbh != ofibh.ebh) brelse(ofibh.ebh); brelse(ofibh.sbh); @@ -1299,13 +1221,13 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, end_rename: brelse(dir_bh); - if (nfi) - { + if (nfi) { if (nfibh.sbh != nfibh.ebh) brelse(nfibh.ebh); brelse(nfibh.sbh); } unlock_kernel(); + return retval; } diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h index e82aae6..65ff479 100644 --- a/fs/udf/osta_udf.h +++ b/fs/udf/osta_udf.h @@ -65,30 +65,26 @@ #define IS_DF_HARD_WRITE_PROTECT 0x01 #define IS_DF_SOFT_WRITE_PROTECT 0x02 -struct UDFIdentSuffix -{ +struct UDFIdentSuffix { __le16 UDFRevision; uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[4]; } __attribute__ ((packed)); -struct impIdentSuffix -{ +struct impIdentSuffix { uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[6]; } __attribute__ ((packed)); -struct appIdentSuffix -{ +struct appIdentSuffix { uint8_t impUse[8]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */ /* Implementation Use (UDF 2.50 2.2.6.4) */ -struct logicalVolIntegrityDescImpUse -{ +struct logicalVolIntegrityDescImpUse { regid impIdent; __le32 numFiles; __le32 numDirs; @@ -100,8 +96,7 @@ struct logicalVolIntegrityDescImpUse /* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */ /* Implementation Use (UDF 2.50 2.2.7.2) */ -struct impUseVolDescImpUse -{ +struct impUseVolDescImpUse { charspec LVICharset; dstring logicalVolIdent[128]; dstring LVInfo1[36]; @@ -111,8 +106,7 @@ struct impUseVolDescImpUse uint8_t impUse[128]; } __attribute__ ((packed)); -struct udfPartitionMap2 -{ +struct udfPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; @@ -122,8 +116,7 @@ struct udfPartitionMap2 } __attribute__ ((packed)); /* Virtual Partition Map (UDF 2.50 2.2.8) */ -struct virtualPartitionMap -{ +struct virtualPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; @@ -134,24 +127,22 @@ struct virtualPartitionMap } __attribute__ ((packed)); /* Sparable Partition Map (UDF 2.50 2.2.9) */ -struct sparablePartitionMap -{ - uint8_t partitionMapType; - uint8_t partitionMapLength; - uint8_t reserved1[2]; - regid partIdent; - __le16 volSeqNum; - __le16 partitionNum; - __le16 packetLength; - uint8_t numSparingTables; - uint8_t reserved2[1]; - __le32 sizeSparingTable; - __le32 locSparingTable[4]; +struct sparablePartitionMap { + uint8_t partitionMapType; + uint8_t partitionMapLength; + uint8_t reserved1[2]; + regid partIdent; + __le16 volSeqNum; + __le16 partitionNum; + __le16 packetLength; + uint8_t numSparingTables; + uint8_t reserved2[1]; + __le32 sizeSparingTable; + __le32 locSparingTable[4]; } __attribute__ ((packed)); /* Metadata Partition Map (UDF 2.4.0 2.2.10) */ -struct metadataPartitionMap -{ +struct metadataPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; @@ -168,18 +159,16 @@ struct metadataPartitionMap } __attribute__ ((packed)); /* Virtual Allocation Table (UDF 1.5 2.2.10) */ -struct virtualAllocationTable15 -{ +struct virtualAllocationTable15 { __le32 VirtualSector[0]; regid vatIdent; __le32 previousVATICBLoc; -} __attribute__ ((packed)); +} __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT15 0x00U /* Virtual Allocation Table (UDF 2.50 2.2.11) */ -struct virtualAllocationTable20 -{ +struct virtualAllocationTable20 { __le16 lengthHeader; __le16 lengthImpUse; dstring logicalVolIdent[128]; @@ -197,14 +186,12 @@ struct virtualAllocationTable20 #define ICBTAG_FILE_TYPE_VAT20 0xF8U /* Sparing Table (UDF 2.50 2.2.12) */ -struct sparingEntry -{ +struct sparingEntry { __le32 origLocation; __le32 mappedLocation; } __attribute__ ((packed)); -struct sparingTable -{ +struct sparingTable { tag descTag; regid sparingIdent; __le16 reallocationTableLen; @@ -220,8 +207,7 @@ struct sparingTable #define ICBTAG_FILE_TYPE_BITMAP 0xFC /* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ -struct allocDescImpUse -{ +struct allocDescImpUse { __le16 flags; uint8_t impUse[4]; } __attribute__ ((packed)); @@ -233,15 +219,13 @@ struct allocDescImpUse /* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */ /* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */ -struct freeEaSpace -{ +struct freeEaSpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */ -struct DVDCopyrightImpUse -{ +struct DVDCopyrightImpUse { __le16 headerChecksum; uint8_t CGMSInfo; uint8_t dataType; @@ -250,8 +234,7 @@ struct DVDCopyrightImpUse /* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */ /* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */ -struct freeAppEASpace -{ +struct freeAppEASpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); diff --git a/fs/udf/partition.c b/fs/udf/partition.c index 467a261..aaab24c 100644 --- a/fs/udf/partition.c +++ b/fs/udf/partition.c @@ -14,7 +14,7 @@ * * HISTORY * - * 12/06/98 blf Created file. + * 12/06/98 blf Created file. * */ @@ -28,12 +28,12 @@ #include <linux/slab.h> #include <linux/buffer_head.h> -inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { - if (partition >= UDF_SB_NUMPARTS(sb)) - { + if (partition >= UDF_SB_NUMPARTS(sb)) { udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", - block, partition, offset); + block, partition, offset); return 0xFFFFFFFF; } if (UDF_SB_PARTFUNC(sb, partition)) @@ -42,7 +42,8 @@ inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t return UDF_SB_PARTROOT(sb, partition) + block + offset; } -uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { struct buffer_head *bh = NULL; uint32_t newblock; @@ -51,31 +52,26 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t); - if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) - { + if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) { udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", - block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); + block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); return 0xFFFFFFFF; } - if (block >= index) - { + if (block >= index) { block -= index; newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t))); index = block % (sb->s_blocksize / sizeof(uint32_t)); - } - else - { + } else { newblock = 0; index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block; } loc = udf_block_map(UDF_SB_VAT(sb), newblock); - if (!(bh = sb_bread(sb, loc))) - { + if (!(bh = sb_bread(sb, loc))) { udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", - sb, block, partition, loc, index); + sb, block, partition, loc, index); return 0xFFFFFFFF; } @@ -83,50 +79,49 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t brelse(bh); - if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) - { + if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) { udf_debug("recursive call to udf_get_pblock!\n"); return 0xFFFFFFFF; } - return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); + return udf_get_pblock(sb, loc, + UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, + offset); } -inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +inline uint32_t udf_get_pblock_virt20(struct super_block * sb, uint32_t block, + uint16_t partition, uint32_t offset) { return udf_get_pblock_virt15(sb, block, partition, offset); } -uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +uint32_t udf_get_pblock_spar15(struct super_block * sb, uint32_t block, + uint16_t partition, uint32_t offset) { int i; struct sparingTable *st = NULL; uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1); - for (i=0; i<4; i++) - { - if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL) - { + for (i = 0; i < 4; i++) { + if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data; break; } } - if (st) - { - for (i=0; i<le16_to_cpu(st->reallocationTableLen); i++) - { - if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0) + if (st) { + for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) { + if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0) { break; - else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet) - { + } else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet) { return le32_to_cpu(st->mapEntry[i].mappedLocation) + ((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1)); - } - else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet) + } else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet) { break; + } } } + return UDF_SB_PARTROOT(sb,partition) + block + offset; } @@ -138,18 +133,14 @@ int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) uint32_t packet; int i, j, k, l; - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { + for (i = 0; i < UDF_SB_NUMPARTS(sb); i++) { if (old_block > UDF_SB_PARTROOT(sb,i) && - old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i)) - { + old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i)) { sdata = &UDF_SB_TYPESPAR(sb,i); packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1); - for (j=0; j<4; j++) - { - if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) - { + for (j = 0; j < 4; j++) { + if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; break; } @@ -158,14 +149,10 @@ int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) if (!st) return 1; - for (k=0; k<le16_to_cpu(st->reallocationTableLen); k++) - { - if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF) - { - for (; j<4; j++) - { - if (sdata->s_spar_map[j]) - { + for (k = 0; k < le16_to_cpu(st->reallocationTableLen); k++) { + if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF) { + for (; j < 4; j++) { + if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; st->mapEntry[k].origLocation = cpu_to_le32(packet); udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); @@ -175,28 +162,23 @@ int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; - } - else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet) - { + } else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet) { *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); return 0; - } - else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet) + } else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet) { break; + } } - for (l=k; l<le16_to_cpu(st->reallocationTableLen); l++) - { - if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF) - { - for (; j<4; j++) - { - if (sdata->s_spar_map[j]) - { + + for (l = k; l < le16_to_cpu(st->reallocationTableLen); l++) { + if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF) { + for (; j < 4; j++) { + if (sdata->s_spar_map[j]) { st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; mapEntry = st->mapEntry[l]; mapEntry.origLocation = cpu_to_le32(packet); - memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry)); + memmove(&st->mapEntry[k + 1], &st->mapEntry[k], (l - k) * sizeof(struct sparingEntry)); st->mapEntry[k] = mapEntry; udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); mark_buffer_dirty(sdata->s_spar_map[j]); @@ -207,11 +189,12 @@ int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) return 0; } } + return 1; - } + } /* if old_block */ } - if (i == UDF_SB_NUMPARTS(sb)) - { + + if (i == UDF_SB_NUMPARTS(sb)) { /* outside of partitions */ /* for now, fail =) */ return 1; diff --git a/fs/udf/super.c b/fs/udf/super.c index 6658afb..7b30964 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -38,7 +38,7 @@ * 12/20/98 find the free space bitmap (if it exists) */ -#include "udfdecl.h" +#include "udfdecl.h" #include <linux/blkdev.h> #include <linux/slab.h> @@ -80,12 +80,15 @@ static int udf_remount_fs(struct super_block *, int *, char *); static int udf_check_valid(struct super_block *, int, int); static int udf_vrs(struct super_block *sb, int silent); static int udf_load_partition(struct super_block *, kernel_lb_addr *); -static int udf_load_logicalvol(struct super_block *, struct buffer_head *, kernel_lb_addr *); +static int udf_load_logicalvol(struct super_block *, struct buffer_head *, + kernel_lb_addr *); static void udf_load_logicalvolint(struct super_block *, kernel_extent_ad); static void udf_find_anchor(struct super_block *); -static int udf_find_fileset(struct super_block *, kernel_lb_addr *, kernel_lb_addr *); +static int udf_find_fileset(struct super_block *, kernel_lb_addr *, + kernel_lb_addr *); static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); -static void udf_load_fileset(struct super_block *, struct buffer_head *, kernel_lb_addr *); +static void udf_load_fileset(struct super_block *, struct buffer_head *, + kernel_lb_addr *); static void udf_load_partdesc(struct super_block *, struct buffer_head *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); @@ -94,7 +97,8 @@ static int udf_statfs(struct dentry *, struct kstatfs *); /* UDF filesystem type */ static int udf_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super, mnt); } @@ -107,7 +111,7 @@ static struct file_system_type udf_fstype = { .fs_flags = FS_REQUIRES_DEV, }; -static struct kmem_cache * udf_inode_cachep; +static struct kmem_cache *udf_inode_cachep; static struct inode *udf_alloc_inode(struct super_block *sb) { @@ -130,9 +134,9 @@ static void udf_destroy_inode(struct inode *inode) kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } -static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) +static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) { - struct udf_inode_info *ei = (struct udf_inode_info *) foo; + struct udf_inode_info *ei = (struct udf_inode_info *)foo; ei->i_ext.i_data = NULL; inode_init_once(&ei->vfs_inode); @@ -142,10 +146,10 @@ static int init_inodecache(void) { udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once, NULL); - if (udf_inode_cachep == NULL) + 0, (SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD), + init_once); + if (!udf_inode_cachep) return -ENOMEM; return 0; } @@ -157,19 +161,18 @@ static void destroy_inodecache(void) /* Superblock operations */ static const struct super_operations udf_sb_ops = { - .alloc_inode = udf_alloc_inode, - .destroy_inode = udf_destroy_inode, - .write_inode = udf_write_inode, - .delete_inode = udf_delete_inode, - .clear_inode = udf_clear_inode, - .put_super = udf_put_super, - .write_super = udf_write_super, - .statfs = udf_statfs, - .remount_fs = udf_remount_fs, + .alloc_inode = udf_alloc_inode, + .destroy_inode = udf_destroy_inode, + .write_inode = udf_write_inode, + .delete_inode = udf_delete_inode, + .clear_inode = udf_clear_inode, + .put_super = udf_put_super, + .write_super = udf_write_super, + .statfs = udf_statfs, + .remount_fs = udf_remount_fs, }; -struct udf_options -{ +struct udf_options { unsigned char novrs; unsigned int blocksize; unsigned int session; @@ -189,15 +192,19 @@ struct udf_options static int __init init_udf_fs(void) { int err; + err = init_inodecache(); if (err) goto out1; err = register_filesystem(&udf_fstype); if (err) goto out; + return 0; + out: destroy_inodecache(); + out1: return err; } @@ -235,7 +242,7 @@ module_exit(exit_udf_fs) * * The remaining are for debugging and disaster recovery: * - * novrs Skip volume sequence recognition + * novrs Skip volume sequence recognition * * The following expect a offset from 0. * @@ -275,36 +282,35 @@ enum { }; static match_table_t tokens = { - {Opt_novrs, "novrs"}, - {Opt_nostrict, "nostrict"}, - {Opt_bs, "bs=%u"}, - {Opt_unhide, "unhide"}, - {Opt_undelete, "undelete"}, - {Opt_noadinicb, "noadinicb"}, - {Opt_adinicb, "adinicb"}, - {Opt_shortad, "shortad"}, - {Opt_longad, "longad"}, - {Opt_uforget, "uid=forget"}, - {Opt_uignore, "uid=ignore"}, - {Opt_gforget, "gid=forget"}, - {Opt_gignore, "gid=ignore"}, - {Opt_gid, "gid=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_session, "session=%u"}, - {Opt_lastblock, "lastblock=%u"}, - {Opt_anchor, "anchor=%u"}, - {Opt_volume, "volume=%u"}, - {Opt_partition, "partition=%u"}, - {Opt_fileset, "fileset=%u"}, - {Opt_rootdir, "rootdir=%u"}, - {Opt_utf8, "utf8"}, - {Opt_iocharset, "iocharset=%s"}, - {Opt_err, NULL} + {Opt_novrs, "novrs"}, + {Opt_nostrict, "nostrict"}, + {Opt_bs, "bs=%u"}, + {Opt_unhide, "unhide"}, + {Opt_undelete, "undelete"}, + {Opt_noadinicb, "noadinicb"}, + {Opt_adinicb, "adinicb"}, + {Opt_shortad, "shortad"}, + {Opt_longad, "longad"}, + {Opt_uforget, "uid=forget"}, + {Opt_uignore, "uid=ignore"}, + {Opt_gforget, "gid=forget"}, + {Opt_gignore, "gid=ignore"}, + {Opt_gid, "gid=%u"}, + {Opt_uid, "uid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_session, "session=%u"}, + {Opt_lastblock, "lastblock=%u"}, + {Opt_anchor, "anchor=%u"}, + {Opt_volume, "volume=%u"}, + {Opt_partition, "partition=%u"}, + {Opt_fileset, "fileset=%u"}, + {Opt_rootdir, "rootdir=%u"}, + {Opt_utf8, "utf8"}, + {Opt_iocharset, "iocharset=%s"}, + {Opt_err, NULL} }; -static int -udf_parse_options(char *options, struct udf_options *uopt) +static int udf_parse_options(char *options, struct udf_options *uopt) { char *p; int option; @@ -323,145 +329,143 @@ udf_parse_options(char *options, struct udf_options *uopt) if (!options) return 1; - while ((p = strsep(&options, ",")) != NULL) - { + while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; token = match_token(p, tokens, args); - switch (token) - { - case Opt_novrs: - uopt->novrs = 1; - case Opt_bs: - if (match_int(&args[0], &option)) - return 0; - uopt->blocksize = option; - break; - case Opt_unhide: - uopt->flags |= (1 << UDF_FLAG_UNHIDE); - break; - case Opt_undelete: - uopt->flags |= (1 << UDF_FLAG_UNDELETE); - break; - case Opt_noadinicb: - uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_adinicb: - uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_shortad: - uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_longad: - uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_gid: - if (match_int(args, &option)) - return 0; - uopt->gid = option; - break; - case Opt_uid: - if (match_int(args, &option)) - return 0; - uopt->uid = option; - break; - case Opt_umask: - if (match_octal(args, &option)) - return 0; - uopt->umask = option; - break; - case Opt_nostrict: - uopt->flags &= ~(1 << UDF_FLAG_STRICT); - break; - case Opt_session: - if (match_int(args, &option)) - return 0; - uopt->session = option; - break; - case Opt_lastblock: - if (match_int(args, &option)) - return 0; - uopt->lastblock = option; - break; - case Opt_anchor: - if (match_int(args, &option)) - return 0; - uopt->anchor = option; - break; - case Opt_volume: - if (match_int(args, &option)) - return 0; - uopt->volume = option; - break; - case Opt_partition: - if (match_int(args, &option)) - return 0; - uopt->partition = option; - break; - case Opt_fileset: - if (match_int(args, &option)) - return 0; - uopt->fileset = option; - break; - case Opt_rootdir: - if (match_int(args, &option)) - return 0; - uopt->rootdir = option; - break; - case Opt_utf8: - uopt->flags |= (1 << UDF_FLAG_UTF8); - break; + switch (token) { + case Opt_novrs: + uopt->novrs = 1; + case Opt_bs: + if (match_int(&args[0], &option)) + return 0; + uopt->blocksize = option; + break; + case Opt_unhide: + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + break; + case Opt_undelete: + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + break; + case Opt_noadinicb: + uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_adinicb: + uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_shortad: + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_longad: + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_gid: + if (match_int(args, &option)) + return 0; + uopt->gid = option; + break; + case Opt_uid: + if (match_int(args, &option)) + return 0; + uopt->uid = option; + break; + case Opt_umask: + if (match_octal(args, &option)) + return 0; + uopt->umask = option; + break; + case Opt_nostrict: + uopt->flags &= ~(1 << UDF_FLAG_STRICT); + break; + case Opt_session: + if (match_int(args, &option)) + return 0; + uopt->session = option; + break; + case Opt_lastblock: + if (match_int(args, &option)) + return 0; + uopt->lastblock = option; + break; + case Opt_anchor: + if (match_int(args, &option)) + return 0; + uopt->anchor = option; + break; + case Opt_volume: + if (match_int(args, &option)) + return 0; + uopt->volume = option; + break; + case Opt_partition: + if (match_int(args, &option)) + return 0; + uopt->partition = option; + break; + case Opt_fileset: + if (match_int(args, &option)) + return 0; + uopt->fileset = option; + break; + case Opt_rootdir: + if (match_int(args, &option)) + return 0; + uopt->rootdir = option; + break; + case Opt_utf8: + uopt->flags |= (1 << UDF_FLAG_UTF8); + break; #ifdef CONFIG_UDF_NLS - case Opt_iocharset: - uopt->nls_map = load_nls(args[0].from); - uopt->flags |= (1 << UDF_FLAG_NLS_MAP); - break; + case Opt_iocharset: + uopt->nls_map = load_nls(args[0].from); + uopt->flags |= (1 << UDF_FLAG_NLS_MAP); + break; #endif - case Opt_uignore: - uopt->flags |= (1 << UDF_FLAG_UID_IGNORE); - break; - case Opt_uforget: - uopt->flags |= (1 << UDF_FLAG_UID_FORGET); - break; - case Opt_gignore: - uopt->flags |= (1 << UDF_FLAG_GID_IGNORE); - break; - case Opt_gforget: - uopt->flags |= (1 << UDF_FLAG_GID_FORGET); - break; - default: - printk(KERN_ERR "udf: bad mount option \"%s\" " - "or missing value\n", p); + case Opt_uignore: + uopt->flags |= (1 << UDF_FLAG_UID_IGNORE); + break; + case Opt_uforget: + uopt->flags |= (1 << UDF_FLAG_UID_FORGET); + break; + case Opt_gignore: + uopt->flags |= (1 << UDF_FLAG_GID_IGNORE); + break; + case Opt_gforget: + uopt->flags |= (1 << UDF_FLAG_GID_FORGET); + break; + default: + printk(KERN_ERR "udf: bad mount option \"%s\" " + "or missing value\n", p); return 0; } } return 1; } -void -udf_write_super(struct super_block *sb) +void udf_write_super(struct super_block *sb) { lock_kernel(); + if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); sb->s_dirt = 0; + unlock_kernel(); } -static int -udf_remount_fs(struct super_block *sb, int *flags, char *options) +static int udf_remount_fs(struct super_block *sb, int *flags, char *options) { struct udf_options uopt; - uopt.flags = UDF_SB(sb)->s_flags ; - uopt.uid = UDF_SB(sb)->s_uid ; - uopt.gid = UDF_SB(sb)->s_gid ; - uopt.umask = UDF_SB(sb)->s_umask ; + uopt.flags = UDF_SB(sb)->s_flags; + uopt.uid = UDF_SB(sb)->s_uid; + uopt.gid = UDF_SB(sb)->s_gid; + uopt.umask = UDF_SB(sb)->s_umask; - if ( !udf_parse_options(options, &uopt) ) + if (!udf_parse_options(options, &uopt)) return -EINVAL; UDF_SB(sb)->s_flags = uopt.flags; @@ -512,27 +516,26 @@ udf_remount_fs(struct super_block *sb, int *flags, char *options) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static int -udf_set_blocksize(struct super_block *sb, int bsize) +static int udf_set_blocksize(struct super_block *sb, int bsize) { if (!sb_min_blocksize(sb, bsize)) { udf_debug("Bad block size (%d)\n", bsize); printk(KERN_ERR "udf: bad block size (%d)\n", bsize); return 0; } + return sb->s_blocksize; } -static int -udf_vrs(struct super_block *sb, int silent) +static int udf_vrs(struct super_block *sb, int silent) { struct volStructDesc *vsd = NULL; int sector = 32768; int sectorsize; struct buffer_head *bh = NULL; - int iso9660=0; - int nsr02=0; - int nsr03=0; + int iso9660 = 0; + int nsr02 = 0; + int nsr03 = 0; /* Block size must be a multiple of 512 */ if (sb->s_blocksize & 511) @@ -546,10 +549,9 @@ udf_vrs(struct super_block *sb, int silent) sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", - (sector >> sb->s_blocksize_bits), sb->s_blocksize); + (sector >> sb->s_blocksize_bits), sb->s_blocksize); /* Process the sequence (if applicable) */ - for (;!nsr02 && !nsr03; sector += sectorsize) - { + for (; !nsr02 && !nsr03; sector += sectorsize) { /* Read a block */ bh = udf_tread(sb, sector >> sb->s_blocksize_bits); if (!bh) @@ -557,52 +559,45 @@ udf_vrs(struct super_block *sb, int silent) /* Look for ISO descriptors */ vsd = (struct volStructDesc *)(bh->b_data + - (sector & (sb->s_blocksize - 1))); + (sector & (sb->s_blocksize - 1))); - if (vsd->stdIdent[0] == 0) - { + if (vsd->stdIdent[0] == 0) { brelse(bh); break; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) - { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) { iso9660 = sector; - switch (vsd->structType) - { - case 0: - udf_debug("ISO9660 Boot Record found\n"); - break; - case 1: - udf_debug("ISO9660 Primary Volume Descriptor found\n"); - break; - case 2: - udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); - break; - case 3: - udf_debug("ISO9660 Volume Partition Descriptor found\n"); - break; - case 255: - udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); - break; - default: - udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); - break; + switch (vsd->structType) { + case 0: + udf_debug("ISO9660 Boot Record found\n"); + break; + case 1: + udf_debug + ("ISO9660 Primary Volume Descriptor found\n"); + break; + case 2: + udf_debug + ("ISO9660 Supplementary Volume Descriptor found\n"); + break; + case 3: + udf_debug + ("ISO9660 Volume Partition Descriptor found\n"); + break; + case 255: + udf_debug + ("ISO9660 Volume Descriptor Set Terminator found\n"); + break; + default: + udf_debug("ISO9660 VRS (%u) found\n", + vsd->structType); + break; } - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN)) - { - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN)) - { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN)) { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN)) { brelse(bh); break; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN)) - { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN)) { nsr02 = sector; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN)) - { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN)) { nsr03 = sector; } brelse(bh); @@ -635,8 +630,7 @@ udf_vrs(struct super_block *sb, int silent) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static void -udf_find_anchor(struct super_block *sb) +static void udf_find_anchor(struct super_block *sb) { int lastblock = UDF_SB_LASTBLOCK(sb); struct buffer_head *bh = NULL; @@ -644,8 +638,7 @@ udf_find_anchor(struct super_block *sb) uint32_t location; int i; - if (lastblock) - { + if (lastblock) { int varlastblock = udf_variable_to_fixed(lastblock); int last[] = { lastblock, lastblock - 2, lastblock - 150, lastblock - 152, @@ -663,74 +656,54 @@ udf_find_anchor(struct super_block *sb) * however, if the disc isn't closed, it could be 512 */ for (i = 0; !lastblock && i < ARRAY_SIZE(last); i++) { - if (last[i] < 0 || !(bh = sb_bread(sb, last[i]))) - { + if (last[i] < 0 || !(bh = sb_bread(sb, last[i]))) { ident = location = 0; - } - else - { + } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); brelse(bh); } - if (ident == TAG_IDENT_AVDP) - { - if (location == last[i] - UDF_SB_SESSION(sb)) - { + if (ident == TAG_IDENT_AVDP) { + if (location == last[i] - UDF_SB_SESSION(sb)) { lastblock = UDF_SB_ANCHOR(sb)[0] = last[i] - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = last[i] - 256 - UDF_SB_SESSION(sb); - } - else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) - { + } else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb); UDF_SB_ANCHOR(sb)[1] = lastblock - 256 - UDF_SB_SESSION(sb); - } - else + } else { udf_debug("Anchor found at block %d, location mismatch %d.\n", - last[i], location); - } - else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) - { + last[i], location); + } + } else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[3] = 512; - } - else - { - if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256))) - { + } else { + if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256))) { ident = location = 0; - } - else - { + } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); brelse(bh); } - + if (ident == TAG_IDENT_AVDP && - location == last[i] - 256 - UDF_SB_SESSION(sb)) - { + location == last[i] - 256 - UDF_SB_SESSION(sb)) { lastblock = last[i]; UDF_SB_ANCHOR(sb)[1] = last[i] - 256; - } - else - { - if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb)))) - { + } else { + if (last[i] < 312 + UDF_SB_SESSION(sb) || + !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb)))) { ident = location = 0; - } - else - { + } else { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); brelse(bh); } - + if (ident == TAG_IDENT_AVDP && - location == udf_variable_to_fixed(last[i]) - 256) - { + location == udf_variable_to_fixed(last[i]) - 256) { UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); lastblock = udf_variable_to_fixed(last[i]); UDF_SB_ANCHOR(sb)[1] = lastblock - 256; @@ -740,11 +713,9 @@ udf_find_anchor(struct super_block *sb) } } - if (!lastblock) - { + if (!lastblock) { /* We havn't found the lastblock. check 312 */ - if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb)))) - { + if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb)))) { ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); brelse(bh); @@ -755,19 +726,14 @@ udf_find_anchor(struct super_block *sb) } for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { - if (UDF_SB_ANCHOR(sb)[i]) - { - if (!(bh = udf_read_tagged(sb, - UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) - { + if (UDF_SB_ANCHOR(sb)[i]) { + if (!(bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], + UDF_SB_ANCHOR(sb)[i], &ident))) { UDF_SB_ANCHOR(sb)[i] = 0; - } - else - { + } else { brelse(bh); - if ((ident != TAG_IDENT_AVDP) && (i || - (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE))) - { + if ((ident != TAG_IDENT_AVDP) && + (i || (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE))) { UDF_SB_ANCHOR(sb)[i] = 0; } } @@ -777,89 +743,78 @@ udf_find_anchor(struct super_block *sb) UDF_SB_LASTBLOCK(sb) = lastblock; } -static int -udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root) +static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root) { struct buffer_head *bh = NULL; long lastblock; uint16_t ident; if (fileset->logicalBlockNum != 0xFFFFFFFF || - fileset->partitionReferenceNum != 0xFFFF) - { + fileset->partitionReferenceNum != 0xFFFF) { bh = udf_read_ptagged(sb, *fileset, 0, &ident); - if (!bh) + if (!bh) { return 1; - else if (ident != TAG_IDENT_FSD) - { + } else if (ident != TAG_IDENT_FSD) { brelse(bh); return 1; } - + } - if (!bh) /* Search backwards through the partitions */ - { + if (!bh) { /* Search backwards through the partitions */ kernel_lb_addr newfileset; +/* --> cvg: FIXME - is it reasonable? */ return 1; - - for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1; - (newfileset.partitionReferenceNum != 0xFFFF && - fileset->logicalBlockNum == 0xFFFFFFFF && - fileset->partitionReferenceNum == 0xFFFF); - newfileset.partitionReferenceNum--) - { + + for (newfileset.partitionReferenceNum = UDF_SB_NUMPARTS(sb) - 1; + (newfileset.partitionReferenceNum != 0xFFFF && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); + newfileset.partitionReferenceNum--) { lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum); newfileset.logicalBlockNum = 0; - do - { + do { bh = udf_read_ptagged(sb, newfileset, 0, &ident); - if (!bh) - { - newfileset.logicalBlockNum ++; + if (!bh) { + newfileset.logicalBlockNum++; continue; } - switch (ident) + switch (ident) { + case TAG_IDENT_SBD: { - case TAG_IDENT_SBD: - { - struct spaceBitmapDesc *sp; - sp = (struct spaceBitmapDesc *)bh->b_data; - newfileset.logicalBlockNum += 1 + - ((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1) - >> sb->s_blocksize_bits); - brelse(bh); - break; - } - case TAG_IDENT_FSD: - { - *fileset = newfileset; - break; - } - default: - { - newfileset.logicalBlockNum ++; - brelse(bh); - bh = NULL; - break; - } + struct spaceBitmapDesc *sp; + sp = (struct spaceBitmapDesc *)bh->b_data; + newfileset.logicalBlockNum += 1 + + ((le32_to_cpu(sp->numOfBytes) + + sizeof(struct spaceBitmapDesc) - 1) + >> sb->s_blocksize_bits); + brelse(bh); + break; } - } - while (newfileset.logicalBlockNum < lastblock && - fileset->logicalBlockNum == 0xFFFFFFFF && - fileset->partitionReferenceNum == 0xFFFF); + case TAG_IDENT_FSD: + *fileset = newfileset; + break; + default: + newfileset.logicalBlockNum++; + brelse(bh); + bh = NULL; + break; + } + } while (newfileset.logicalBlockNum < lastblock && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); } } if ((fileset->logicalBlockNum != 0xFFFFFFFF || - fileset->partitionReferenceNum != 0xFFFF) && bh) - { + fileset->partitionReferenceNum != 0xFFFF) && bh) { udf_debug("Fileset at block=%d, partition=%d\n", - fileset->logicalBlockNum, fileset->partitionReferenceNum); + fileset->logicalBlockNum, + fileset->partitionReferenceNum); UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; udf_load_fileset(sb, bh, root); @@ -869,8 +824,7 @@ udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr return 1; } -static void -udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) +static void udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) { struct primaryVolDesc *pvoldesc; time_t recording; @@ -880,37 +834,34 @@ udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) pvoldesc = (struct primaryVolDesc *)bh->b_data; - if ( udf_stamp_to_time(&recording, &recording_usec, - lets_to_cpu(pvoldesc->recordingDateAndTime)) ) - { + if (udf_stamp_to_time(&recording, &recording_usec, + lets_to_cpu(pvoldesc->recordingDateAndTime))) { kernel_timestamp ts; ts = lets_to_cpu(pvoldesc->recordingDateAndTime); udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n", - recording, recording_usec, - ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); + recording, recording_usec, + ts.year, ts.month, ts.day, ts.hour, + ts.minute, ts.typeAndTimezone); UDF_SB_RECORDTIME(sb).tv_sec = recording; UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000; } - if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) - { - if (udf_CS0toUTF8(&outstr, &instr)) - { - strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, + if (!udf_build_ustr(&instr, pvoldesc->volIdent, 32)) { + if (udf_CS0toUTF8(&outstr, &instr)) { + strncpy(UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len > 31 ? 31 : outstr.u_len); udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb)); } } - if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) ) - { + if (!udf_build_ustr(&instr, pvoldesc->volSetIdent, 128)) { if (udf_CS0toUTF8(&outstr, &instr)) udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); } } -static void -udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root) +static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, + kernel_lb_addr *root) { struct fileSetDesc *fset; @@ -920,24 +871,21 @@ udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum); - udf_debug("Rootdir at block=%d, partition=%d\n", - root->logicalBlockNum, root->partitionReferenceNum); + udf_debug("Rootdir at block=%d, partition=%d\n", + root->logicalBlockNum, root->partitionReferenceNum); } -static void -udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) +static void udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) { struct partitionDesc *p; int i; p = (struct partitionDesc *)bh->b_data; - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { - udf_debug("Searching map: (%d == %d)\n", - UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber)); - if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) - { + for (i = 0; i < UDF_SB_NUMPARTS(sb); i++) { + udf_debug("Searching map: (%d == %d)\n", + UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber)); + if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) { UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation); if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY) @@ -950,79 +898,76 @@ udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE; if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) || - !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) - { + !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) { struct partitionHeaderDesc *phd; phd = (struct partitionHeaderDesc *)(p->partitionContentsUse); - if (phd->unallocSpaceTable.extLength) - { - kernel_lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i }; + if (phd->unallocSpaceTable.extLength) { + kernel_lb_addr loc = { + .logicalBlockNum = le32_to_cpu(phd->unallocSpaceTable.extPosition), + .partitionReferenceNum = i, + }; UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE; udf_debug("unallocSpaceTable (part %d) @ %ld\n", - i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); + i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); } - if (phd->unallocSpaceBitmap.extLength) - { + if (phd->unallocSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_uspace); - if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL) - { + if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength = le32_to_cpu(phd->unallocSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition = le32_to_cpu(phd->unallocSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; udf_debug("unallocSpaceBitmap (part %d) @ %d\n", - i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition); + i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition); } } if (phd->partitionIntegrityTable.extLength) udf_debug("partitionIntegrityTable (part %d)\n", i); - if (phd->freedSpaceTable.extLength) - { - kernel_lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i }; + if (phd->freedSpaceTable.extLength) { + kernel_lb_addr loc = { + .logicalBlockNum = le32_to_cpu(phd->freedSpaceTable.extPosition), + .partitionReferenceNum = i, + }; UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table = udf_iget(sb, loc); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE; udf_debug("freedSpaceTable (part %d) @ %ld\n", - i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino); + i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino); } - if (phd->freedSpaceBitmap.extLength) - { + if (phd->freedSpaceBitmap.extLength) { UDF_SB_ALLOC_BITMAP(sb, i, s_fspace); - if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL) - { + if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL) { UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength = le32_to_cpu(phd->freedSpaceBitmap.extLength); UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition = le32_to_cpu(phd->freedSpaceBitmap.extPosition); UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP; udf_debug("freedSpaceBitmap (part %d) @ %d\n", - i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition); + i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition); } } } break; } } - if (i == UDF_SB_NUMPARTS(sb)) - { - udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber)); - } - else - { + if (i == UDF_SB_NUMPARTS(sb)) { + udf_debug("Partition (%d) not found in partition map\n", + le16_to_cpu(p->partitionNumber)); + } else { udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n", - le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i), - UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i)); + le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i), + UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i)); } } -static int -udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_addr *fileset) +static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, + kernel_lb_addr *fileset) { struct logicalVolDesc *lvd; int i, j, offset; @@ -1032,37 +977,27 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_a UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps)); - for (i=0,offset=0; - i<UDF_SB_NUMPARTS(sb) && offset<le32_to_cpu(lvd->mapTableLength); - i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) - { + for (i = 0, offset = 0; + i < UDF_SB_NUMPARTS(sb) && offset < le32_to_cpu(lvd->mapTableLength); + i++, offset += ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) { type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType; - if (type == 1) - { + if (type == 1) { struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); UDF_SB_PARTFUNC(sb,i) = NULL; - } - else if (type == 2) - { + } else if (type == 2) { struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]); - if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) - { - if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) - { + if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) { + if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; - } - else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) - { + } else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; } - } - else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) - { + } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { uint32_t loc; uint16_t ident; struct sparingTable *st; @@ -1070,26 +1005,21 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_a UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength); - for (j=0; j<spm->numSparingTables; j++) - { + for (j = 0; j < spm->numSparingTables; j++) { loc = le32_to_cpu(spm->locSparingTable[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = udf_read_tagged(sb, loc, loc, &ident); - if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) - { + if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data; if (ident != 0 || - strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) - { + strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) { brelse(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]); UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL; } } } UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; - } - else - { + } else { udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); continue; } @@ -1097,20 +1027,20 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_a UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum); } udf_debug("Partition (%d:%d) type %d on volume %d\n", - i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i)); + i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i)); } - if (fileset) - { + if (fileset) { long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]); *fileset = lelb_to_cpu(la->extLocation); udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n", - fileset->logicalBlockNum, - fileset->partitionReferenceNum); + fileset->logicalBlockNum, + fileset->partitionReferenceNum); } if (lvd->integritySeqExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); + return 0; } @@ -1118,26 +1048,24 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_a * udf_load_logicalvolint * */ -static void -udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) +static void udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) { struct buffer_head *bh = NULL; uint16_t ident; while (loc.extLength > 0 && - (bh = udf_read_tagged(sb, loc.extLocation, - loc.extLocation, &ident)) && - ident == TAG_IDENT_LVID) - { + (bh = udf_read_tagged(sb, loc.extLocation, + loc.extLocation, &ident)) && + ident == TAG_IDENT_LVID) { UDF_SB_LVIDBH(sb) = bh; - + if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); - + if (UDF_SB_LVIDBH(sb) != bh) brelse(bh); loc.extLength -= sb->s_blocksize; - loc.extLocation ++; + loc.extLocation++; } if (UDF_SB_LVIDBH(sb) != bh) brelse(bh); @@ -1158,15 +1086,15 @@ udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static int -udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_lb_addr *fileset) +static int udf_process_sequence(struct super_block *sb, long block, long lastblock, + kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; struct generic_desc *gd; struct volDescPtr *vdp; - int done=0; - int i,j; + int done = 0; + int i, j; uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; @@ -1174,93 +1102,81 @@ udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_ memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); /* Read the main descriptor sequence */ - for (;(!done && block <= lastblock); block++) - { + for (; (!done && block <= lastblock); block++) { bh = udf_read_tagged(sb, block, block, &ident); - if (!bh) + if (!bh) break; /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; vdsn = le32_to_cpu(gd->volDescSeqNum); - switch (ident) - { - case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ - if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_PRIMARY_VOL_DESC].block = block; - } - break; - case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ - if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) - { - vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; - vds[VDS_POS_VOL_DESC_PTR].block = block; - - vdp = (struct volDescPtr *)bh->b_data; - next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); - next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength); - next_e = next_e >> sb->s_blocksize_bits; - next_e += next_s; - } - break; - case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ - if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_IMP_USE_VOL_DESC].block = block; - } - break; - case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ - if (!vds[VDS_POS_PARTITION_DESC].block) - vds[VDS_POS_PARTITION_DESC].block = block; - break; - case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ - if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_LOGICAL_VOL_DESC].block = block; - } - break; - case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ - if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) - { - vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; - } - break; - case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ - vds[VDS_POS_TERMINATING_DESC].block = block; - if (next_e) - { - block = next_s; - lastblock = next_e; - next_s = next_e = 0; - } - else - done = 1; - break; + switch (ident) { + case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ + if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) { + vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_PRIMARY_VOL_DESC].block = block; + } + break; + case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ + if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) { + vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; + vds[VDS_POS_VOL_DESC_PTR].block = block; + + vdp = (struct volDescPtr *)bh->b_data; + next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); + next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength); + next_e = next_e >> sb->s_blocksize_bits; + next_e += next_s; + } + break; + case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ + if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) { + vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_IMP_USE_VOL_DESC].block = block; + } + break; + case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ + if (!vds[VDS_POS_PARTITION_DESC].block) + vds[VDS_POS_PARTITION_DESC].block = block; + break; + case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ + if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) { + vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_LOGICAL_VOL_DESC].block = block; + } + break; + case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ + if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) { + vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; + vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; + } + break; + case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ + vds[VDS_POS_TERMINATING_DESC].block = block; + if (next_e) { + block = next_s; + lastblock = next_e; + next_s = next_e = 0; + } else { + done = 1; + } + break; } brelse(bh); } - for (i=0; i<VDS_POS_LENGTH; i++) - { - if (vds[i].block) - { + for (i = 0; i < VDS_POS_LENGTH; i++) { + if (vds[i].block) { bh = udf_read_tagged(sb, vds[i].block, vds[i].block, &ident); - if (i == VDS_POS_PRIMARY_VOL_DESC) + if (i == VDS_POS_PRIMARY_VOL_DESC) { udf_load_pvoldesc(sb, bh); - else if (i == VDS_POS_LOGICAL_VOL_DESC) + } else if (i == VDS_POS_LOGICAL_VOL_DESC) { udf_load_logicalvol(sb, bh, fileset); - else if (i == VDS_POS_PARTITION_DESC) - { + } else if (i == VDS_POS_PARTITION_DESC) { struct buffer_head *bh2 = NULL; udf_load_partdesc(sb, bh); - for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++) - { + for (j = vds[i].block + 1; j < vds[VDS_POS_TERMINATING_DESC].block; j++) { bh2 = udf_read_tagged(sb, j, j, &ident); gd = (struct generic_desc *)bh2->b_data; if (ident == TAG_IDENT_PD) @@ -1278,31 +1194,28 @@ udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_ /* * udf_check_valid() */ -static int -udf_check_valid(struct super_block *sb, int novrs, int silent) +static int udf_check_valid(struct super_block *sb, int novrs, int silent) { long block; - if (novrs) - { + if (novrs) { udf_debug("Validity check skipped because of novrs option\n"); return 0; } /* Check that it is NSR02 compliant */ /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ - else if ((block = udf_vrs(sb, silent)) == -1) - { - udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); + else if ((block = udf_vrs(sb, silent)) == -1) { + udf_debug("Failed to read byte 32768. Assuming open disc. " + "Skipping validity check\n"); if (!UDF_SB_LASTBLOCK(sb)) UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); return 0; - } - else + } else { return !block; + } } -static int -udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) +static int udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) { struct anchorVolDescPtr *anchor; uint16_t ident; @@ -1314,14 +1227,14 @@ udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) return 1; for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); i++) { - if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb, - UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) - { + if (UDF_SB_ANCHOR(sb)[i] && + (bh = udf_read_tagged(sb, UDF_SB_ANCHOR(sb)[i], + UDF_SB_ANCHOR(sb)[i], &ident))) { anchor = (struct anchorVolDescPtr *)bh->b_data; /* Locate the main sequence */ - main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation ); - main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength ); + main_s = le32_to_cpu(anchor->mainVolDescSeqExt.extLocation); + main_e = le32_to_cpu(anchor->mainVolDescSeqExt.extLength ); main_e = main_e >> sb->s_blocksize_bits; main_e += main_s; @@ -1336,8 +1249,7 @@ udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) /* Process the main & reserve sequences */ /* responsible for finding the PartitionDesc(s) */ if (!(udf_process_sequence(sb, main_s, main_e, fileset) && - udf_process_sequence(sb, reserve_s, reserve_e, fileset))) - { + udf_process_sequence(sb, reserve_s, reserve_e, fileset))) { break; } } @@ -1349,70 +1261,68 @@ udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) } else udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { - switch (UDF_SB_PARTTYPE(sb, i)) - { - case UDF_VIRTUAL_MAP15: - case UDF_VIRTUAL_MAP20: - { - kernel_lb_addr ino; + for (i = 0; i < UDF_SB_NUMPARTS(sb); i++) { + kernel_lb_addr uninitialized_var(ino); + switch (UDF_SB_PARTTYPE(sb, i)) { + case UDF_VIRTUAL_MAP15: + case UDF_VIRTUAL_MAP20: + if (!UDF_SB_LASTBLOCK(sb)) { + UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); + udf_find_anchor(sb); + } - if (!UDF_SB_LASTBLOCK(sb)) - { - UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); - udf_find_anchor(sb); - } + if (!UDF_SB_LASTBLOCK(sb)) { + udf_debug("Unable to determine Lastblock (For " + "Virtual Partition)\n"); + return 1; + } - if (!UDF_SB_LASTBLOCK(sb)) - { - udf_debug("Unable to determine Lastblock (For Virtual Partition)\n"); - return 1; + for (j = 0; j < UDF_SB_NUMPARTS(sb); j++) { + if (j != i && UDF_SB_PARTVSN(sb, i) == + UDF_SB_PARTVSN(sb, j) && + UDF_SB_PARTNUM(sb, i) == + UDF_SB_PARTNUM(sb, j)) { + ino.partitionReferenceNum = j; + ino.logicalBlockNum = + UDF_SB_LASTBLOCK(sb) - + UDF_SB_PARTROOT(sb, j); + break; } + } - for (j=0; j<UDF_SB_NUMPARTS(sb); j++) - { - if (j != i && - UDF_SB_PARTVSN(sb,i) == UDF_SB_PARTVSN(sb,j) && - UDF_SB_PARTNUM(sb,i) == UDF_SB_PARTNUM(sb,j)) - { - ino.partitionReferenceNum = j; - ino.logicalBlockNum = UDF_SB_LASTBLOCK(sb) - - UDF_SB_PARTROOT(sb,j); - break; - } - } + if (j == UDF_SB_NUMPARTS(sb)) + return 1; - if (j == UDF_SB_NUMPARTS(sb)) - return 1; + if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino))) + return 1; - if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino))) - return 1; + if (UDF_SB_PARTTYPE(sb, i) == UDF_VIRTUAL_MAP15) { + UDF_SB_TYPEVIRT(sb, i).s_start_offset = + udf_ext0_offset(UDF_SB_VAT(sb)); + UDF_SB_TYPEVIRT(sb, i).s_num_entries = + (UDF_SB_VAT(sb)->i_size - 36) >> 2; + } else if (UDF_SB_PARTTYPE(sb, i) == UDF_VIRTUAL_MAP20) { + struct buffer_head *bh = NULL; + uint32_t pos; - if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15) - { - UDF_SB_TYPEVIRT(sb,i).s_start_offset = udf_ext0_offset(UDF_SB_VAT(sb)); - UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) >> 2; - } - else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) - { - struct buffer_head *bh = NULL; - uint32_t pos; - - pos = udf_block_map(UDF_SB_VAT(sb), 0); - bh = sb_bread(sb, pos); - if (!bh) - return 1; - UDF_SB_TYPEVIRT(sb,i).s_start_offset = - le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) + - udf_ext0_offset(UDF_SB_VAT(sb)); - UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - - UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2; - brelse(bh); - } - UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); - UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); + pos = udf_block_map(UDF_SB_VAT(sb), 0); + bh = sb_bread(sb, pos); + if (!bh) + return 1; + UDF_SB_TYPEVIRT(sb, i).s_start_offset = + le16_to_cpu(((struct + virtualAllocationTable20 *)bh->b_data + + udf_ext0_offset(UDF_SB_VAT(sb)))-> + lengthHeader) + + udf_ext0_offset(UDF_SB_VAT(sb)); + UDF_SB_TYPEVIRT(sb, i).s_num_entries = + (UDF_SB_VAT(sb)->i_size - + UDF_SB_TYPEVIRT(sb, i).s_start_offset) >> 2; + brelse(bh); } + UDF_SB_PARTROOT(sb, i) = udf_get_pblock(sb, 0, i, 0); + UDF_SB_PARTLEN(sb, i) = UDF_SB_PARTLEN(sb, + ino.partitionReferenceNum); } } return 0; @@ -1420,26 +1330,28 @@ udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) static void udf_open_lvid(struct super_block *sb) { - if (UDF_SB_LVIDBH(sb)) - { + if (UDF_SB_LVIDBH(sb)) { int i; kernel_timestamp cpu_time; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); + UDF_SB_LVID(sb)->recordingDateAndTime = + cpu_to_lets(cpu_time); UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; UDF_SB_LVID(sb)->descTag.descCRC = - cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), - le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); + cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), + le16_to_cpu(UDF_SB_LVID(sb)->descTag. + descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += - ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; + ((uint8_t *) & + (UDF_SB_LVID(sb)->descTag))[i]; mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } @@ -1447,12 +1359,11 @@ static void udf_open_lvid(struct super_block *sb) static void udf_close_lvid(struct super_block *sb) { - if (UDF_SB_LVIDBH(sb) && - UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) - { - int i; - kernel_timestamp cpu_time; + kernel_timestamp cpu_time; + int i; + if (UDF_SB_LVIDBH(sb) && + UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) { UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) @@ -1467,10 +1378,10 @@ static void udf_close_lvid(struct super_block *sb) UDF_SB_LVID(sb)->descTag.descCRC = cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), - le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); + le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); UDF_SB_LVID(sb)->descTag.tagChecksum = 0; - for (i=0; i<16; i++) + for (i = 0; i < 16; i++) if (i != 4) UDF_SB_LVID(sb)->descTag.tagChecksum += ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; @@ -1498,7 +1409,7 @@ static void udf_close_lvid(struct super_block *sb) static int udf_fill_super(struct super_block *sb, void *options, int silent) { int i; - struct inode *inode=NULL; + struct inode *inode = NULL; struct udf_options uopt; kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; @@ -1511,6 +1422,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sbi = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; + sb->s_fs_info = sbi; memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info)); @@ -1520,15 +1432,13 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) goto error_out; if (uopt.flags & (1 << UDF_FLAG_UTF8) && - uopt.flags & (1 << UDF_FLAG_NLS_MAP)) - { + uopt.flags & (1 << UDF_FLAG_NLS_MAP)) { udf_error(sb, "udf_read_super", - "utf8 cannot be combined with iocharset\n"); + "utf8 cannot be combined with iocharset\n"); goto error_out; } #ifdef CONFIG_UDF_NLS - if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) - { + if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) { uopt.nls_map = load_nls_default(); if (!uopt.nls_map) uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP); @@ -1552,7 +1462,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) if (!udf_set_blocksize(sb, uopt.blocksize)) goto error_out; - if ( uopt.session == 0xFFFFFFFF ) + if (uopt.session == 0xFFFFFFFF) UDF_SB_SESSION(sb) = udf_get_last_session(sb); else UDF_SB_SESSION(sb) = uopt.session; @@ -1564,10 +1474,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) UDF_SB_ANCHOR(sb)[2] = uopt.anchor; UDF_SB_ANCHOR(sb)[3] = 256; - if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ - { + if (udf_check_valid(sb, uopt.novrs, silent)) { /* read volume recognition sequences */ printk("UDF-fs: No VRS found\n"); - goto error_out; + goto error_out; } udf_find_anchor(sb); @@ -1579,29 +1488,24 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; - if (udf_load_partition(sb, &fileset)) - { + if (udf_load_partition(sb, &fileset)) { printk("UDF-fs: No partition found (1)\n"); goto error_out; } udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); - if ( UDF_SB_LVIDBH(sb) ) - { + if (UDF_SB_LVIDBH(sb)) { uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev); uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); /* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */ - if (minUDFReadRev > UDF_MAX_READ_VERSION) - { + if (minUDFReadRev > UDF_MAX_READ_VERSION) { printk("UDF-fs: minUDFReadRev=%x (max is %x)\n", - le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev), - UDF_MAX_READ_VERSION); + le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev), + UDF_MAX_READ_VERSION); goto error_out; - } - else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) - { + } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) { sb->s_flags |= MS_RDONLY; } @@ -1613,8 +1517,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS); } - if ( !UDF_SB_NUMPARTS(sb) ) - { + if (!UDF_SB_NUMPARTS(sb)) { printk("UDF-fs: No partition found (2)\n"); goto error_out; } @@ -1624,20 +1527,19 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sb->s_flags |= MS_RDONLY; } - if ( udf_find_fileset(sb, &fileset, &rootdir) ) - { + if (udf_find_fileset(sb, &fileset, &rootdir)) { printk("UDF-fs: No fileset found\n"); goto error_out; } - if (!silent) - { + if (!silent) { kernel_timestamp ts; udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb)); - udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", - UDFFS_VERSION, UDFFS_DATE, - UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, - ts.typeAndTimezone); + udf_info("UDF %s (%s) Mounting volume '%s', " + "timestamp %04u/%02u/%02u %02u:%02u (%x)\n", + UDFFS_VERSION, UDFFS_DATE, + UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, + ts.typeAndTimezone); } if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); @@ -1645,18 +1547,16 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) /* Assign the root inode */ /* assign inodes by physical block number */ /* perhaps it's not extensible enough, but for now ... */ - inode = udf_iget(sb, rootdir); - if (!inode) - { + inode = udf_iget(sb, rootdir); + if (!inode) { printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n", - rootdir.logicalBlockNum, rootdir.partitionReferenceNum); + rootdir.logicalBlockNum, rootdir.partitionReferenceNum); goto error_out; } /* Allocate a dentry for the root inode */ sb->s_root = d_alloc_root(inode); - if (!sb->s_root) - { + if (!sb->s_root) { printk("UDF-fs: Couldn't allocate root dentry\n"); iput(inode); goto error_out; @@ -1667,19 +1567,17 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) error_out: if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); - if (UDF_SB_NUMPARTS(sb)) - { + if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); + UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb), s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); - if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) - { - for (i=0; i<4; i++) + UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb), s_fspace); + if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { + for (i = 0; i < 4; i++) brelse(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } @@ -1693,16 +1591,16 @@ error_out: UDF_SB_FREE(sb); kfree(sbi); sb->s_fs_info = NULL; + return -EINVAL; } void udf_error(struct super_block *sb, const char *function, - const char *fmt, ...) + const char *fmt, ...) { va_list args; - if (!(sb->s_flags & MS_RDONLY)) - { + if (!(sb->s_flags & MS_RDONLY)) { /* mark sb error */ sb->s_dirt = 1; } @@ -1714,15 +1612,15 @@ void udf_error(struct super_block *sb, const char *function, } void udf_warning(struct super_block *sb, const char *function, - const char *fmt, ...) + const char *fmt, ...) { va_list args; - va_start (args, fmt); + va_start(args, fmt); vsnprintf(error_buf, sizeof(error_buf), fmt, args); va_end(args); printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n", - sb->s_id, function, error_buf); + sb->s_id, function, error_buf); } /* @@ -1738,26 +1636,23 @@ void udf_warning(struct super_block *sb, const char *function, * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static void -udf_put_super(struct super_block *sb) +static void udf_put_super(struct super_block *sb) { int i; if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); - if (UDF_SB_NUMPARTS(sb)) - { + if (UDF_SB_NUMPARTS(sb)) { if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); + UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb), s_uspace); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); - if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) - { - for (i=0; i<4; i++) + UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb), s_fspace); + if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) { + for (i = 0; i < 4; i++) brelse(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); } } @@ -1786,8 +1681,7 @@ udf_put_super(struct super_block *sb) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static int -udf_statfs(struct dentry *dentry, struct kstatfs *buf) +static int udf_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -1797,11 +1691,11 @@ udf_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = udf_count_free(sb); buf->f_bavail = buf->f_bfree; buf->f_files = (UDF_SB_LVIDBH(sb) ? - (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + - le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree; + (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + + le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree; buf->f_ffree = buf->f_bfree; /* __kernel_fsid_t f_fsid */ - buf->f_namelen = UDF_NAME_LEN-2; + buf->f_namelen = UDF_NAME_LEN - 2; return 0; } @@ -1810,8 +1704,7 @@ static unsigned char udf_bitmap_lookup[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; -static unsigned int -udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) +static unsigned int udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) { struct buffer_head *bh = NULL; unsigned int accum = 0; @@ -1830,13 +1723,10 @@ udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_read_ptagged(sb, loc, 0, &ident); - if (!bh) - { + if (!bh) { printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; - } - else if (ident != TAG_IDENT_SBD) - { + } else if (ident != TAG_IDENT_SBD) { brelse(bh); printk(KERN_ERR "udf: udf_count_free failed\n"); goto out; @@ -1847,23 +1737,19 @@ udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) index = sizeof(struct spaceBitmapDesc); /* offset in first block only */ ptr = (uint8_t *)bh->b_data; - while ( bytes > 0 ) - { - while ((bytes > 0) && (index < sb->s_blocksize)) - { + while (bytes > 0) { + while ((bytes > 0) && (index < sb->s_blocksize)) { value = ptr[index]; - accum += udf_bitmap_lookup[ value & 0x0f ]; - accum += udf_bitmap_lookup[ value >> 4 ]; + accum += udf_bitmap_lookup[value & 0x0f]; + accum += udf_bitmap_lookup[value >> 4]; index++; bytes--; } - if ( bytes ) - { + if (bytes) { brelse(bh); newblock = udf_get_lb_pblock(sb, loc, ++block); bh = udf_tread(sb, newblock); - if (!bh) - { + if (!bh) { udf_debug("read failed\n"); goto out; } @@ -1879,8 +1765,7 @@ out: return accum; } -static unsigned int -udf_count_free_table(struct super_block *sb, struct inode * table) +static unsigned int udf_count_free_table(struct super_block *sb, struct inode *table) { unsigned int accum = 0; uint32_t elen; @@ -1894,26 +1779,23 @@ udf_count_free_table(struct super_block *sb, struct inode * table) epos.offset = sizeof(struct unallocSpaceEntry); epos.bh = NULL; - while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) + while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { accum += (elen >> table->i_sb->s_blocksize_bits); + } brelse(epos.bh); unlock_kernel(); return accum; } - -static unsigned int -udf_count_free(struct super_block *sb) + +static unsigned int udf_count_free(struct super_block *sb) { unsigned int accum = 0; - if (UDF_SB_LVIDBH(sb)) - { - if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) - { + if (UDF_SB_LVIDBH(sb)) { + if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) { accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); - if (accum == 0xFFFFFFFF) accum = 0; } @@ -1922,28 +1804,24 @@ udf_count_free(struct super_block *sb) if (accum) return accum; - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - { + if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) { accum += udf_count_free_bitmap(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap); + UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap); } - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - { + if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) { accum += udf_count_free_bitmap(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); + UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); } if (accum) return accum; - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) - { + if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) { accum += udf_count_free_table(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); + UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); } - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) - { + if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) { accum += udf_count_free_table(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); + UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); } return accum; diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 12613b6..e6f933d 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -11,7 +11,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1999 Stelias Computing Inc * * HISTORY * @@ -39,35 +39,33 @@ static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen, char int elen = 0; char *p = to; - while (elen < fromlen) - { + while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); - switch (pc->componentType) - { - case 1: - if (pc->lengthComponentIdent == 0) - { - p = to; - *p++ = '/'; - } - break; - case 3: - memcpy(p, "../", 3); - p += 3; - break; - case 4: - memcpy(p, "./", 2); - p += 2; - /* that would be . - just ignore */ - break; - case 5: - p += udf_get_filename(sb, pc->componentIdent, p, pc->lengthComponentIdent); + switch (pc->componentType) { + case 1: + if (pc->lengthComponentIdent == 0) { + p = to; *p++ = '/'; - break; + } + break; + case 3: + memcpy(p, "../", 3); + p += 3; + break; + case 4: + memcpy(p, "./", 2); + p += 2; + /* that would be . - just ignore */ + break; + case 5: + p += udf_get_filename(sb, pc->componentIdent, p, + pc->lengthComponentIdent); + *p++ = '/'; + break; } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; } - if (p > to+1) + if (p > to + 1) p[-1] = '\0'; else p[0] = '\0'; @@ -82,10 +80,9 @@ static int udf_symlink_filler(struct file *file, struct page *page) char *p = kmap(page); lock_kernel(); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) { symlink = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); - else - { + } else { bh = sb_bread(inode->i_sb, udf_block_map(inode, 0)); if (!bh) @@ -102,6 +99,7 @@ static int udf_symlink_filler(struct file *file, struct page *page) kunmap(page); unlock_page(page); return 0; + out: unlock_kernel(); SetPageError(page); diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 60d2776..7fc3912 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -28,35 +28,36 @@ #include "udf_i.h" #include "udf_sb.h" -static void extent_trunc(struct inode * inode, struct extent_position *epos, - kernel_lb_addr eloc, int8_t etype, uint32_t elen, uint32_t nelen) +static void extent_trunc(struct inode *inode, struct extent_position *epos, + kernel_lb_addr eloc, int8_t etype, uint32_t elen, + uint32_t nelen) { - kernel_lb_addr neloc = { 0, 0 }; - int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; - int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + kernel_lb_addr neloc = {}; + int last_block = (elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; + int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; - if (nelen) - { - if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block); + if (nelen) { + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + udf_free_blocks(inode->i_sb, inode, eloc, 0, + last_block); etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30); - } - else + } else neloc = eloc; nelen = (etype << 30) | nelen; } - if (elen != nelen) - { + if (elen != nelen) { udf_write_aext(inode, epos, neloc, nelen, 0); - if (last_block - first_block > 0) - { + if (last_block - first_block > 0) { if (etype == (EXT_RECORDED_ALLOCATED >> 30)) mark_inode_dirty(inode); if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block); + udf_free_blocks(inode->i_sb, inode, eloc, + first_block, + last_block - first_block); } } } @@ -67,7 +68,7 @@ static void extent_trunc(struct inode * inode, struct extent_position *epos, */ void udf_truncate_tail_extent(struct inode *inode) { - struct extent_position epos = { NULL, 0, {0, 0}}; + struct extent_position epos = {}; kernel_lb_addr eloc; uint32_t elen, nelen; uint64_t lbcount = 0; @@ -89,8 +90,7 @@ void udf_truncate_tail_extent(struct inode *inode) BUG(); /* Find the last extent in the file */ - while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) - { + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { etype = netype; lbcount += elen; if (lbcount > inode->i_size) { @@ -123,7 +123,7 @@ void udf_truncate_tail_extent(struct inode *inode) void udf_discard_prealloc(struct inode *inode) { - struct extent_position epos = { NULL, 0, {0, 0}}; + struct extent_position epos = { NULL, 0, {0, 0} }; kernel_lb_addr eloc; uint32_t elen; uint64_t lbcount = 0; @@ -131,7 +131,7 @@ void udf_discard_prealloc(struct inode *inode) int adsize; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || - inode->i_size == UDF_I_LENEXTENTS(inode)) + inode->i_size == UDF_I_LENEXTENTS(inode)) return; if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) @@ -153,15 +153,21 @@ void udf_discard_prealloc(struct inode *inode) lbcount -= elen; extent_trunc(inode, &epos, eloc, etype, elen, 0); if (!epos.bh) { - UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode); + UDF_I_LENALLOC(inode) = + epos.offset - udf_file_entry_alloc_offset(inode); mark_inode_dirty(inode); } else { - struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); - aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc)); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) + struct allocExtDesc *aed = + (struct allocExtDesc *)(epos.bh->b_data); + aed->lengthAllocDescs = + cpu_to_le32(epos.offset - + sizeof(struct allocExtDesc)); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(inode->i_sb) >= 0x0201) udf_update_tag(epos.bh->b_data, epos.offset); else - udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); + udf_update_tag(epos.bh->b_data, + sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(epos.bh, inode); } } @@ -171,10 +177,10 @@ void udf_discard_prealloc(struct inode *inode) brelse(epos.bh); } -void udf_truncate_extents(struct inode * inode) +void udf_truncate_extents(struct inode *inode) { struct extent_position epos; - kernel_lb_addr eloc, neloc = { 0, 0 }; + kernel_lb_addr eloc, neloc = {}; uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc; int8_t etype; struct super_block *sb = inode->i_sb; @@ -190,9 +196,9 @@ void udf_truncate_extents(struct inode * inode) BUG(); etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); - byte_offset = (offset << sb->s_blocksize_bits) + (inode->i_size & (sb->s_blocksize-1)); - if (etype != -1) - { + byte_offset = (offset << sb->s_blocksize_bits) + + (inode->i_size & (sb->s_blocksize - 1)); + if (etype != -1) { epos.offset -= adsize; extent_trunc(inode, &epos, eloc, etype, elen, byte_offset); epos.offset += adsize; @@ -206,35 +212,33 @@ void udf_truncate_extents(struct inode * inode) else lenalloc -= sizeof(struct allocExtDesc); - while ((etype = udf_current_aext(inode, &epos, &eloc, &elen, 0)) != -1) - { - if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) - { + while ((etype = udf_current_aext(inode, &epos, &eloc, &elen, 0)) != -1) { + if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { udf_write_aext(inode, &epos, neloc, nelen, 0); - if (indirect_ext_len) - { + if (indirect_ext_len) { /* We managed to free all extents in the * indirect extent - free it too */ if (!epos.bh) BUG(); - udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len); - } - else - { - if (!epos.bh) - { + udf_free_blocks(sb, inode, epos.block, + 0, indirect_ext_len); + } else { + if (!epos.bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); - } - else - { - struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); - aed->lengthAllocDescs = cpu_to_le32(lenalloc); - if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201) - udf_update_tag(epos.bh->b_data, lenalloc + - sizeof(struct allocExtDesc)); + } else { + struct allocExtDesc *aed = + (struct allocExtDesc *)(epos.bh->b_data); + aed->lengthAllocDescs = + cpu_to_le32(lenalloc); + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(sb) >= 0x0201) + udf_update_tag(epos.bh->b_data, + lenalloc + + sizeof(struct allocExtDesc)); else - udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); + udf_update_tag(epos.bh->b_data, + sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(epos.bh, inode); } } @@ -243,49 +247,41 @@ void udf_truncate_extents(struct inode * inode) epos.block = eloc; epos.bh = udf_tread(sb, udf_get_lb_pblock(sb, eloc, 0)); if (elen) - indirect_ext_len = (elen + - sb->s_blocksize - 1) >> + indirect_ext_len = (elen + sb->s_blocksize -1) >> sb->s_blocksize_bits; else indirect_ext_len = 1; - } - else - { + } else { extent_trunc(inode, &epos, eloc, etype, elen, 0); epos.offset += adsize; } } - if (indirect_ext_len) - { + if (indirect_ext_len) { if (!epos.bh) BUG(); - udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len); - } - else - { - if (!epos.bh) - { + udf_free_blocks(sb, inode, epos.block, 0, + indirect_ext_len); + } else { + if (!epos.bh) { UDF_I_LENALLOC(inode) = lenalloc; mark_inode_dirty(inode); - } - else - { - struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); + } else { + struct allocExtDesc *aed = + (struct allocExtDesc *)(epos.bh->b_data); aed->lengthAllocDescs = cpu_to_le32(lenalloc); - if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201) - udf_update_tag(epos.bh->b_data, lenalloc + - sizeof(struct allocExtDesc)); + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || + UDF_SB_UDFREV(sb) >= 0x0201) + udf_update_tag(epos.bh->b_data, + lenalloc + sizeof(struct allocExtDesc)); else - udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); + udf_update_tag(epos.bh->b_data, + sizeof(struct allocExtDesc)); mark_buffer_dirty_inode(epos.bh, inode); } } - } - else if (inode->i_size) - { - if (byte_offset) - { + } else if (inode->i_size) { + if (byte_offset) { kernel_long_ad extent; /* @@ -293,21 +289,23 @@ void udf_truncate_extents(struct inode * inode) * no extent above inode->i_size => truncate is * extending the file by 'offset' blocks. */ - if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || + if ((!epos.bh && + epos.offset == udf_file_entry_alloc_offset(inode)) || (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { /* File has no extents at all or has empty last * indirect extent! Create a fake extent... */ extent.extLocation.logicalBlockNum = 0; extent.extLocation.partitionReferenceNum = 0; extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; - } - else { + } else { epos.offset -= adsize; etype = udf_next_aext(inode, &epos, - &extent.extLocation, &extent.extLength, 0); + &extent.extLocation, + &extent.extLength, 0); extent.extLength |= etype << 30; } - udf_extend_file(inode, &epos, &extent, offset+((inode->i_size & (sb->s_blocksize-1)) != 0)); + udf_extend_file(inode, &epos, &extent, + offset + ((inode->i_size & (sb->s_blocksize - 1)) != 0)); } } UDF_I_LENEXTENTS(inode) = inode->i_size; diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 3b2e6c8..3e937d3 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -41,8 +41,7 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb) #define UDF_SB_FREE(X)\ {\ - if (UDF_SB(X))\ - {\ + if (UDF_SB(X)) {\ kfree(UDF_SB_PARTMAPS(X));\ UDF_SB_PARTMAPS(X) = NULL;\ }\ @@ -51,13 +50,10 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb) #define UDF_SB_ALLOC_PARTMAPS(X,Y)\ {\ UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ - if (UDF_SB_PARTMAPS(X) != NULL)\ - {\ + if (UDF_SB_PARTMAPS(X) != NULL) {\ UDF_SB_NUMPARTS(X) = Y;\ memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\ - }\ - else\ - {\ + } else {\ UDF_SB_NUMPARTS(X) = 0;\ udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\ }\ @@ -72,15 +68,12 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb) UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\ else\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\ - if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\ - {\ + if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL) {\ memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\ (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\ - }\ - else\ - {\ + } else {\ udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\ }\ } @@ -90,8 +83,7 @@ static inline struct udf_sb_info *UDF_SB(struct super_block *sb) int i;\ int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\ int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ - for (i=0; i<nr_groups; i++)\ - {\ + for (i = 0; i < nr_groups; i++) {\ if (UDF_SB_BITMAP(X,Y,Z,i))\ brelse(UDF_SB_BITMAP(X,Y,Z,i));\ }\ diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index f581f2f..c8016cc 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -50,30 +50,26 @@ extern const struct address_space_operations udf_aops; extern const struct address_space_operations udf_adinicb_aops; extern const struct address_space_operations udf_symlink_aops; -struct udf_fileident_bh -{ +struct udf_fileident_bh { struct buffer_head *sbh; struct buffer_head *ebh; int soffset; int eoffset; }; -struct udf_vds_record -{ +struct udf_vds_record { uint32_t block; uint32_t volDescSeqNum; }; -struct generic_desc -{ +struct generic_desc { tag descTag; __le32 volDescSeqNum; }; -struct ustr -{ +struct ustr { uint8_t u_cmpID; - uint8_t u_name[UDF_NAME_LEN-2]; + uint8_t u_name[UDF_NAME_LEN - 2]; uint8_t u_len; }; @@ -83,44 +79,58 @@ struct extent_position { kernel_lb_addr block; }; - /* super.c */ extern void udf_error(struct super_block *, const char *, const char *, ...); extern void udf_warning(struct super_block *, const char *, const char *, ...); /* namei.c */ -extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *); +extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, + struct fileIdentDesc *, struct udf_fileident_bh *, + uint8_t *, uint8_t *); /* file.c */ -extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +extern int udf_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); /* inode.c */ extern struct inode *udf_iget(struct super_block *, kernel_lb_addr); extern int udf_sync_inode(struct inode *); extern void udf_expand_file_adinicb(struct inode *, int, int *); -extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); -extern struct buffer_head * udf_bread(struct inode *, int, int, int *); +extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *); +extern struct buffer_head *udf_bread(struct inode *, int, int, int *); extern void udf_truncate(struct inode *); extern void udf_read_inode(struct inode *); extern void udf_delete_inode(struct inode *); extern void udf_clear_inode(struct inode *); extern int udf_write_inode(struct inode *, int); extern long udf_block_map(struct inode *, sector_t); -extern int udf_extend_file(struct inode *, struct extent_position *, kernel_long_ad *, sector_t); -extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, kernel_lb_addr *, uint32_t *, sector_t *); -extern int8_t udf_add_aext(struct inode *, struct extent_position *, kernel_lb_addr, uint32_t, int); -extern int8_t udf_write_aext(struct inode *, struct extent_position *, kernel_lb_addr, uint32_t, int); -extern int8_t udf_delete_aext(struct inode *, struct extent_position, kernel_lb_addr, uint32_t); -extern int8_t udf_next_aext(struct inode *, struct extent_position *, kernel_lb_addr *, uint32_t *, int); -extern int8_t udf_current_aext(struct inode *, struct extent_position *, kernel_lb_addr *, uint32_t *, int); +extern int udf_extend_file(struct inode *, struct extent_position *, + kernel_long_ad *, sector_t); +extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, + kernel_lb_addr *, uint32_t *, sector_t *); +extern int8_t udf_add_aext(struct inode *, struct extent_position *, + kernel_lb_addr, uint32_t, int); +extern int8_t udf_write_aext(struct inode *, struct extent_position *, + kernel_lb_addr, uint32_t, int); +extern int8_t udf_delete_aext(struct inode *, struct extent_position, + kernel_lb_addr, uint32_t); +extern int8_t udf_next_aext(struct inode *, struct extent_position *, + kernel_lb_addr *, uint32_t *, int); +extern int8_t udf_current_aext(struct inode *, struct extent_position *, + kernel_lb_addr *, uint32_t *, int); /* misc.c */ extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tread(struct super_block *, int); -extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t); -extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t); -extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *); -extern struct buffer_head *udf_read_ptagged(struct super_block *, kernel_lb_addr, uint32_t, uint16_t *); +extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, + uint32_t, uint8_t); +extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, + uint8_t); +extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, + uint32_t, uint16_t *); +extern struct buffer_head *udf_read_ptagged(struct super_block *, + kernel_lb_addr, uint32_t, + uint16_t *); extern void udf_update_tag(char *, int); extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int); @@ -129,21 +139,26 @@ extern unsigned int udf_get_last_session(struct super_block *); extern unsigned long udf_get_last_block(struct super_block *); /* partition.c */ -extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t); +extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, + uint32_t); extern int udf_relocate_blocks(struct super_block *, long, long *); /* unicode.c */ extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); -extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int); +extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, + int); extern int udf_build_ustr(struct ustr *, dstring *, int); extern int udf_CS0toUTF8(struct ustr *, struct ustr *); /* ialloc.c */ extern void udf_free_inode(struct inode *); -extern struct inode * udf_new_inode (struct inode *, int, int *); +extern struct inode *udf_new_inode(struct inode *, int, int *); /* truncate.c */ extern void udf_truncate_tail_extent(struct inode *); @@ -151,18 +166,27 @@ extern void udf_discard_prealloc(struct inode *); extern void udf_truncate_extents(struct inode *); /* balloc.c */ -extern void udf_free_blocks(struct super_block *, struct inode *, kernel_lb_addr, uint32_t, uint32_t); -extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t); -extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *); +extern void udf_free_blocks(struct super_block *, struct inode *, + kernel_lb_addr, uint32_t, uint32_t); +extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, + uint32_t, uint32_t); +extern int udf_new_block(struct super_block *, struct inode *, uint16_t, + uint32_t, int *); /* fsync.c */ extern int udf_fsync_file(struct file *, struct dentry *, int); /* directory.c */ -extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, struct extent_position *, kernel_lb_addr *, uint32_t *, sector_t *); -extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset); -extern long_ad * udf_get_filelongad(uint8_t *, int, int *, int); -extern short_ad * udf_get_fileshortad(uint8_t *, int, int *, int); +extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *, + struct udf_fileident_bh *, + struct fileIdentDesc *, + struct extent_position *, + kernel_lb_addr *, uint32_t *, + sector_t *); +extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, + int *offset); +extern long_ad *udf_get_filelongad(uint8_t *, int, int *, int); +extern short_ad *udf_get_fileshortad(uint8_t *, int, int *, int); /* crc.c */ extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); @@ -171,4 +195,4 @@ extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); extern time_t *udf_stamp_to_time(time_t *, long *, kernel_timestamp); extern kernel_timestamp *udf_time_to_stamp(kernel_timestamp *, struct timespec); -#endif /* __UDF_DECL_H */ +#endif /* __UDF_DECL_H */ diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h index 17d3788..c4bd120 100644 --- a/fs/udf/udfend.h +++ b/fs/udf/udfend.h @@ -7,74 +7,92 @@ static inline kernel_lb_addr lelb_to_cpu(lb_addr in) { kernel_lb_addr out; + out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum); out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum); + return out; } static inline lb_addr cpu_to_lelb(kernel_lb_addr in) { lb_addr out; + out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); + return out; } static inline kernel_timestamp lets_to_cpu(timestamp in) { kernel_timestamp out; + memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone); out.year = le16_to_cpu(in.year); + return out; } static inline short_ad lesa_to_cpu(short_ad in) { short_ad out; + out.extLength = le32_to_cpu(in.extLength); out.extPosition = le32_to_cpu(in.extPosition); + return out; } static inline short_ad cpu_to_lesa(short_ad in) { short_ad out; + out.extLength = cpu_to_le32(in.extLength); out.extPosition = cpu_to_le32(in.extPosition); + return out; } static inline kernel_long_ad lela_to_cpu(long_ad in) { kernel_long_ad out; + out.extLength = le32_to_cpu(in.extLength); out.extLocation = lelb_to_cpu(in.extLocation); + return out; } static inline long_ad cpu_to_lela(kernel_long_ad in) { long_ad out; + out.extLength = cpu_to_le32(in.extLength); out.extLocation = cpu_to_lelb(in.extLocation); + return out; } static inline kernel_extent_ad leea_to_cpu(extent_ad in) { kernel_extent_ad out; + out.extLength = le32_to_cpu(in.extLength); out.extLocation = le32_to_cpu(in.extLocation); + return out; } static inline timestamp cpu_to_lets(kernel_timestamp in) { timestamp out; + memcpy(&out, &in, sizeof(timestamp)); out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone); out.year = cpu_to_le16(in.year); + return out; } diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c index 85d8dbe..3fd80eb 100644 --- a/fs/udf/udftime.c +++ b/fs/udf/udftime.c @@ -18,18 +18,18 @@ Boston, MA 02111-1307, USA. */ /* - * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time + * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time * 10/04/98: added new table-based lookup after seeing how ugly the gnu code is * blf 09/27/99: ripped out all the old code and inserted new table from - * John Brockmeyer (without leap second corrections) - * rewrote udf_stamp_to_time and fixed timezone accounting in - udf_time_to_stamp. + * John Brockmeyer (without leap second corrections) + * rewrote udf_stamp_to_time and fixed timezone accounting in + * udf_time_to_stamp. */ /* * We don't take into account leap seconds. This may be correct or incorrect. * For more NIST information (especially dealing with leap seconds), see: - * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm + * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm */ #include <linux/types.h> @@ -46,36 +46,35 @@ #endif /* How many days come before each month (0-12). */ -static const unsigned short int __mon_yday[2][13] = -{ +static const unsigned short int __mon_yday[2][13] = { /* Normal years. */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, /* Leap years. */ - { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; #define MAX_YEAR_SECONDS 69 -#define SPD 0x15180 /*3600*24*/ -#define SPY(y,l,s) (SPD * (365*y+l)+s) +#define SPD 0x15180 /*3600*24 */ +#define SPY(y,l,s) (SPD * (365*y+l)+s) static time_t year_seconds[MAX_YEAR_SECONDS]= { -/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), -/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), -/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), -/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), -/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), -/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), -/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), -/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), -/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), -/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), -/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), -/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), -/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), -/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), -/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), -/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), -/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), +/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), +/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), +/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), +/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), +/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), +/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), +/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), +/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), +/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), +/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), +/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), +/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), +/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), +/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), +/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), +/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), +/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), /*2038*/ SPY(68,17,0) }; @@ -84,27 +83,24 @@ extern struct timezone sys_tz; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) -time_t * -udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) +time_t *udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) { int yday; uint8_t type = src.typeAndTimezone >> 12; int16_t offset; - if (type == 1) - { + if (type == 1) { offset = src.typeAndTimezone << 4; /* sign extent offset */ offset = (offset >> 4); if (offset == -2047) /* unspecified offset */ offset = 0; - } - else + } else { offset = 0; + } if ((src.year < EPOCH_YEAR) || - (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS)) - { + (src.year >= EPOCH_YEAR + MAX_YEAR_SECONDS)) { *dest = -1; *dest_usec = -1; return NULL; @@ -113,15 +109,13 @@ udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) *dest -= offset * 60; yday = ((__mon_yday[__isleap (src.year)] - [src.month-1]) + (src.day-1)); - *dest += ( ( (yday* 24) + src.hour ) * 60 + src.minute ) * 60 + src.second; + [src.month - 1]) + (src.day - 1)); + *dest += ( ( (yday * 24) + src.hour ) * 60 + src.minute ) * 60 + src.second; *dest_usec = src.centiseconds * 10000 + src.hundredsOfMicroseconds * 100 + src.microseconds; return dest; } - -kernel_timestamp * -udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) +kernel_timestamp *udf_time_to_stamp(kernel_timestamp * dest, struct timespec ts) { long int days, rem, y; const unsigned short int *ip; @@ -146,19 +140,18 @@ udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) #define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) - while (days < 0 || days >= (__isleap(y) ? 366 : 365)) - { + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { long int yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ days -= ((yg - y) * 365 - + LEAPS_THRU_END_OF (yg - 1) - - LEAPS_THRU_END_OF (y - 1)); + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); y = yg; } dest->year = y; ip = __mon_yday[__isleap(y)]; - for (y = 11; days < (long int) ip[y]; --y) + for (y = 11; days < (long int)ip[y]; --y) continue; days -= ip[y]; dest->month = y + 1; @@ -167,7 +160,7 @@ udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) dest->centiseconds = ts.tv_nsec / 10000000; dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100; dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 - - dest->hundredsOfMicroseconds * 100); + dest->hundredsOfMicroseconds * 100); return dest; } diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 706c92e..9e6099c 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -31,12 +31,14 @@ static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { - if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) ) + if ((!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN - 2)) return 0; + memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; dest->u_len = strlen; + return strlen; } @@ -47,14 +49,15 @@ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) { int usesize; - if ( (!dest) || (!ptr) || (!size) ) + if ((!dest) || (!ptr) || (!size)) return -1; memset(dest, 0, sizeof(struct ustr)); - usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size; - dest->u_cmpID=ptr[0]; - dest->u_len=ptr[size-1]; - memcpy(dest->u_name, ptr+1, usesize-1); + usesize = (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size; + dest->u_cmpID = ptr[0]; + dest->u_len = ptr[size - 1]; + memcpy(dest->u_name, ptr + 1, usesize - 1); + return 0; } @@ -63,13 +66,14 @@ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) */ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { - if ( (!dest) || (!ptr) || (!exactsize) ) + if ((!dest) || (!ptr) || (!exactsize)) return -1; memset(dest, 0, sizeof(struct ustr)); - dest->u_cmpID=ptr[0]; - dest->u_len=exactsize-1; - memcpy(dest->u_name, ptr+1, exactsize-1); + dest->u_cmpID = ptr[0]; + dest->u_len = exactsize - 1; + memcpy(dest->u_name, ptr + 1, exactsize - 1); + return 0; } @@ -108,22 +112,20 @@ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; - if (ocu_len == 0) - { + if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } - if ((cmp_id != 8) && (cmp_id != 16)) - { - printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); + if ((cmp_id != 8) && (cmp_id != 16)) { + printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", + cmp_id, ocu_i->u_name); return 0; } - for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) - { + for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; @@ -131,21 +133,18 @@ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) c = (c << 8) | ocu[i++]; /* Compress Unicode to UTF-8 */ - if (c < 0x80U) + if (c < 0x80U) { utf_o->u_name[utf_o->u_len++] = (uint8_t)c; - else if (c < 0x800U) - { + } else if (c < 0x800U) { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); - } - else - { + } else { utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f)); utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); } } - utf_o->u_cmpID=8; + utf_o->u_cmpID = 8; return utf_o->u_len; } @@ -186,61 +185,46 @@ try_again: u_len = 0U; utf_char = 0U; utf_cnt = 0U; - for (i = 0U; i < utf->u_len; i++) - { + for (i = 0U; i < utf->u_len; i++) { c = (uint8_t)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ - if (utf_cnt) - { + if (utf_cnt) { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; - } - else - { + } else { /* Check for a multi-byte UTF-8 character */ - if (c & 0x80U) - { + if (c & 0x80U) { /* Start a multi-byte UTF-8 character */ - if ((c & 0xe0U) == 0xc0U) - { + if ((c & 0xe0U) == 0xc0U) { utf_char = c & 0x1fU; utf_cnt = 1; - } - else if ((c & 0xf0U) == 0xe0U) - { + } else if ((c & 0xf0U) == 0xe0U) { utf_char = c & 0x0fU; utf_cnt = 2; - } - else if ((c & 0xf8U) == 0xf0U) - { + } else if ((c & 0xf8U) == 0xf0U) { utf_char = c & 0x07U; utf_cnt = 3; - } - else if ((c & 0xfcU) == 0xf8U) - { + } else if ((c & 0xfcU) == 0xf8U) { utf_char = c & 0x03U; utf_cnt = 4; - } - else if ((c & 0xfeU) == 0xfcU) - { + } else if ((c & 0xfeU) == 0xfcU) { utf_char = c & 0x01U; utf_cnt = 5; - } - else + } else { goto error_out; + } continue; - } else + } else { /* Single byte UTF-8 character (most common) */ utf_char = c; + } } /* Choose no compression if necessary */ - if (utf_char > max_val) - { - if ( 0xffU == max_val ) - { + if (utf_char > max_val) { + if (max_val == 0xffU) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; @@ -248,26 +232,25 @@ try_again: goto error_out; } - if (max_val == 0xffffU) - { + if (max_val == 0xffffU) { ocu[++u_len] = (uint8_t)(utf_char >> 8); } ocu[++u_len] = (uint8_t)(utf_char & 0xffU); } - - if (utf_cnt) - { + if (utf_cnt) { error_out: ocu[++u_len] = '?'; printk(KERN_DEBUG "udf: bad UTF-8 character\n"); } ocu[length - 1] = (uint8_t)u_len + 1; + return u_len + 1; } -static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i) +static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, + struct ustr *ocu_i) { uint8_t *ocu; uint32_t c; @@ -280,36 +263,35 @@ static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr * cmp_id = ocu_i->u_cmpID; utf_o->u_len = 0; - if (ocu_len == 0) - { + if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); utf_o->u_cmpID = 0; utf_o->u_len = 0; return 0; } - if ((cmp_id != 8) && (cmp_id != 16)) - { - printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); + if ((cmp_id != 8) && (cmp_id != 16)) { + printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", + cmp_id, ocu_i->u_name); return 0; } - for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) - { + for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; - utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], - UDF_NAME_LEN - utf_o->u_len); + utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], + UDF_NAME_LEN - utf_o->u_len); } - utf_o->u_cmpID=8; + utf_o->u_cmpID = 8; return utf_o->u_len; } -static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length) +static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, + int length) { unsigned len, i, max_val; uint16_t uni_char; @@ -321,19 +303,17 @@ static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, i try_again: u_len = 0U; - for (i = 0U; i < uni->u_len; i++) - { - len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char); + for (i = 0U; i < uni->u_len; i++) { + len = nls->char2uni(&uni->u_name[i], uni->u_len - i, &uni_char); if (len <= 0) continue; - if (uni_char > max_val) - { + if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } - + if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(uni_char >> 8); ocu[++u_len] = (uint8_t)(uni_char & 0xffU); @@ -344,112 +324,98 @@ try_again: return u_len + 1; } -int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen) +int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, + int flen) { struct ustr filename, unifilename; int len; - if (udf_build_ustr_exact(&unifilename, sname, flen)) - { + if (udf_build_ustr_exact(&unifilename, sname, flen)) { return 0; } - if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) - { - if (!udf_CS0toUTF8(&filename, &unifilename) ) - { + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { + if (!udf_CS0toUTF8(&filename, &unifilename)) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } - } - else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - { - if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) ) - { + } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { + if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename)) { udf_debug("Failed in udf_get_filename: sname = %s\n", sname); return 0; } - } - else + } else { return 0; + } - if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, - unifilename.u_name, unifilename.u_len))) - { + len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, + unifilename.u_name, unifilename.u_len); + if (len) { return len; } + return 0; } -int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen) +int udf_put_filename(struct super_block *sb, const uint8_t *sname, + uint8_t *dname, int flen) { struct ustr unifilename; int namelen; - if ( !(udf_char_to_ustr(&unifilename, sname, flen)) ) - { + if (!(udf_char_to_ustr(&unifilename, sname, flen))) { return 0; } - if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) - { - if ( !(namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN)) ) - { + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { + namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN); + if (!namelen) { return 0; } - } - else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - { - if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN)) ) - { + } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { + namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN); + if (!namelen) { return 0; } - } - else + } else { return 0; + } return namelen; } #define ILLEGAL_CHAR_MARK '_' -#define EXT_MARK '.' -#define CRC_MARK '#' -#define EXT_SIZE 5 +#define EXT_MARK '.' +#define CRC_MARK '#' +#define EXT_SIZE 5 -static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen) +static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, + uint8_t *fidName, int fidNameLen) { - int index, newIndex = 0, needsCRC = 0; + int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; unsigned short valueCRC; uint8_t curr; const uint8_t hexChar[] = "0123456789ABCDEF"; - if (udfName[0] == '.' && (udfLen == 1 || - (udfLen == 2 && udfName[1] == '.'))) - { + if (udfName[0] == '.' && + (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { needsCRC = 1; newIndex = udfLen; memcpy(newName, udfName, udfLen); - } - else - { - for (index = 0; index < udfLen; index++) - { + } else { + for (index = 0; index < udfLen; index++) { curr = udfName[index]; - if (curr == '/' || curr == 0) - { + if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; - while (index+1 < udfLen && (udfName[index+1] == '/' || - udfName[index+1] == 0)) + while (index + 1 < udfLen && (udfName[index + 1] == '/' || + udfName[index + 1] == 0)) index++; - } - if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) - { - if (udfLen == index + 1) + } if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) { + if (udfLen == index + 1) { hasExt = 0; - else - { + } else { hasExt = 1; extIndex = index; newExtIndex = newIndex; @@ -461,26 +427,22 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen needsCRC = 1; } } - if (needsCRC) - { + if (needsCRC) { uint8_t ext[EXT_SIZE]; int localExtIndex = 0; - if (hasExt) - { + if (hasExt) { int maxFilenameLen; - for(index = 0; index<EXT_SIZE && extIndex + index +1 < udfLen; - index++ ) - { + for(index = 0; index < EXT_SIZE && extIndex + index + 1 < udfLen; index++) { curr = udfName[extIndex + index + 1]; - if (curr == '/' || curr == 0) - { + if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; - while(extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE - && (udfName[extIndex + index + 2] == '/' || - udfName[extIndex + index + 2] == 0))) + while(extIndex + index + 2 < udfLen && + (index + 1 < EXT_SIZE + && (udfName[extIndex + index + 2] == '/' || + udfName[extIndex + index + 2] == 0))) index++; } ext[localExtIndex++] = curr; @@ -490,9 +452,9 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen newIndex = maxFilenameLen; else newIndex = newExtIndex; - } - else if (newIndex > 250) + } else if (newIndex > 250) { newIndex = 250; + } newName[newIndex++] = CRC_MARK; valueCRC = udf_crc(fidName, fidNameLen, 0); newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; @@ -500,12 +462,12 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; - if (hasExt) - { + if (hasExt) { newName[newIndex++] = EXT_MARK; - for (index = 0;index < localExtIndex ;index++ ) + for (index = 0; index < localExtIndex; index++) newName[newIndex++] = ext[index]; } } + return newIndex; } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 22ff6ed..73402c5 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -87,6 +87,7 @@ #include <linux/smp_lock.h> #include <linux/buffer_head.h> #include <linux/vfs.h> +#include <linux/log2.h> #include "swab.h" #include "util.h" @@ -854,7 +855,7 @@ magic_found: uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask); uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift); - if (uspi->s_fsize & (uspi->s_fsize - 1)) { + if (!is_power_of_2(uspi->s_fsize)) { printk(KERN_ERR "ufs_read_super: fragment size %u is not a power of 2\n", uspi->s_fsize); goto failed; @@ -869,7 +870,7 @@ magic_found: uspi->s_fsize); goto failed; } - if (uspi->s_bsize & (uspi->s_bsize - 1)) { + if (!is_power_of_2(uspi->s_bsize)) { printk(KERN_ERR "ufs_read_super: block size %u is not a power of 2\n", uspi->s_bsize); goto failed; @@ -1239,14 +1240,14 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag inode_init_once(&ei->vfs_inode); } - + static int init_inodecache(void) { ufs_inode_cachep = kmem_cache_create("ufs_inode_cache", sizeof(struct ufs_inode_info), 0, (SLAB_RECLAIM_ACCOUNT| SLAB_MEM_SPREAD), - init_once, NULL); + init_once); if (ufs_inode_cachep == NULL) return -ENOMEM; return 0; diff --git a/fs/utimes.c b/fs/utimes.c index b3c8895..682eb63 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -106,7 +106,7 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags if (IS_IMMUTABLE(inode)) goto dput_and_out; - if (current->fsuid != inode->i_uid) { + if (!is_owner_or_cap(inode)) { if (f) { if (!(f->f_mode & FMODE_WRITE)) goto dput_and_out; @@ -60,8 +60,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) return -EPERM; if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) && - (mask & MAY_WRITE) && (current->fsuid != inode->i_uid) && - !capable(CAP_FOWNER)) + (mask & MAY_WRITE) && !is_owner_or_cap(inode)) return -EPERM; } diff --git a/fs/xfs/Makefile-linux-2.6 b/fs/xfs/Makefile-linux-2.6 index b49989b..e7a9a83 100644 --- a/fs/xfs/Makefile-linux-2.6 +++ b/fs/xfs/Makefile-linux-2.6 @@ -64,6 +64,7 @@ xfs-y += xfs_alloc.o \ xfs_dir2_sf.o \ xfs_error.o \ xfs_extfree_item.o \ + xfs_filestream.o \ xfs_fsops.o \ xfs_ialloc.o \ xfs_ialloc_btree.o \ @@ -77,6 +78,7 @@ xfs-y += xfs_alloc.o \ xfs_log.o \ xfs_log_recover.o \ xfs_mount.o \ + xfs_mru_cache.o \ xfs_rename.o \ xfs_trans.o \ xfs_trans_ail.o \ diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h index 9ebabdf..b4acc7f 100644 --- a/fs/xfs/linux-2.6/kmem.h +++ b/fs/xfs/linux-2.6/kmem.h @@ -74,14 +74,14 @@ extern void kmem_free(void *, size_t); static inline kmem_zone_t * kmem_zone_init(int size, char *zone_name) { - return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL); + return kmem_cache_create(zone_name, size, 0, 0, NULL); } static inline kmem_zone_t * kmem_zone_init_flags(int size, char *zone_name, unsigned long flags, void (*construct)(void *, kmem_zone_t *, unsigned long)) { - return kmem_cache_create(zone_name, size, 0, flags, construct, NULL); + return kmem_cache_create(zone_name, size, 0, flags, construct); } static inline void @@ -100,25 +100,6 @@ kmem_zone_destroy(kmem_zone_t *zone) extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast); extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast); -/* - * Low memory cache shrinkers - */ - -typedef struct shrinker *kmem_shaker_t; -typedef int (*kmem_shake_func_t)(int, gfp_t); - -static inline kmem_shaker_t -kmem_shake_register(kmem_shake_func_t sfunc) -{ - return set_shrinker(DEFAULT_SEEKS, sfunc); -} - -static inline void -kmem_shake_deregister(kmem_shaker_t shrinker) -{ - remove_shrinker(shrinker); -} - static inline int kmem_shake_allow(gfp_t gfp_mask) { diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 7361861..fd4105d 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -108,14 +108,19 @@ xfs_page_trace( /* * Schedule IO completion handling on a xfsdatad if this was - * the final hold on this ioend. + * the final hold on this ioend. If we are asked to wait, + * flush the workqueue. */ STATIC void xfs_finish_ioend( - xfs_ioend_t *ioend) + xfs_ioend_t *ioend, + int wait) { - if (atomic_dec_and_test(&ioend->io_remaining)) + if (atomic_dec_and_test(&ioend->io_remaining)) { queue_work(xfsdatad_workqueue, &ioend->io_work); + if (wait) + flush_workqueue(xfsdatad_workqueue); + } } /* @@ -156,6 +161,8 @@ xfs_setfilesize( xfs_fsize_t bsize; ip = xfs_vtoi(ioend->io_vnode); + if (!ip) + return; ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFREG); ASSERT(ioend->io_type != IOMAP_READ); @@ -334,7 +341,7 @@ xfs_end_bio( bio->bi_end_io = NULL; bio_put(bio); - xfs_finish_ioend(ioend); + xfs_finish_ioend(ioend, 0); return 0; } @@ -470,7 +477,7 @@ xfs_submit_ioend( } if (bio) xfs_submit_ioend_bio(ioend, bio); - xfs_finish_ioend(ioend); + xfs_finish_ioend(ioend, 0); } while ((ioend = next) != NULL); } @@ -1003,6 +1010,8 @@ xfs_page_state_convert( if (buffer_unwritten(bh) || buffer_delay(bh) || ((buffer_uptodate(bh) || PageUptodate(page)) && !buffer_mapped(bh) && (unmapped || startio))) { + int new_ioend = 0; + /* * Make sure we don't use a read-only iomap */ @@ -1021,6 +1030,15 @@ xfs_page_state_convert( } if (!iomap_valid) { + /* + * if we didn't have a valid mapping then we + * need to ensure that we put the new mapping + * in a new ioend structure. This needs to be + * done to ensure that the ioends correctly + * reflect the block mappings at io completion + * for unwritten extent conversion. + */ + new_ioend = 1; if (type == IOMAP_NEW) { size = xfs_probe_cluster(inode, page, bh, head, 0); @@ -1040,7 +1058,7 @@ xfs_page_state_convert( if (startio) { xfs_add_to_ioend(inode, bh, offset, type, &ioend, - !iomap_valid); + new_ioend); } else { set_buffer_dirty(bh); unlock_buffer(bh); @@ -1416,6 +1434,13 @@ xfs_end_io_direct( * This is not necessary for synchronous direct I/O, but we do * it anyway to keep the code uniform and simpler. * + * Well, if only it were that simple. Because synchronous direct I/O + * requires extent conversion to occur *before* we return to userspace, + * we have to wait for extent conversion to complete. Look at the + * iocb that has been passed to us to determine if this is AIO or + * not. If it is synchronous, tell xfs_finish_ioend() to kick the + * workqueue and wait for it to complete. + * * The core direct I/O code might be changed to always call the * completion handler in the future, in which case all this can * go away. @@ -1423,9 +1448,9 @@ xfs_end_io_direct( ioend->io_offset = offset; ioend->io_size = size; if (ioend->io_type == IOMAP_READ) { - xfs_finish_ioend(ioend); + xfs_finish_ioend(ioend, 0); } else if (private && size > 0) { - xfs_finish_ioend(ioend); + xfs_finish_ioend(ioend, is_sync_kiocb(iocb)); } else { /* * A direct I/O write ioend starts it's life in unwritten @@ -1434,7 +1459,7 @@ xfs_end_io_direct( * handler. */ INIT_WORK(&ioend->io_work, xfs_end_bio_written); - xfs_finish_ioend(ioend); + xfs_finish_ioend(ioend, 0); } /* diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index fe4f66a..b0f0e58 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -35,10 +35,13 @@ #include <linux/freezer.h> static kmem_zone_t *xfs_buf_zone; -static kmem_shaker_t xfs_buf_shake; STATIC int xfsbufd(void *); STATIC int xfsbufd_wakeup(int, gfp_t); STATIC void xfs_buf_delwri_queue(xfs_buf_t *, int); +static struct shrinker xfs_buf_shake = { + .shrink = xfsbufd_wakeup, + .seeks = DEFAULT_SEEKS, +}; static struct workqueue_struct *xfslogd_workqueue; struct workqueue_struct *xfsdatad_workqueue; @@ -314,7 +317,7 @@ xfs_buf_free( ASSERT(list_empty(&bp->b_hash_list)); - if (bp->b_flags & _XBF_PAGE_CACHE) { + if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) { uint i; if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1)) @@ -323,18 +326,11 @@ xfs_buf_free( for (i = 0; i < bp->b_page_count; i++) { struct page *page = bp->b_pages[i]; - ASSERT(!PagePrivate(page)); + if (bp->b_flags & _XBF_PAGE_CACHE) + ASSERT(!PagePrivate(page)); page_cache_release(page); } _xfs_buf_free_pages(bp); - } else if (bp->b_flags & _XBF_KMEM_ALLOC) { - /* - * XXX(hch): bp->b_count_desired might be incorrect (see - * xfs_buf_associate_memory for details), but fortunately - * the Linux version of kmem_free ignores the len argument.. - */ - kmem_free(bp->b_addr, bp->b_count_desired); - _xfs_buf_free_pages(bp); } xfs_buf_deallocate(bp); @@ -764,43 +760,44 @@ xfs_buf_get_noaddr( size_t len, xfs_buftarg_t *target) { - size_t malloc_len = len; + unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT; + int error, i; xfs_buf_t *bp; - void *data; - int error; bp = xfs_buf_allocate(0); if (unlikely(bp == NULL)) goto fail; _xfs_buf_initialize(bp, target, 0, len, 0); - try_again: - data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL | KM_LARGE); - if (unlikely(data == NULL)) + error = _xfs_buf_get_pages(bp, page_count, 0); + if (error) goto fail_free_buf; - /* check whether alignment matches.. */ - if ((__psunsigned_t)data != - ((__psunsigned_t)data & ~target->bt_smask)) { - /* .. else double the size and try again */ - kmem_free(data, malloc_len); - malloc_len <<= 1; - goto try_again; + for (i = 0; i < page_count; i++) { + bp->b_pages[i] = alloc_page(GFP_KERNEL); + if (!bp->b_pages[i]) + goto fail_free_mem; } + bp->b_flags |= _XBF_PAGES; - error = xfs_buf_associate_memory(bp, data, len); - if (error) + error = _xfs_buf_map_pages(bp, XBF_MAPPED); + if (unlikely(error)) { + printk(KERN_WARNING "%s: failed to map pages\n", + __FUNCTION__); goto fail_free_mem; - bp->b_flags |= _XBF_KMEM_ALLOC; + } xfs_buf_unlock(bp); - XB_TRACE(bp, "no_daddr", data); + XB_TRACE(bp, "no_daddr", len); return bp; + fail_free_mem: - kmem_free(data, malloc_len); + while (--i >= 0) + __free_page(bp->b_pages[i]); + _xfs_buf_free_pages(bp); fail_free_buf: - xfs_buf_free(bp); + xfs_buf_deallocate(bp); fail: return NULL; } @@ -1453,6 +1450,7 @@ xfs_free_buftarg( int external) { xfs_flush_buftarg(btp, 1); + xfs_blkdev_issue_flush(btp); if (external) xfs_blkdev_put(btp->bt_bdev); xfs_free_bufhash(btp); @@ -1837,14 +1835,9 @@ xfs_buf_init(void) if (!xfsdatad_workqueue) goto out_destroy_xfslogd_workqueue; - xfs_buf_shake = kmem_shake_register(xfsbufd_wakeup); - if (!xfs_buf_shake) - goto out_destroy_xfsdatad_workqueue; - + register_shrinker(&xfs_buf_shake); return 0; - out_destroy_xfsdatad_workqueue: - destroy_workqueue(xfsdatad_workqueue); out_destroy_xfslogd_workqueue: destroy_workqueue(xfslogd_workqueue); out_free_buf_zone: @@ -1859,7 +1852,7 @@ xfs_buf_init(void) void xfs_buf_terminate(void) { - kmem_shake_deregister(xfs_buf_shake); + unregister_shrinker(&xfs_buf_shake); destroy_workqueue(xfsdatad_workqueue); destroy_workqueue(xfslogd_workqueue); kmem_zone_destroy(xfs_buf_zone); diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index b6241f6..b5908a3 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -63,7 +63,7 @@ typedef enum { /* flags used only internally */ _XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */ - _XBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */ + _XBF_PAGES = (1 << 18), /* backed by refcounted pages */ _XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */ _XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */ } xfs_buf_flags_t; diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 8c43cd2..0d4001e 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -184,15 +184,6 @@ xfs_file_open( } STATIC int -xfs_file_close( - struct file *filp, - fl_owner_t id) -{ - return -bhv_vop_close(vn_from_inode(filp->f_path.dentry->d_inode), 0, - file_count(filp) > 1 ? L_FALSE : L_TRUE, NULL); -} - -STATIC int xfs_file_release( struct inode *inode, struct file *filp) @@ -221,19 +212,18 @@ xfs_file_fsync( } #ifdef CONFIG_XFS_DMAPI -STATIC struct page * -xfs_vm_nopage( - struct vm_area_struct *area, - unsigned long address, - int *type) +STATIC int +xfs_vm_fault( + struct vm_area_struct *vma, + struct vm_fault *vmf) { - struct inode *inode = area->vm_file->f_path.dentry->d_inode; + struct inode *inode = vma->vm_file->f_path.dentry->d_inode; bhv_vnode_t *vp = vn_from_inode(inode); ASSERT_ALWAYS(vp->v_vfsp->vfs_flag & VFS_DMI); - if (XFS_SEND_MMAP(XFS_VFSTOM(vp->v_vfsp), area, 0)) - return NULL; - return filemap_nopage(area, address, type); + if (XFS_SEND_MMAP(XFS_VFSTOM(vp->v_vfsp), vma, 0)) + return VM_FAULT_SIGBUS; + return filemap_fault(vma, vmf); } #endif /* CONFIG_XFS_DMAPI */ @@ -319,6 +309,7 @@ xfs_file_mmap( struct vm_area_struct *vma) { vma->vm_ops = &xfs_file_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; #ifdef CONFIG_XFS_DMAPI if (vn_from_inode(filp->f_path.dentry->d_inode)->v_vfsp->vfs_flag & VFS_DMI) @@ -422,6 +413,20 @@ xfs_file_open_exec( } #endif /* HAVE_FOP_OPEN_EXEC */ +/* + * mmap()d file has taken write protection fault and is being made + * writable. We can set the page state up correctly for a writable + * page, which means we can do correct delalloc accounting (ENOSPC + * checking!) and unwritten extent mapping. + */ +STATIC int +xfs_vm_page_mkwrite( + struct vm_area_struct *vma, + struct page *page) +{ + return block_page_mkwrite(vma, page, xfs_get_blocks); +} + const struct file_operations xfs_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -436,7 +441,6 @@ const struct file_operations xfs_file_operations = { #endif .mmap = xfs_file_mmap, .open = xfs_file_open, - .flush = xfs_file_close, .release = xfs_file_release, .fsync = xfs_file_fsync, #ifdef HAVE_FOP_OPEN_EXEC @@ -458,7 +462,6 @@ const struct file_operations xfs_invis_file_operations = { #endif .mmap = xfs_file_mmap, .open = xfs_file_open, - .flush = xfs_file_close, .release = xfs_file_release, .fsync = xfs_file_fsync, }; @@ -475,14 +478,14 @@ const struct file_operations xfs_dir_file_operations = { }; static struct vm_operations_struct xfs_file_vm_ops = { - .nopage = filemap_nopage, - .populate = filemap_populate, + .fault = filemap_fault, + .page_mkwrite = xfs_vm_page_mkwrite, }; #ifdef CONFIG_XFS_DMAPI static struct vm_operations_struct xfs_dmapi_file_vm_ops = { - .nopage = xfs_vm_nopage, - .populate = filemap_populate, + .fault = xfs_vm_fault, + .page_mkwrite = xfs_vm_page_mkwrite, #ifdef HAVE_VMOP_MPROTECT .mprotect = xfs_vm_mprotect, #endif diff --git a/fs/xfs/linux-2.6/xfs_globals.c b/fs/xfs/linux-2.6/xfs_globals.c index ed3a5e1..bb72c3d 100644 --- a/fs/xfs/linux-2.6/xfs_globals.c +++ b/fs/xfs/linux-2.6/xfs_globals.c @@ -46,6 +46,7 @@ xfs_param_t xfs_params = { .inherit_nosym = { 0, 0, 1 }, .rotorstep = { 1, 1, 255 }, .inherit_nodfrg = { 0, 1, 1 }, + .fstrm_timer = { 1, 50, 3600*100}, }; /* diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index ff5c41f..5917808 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -1019,7 +1019,7 @@ xfs_ioc_bulkstat( if (cmd == XFS_IOC_FSINUMBERS) error = xfs_inumbers(mp, &inlast, &count, - bulkreq.ubuffer); + bulkreq.ubuffer, xfs_inumbers_fmt); else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) error = xfs_bulkstat_single(mp, &inlast, bulkreq.ubuffer, &done); diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index b83cebc..141cf15 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -23,10 +23,25 @@ #include <linux/fs.h> #include <asm/uaccess.h> #include "xfs.h" -#include "xfs_types.h" #include "xfs_fs.h" +#include "xfs_bit.h" +#include "xfs_log.h" +#include "xfs_inum.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "xfs_dir2.h" +#include "xfs_dmapi.h" +#include "xfs_mount.h" +#include "xfs_bmap_btree.h" +#include "xfs_attr_sf.h" +#include "xfs_dir2_sf.h" #include "xfs_vfs.h" #include "xfs_vnode.h" +#include "xfs_dinode.h" +#include "xfs_inode.h" +#include "xfs_itable.h" +#include "xfs_error.h" #include "xfs_dfrag.h" #define _NATIVE_IOC(cmd, type) \ @@ -34,6 +49,7 @@ #if defined(CONFIG_IA64) || defined(CONFIG_X86_64) #define BROKEN_X86_ALIGNMENT +#define _PACKED __attribute__((packed)) /* on ia32 l_start is on a 32-bit boundary */ typedef struct xfs_flock64_32 { __s16 l_type; @@ -75,35 +91,276 @@ xfs_ioctl32_flock( return (unsigned long)p; } +typedef struct compat_xfs_fsop_geom_v1 { + __u32 blocksize; /* filesystem (data) block size */ + __u32 rtextsize; /* realtime extent size */ + __u32 agblocks; /* fsblocks in an AG */ + __u32 agcount; /* number of allocation groups */ + __u32 logblocks; /* fsblocks in the log */ + __u32 sectsize; /* (data) sector size, bytes */ + __u32 inodesize; /* inode size in bytes */ + __u32 imaxpct; /* max allowed inode space(%) */ + __u64 datablocks; /* fsblocks in data subvolume */ + __u64 rtblocks; /* fsblocks in realtime subvol */ + __u64 rtextents; /* rt extents in realtime subvol*/ + __u64 logstart; /* starting fsblock of the log */ + unsigned char uuid[16]; /* unique id of the filesystem */ + __u32 sunit; /* stripe unit, fsblocks */ + __u32 swidth; /* stripe width, fsblocks */ + __s32 version; /* structure version */ + __u32 flags; /* superblock version flags */ + __u32 logsectsize; /* log sector size, bytes */ + __u32 rtsectsize; /* realtime sector size, bytes */ + __u32 dirblocksize; /* directory block size, bytes */ +} __attribute__((packed)) compat_xfs_fsop_geom_v1_t; + +#define XFS_IOC_FSGEOMETRY_V1_32 \ + _IOR ('X', 100, struct compat_xfs_fsop_geom_v1) + +STATIC unsigned long xfs_ioctl32_geom_v1(unsigned long arg) +{ + compat_xfs_fsop_geom_v1_t __user *p32 = (void __user *)arg; + xfs_fsop_geom_v1_t __user *p = compat_alloc_user_space(sizeof(*p)); + + if (copy_in_user(p, p32, sizeof(*p32))) + return -EFAULT; + return (unsigned long)p; +} + +typedef struct compat_xfs_inogrp { + __u64 xi_startino; /* starting inode number */ + __s32 xi_alloccount; /* # bits set in allocmask */ + __u64 xi_allocmask; /* mask of allocated inodes */ +} __attribute__((packed)) compat_xfs_inogrp_t; + +STATIC int xfs_inumbers_fmt_compat( + void __user *ubuffer, + const xfs_inogrp_t *buffer, + long count, + long *written) +{ + compat_xfs_inogrp_t *p32 = ubuffer; + long i; + + for (i = 0; i < count; i++) { + if (put_user(buffer[i].xi_startino, &p32[i].xi_startino) || + put_user(buffer[i].xi_alloccount, &p32[i].xi_alloccount) || + put_user(buffer[i].xi_allocmask, &p32[i].xi_allocmask)) + return -EFAULT; + } + *written = count * sizeof(*p32); + return 0; +} + #else -typedef struct xfs_fsop_bulkreq32 { +#define xfs_inumbers_fmt_compat xfs_inumbers_fmt +#define _PACKED + +#endif + +/* XFS_IOC_FSBULKSTAT and friends */ + +typedef struct compat_xfs_bstime { + __s32 tv_sec; /* seconds */ + __s32 tv_nsec; /* and nanoseconds */ +} compat_xfs_bstime_t; + +STATIC int xfs_bstime_store_compat( + compat_xfs_bstime_t __user *p32, + const xfs_bstime_t *p) +{ + __s32 sec32; + + sec32 = p->tv_sec; + if (put_user(sec32, &p32->tv_sec) || + put_user(p->tv_nsec, &p32->tv_nsec)) + return -EFAULT; + return 0; +} + +typedef struct compat_xfs_bstat { + __u64 bs_ino; /* inode number */ + __u16 bs_mode; /* type and mode */ + __u16 bs_nlink; /* number of links */ + __u32 bs_uid; /* user id */ + __u32 bs_gid; /* group id */ + __u32 bs_rdev; /* device value */ + __s32 bs_blksize; /* block size */ + __s64 bs_size; /* file size */ + compat_xfs_bstime_t bs_atime; /* access time */ + compat_xfs_bstime_t bs_mtime; /* modify time */ + compat_xfs_bstime_t bs_ctime; /* inode change time */ + int64_t bs_blocks; /* number of blocks */ + __u32 bs_xflags; /* extended flags */ + __s32 bs_extsize; /* extent size */ + __s32 bs_extents; /* number of extents */ + __u32 bs_gen; /* generation count */ + __u16 bs_projid; /* project id */ + unsigned char bs_pad[14]; /* pad space, unused */ + __u32 bs_dmevmask; /* DMIG event mask */ + __u16 bs_dmstate; /* DMIG state info */ + __u16 bs_aextents; /* attribute number of extents */ +} _PACKED compat_xfs_bstat_t; + +STATIC int xfs_bulkstat_one_fmt_compat( + void __user *ubuffer, + const xfs_bstat_t *buffer) +{ + compat_xfs_bstat_t __user *p32 = ubuffer; + + if (put_user(buffer->bs_ino, &p32->bs_ino) || + put_user(buffer->bs_mode, &p32->bs_mode) || + put_user(buffer->bs_nlink, &p32->bs_nlink) || + put_user(buffer->bs_uid, &p32->bs_uid) || + put_user(buffer->bs_gid, &p32->bs_gid) || + put_user(buffer->bs_rdev, &p32->bs_rdev) || + put_user(buffer->bs_blksize, &p32->bs_blksize) || + put_user(buffer->bs_size, &p32->bs_size) || + xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) || + xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) || + xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) || + put_user(buffer->bs_blocks, &p32->bs_blocks) || + put_user(buffer->bs_xflags, &p32->bs_xflags) || + put_user(buffer->bs_extsize, &p32->bs_extsize) || + put_user(buffer->bs_extents, &p32->bs_extents) || + put_user(buffer->bs_gen, &p32->bs_gen) || + put_user(buffer->bs_projid, &p32->bs_projid) || + put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) || + put_user(buffer->bs_dmstate, &p32->bs_dmstate) || + put_user(buffer->bs_aextents, &p32->bs_aextents)) + return -EFAULT; + return sizeof(*p32); +} + + + +typedef struct compat_xfs_fsop_bulkreq { compat_uptr_t lastip; /* last inode # pointer */ __s32 icount; /* count of entries in buffer */ compat_uptr_t ubuffer; /* user buffer for inode desc. */ - __s32 ocount; /* output count pointer */ -} xfs_fsop_bulkreq32_t; + compat_uptr_t ocount; /* output count pointer */ +} compat_xfs_fsop_bulkreq_t; -STATIC unsigned long -xfs_ioctl32_bulkstat( - unsigned long arg) +#define XFS_IOC_FSBULKSTAT_32 \ + _IOWR('X', 101, struct compat_xfs_fsop_bulkreq) +#define XFS_IOC_FSBULKSTAT_SINGLE_32 \ + _IOWR('X', 102, struct compat_xfs_fsop_bulkreq) +#define XFS_IOC_FSINUMBERS_32 \ + _IOWR('X', 103, struct compat_xfs_fsop_bulkreq) + +/* copied from xfs_ioctl.c */ +STATIC int +xfs_ioc_bulkstat_compat( + xfs_mount_t *mp, + unsigned int cmd, + void __user *arg) { - xfs_fsop_bulkreq32_t __user *p32 = (void __user *)arg; - xfs_fsop_bulkreq_t __user *p = compat_alloc_user_space(sizeof(*p)); + compat_xfs_fsop_bulkreq_t __user *p32 = (void __user *)arg; u32 addr; + xfs_fsop_bulkreq_t bulkreq; + int count; /* # of records returned */ + xfs_ino_t inlast; /* last inode number */ + int done; + int error; + + /* done = 1 if there are more stats to get and if bulkstat */ + /* should be called again (unused here, but used in dmapi) */ + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (XFS_FORCED_SHUTDOWN(mp)) + return -XFS_ERROR(EIO); + + if (get_user(addr, &p32->lastip)) + return -EFAULT; + bulkreq.lastip = compat_ptr(addr); + if (get_user(bulkreq.icount, &p32->icount) || + get_user(addr, &p32->ubuffer)) + return -EFAULT; + bulkreq.ubuffer = compat_ptr(addr); + if (get_user(addr, &p32->ocount)) + return -EFAULT; + bulkreq.ocount = compat_ptr(addr); + + if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64))) + return -XFS_ERROR(EFAULT); + + if ((count = bulkreq.icount) <= 0) + return -XFS_ERROR(EINVAL); + + if (cmd == XFS_IOC_FSINUMBERS) + error = xfs_inumbers(mp, &inlast, &count, + bulkreq.ubuffer, xfs_inumbers_fmt_compat); + else { + /* declare a var to get a warning in case the type changes */ + bulkstat_one_fmt_pf formatter = xfs_bulkstat_one_fmt_compat; + error = xfs_bulkstat(mp, &inlast, &count, + xfs_bulkstat_one, formatter, + sizeof(compat_xfs_bstat_t), bulkreq.ubuffer, + BULKSTAT_FG_QUICK, &done); + } + if (error) + return -error; + + if (bulkreq.ocount != NULL) { + if (copy_to_user(bulkreq.lastip, &inlast, + sizeof(xfs_ino_t))) + return -XFS_ERROR(EFAULT); + + if (copy_to_user(bulkreq.ocount, &count, sizeof(count))) + return -XFS_ERROR(EFAULT); + } + + return 0; +} + + + +typedef struct compat_xfs_fsop_handlereq { + __u32 fd; /* fd for FD_TO_HANDLE */ + compat_uptr_t path; /* user pathname */ + __u32 oflags; /* open flags */ + compat_uptr_t ihandle; /* user supplied handle */ + __u32 ihandlen; /* user supplied length */ + compat_uptr_t ohandle; /* user buffer for handle */ + compat_uptr_t ohandlen; /* user buffer length */ +} compat_xfs_fsop_handlereq_t; + +#define XFS_IOC_PATH_TO_FSHANDLE_32 \ + _IOWR('X', 104, struct compat_xfs_fsop_handlereq) +#define XFS_IOC_PATH_TO_HANDLE_32 \ + _IOWR('X', 105, struct compat_xfs_fsop_handlereq) +#define XFS_IOC_FD_TO_HANDLE_32 \ + _IOWR('X', 106, struct compat_xfs_fsop_handlereq) +#define XFS_IOC_OPEN_BY_HANDLE_32 \ + _IOWR('X', 107, struct compat_xfs_fsop_handlereq) +#define XFS_IOC_READLINK_BY_HANDLE_32 \ + _IOWR('X', 108, struct compat_xfs_fsop_handlereq) + +STATIC unsigned long xfs_ioctl32_fshandle(unsigned long arg) +{ + compat_xfs_fsop_handlereq_t __user *p32 = (void __user *)arg; + xfs_fsop_handlereq_t __user *p = compat_alloc_user_space(sizeof(*p)); + u32 addr; - if (get_user(addr, &p32->lastip) || - put_user(compat_ptr(addr), &p->lastip) || - copy_in_user(&p->icount, &p32->icount, sizeof(s32)) || - get_user(addr, &p32->ubuffer) || - put_user(compat_ptr(addr), &p->ubuffer) || - get_user(addr, &p32->ocount) || - put_user(compat_ptr(addr), &p->ocount)) + if (copy_in_user(&p->fd, &p32->fd, sizeof(__u32)) || + get_user(addr, &p32->path) || + put_user(compat_ptr(addr), &p->path) || + copy_in_user(&p->oflags, &p32->oflags, sizeof(__u32)) || + get_user(addr, &p32->ihandle) || + put_user(compat_ptr(addr), &p->ihandle) || + copy_in_user(&p->ihandlen, &p32->ihandlen, sizeof(__u32)) || + get_user(addr, &p32->ohandle) || + put_user(compat_ptr(addr), &p->ohandle) || + get_user(addr, &p32->ohandlen) || + put_user(compat_ptr(addr), &p->ohandlen)) return -EFAULT; return (unsigned long)p; } -#endif + STATIC long xfs_compat_ioctl( @@ -118,7 +375,6 @@ xfs_compat_ioctl( switch (cmd) { case XFS_IOC_DIOINFO: - case XFS_IOC_FSGEOMETRY_V1: case XFS_IOC_FSGEOMETRY: case XFS_IOC_GETVERSION: case XFS_IOC_GETXFLAGS: @@ -131,12 +387,7 @@ xfs_compat_ioctl( case XFS_IOC_GETBMAPA: case XFS_IOC_GETBMAPX: /* not handled - case XFS_IOC_FD_TO_HANDLE: - case XFS_IOC_PATH_TO_HANDLE: - case XFS_IOC_PATH_TO_FSHANDLE: - case XFS_IOC_OPEN_BY_HANDLE: case XFS_IOC_FSSETDM_BY_HANDLE: - case XFS_IOC_READLINK_BY_HANDLE: case XFS_IOC_ATTRLIST_BY_HANDLE: case XFS_IOC_ATTRMULTI_BY_HANDLE: */ @@ -166,6 +417,10 @@ xfs_compat_ioctl( arg = xfs_ioctl32_flock(arg); cmd = _NATIVE_IOC(cmd, struct xfs_flock64); break; + case XFS_IOC_FSGEOMETRY_V1_32: + arg = xfs_ioctl32_geom_v1(arg); + cmd = _NATIVE_IOC(cmd, struct xfs_fsop_geom_v1); + break; #else /* These are handled fine if no alignment issues */ case XFS_IOC_ALLOCSP: @@ -176,18 +431,28 @@ xfs_compat_ioctl( case XFS_IOC_FREESP64: case XFS_IOC_RESVSP64: case XFS_IOC_UNRESVSP64: + case XFS_IOC_FSGEOMETRY_V1: break; /* xfs_bstat_t still has wrong u32 vs u64 alignment */ case XFS_IOC_SWAPEXT: break; - case XFS_IOC_FSBULKSTAT_SINGLE: - case XFS_IOC_FSBULKSTAT: - case XFS_IOC_FSINUMBERS: - arg = xfs_ioctl32_bulkstat(arg); - break; #endif + case XFS_IOC_FSBULKSTAT_32: + case XFS_IOC_FSBULKSTAT_SINGLE_32: + case XFS_IOC_FSINUMBERS_32: + cmd = _NATIVE_IOC(cmd, struct xfs_fsop_bulkreq); + return xfs_ioc_bulkstat_compat(XFS_BHVTOI(VNHEAD(vp))->i_mount, + cmd, (void*)arg); + case XFS_IOC_FD_TO_HANDLE_32: + case XFS_IOC_PATH_TO_HANDLE_32: + case XFS_IOC_PATH_TO_FSHANDLE_32: + case XFS_IOC_OPEN_BY_HANDLE_32: + case XFS_IOC_READLINK_BY_HANDLE_32: + arg = xfs_ioctl32_fshandle(arg); + cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq); + break; default: return -ENOIOCTLCMD; } diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index af24a45..330c4ba 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -123,6 +123,7 @@ #define xfs_inherit_nosymlinks xfs_params.inherit_nosym.val #define xfs_rotorstep xfs_params.rotorstep.val #define xfs_inherit_nodefrag xfs_params.inherit_nodfrg.val +#define xfs_fstrm_centisecs xfs_params.fstrm_timer.val #define current_cpu() (raw_smp_processor_id()) #define current_pid() (current->pid) diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index bf9a9d5..4528f9a 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -547,7 +547,8 @@ vfs_sync_worker( if (!(vfsp->vfs_flag & VFS_RDONLY)) error = bhv_vfs_sync(vfsp, SYNC_FSDATA | SYNC_BDFLUSH | \ - SYNC_ATTR | SYNC_REFCACHE, NULL); + SYNC_ATTR | SYNC_REFCACHE | SYNC_SUPER, + NULL); vfsp->vfs_sync_seq++; wake_up(&vfsp->vfs_wait_single_sync_task); } @@ -561,6 +562,7 @@ xfssyncd( bhv_vfs_sync_work_t *work, *n; LIST_HEAD (tmp); + set_freezable(); timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10); for (;;) { timeleft = schedule_timeout_interruptible(timeleft); @@ -663,7 +665,7 @@ xfs_fs_sync_super( * occur here so don't bother flushing the buftarg (i.e * SYNC_QUIESCE) because it'll just get dirty again. */ - flags = SYNC_FSDATA | SYNC_DELWRI | SYNC_WAIT | SYNC_IOWAIT; + flags = SYNC_DATA_QUIESCE; } else flags = SYNC_FSDATA | (wait ? SYNC_WAIT : 0); diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h index 33dd1ca..201cc32 100644 --- a/fs/xfs/linux-2.6/xfs_super.h +++ b/fs/xfs/linux-2.6/xfs_super.h @@ -18,6 +18,8 @@ #ifndef __XFS_SUPER_H__ #define __XFS_SUPER_H__ +#include <linux/exportfs.h> + #ifdef CONFIG_XFS_DMAPI # define vfs_insertdmapi(vfs) vfs_insertops(vfsp, &xfs_dmops) # define vfs_initdmapi() dmapi_init() diff --git a/fs/xfs/linux-2.6/xfs_sysctl.c b/fs/xfs/linux-2.6/xfs_sysctl.c index cd6eaa4..bb997d7 100644 --- a/fs/xfs/linux-2.6/xfs_sysctl.c +++ b/fs/xfs/linux-2.6/xfs_sysctl.c @@ -210,6 +210,17 @@ static ctl_table xfs_table[] = { .extra1 = &xfs_params.inherit_nodfrg.min, .extra2 = &xfs_params.inherit_nodfrg.max }, + { + .ctl_name = XFS_FILESTREAM_TIMER, + .procname = "filestream_centisecs", + .data = &xfs_params.fstrm_timer.val, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &xfs_params.fstrm_timer.min, + .extra2 = &xfs_params.fstrm_timer.max, + }, /* please keep this the last entry */ #ifdef CONFIG_PROC_FS { diff --git a/fs/xfs/linux-2.6/xfs_sysctl.h b/fs/xfs/linux-2.6/xfs_sysctl.h index a631fb8..98b97e3 100644 --- a/fs/xfs/linux-2.6/xfs_sysctl.h +++ b/fs/xfs/linux-2.6/xfs_sysctl.h @@ -47,6 +47,7 @@ typedef struct xfs_param { xfs_sysctl_val_t inherit_nosym; /* Inherit the "nosymlinks" flag. */ xfs_sysctl_val_t rotorstep; /* inode32 AG rotoring control knob */ xfs_sysctl_val_t inherit_nodfrg;/* Inherit the "nodefrag" inode flag. */ + xfs_sysctl_val_t fstrm_timer; /* Filestream dir-AG assoc'n timeout. */ } xfs_param_t; /* @@ -86,6 +87,7 @@ enum { XFS_INHERIT_NOSYM = 19, XFS_ROTORSTEP = 20, XFS_INHERIT_NODFRG = 21, + XFS_FILESTREAM_TIMER = 22, }; extern xfs_param_t xfs_params; diff --git a/fs/xfs/linux-2.6/xfs_vfs.h b/fs/xfs/linux-2.6/xfs_vfs.h index e2c2ce9..dca3481 100644 --- a/fs/xfs/linux-2.6/xfs_vfs.h +++ b/fs/xfs/linux-2.6/xfs_vfs.h @@ -92,6 +92,21 @@ typedef enum { #define SYNC_REFCACHE 0x0040 /* prune some of the nfs ref cache */ #define SYNC_REMOUNT 0x0080 /* remount readonly, no dummy LRs */ #define SYNC_IOWAIT 0x0100 /* wait for all I/O to complete */ +#define SYNC_SUPER 0x0200 /* flush superblock to disk */ + +/* + * When remounting a filesystem read-only or freezing the filesystem, + * we have two phases to execute. This first phase is syncing the data + * before we quiesce the fielsystem, and the second is flushing all the + * inodes out after we've waited for all the transactions created by + * the first phase to complete. The second phase uses SYNC_INODE_QUIESCE + * to ensure that the inodes are written to their location on disk + * rather than just existing in transactions in the log. This means + * after a quiesce there is no log replay required to write the inodes + * to disk (this is the main difference between a sync and a quiesce). + */ +#define SYNC_DATA_QUIESCE (SYNC_DELWRI|SYNC_FSDATA|SYNC_WAIT|SYNC_IOWAIT) +#define SYNC_INODE_QUIESCE (SYNC_REMOUNT|SYNC_ATTR|SYNC_WAIT) #define SHUTDOWN_META_IO_ERROR 0x0001 /* write attempt to metadata failed */ #define SHUTDOWN_LOG_IO_ERROR 0x0002 /* write attempt to the log failed */ diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h index 013048a..5742d65 100644 --- a/fs/xfs/linux-2.6/xfs_vnode.h +++ b/fs/xfs/linux-2.6/xfs_vnode.h @@ -129,10 +129,7 @@ typedef enum bhv_vchange { VCHANGE_FLAGS_IOEXCL_COUNT = 4 } bhv_vchange_t; -typedef enum { L_FALSE, L_TRUE } lastclose_t; - typedef int (*vop_open_t)(bhv_desc_t *, struct cred *); -typedef int (*vop_close_t)(bhv_desc_t *, int, lastclose_t, struct cred *); typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *, const struct iovec *, unsigned int, loff_t *, int, struct cred *); @@ -200,7 +197,6 @@ typedef int (*vop_iflush_t)(bhv_desc_t *, int); typedef struct bhv_vnodeops { bhv_position_t vn_position; /* position within behavior chain */ vop_open_t vop_open; - vop_close_t vop_close; vop_read_t vop_read; vop_write_t vop_write; vop_splice_read_t vop_splice_read; @@ -245,7 +241,6 @@ typedef struct bhv_vnodeops { #define VNHEAD(vp) ((vp)->v_bh.bh_first) #define VOP(op, vp) (*((bhv_vnodeops_t *)VNHEAD(vp)->bd_ops)->op) #define bhv_vop_open(vp, cr) VOP(vop_open, vp)(VNHEAD(vp),cr) -#define bhv_vop_close(vp, f,last,cr) VOP(vop_close, vp)(VNHEAD(vp),f,last,cr) #define bhv_vop_read(vp,file,iov,segs,offset,ioflags,cr) \ VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) #define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \ diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 3e4a8ad..2d274b2 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -62,10 +62,8 @@ uint ndquot; kmem_zone_t *qm_dqzone; kmem_zone_t *qm_dqtrxzone; -static kmem_shaker_t xfs_qm_shaker; static cred_t xfs_zerocr; -static xfs_inode_t xfs_zeroino; STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int); STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); @@ -79,6 +77,11 @@ STATIC int xfs_qm_init_quotainos(xfs_mount_t *); STATIC int xfs_qm_init_quotainfo(xfs_mount_t *); STATIC int xfs_qm_shake(int, gfp_t); +static struct shrinker xfs_qm_shaker = { + .shrink = xfs_qm_shake, + .seeks = DEFAULT_SEEKS, +}; + #ifdef DEBUG extern mutex_t qcheck_lock; #endif @@ -150,7 +153,7 @@ xfs_Gqm_init(void) } else xqm->qm_dqzone = qm_dqzone; - xfs_qm_shaker = kmem_shake_register(xfs_qm_shake); + register_shrinker(&xfs_qm_shaker); /* * The t_dqinfo portion of transactions. @@ -182,7 +185,7 @@ xfs_qm_destroy( ASSERT(xqm != NULL); ASSERT(xqm->qm_nrefs == 0); - kmem_shake_deregister(xfs_qm_shaker); + unregister_shrinker(&xfs_qm_shaker); hsize = xqm->qm_dqhashmask + 1; for (i = 0; i < hsize; i++) { xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i])); @@ -1415,7 +1418,7 @@ xfs_qm_qino_alloc( return error; } - if ((error = xfs_dir_ialloc(&tp, &xfs_zeroino, S_IFREG, 1, 0, + if ((error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, &xfs_zerocr, 0, 1, ip, &committed))) { xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); diff --git a/fs/xfs/xfs.h b/fs/xfs/xfs.h index bf0a120..b5a7d92 100644 --- a/fs/xfs/xfs.h +++ b/fs/xfs/xfs.h @@ -38,6 +38,7 @@ #define XFS_RW_TRACE 1 #define XFS_BUF_TRACE 1 #define XFS_VNODE_TRACE 1 +#define XFS_FILESTREAMS_TRACE 1 #endif #include <linux-2.6/xfs_linux.h> diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 9ece7f87..51c09c1 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -68,6 +68,7 @@ typedef struct xfs_agf { __be32 agf_flcount; /* count of blocks in freelist */ __be32 agf_freeblks; /* total free blocks */ __be32 agf_longest; /* longest free space */ + __be32 agf_btreeblks; /* # of blocks held in AGF btrees */ } xfs_agf_t; #define XFS_AGF_MAGICNUM 0x00000001 @@ -81,7 +82,8 @@ typedef struct xfs_agf { #define XFS_AGF_FLCOUNT 0x00000100 #define XFS_AGF_FREEBLKS 0x00000200 #define XFS_AGF_LONGEST 0x00000400 -#define XFS_AGF_NUM_BITS 11 +#define XFS_AGF_BTREEBLKS 0x00000800 +#define XFS_AGF_NUM_BITS 12 #define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1) /* disk block (xfs_daddr_t) in the AG */ @@ -186,12 +188,15 @@ typedef struct xfs_perag __uint32_t pagf_flcount; /* count of blocks in freelist */ xfs_extlen_t pagf_freeblks; /* total free blocks */ xfs_extlen_t pagf_longest; /* longest free space */ + __uint32_t pagf_btreeblks; /* # of blocks held in AGF btrees */ xfs_agino_t pagi_freecount; /* number of free inodes */ + xfs_agino_t pagi_count; /* number of allocated inodes */ + int pagb_count; /* pagb slots in use */ #ifdef __KERNEL__ lock_t pagb_lock; /* lock for pagb_list */ #endif - int pagb_count; /* pagb slots in use */ xfs_perag_busy_t *pagb_list; /* unstable blocks */ + atomic_t pagf_fstrms; /* # of filestreams active in this AG */ } xfs_perag_t; #define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels) diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 8e9a40a..012a649 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -55,17 +55,17 @@ xfs_alloc_search_busy(xfs_trans_t *tp, ktrace_t *xfs_alloc_trace_buf; #define TRACE_ALLOC(s,a) \ - xfs_alloc_trace_alloc(fname, s, a, __LINE__) + xfs_alloc_trace_alloc(__FUNCTION__, s, a, __LINE__) #define TRACE_FREE(s,a,b,x,f) \ - xfs_alloc_trace_free(fname, s, mp, a, b, x, f, __LINE__) + xfs_alloc_trace_free(__FUNCTION__, s, mp, a, b, x, f, __LINE__) #define TRACE_MODAGF(s,a,f) \ - xfs_alloc_trace_modagf(fname, s, mp, a, f, __LINE__) -#define TRACE_BUSY(fname,s,ag,agb,l,sl,tp) \ - xfs_alloc_trace_busy(fname, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSY, __LINE__) -#define TRACE_UNBUSY(fname,s,ag,sl,tp) \ - xfs_alloc_trace_busy(fname, s, mp, ag, -1, -1, sl, tp, XFS_ALLOC_KTRACE_UNBUSY, __LINE__) -#define TRACE_BUSYSEARCH(fname,s,ag,agb,l,sl,tp) \ - xfs_alloc_trace_busy(fname, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSYSEARCH, __LINE__) + xfs_alloc_trace_modagf(__FUNCTION__, s, mp, a, f, __LINE__) +#define TRACE_BUSY(__FUNCTION__,s,ag,agb,l,sl,tp) \ + xfs_alloc_trace_busy(__FUNCTION__, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSY, __LINE__) +#define TRACE_UNBUSY(__FUNCTION__,s,ag,sl,tp) \ + xfs_alloc_trace_busy(__FUNCTION__, s, mp, ag, -1, -1, sl, tp, XFS_ALLOC_KTRACE_UNBUSY, __LINE__) +#define TRACE_BUSYSEARCH(__FUNCTION__,s,ag,agb,l,sl,tp) \ + xfs_alloc_trace_busy(__FUNCTION__, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSYSEARCH, __LINE__) #else #define TRACE_ALLOC(s,a) #define TRACE_FREE(s,a,b,x,f) @@ -420,7 +420,7 @@ xfs_alloc_read_agfl( */ STATIC void xfs_alloc_trace_alloc( - char *name, /* function tag string */ + const char *name, /* function tag string */ char *str, /* additional string */ xfs_alloc_arg_t *args, /* allocation argument structure */ int line) /* source line number */ @@ -453,7 +453,7 @@ xfs_alloc_trace_alloc( */ STATIC void xfs_alloc_trace_free( - char *name, /* function tag string */ + const char *name, /* function tag string */ char *str, /* additional string */ xfs_mount_t *mp, /* file system mount point */ xfs_agnumber_t agno, /* allocation group number */ @@ -479,7 +479,7 @@ xfs_alloc_trace_free( */ STATIC void xfs_alloc_trace_modagf( - char *name, /* function tag string */ + const char *name, /* function tag string */ char *str, /* additional string */ xfs_mount_t *mp, /* file system mount point */ xfs_agf_t *agf, /* new agf value */ @@ -507,7 +507,7 @@ xfs_alloc_trace_modagf( STATIC void xfs_alloc_trace_busy( - char *name, /* function tag string */ + const char *name, /* function tag string */ char *str, /* additional string */ xfs_mount_t *mp, /* file system mount point */ xfs_agnumber_t agno, /* allocation group number */ @@ -549,9 +549,6 @@ xfs_alloc_ag_vextent( xfs_alloc_arg_t *args) /* argument structure for allocation */ { int error=0; -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_ag_vextent"; -#endif ASSERT(args->minlen > 0); ASSERT(args->maxlen > 0); @@ -635,9 +632,6 @@ xfs_alloc_ag_vextent_exact( xfs_agblock_t fbno; /* start block of found extent */ xfs_agblock_t fend; /* end block of found extent */ xfs_extlen_t flen; /* length of found extent */ -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_ag_vextent_exact"; -#endif int i; /* success/failure of operation */ xfs_agblock_t maxend; /* end of maximal extent */ xfs_agblock_t minend; /* end of minimal extent */ @@ -737,9 +731,6 @@ xfs_alloc_ag_vextent_near( xfs_btree_cur_t *bno_cur_gt; /* cursor for bno btree, right side */ xfs_btree_cur_t *bno_cur_lt; /* cursor for bno btree, left side */ xfs_btree_cur_t *cnt_cur; /* cursor for count btree */ -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_ag_vextent_near"; -#endif xfs_agblock_t gtbno; /* start bno of right side entry */ xfs_agblock_t gtbnoa; /* aligned ... */ xfs_extlen_t gtdiff; /* difference to right side entry */ @@ -1270,9 +1261,6 @@ xfs_alloc_ag_vextent_size( int error; /* error result */ xfs_agblock_t fbno; /* start of found freespace */ xfs_extlen_t flen; /* length of found freespace */ -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_ag_vextent_size"; -#endif int i; /* temp status variable */ xfs_agblock_t rbno; /* returned block number */ xfs_extlen_t rlen; /* length of returned extent */ @@ -1427,9 +1415,6 @@ xfs_alloc_ag_vextent_small( int error; xfs_agblock_t fbno; xfs_extlen_t flen; -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_ag_vextent_small"; -#endif int i; if ((error = xfs_alloc_decrement(ccur, 0, &i))) @@ -1447,7 +1432,8 @@ xfs_alloc_ag_vextent_small( else if (args->minlen == 1 && args->alignment == 1 && !args->isfl && (be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_flcount) > args->minleft)) { - if ((error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno))) + error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno, 0); + if (error) goto error0; if (fbno != NULLAGBLOCK) { if (args->userdata) { @@ -1515,9 +1501,6 @@ xfs_free_ag_extent( xfs_btree_cur_t *bno_cur; /* cursor for by-block btree */ xfs_btree_cur_t *cnt_cur; /* cursor for by-size btree */ int error; /* error return value */ -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_free_ag_extent"; -#endif xfs_agblock_t gtbno; /* start of right neighbor block */ xfs_extlen_t gtlen; /* length of right neighbor block */ int haveleft; /* have a left neighbor block */ @@ -1923,7 +1906,8 @@ xfs_alloc_fix_freelist( while (be32_to_cpu(agf->agf_flcount) > need) { xfs_buf_t *bp; - if ((error = xfs_alloc_get_freelist(tp, agbp, &bno))) + error = xfs_alloc_get_freelist(tp, agbp, &bno, 0); + if (error) return error; if ((error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1))) return error; @@ -1973,8 +1957,9 @@ xfs_alloc_fix_freelist( * Put each allocated block on the list. */ for (bno = targs.agbno; bno < targs.agbno + targs.len; bno++) { - if ((error = xfs_alloc_put_freelist(tp, agbp, agflbp, - bno))) + error = xfs_alloc_put_freelist(tp, agbp, + agflbp, bno, 0); + if (error) return error; } } @@ -1991,16 +1976,15 @@ int /* error */ xfs_alloc_get_freelist( xfs_trans_t *tp, /* transaction pointer */ xfs_buf_t *agbp, /* buffer containing the agf structure */ - xfs_agblock_t *bnop) /* block address retrieved from freelist */ + xfs_agblock_t *bnop, /* block address retrieved from freelist */ + int btreeblk) /* destination is a AGF btree */ { xfs_agf_t *agf; /* a.g. freespace structure */ xfs_agfl_t *agfl; /* a.g. freelist structure */ xfs_buf_t *agflbp;/* buffer for a.g. freelist structure */ xfs_agblock_t bno; /* block number returned */ int error; -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_get_freelist"; -#endif + int logflags; xfs_mount_t *mp; /* mount structure */ xfs_perag_t *pag; /* per allocation group data */ @@ -2032,8 +2016,16 @@ xfs_alloc_get_freelist( be32_add(&agf->agf_flcount, -1); xfs_trans_agflist_delta(tp, -1); pag->pagf_flcount--; - TRACE_MODAGF(NULL, agf, XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT); - xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT); + + logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT; + if (btreeblk) { + be32_add(&agf->agf_btreeblks, 1); + pag->pagf_btreeblks++; + logflags |= XFS_AGF_BTREEBLKS; + } + + TRACE_MODAGF(NULL, agf, logflags); + xfs_alloc_log_agf(tp, agbp, logflags); *bnop = bno; /* @@ -2071,6 +2063,7 @@ xfs_alloc_log_agf( offsetof(xfs_agf_t, agf_flcount), offsetof(xfs_agf_t, agf_freeblks), offsetof(xfs_agf_t, agf_longest), + offsetof(xfs_agf_t, agf_btreeblks), sizeof(xfs_agf_t) }; @@ -2106,15 +2099,14 @@ xfs_alloc_put_freelist( xfs_trans_t *tp, /* transaction pointer */ xfs_buf_t *agbp, /* buffer for a.g. freelist header */ xfs_buf_t *agflbp,/* buffer for a.g. free block array */ - xfs_agblock_t bno) /* block being freed */ + xfs_agblock_t bno, /* block being freed */ + int btreeblk) /* block came from a AGF btree */ { xfs_agf_t *agf; /* a.g. freespace structure */ xfs_agfl_t *agfl; /* a.g. free block array */ __be32 *blockp;/* pointer to array entry */ int error; -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_put_freelist"; -#endif + int logflags; xfs_mount_t *mp; /* mount structure */ xfs_perag_t *pag; /* per allocation group data */ @@ -2132,11 +2124,22 @@ xfs_alloc_put_freelist( be32_add(&agf->agf_flcount, 1); xfs_trans_agflist_delta(tp, 1); pag->pagf_flcount++; + + logflags = XFS_AGF_FLLAST | XFS_AGF_FLCOUNT; + if (btreeblk) { + be32_add(&agf->agf_btreeblks, -1); + pag->pagf_btreeblks--; + logflags |= XFS_AGF_BTREEBLKS; + } + + TRACE_MODAGF(NULL, agf, logflags); + xfs_alloc_log_agf(tp, agbp, logflags); + ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)); blockp = &agfl->agfl_bno[be32_to_cpu(agf->agf_fllast)]; *blockp = cpu_to_be32(bno); - TRACE_MODAGF(NULL, agf, XFS_AGF_FLLAST | XFS_AGF_FLCOUNT); - xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLLAST | XFS_AGF_FLCOUNT); + TRACE_MODAGF(NULL, agf, logflags); + xfs_alloc_log_agf(tp, agbp, logflags); xfs_trans_log_buf(tp, agflbp, (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl), (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl + @@ -2196,6 +2199,7 @@ xfs_alloc_read_agf( pag = &mp->m_perag[agno]; if (!pag->pagf_init) { pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks); + pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks); pag->pagf_flcount = be32_to_cpu(agf->agf_flcount); pag->pagf_longest = be32_to_cpu(agf->agf_longest); pag->pagf_levels[XFS_BTNUM_BNOi] = @@ -2235,9 +2239,6 @@ xfs_alloc_vextent( xfs_agblock_t agsize; /* allocation group size */ int error; int flags; /* XFS_ALLOC_FLAG_... locking flags */ -#ifdef XFS_ALLOC_TRACE - static char fname[] = "xfs_alloc_vextent"; -#endif xfs_extlen_t minleft;/* minimum left value, temp copy */ xfs_mount_t *mp; /* mount structure pointer */ xfs_agnumber_t sagno; /* starting allocation group number */ diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h index 5a42561..5aec15d 100644 --- a/fs/xfs/xfs_alloc.h +++ b/fs/xfs/xfs_alloc.h @@ -136,7 +136,8 @@ int /* error */ xfs_alloc_get_freelist( struct xfs_trans *tp, /* transaction pointer */ struct xfs_buf *agbp, /* buffer containing the agf structure */ - xfs_agblock_t *bnop); /* block address retrieved from freelist */ + xfs_agblock_t *bnop, /* block address retrieved from freelist */ + int btreeblk); /* destination is a AGF btree */ /* * Log the given fields from the agf structure. @@ -165,7 +166,8 @@ xfs_alloc_put_freelist( struct xfs_trans *tp, /* transaction pointer */ struct xfs_buf *agbp, /* buffer for a.g. freelist header */ struct xfs_buf *agflbp,/* buffer for a.g. free block array */ - xfs_agblock_t bno); /* block being freed */ + xfs_agblock_t bno, /* block being freed */ + int btreeblk); /* owner was a AGF btree */ /* * Read in the allocation group header (free/alloc section). diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index 74cadf9..1603ce5 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c @@ -226,8 +226,9 @@ xfs_alloc_delrec( /* * Put this buffer/block on the ag's freelist. */ - if ((error = xfs_alloc_put_freelist(cur->bc_tp, - cur->bc_private.a.agbp, NULL, bno))) + error = xfs_alloc_put_freelist(cur->bc_tp, + cur->bc_private.a.agbp, NULL, bno, 1); + if (error) return error; /* * Since blocks move to the free list without the @@ -549,8 +550,9 @@ xfs_alloc_delrec( /* * Free the deleting block by putting it on the freelist. */ - if ((error = xfs_alloc_put_freelist(cur->bc_tp, cur->bc_private.a.agbp, - NULL, rbno))) + error = xfs_alloc_put_freelist(cur->bc_tp, + cur->bc_private.a.agbp, NULL, rbno, 1); + if (error) return error; /* * Since blocks move to the free list without the coordination @@ -1320,8 +1322,9 @@ xfs_alloc_newroot( /* * Get a buffer from the freelist blocks, for the new root. */ - if ((error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp, - &nbno))) + error = xfs_alloc_get_freelist(cur->bc_tp, + cur->bc_private.a.agbp, &nbno, 1); + if (error) return error; /* * None available, we fail. @@ -1604,8 +1607,9 @@ xfs_alloc_split( * Allocate the new block from the freelist. * If we can't do it, we're toast. Give up. */ - if ((error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp, - &rbno))) + error = xfs_alloc_get_freelist(cur->bc_tp, + cur->bc_private.a.agbp, &rbno, 1); + if (error) return error; if (rbno == NULLAGBLOCK) { *stat = 0; diff --git a/fs/xfs/xfs_bit.c b/fs/xfs/xfs_bit.c index 1afe07f..fab0b6d 100644 --- a/fs/xfs/xfs_bit.c +++ b/fs/xfs/xfs_bit.c @@ -66,44 +66,6 @@ static const char xfs_highbit[256] = { #endif /* - * Count of bits set in byte, 0..8. - */ -static const char xfs_countbit[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, /* 00 .. 07 */ - 1, 2, 2, 3, 2, 3, 3, 4, /* 08 .. 0f */ - 1, 2, 2, 3, 2, 3, 3, 4, /* 10 .. 17 */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 18 .. 1f */ - 1, 2, 2, 3, 2, 3, 3, 4, /* 20 .. 27 */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 28 .. 2f */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 30 .. 37 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* 38 .. 3f */ - 1, 2, 2, 3, 2, 3, 3, 4, /* 40 .. 47 */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 48 .. 4f */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 50 .. 57 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* 58 .. 5f */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 60 .. 67 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* 68 .. 6f */ - 3, 4, 4, 5, 4, 5, 5, 6, /* 70 .. 77 */ - 4, 5, 5, 6, 5, 6, 6, 7, /* 78 .. 7f */ - 1, 2, 2, 3, 2, 3, 3, 4, /* 80 .. 87 */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 88 .. 8f */ - 2, 3, 3, 4, 3, 4, 4, 5, /* 90 .. 97 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* 98 .. 9f */ - 2, 3, 3, 4, 3, 4, 4, 5, /* a0 .. a7 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* a8 .. af */ - 3, 4, 4, 5, 4, 5, 5, 6, /* b0 .. b7 */ - 4, 5, 5, 6, 5, 6, 6, 7, /* b8 .. bf */ - 2, 3, 3, 4, 3, 4, 4, 5, /* c0 .. c7 */ - 3, 4, 4, 5, 4, 5, 5, 6, /* c8 .. cf */ - 3, 4, 4, 5, 4, 5, 5, 6, /* d0 .. d7 */ - 4, 5, 5, 6, 5, 6, 6, 7, /* d8 .. df */ - 3, 4, 4, 5, 4, 5, 5, 6, /* e0 .. e7 */ - 4, 5, 5, 6, 5, 6, 6, 7, /* e8 .. ef */ - 4, 5, 5, 6, 5, 6, 6, 7, /* f0 .. f7 */ - 5, 6, 6, 7, 6, 7, 7, 8, /* f8 .. ff */ -}; - -/* * xfs_highbit32: get high bit set out of 32-bit argument, -1 if none set. */ inline int @@ -167,56 +129,21 @@ xfs_highbit64( /* - * Count the number of bits set in the bitmap starting with bit - * start_bit. Size is the size of the bitmap in words. - * - * Do the counting by mapping a byte value to the number of set - * bits for that value using the xfs_countbit array, i.e. - * xfs_countbit[0] == 0, xfs_countbit[1] == 1, xfs_countbit[2] == 1, - * xfs_countbit[3] == 2, etc. + * Return whether bitmap is empty. + * Size is number of words in the bitmap, which is padded to word boundary + * Returns 1 for empty, 0 for non-empty. */ int -xfs_count_bits(uint *map, uint size, uint start_bit) +xfs_bitmap_empty(uint *map, uint size) { - register int bits; - register unsigned char *bytep; - register unsigned char *end_map; - int byte_bit; - - bits = 0; - end_map = (char*)(map + size); - bytep = (char*)(map + (start_bit & ~0x7)); - byte_bit = start_bit & 0x7; - - /* - * If the caller fell off the end of the map, return 0. - */ - if (bytep >= end_map) { - return (0); - } - - /* - * If start_bit is not byte aligned, then process the - * first byte separately. - */ - if (byte_bit != 0) { - /* - * Shift off the bits we don't want to look at, - * before indexing into xfs_countbit. - */ - bits += xfs_countbit[(*bytep >> byte_bit)]; - bytep++; - } + uint i; + uint ret = 0; - /* - * Count the bits in each byte until the end of the bitmap. - */ - while (bytep < end_map) { - bits += xfs_countbit[*bytep]; - bytep++; + for (i = 0; i < size; i++) { + ret |= map[i]; } - return (bits); + return (ret == 0); } /* diff --git a/fs/xfs/xfs_bit.h b/fs/xfs/xfs_bit.h index 0bbe568..082641a 100644 --- a/fs/xfs/xfs_bit.h +++ b/fs/xfs/xfs_bit.h @@ -55,8 +55,8 @@ extern int xfs_lowbit64(__uint64_t v); /* Get high bit set out of 64-bit argument, -1 if none set */ extern int xfs_highbit64(__uint64_t); -/* Count set bits in map starting with start_bit */ -extern int xfs_count_bits(uint *map, uint size, uint start_bit); +/* Return whether bitmap is empty (1 == empty) */ +extern int xfs_bitmap_empty(uint *map, uint size); /* Count continuous one bits in map starting with start_bit */ extern int xfs_contig_bits(uint *map, uint size, uint start_bit); diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index b1ea26e..94b5c5f 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -52,6 +52,7 @@ #include "xfs_quota.h" #include "xfs_trans_space.h" #include "xfs_buf_item.h" +#include "xfs_filestream.h" #ifdef DEBUG @@ -277,7 +278,7 @@ xfs_bmap_isaeof( STATIC void xfs_bmap_trace_addentry( int opcode, /* operation */ - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(ies) */ @@ -291,7 +292,7 @@ xfs_bmap_trace_addentry( */ STATIC void xfs_bmap_trace_delete( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(entries) deleted */ @@ -304,7 +305,7 @@ xfs_bmap_trace_delete( */ STATIC void xfs_bmap_trace_insert( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(entries) inserted */ @@ -318,7 +319,7 @@ xfs_bmap_trace_insert( */ STATIC void xfs_bmap_trace_post_update( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry updated */ @@ -329,17 +330,25 @@ xfs_bmap_trace_post_update( */ STATIC void xfs_bmap_trace_pre_update( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry to be updated */ int whichfork); /* data or attr fork */ +#define XFS_BMAP_TRACE_DELETE(d,ip,i,c,w) \ + xfs_bmap_trace_delete(__FUNCTION__,d,ip,i,c,w) +#define XFS_BMAP_TRACE_INSERT(d,ip,i,c,r1,r2,w) \ + xfs_bmap_trace_insert(__FUNCTION__,d,ip,i,c,r1,r2,w) +#define XFS_BMAP_TRACE_POST_UPDATE(d,ip,i,w) \ + xfs_bmap_trace_post_update(__FUNCTION__,d,ip,i,w) +#define XFS_BMAP_TRACE_PRE_UPDATE(d,ip,i,w) \ + xfs_bmap_trace_pre_update(__FUNCTION__,d,ip,i,w) #else -#define xfs_bmap_trace_delete(f,d,ip,i,c,w) -#define xfs_bmap_trace_insert(f,d,ip,i,c,r1,r2,w) -#define xfs_bmap_trace_post_update(f,d,ip,i,w) -#define xfs_bmap_trace_pre_update(f,d,ip,i,w) +#define XFS_BMAP_TRACE_DELETE(d,ip,i,c,w) +#define XFS_BMAP_TRACE_INSERT(d,ip,i,c,r1,r2,w) +#define XFS_BMAP_TRACE_POST_UPDATE(d,ip,i,w) +#define XFS_BMAP_TRACE_PRE_UPDATE(d,ip,i,w) #endif /* XFS_BMAP_TRACE */ /* @@ -531,9 +540,6 @@ xfs_bmap_add_extent( xfs_filblks_t da_new; /* new count del alloc blocks used */ xfs_filblks_t da_old; /* old count del alloc blocks used */ int error; /* error return value */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_add_extent"; -#endif xfs_ifork_t *ifp; /* inode fork ptr */ int logflags; /* returned value */ xfs_extnum_t nextents; /* number of extents in file now */ @@ -551,8 +557,8 @@ xfs_bmap_add_extent( * already extents in the list. */ if (nextents == 0) { - xfs_bmap_trace_insert(fname, "insert empty", ip, 0, 1, new, - NULL, whichfork); + XFS_BMAP_TRACE_INSERT("insert empty", ip, 0, 1, new, NULL, + whichfork); xfs_iext_insert(ifp, 0, 1, new); ASSERT(cur == NULL); ifp->if_lastex = 0; @@ -710,9 +716,6 @@ xfs_bmap_add_extent_delay_real( int diff; /* temp value */ xfs_bmbt_rec_t *ep; /* extent entry for idx */ int error; /* error return value */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_add_extent_delay_real"; -#endif int i; /* temp state */ xfs_ifork_t *ifp; /* inode fork pointer */ xfs_fileoff_t new_endoff; /* end offset of new entry */ @@ -808,15 +811,14 @@ xfs_bmap_add_extent_delay_real( * Filling in all of a previously delayed allocation extent. * The left and right neighbors are both contiguous with new. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|LC|RC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + PREV.br_blockcount + RIGHT.br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1, - XFS_DATA_FORK); - xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|LC|RC", ip, idx - 1, XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|LC|RC", ip, idx, 2, XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 2); ip->i_df.if_lastex = idx - 1; ip->i_d.di_nextents--; @@ -855,15 +857,14 @@ xfs_bmap_add_extent_delay_real( * Filling in all of a previously delayed allocation extent. * The left neighbor is contiguous, the right is not. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|LC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + PREV.br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|LC", ip, idx - 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx - 1; - xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|LC", ip, idx, 1, XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 1); if (cur == NULL) rval = XFS_ILOG_DEXT; @@ -892,16 +893,13 @@ xfs_bmap_add_extent_delay_real( * Filling in all of a previously delayed allocation extent. * The right neighbor is contiguous, the left is not. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|RC", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_startblock(ep, new->br_startblock); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount + RIGHT.br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|RC", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; - xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|RC", ip, idx + 1, 1, XFS_DATA_FORK); xfs_iext_remove(ifp, idx + 1, 1); if (cur == NULL) rval = XFS_ILOG_DEXT; @@ -931,11 +929,9 @@ xfs_bmap_add_extent_delay_real( * Neither the left nor right neighbors are contiguous with * the new one. */ - xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_startblock(ep, new->br_startblock); - xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("LF|RF", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; ip->i_d.di_nextents++; if (cur == NULL) @@ -963,17 +959,14 @@ xfs_bmap_add_extent_delay_real( * Filling in the first part of a previous delayed allocation. * The left neighbor is contiguous. */ - xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF|LC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + new->br_blockcount); xfs_bmbt_set_startoff(ep, PREV.br_startoff + new->br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("LF|LC", ip, idx - 1, XFS_DATA_FORK); temp = PREV.br_blockcount - new->br_blockcount; - xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF|LC", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, temp); ip->i_df.if_lastex = idx - 1; if (cur == NULL) @@ -995,8 +988,7 @@ xfs_bmap_add_extent_delay_real( temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), STARTBLOCKVAL(PREV.br_startblock)); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("LF|LC", ip, idx, XFS_DATA_FORK); *dnew = temp; /* DELTA: The boundary between two in-core extents moved. */ temp = LEFT.br_startoff; @@ -1009,11 +1001,11 @@ xfs_bmap_add_extent_delay_real( * Filling in the first part of a previous delayed allocation. * The left neighbor is not contiguous. */ - xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_startoff(ep, new_endoff); temp = PREV.br_blockcount - new->br_blockcount; xfs_bmbt_set_blockcount(ep, temp); - xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL, + XFS_BMAP_TRACE_INSERT("LF", ip, idx, 1, new, NULL, XFS_DATA_FORK); xfs_iext_insert(ifp, idx, 1, new); ip->i_df.if_lastex = idx; @@ -1046,8 +1038,7 @@ xfs_bmap_add_extent_delay_real( (cur ? cur->bc_private.b.allocated : 0)); ep = xfs_iext_get_ext(ifp, idx + 1); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("LF", ip, idx + 1, XFS_DATA_FORK); *dnew = temp; /* DELTA: One in-core extent is split in two. */ temp = PREV.br_startoff; @@ -1060,17 +1051,14 @@ xfs_bmap_add_extent_delay_real( * The right neighbor is contiguous with the new allocation. */ temp = PREV.br_blockcount - new->br_blockcount; - xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx, - XFS_DATA_FORK); - xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("RF|RC", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("RF|RC", ip, idx + 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, temp); xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1), new->br_startoff, new->br_startblock, new->br_blockcount + RIGHT.br_blockcount, RIGHT.br_state); - xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("RF|RC", ip, idx + 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx + 1; if (cur == NULL) rval = XFS_ILOG_DEXT; @@ -1091,8 +1079,7 @@ xfs_bmap_add_extent_delay_real( temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), STARTBLOCKVAL(PREV.br_startblock)); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("RF|RC", ip, idx, XFS_DATA_FORK); *dnew = temp; /* DELTA: The boundary between two in-core extents moved. */ temp = PREV.br_startoff; @@ -1106,10 +1093,10 @@ xfs_bmap_add_extent_delay_real( * The right neighbor is not contiguous. */ temp = PREV.br_blockcount - new->br_blockcount; - xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("RF", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, temp); - xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1, - new, NULL, XFS_DATA_FORK); + XFS_BMAP_TRACE_INSERT("RF", ip, idx + 1, 1, new, NULL, + XFS_DATA_FORK); xfs_iext_insert(ifp, idx + 1, 1, new); ip->i_df.if_lastex = idx + 1; ip->i_d.di_nextents++; @@ -1141,7 +1128,7 @@ xfs_bmap_add_extent_delay_real( (cur ? cur->bc_private.b.allocated : 0)); ep = xfs_iext_get_ext(ifp, idx); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("RF", ip, idx, XFS_DATA_FORK); *dnew = temp; /* DELTA: One in-core extent is split in two. */ temp = PREV.br_startoff; @@ -1155,7 +1142,7 @@ xfs_bmap_add_extent_delay_real( * This case is avoided almost all the time. */ temp = new->br_startoff - PREV.br_startoff; - xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("0", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, temp); r[0] = *new; r[1].br_state = PREV.br_state; @@ -1163,7 +1150,7 @@ xfs_bmap_add_extent_delay_real( r[1].br_startoff = new_endoff; temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff; r[1].br_blockcount = temp2; - xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1], + XFS_BMAP_TRACE_INSERT("0", ip, idx + 1, 2, &r[0], &r[1], XFS_DATA_FORK); xfs_iext_insert(ifp, idx + 1, 2, &r[0]); ip->i_df.if_lastex = idx + 1; @@ -1222,13 +1209,11 @@ xfs_bmap_add_extent_delay_real( } ep = xfs_iext_get_ext(ifp, idx); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK); - xfs_bmap_trace_pre_update(fname, "0", ip, idx + 2, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("0", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("0", ip, idx + 2, XFS_DATA_FORK); xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx + 2), NULLSTARTBLOCK((int)temp2)); - xfs_bmap_trace_post_update(fname, "0", ip, idx + 2, - XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("0", ip, idx + 2, XFS_DATA_FORK); *dnew = temp + temp2; /* DELTA: One in-core extent is split in three. */ temp = PREV.br_startoff; @@ -1287,9 +1272,6 @@ xfs_bmap_add_extent_unwritten_real( xfs_btree_cur_t *cur; /* btree cursor */ xfs_bmbt_rec_t *ep; /* extent entry for idx */ int error; /* error return value */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_add_extent_unwritten_real"; -#endif int i; /* temp state */ xfs_ifork_t *ifp; /* inode fork pointer */ xfs_fileoff_t new_endoff; /* end offset of new entry */ @@ -1390,15 +1372,14 @@ xfs_bmap_add_extent_unwritten_real( * Setting all of a previous oldext extent to newext. * The left and right neighbors are both contiguous with new. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|LC|RC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + PREV.br_blockcount + RIGHT.br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1, - XFS_DATA_FORK); - xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|LC|RC", ip, idx - 1, XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|LC|RC", ip, idx, 2, XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 2); ip->i_df.if_lastex = idx - 1; ip->i_d.di_nextents -= 2; @@ -1441,15 +1422,14 @@ xfs_bmap_add_extent_unwritten_real( * Setting all of a previous oldext extent to newext. * The left neighbor is contiguous, the right is not. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|LC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + PREV.br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|LC", ip, idx - 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx - 1; - xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|LC", ip, idx, 1, XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 1); ip->i_d.di_nextents--; if (cur == NULL) @@ -1484,16 +1464,15 @@ xfs_bmap_add_extent_unwritten_real( * Setting all of a previous oldext extent to newext. * The right neighbor is contiguous, the left is not. */ - xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF|RC", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount + RIGHT.br_blockcount); xfs_bmbt_set_state(ep, newext); - xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF|RC", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; - xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1, - XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LF|RF|RC", ip, idx + 1, 1, XFS_DATA_FORK); xfs_iext_remove(ifp, idx + 1, 1); ip->i_d.di_nextents--; if (cur == NULL) @@ -1529,10 +1508,10 @@ xfs_bmap_add_extent_unwritten_real( * Neither the left nor right neighbors are contiguous with * the new one. */ - xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx, + XFS_BMAP_TRACE_PRE_UPDATE("LF|RF", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_state(ep, newext); - xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("LF|RF", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; if (cur == NULL) @@ -1559,21 +1538,21 @@ xfs_bmap_add_extent_unwritten_real( * Setting the first part of a previous oldext extent to newext. * The left neighbor is contiguous. */ - xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LF|LC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), LEFT.br_blockcount + new->br_blockcount); xfs_bmbt_set_startoff(ep, PREV.br_startoff + new->br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1, + XFS_BMAP_TRACE_POST_UPDATE("LF|LC", ip, idx - 1, XFS_DATA_FORK); - xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx, + XFS_BMAP_TRACE_PRE_UPDATE("LF|LC", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_startblock(ep, new->br_startblock + new->br_blockcount); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount - new->br_blockcount); - xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("LF|LC", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx - 1; if (cur == NULL) @@ -1610,15 +1589,15 @@ xfs_bmap_add_extent_unwritten_real( * Setting the first part of a previous oldext extent to newext. * The left neighbor is not contiguous. */ - xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("LF", ip, idx, XFS_DATA_FORK); ASSERT(ep && xfs_bmbt_get_state(ep) == oldext); xfs_bmbt_set_startoff(ep, new_endoff); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount - new->br_blockcount); xfs_bmbt_set_startblock(ep, new->br_startblock + new->br_blockcount); - xfs_bmap_trace_post_update(fname, "LF", ip, idx, XFS_DATA_FORK); - xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL, + XFS_BMAP_TRACE_POST_UPDATE("LF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_INSERT("LF", ip, idx, 1, new, NULL, XFS_DATA_FORK); xfs_iext_insert(ifp, idx, 1, new); ip->i_df.if_lastex = idx; @@ -1653,18 +1632,18 @@ xfs_bmap_add_extent_unwritten_real( * Setting the last part of a previous oldext extent to newext. * The right neighbor is contiguous with the new allocation. */ - xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx, + XFS_BMAP_TRACE_PRE_UPDATE("RF|RC", ip, idx, XFS_DATA_FORK); - xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1, + XFS_BMAP_TRACE_PRE_UPDATE("RF|RC", ip, idx + 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount - new->br_blockcount); - xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("RF|RC", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1), new->br_startoff, new->br_startblock, new->br_blockcount + RIGHT.br_blockcount, newext); - xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1, + XFS_BMAP_TRACE_POST_UPDATE("RF|RC", ip, idx + 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx + 1; if (cur == NULL) @@ -1700,12 +1679,12 @@ xfs_bmap_add_extent_unwritten_real( * Setting the last part of a previous oldext extent to newext. * The right neighbor is not contiguous. */ - xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("RF", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, PREV.br_blockcount - new->br_blockcount); - xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK); - xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1, - new, NULL, XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("RF", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_INSERT("RF", ip, idx + 1, 1, new, NULL, + XFS_DATA_FORK); xfs_iext_insert(ifp, idx + 1, 1, new); ip->i_df.if_lastex = idx + 1; ip->i_d.di_nextents++; @@ -1744,17 +1723,17 @@ xfs_bmap_add_extent_unwritten_real( * newext. Contiguity is impossible here. * One extent becomes three extents. */ - xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("0", ip, idx, XFS_DATA_FORK); xfs_bmbt_set_blockcount(ep, new->br_startoff - PREV.br_startoff); - xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("0", ip, idx, XFS_DATA_FORK); r[0] = *new; r[1].br_startoff = new_endoff; r[1].br_blockcount = PREV.br_startoff + PREV.br_blockcount - new_endoff; r[1].br_startblock = new->br_startblock + new->br_blockcount; r[1].br_state = oldext; - xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1], + XFS_BMAP_TRACE_INSERT("0", ip, idx + 1, 2, &r[0], &r[1], XFS_DATA_FORK); xfs_iext_insert(ifp, idx + 1, 2, &r[0]); ip->i_df.if_lastex = idx + 1; @@ -1845,9 +1824,6 @@ xfs_bmap_add_extent_hole_delay( int rsvd) /* OK to allocate reserved blocks */ { xfs_bmbt_rec_t *ep; /* extent record for idx */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_add_extent_hole_delay"; -#endif xfs_ifork_t *ifp; /* inode fork pointer */ xfs_bmbt_irec_t left; /* left neighbor extent entry */ xfs_filblks_t newlen=0; /* new indirect size */ @@ -1919,7 +1895,7 @@ xfs_bmap_add_extent_hole_delay( */ temp = left.br_blockcount + new->br_blockcount + right.br_blockcount; - xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LC|RC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp); oldlen = STARTBLOCKVAL(left.br_startblock) + @@ -1928,10 +1904,9 @@ xfs_bmap_add_extent_hole_delay( newlen = xfs_bmap_worst_indlen(ip, temp); xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1), NULLSTARTBLOCK((int)newlen)); - xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1, - XFS_DATA_FORK); - xfs_bmap_trace_delete(fname, "LC|RC", ip, idx, 1, + XFS_BMAP_TRACE_POST_UPDATE("LC|RC", ip, idx - 1, XFS_DATA_FORK); + XFS_BMAP_TRACE_DELETE("LC|RC", ip, idx, 1, XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 1); ip->i_df.if_lastex = idx - 1; /* DELTA: Two in-core extents were replaced by one. */ @@ -1946,7 +1921,7 @@ xfs_bmap_add_extent_hole_delay( * Merge the new allocation with the left neighbor. */ temp = left.br_blockcount + new->br_blockcount; - xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LC", ip, idx - 1, XFS_DATA_FORK); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp); oldlen = STARTBLOCKVAL(left.br_startblock) + @@ -1954,7 +1929,7 @@ xfs_bmap_add_extent_hole_delay( newlen = xfs_bmap_worst_indlen(ip, temp); xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1), NULLSTARTBLOCK((int)newlen)); - xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, + XFS_BMAP_TRACE_POST_UPDATE("LC", ip, idx - 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx - 1; /* DELTA: One in-core extent grew into a hole. */ @@ -1968,14 +1943,14 @@ xfs_bmap_add_extent_hole_delay( * on the right. * Merge the new allocation with the right neighbor. */ - xfs_bmap_trace_pre_update(fname, "RC", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_PRE_UPDATE("RC", ip, idx, XFS_DATA_FORK); temp = new->br_blockcount + right.br_blockcount; oldlen = STARTBLOCKVAL(new->br_startblock) + STARTBLOCKVAL(right.br_startblock); newlen = xfs_bmap_worst_indlen(ip, temp); xfs_bmbt_set_allf(ep, new->br_startoff, NULLSTARTBLOCK((int)newlen), temp, right.br_state); - xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK); + XFS_BMAP_TRACE_POST_UPDATE("RC", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; /* DELTA: One in-core extent grew into a hole. */ temp2 = temp; @@ -1989,7 +1964,7 @@ xfs_bmap_add_extent_hole_delay( * Insert a new entry. */ oldlen = newlen = 0; - xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL, + XFS_BMAP_TRACE_INSERT("0", ip, idx, 1, new, NULL, XFS_DATA_FORK); xfs_iext_insert(ifp, idx, 1, new); ip->i_df.if_lastex = idx; @@ -2039,9 +2014,6 @@ xfs_bmap_add_extent_hole_real( { xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */ int error; /* error return value */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_add_extent_hole_real"; -#endif int i; /* temp state */ xfs_ifork_t *ifp; /* inode fork pointer */ xfs_bmbt_irec_t left; /* left neighbor extent entry */ @@ -2118,15 +2090,14 @@ xfs_bmap_add_extent_hole_real( * left and on the right. * Merge all three into a single extent record. */ - xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1, + XFS_BMAP_TRACE_PRE_UPDATE("LC|RC", ip, idx - 1, whichfork); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), left.br_blockcount + new->br_blockcount + right.br_blockcount); - xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1, + XFS_BMAP_TRACE_POST_UPDATE("LC|RC", ip, idx - 1, whichfork); - xfs_bmap_trace_delete(fname, "LC|RC", ip, - idx, 1, whichfork); + XFS_BMAP_TRACE_DELETE("LC|RC", ip, idx, 1, whichfork); xfs_iext_remove(ifp, idx, 1); ifp->if_lastex = idx - 1; XFS_IFORK_NEXT_SET(ip, whichfork, @@ -2168,10 +2139,10 @@ xfs_bmap_add_extent_hole_real( * on the left. * Merge the new allocation with the left neighbor. */ - xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, whichfork); + XFS_BMAP_TRACE_PRE_UPDATE("LC", ip, idx - 1, whichfork); xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), left.br_blockcount + new->br_blockcount); - xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork); + XFS_BMAP_TRACE_POST_UPDATE("LC", ip, idx - 1, whichfork); ifp->if_lastex = idx - 1; if (cur == NULL) { rval = XFS_ILOG_FEXT(whichfork); @@ -2202,11 +2173,11 @@ xfs_bmap_add_extent_hole_real( * on the right. * Merge the new allocation with the right neighbor. */ - xfs_bmap_trace_pre_update(fname, "RC", ip, idx, whichfork); + XFS_BMAP_TRACE_PRE_UPDATE("RC", ip, idx, whichfork); xfs_bmbt_set_allf(ep, new->br_startoff, new->br_startblock, new->br_blockcount + right.br_blockcount, right.br_state); - xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork); + XFS_BMAP_TRACE_POST_UPDATE("RC", ip, idx, whichfork); ifp->if_lastex = idx; if (cur == NULL) { rval = XFS_ILOG_FEXT(whichfork); @@ -2237,8 +2208,7 @@ xfs_bmap_add_extent_hole_real( * real allocation. * Insert a new entry. */ - xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL, - whichfork); + XFS_BMAP_TRACE_INSERT("0", ip, idx, 1, new, NULL, whichfork); xfs_iext_insert(ifp, idx, 1, new); ifp->if_lastex = idx; XFS_IFORK_NEXT_SET(ip, whichfork, @@ -2605,12 +2575,10 @@ xfs_bmap_rtalloc( xfs_extlen_t prod = 0; /* product factor for allocators */ xfs_extlen_t ralen = 0; /* realtime allocation length */ xfs_extlen_t align; /* minimum allocation alignment */ - xfs_rtblock_t rtx; /* realtime extent number */ xfs_rtblock_t rtb; mp = ap->ip->i_mount; - align = ap->ip->i_d.di_extsize ? - ap->ip->i_d.di_extsize : mp->m_sb.sb_rextsize; + align = xfs_get_extsz_hint(ap->ip); prod = align / mp->m_sb.sb_rextsize; error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp, align, 1, ap->eof, 0, @@ -2644,6 +2612,8 @@ xfs_bmap_rtalloc( * pick an extent that will space things out in the rt area. */ if (ap->eof && ap->off == 0) { + xfs_rtblock_t uninitialized_var(rtx); /* realtime extent no */ + error = xfs_rtpick_extent(mp, ap->tp, ralen, &rtx); if (error) return error; @@ -2715,9 +2685,7 @@ xfs_bmap_btalloc( int error; mp = ap->ip->i_mount; - align = (ap->userdata && ap->ip->i_d.di_extsize && - (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE)) ? - ap->ip->i_d.di_extsize : 0; + align = ap->userdata ? xfs_get_extsz_hint(ap->ip) : 0; if (unlikely(align)) { error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp, align, 0, ap->eof, 0, ap->conv, @@ -2727,9 +2695,15 @@ xfs_bmap_btalloc( } nullfb = ap->firstblock == NULLFSBLOCK; fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock); - if (nullfb) - ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino); - else + if (nullfb) { + if (ap->userdata && xfs_inode_is_filestream(ap->ip)) { + ag = xfs_filestream_lookup_ag(ap->ip); + ag = (ag != NULLAGNUMBER) ? ag : 0; + ap->rval = XFS_AGB_TO_FSB(mp, ag, 0); + } else { + ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino); + } + } else ap->rval = ap->firstblock; xfs_bmap_adjacent(ap); @@ -2753,13 +2727,22 @@ xfs_bmap_btalloc( args.firstblock = ap->firstblock; blen = 0; if (nullfb) { - args.type = XFS_ALLOCTYPE_START_BNO; + if (ap->userdata && xfs_inode_is_filestream(ap->ip)) + args.type = XFS_ALLOCTYPE_NEAR_BNO; + else + args.type = XFS_ALLOCTYPE_START_BNO; args.total = ap->total; + /* - * Find the longest available space. - * We're going to try for the whole allocation at once. + * Search for an allocation group with a single extent + * large enough for the request. + * + * If one isn't found, then adjust the minimum allocation + * size to the largest space found. */ startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno); + if (startag == NULLAGNUMBER) + startag = ag = 0; notinit = 0; down_read(&mp->m_peraglock); while (blen < ap->alen) { @@ -2785,6 +2768,35 @@ xfs_bmap_btalloc( blen = longest; } else notinit = 1; + + if (xfs_inode_is_filestream(ap->ip)) { + if (blen >= ap->alen) + break; + + if (ap->userdata) { + /* + * If startag is an invalid AG, we've + * come here once before and + * xfs_filestream_new_ag picked the + * best currently available. + * + * Don't continue looping, since we + * could loop forever. + */ + if (startag == NULLAGNUMBER) + break; + + error = xfs_filestream_new_ag(ap, &ag); + if (error) { + up_read(&mp->m_peraglock); + return error; + } + + /* loop again to set 'blen'*/ + startag = NULLAGNUMBER; + continue; + } + } if (++ag == mp->m_sb.sb_agcount) ag = 0; if (ag == startag) @@ -2809,17 +2821,27 @@ xfs_bmap_btalloc( */ else args.minlen = ap->alen; + + /* + * set the failure fallback case to look in the selected + * AG as the stream may have moved. + */ + if (xfs_inode_is_filestream(ap->ip)) + ap->rval = args.fsbno = XFS_AGB_TO_FSB(mp, ag, 0); } else if (ap->low) { - args.type = XFS_ALLOCTYPE_START_BNO; + if (xfs_inode_is_filestream(ap->ip)) + args.type = XFS_ALLOCTYPE_FIRST_AG; + else + args.type = XFS_ALLOCTYPE_START_BNO; args.total = args.minlen = ap->minlen; } else { args.type = XFS_ALLOCTYPE_NEAR_BNO; args.total = ap->total; args.minlen = ap->minlen; } - if (unlikely(ap->userdata && ap->ip->i_d.di_extsize && - (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE))) { - args.prod = ap->ip->i_d.di_extsize; + /* apply extent size hints if obtained earlier */ + if (unlikely(align)) { + args.prod = align; if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod))) args.mod = (xfs_extlen_t)(args.prod - args.mod); } else if (mp->m_sb.sb_blocksize >= NBPP) { @@ -3051,9 +3073,6 @@ xfs_bmap_del_extent( xfs_bmbt_rec_t *ep; /* current extent entry pointer */ int error; /* error return value */ int flags; /* inode logging flags */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_del_extent"; -#endif xfs_bmbt_irec_t got; /* current extent entry */ xfs_fileoff_t got_endoff; /* first offset past got */ int i; /* temp state */ @@ -3147,7 +3166,7 @@ xfs_bmap_del_extent( /* * Matches the whole extent. Delete the entry. */ - xfs_bmap_trace_delete(fname, "3", ip, idx, 1, whichfork); + XFS_BMAP_TRACE_DELETE("3", ip, idx, 1, whichfork); xfs_iext_remove(ifp, idx, 1); ifp->if_lastex = idx; if (delay) @@ -3168,7 +3187,7 @@ xfs_bmap_del_extent( /* * Deleting the first part of the extent. */ - xfs_bmap_trace_pre_update(fname, "2", ip, idx, whichfork); + XFS_BMAP_TRACE_PRE_UPDATE("2", ip, idx, whichfork); xfs_bmbt_set_startoff(ep, del_endoff); temp = got.br_blockcount - del->br_blockcount; xfs_bmbt_set_blockcount(ep, temp); @@ -3177,13 +3196,13 @@ xfs_bmap_del_extent( temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), da_old); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "2", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("2", ip, idx, whichfork); da_new = temp; break; } xfs_bmbt_set_startblock(ep, del_endblock); - xfs_bmap_trace_post_update(fname, "2", ip, idx, whichfork); + XFS_BMAP_TRACE_POST_UPDATE("2", ip, idx, whichfork); if (!cur) { flags |= XFS_ILOG_FEXT(whichfork); break; @@ -3199,19 +3218,19 @@ xfs_bmap_del_extent( * Deleting the last part of the extent. */ temp = got.br_blockcount - del->br_blockcount; - xfs_bmap_trace_pre_update(fname, "1", ip, idx, whichfork); + XFS_BMAP_TRACE_PRE_UPDATE("1", ip, idx, whichfork); xfs_bmbt_set_blockcount(ep, temp); ifp->if_lastex = idx; if (delay) { temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), da_old); xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); - xfs_bmap_trace_post_update(fname, "1", ip, idx, + XFS_BMAP_TRACE_POST_UPDATE("1", ip, idx, whichfork); da_new = temp; break; } - xfs_bmap_trace_post_update(fname, "1", ip, idx, whichfork); + XFS_BMAP_TRACE_POST_UPDATE("1", ip, idx, whichfork); if (!cur) { flags |= XFS_ILOG_FEXT(whichfork); break; @@ -3228,7 +3247,7 @@ xfs_bmap_del_extent( * Deleting the middle of the extent. */ temp = del->br_startoff - got.br_startoff; - xfs_bmap_trace_pre_update(fname, "0", ip, idx, whichfork); + XFS_BMAP_TRACE_PRE_UPDATE("0", ip, idx, whichfork); xfs_bmbt_set_blockcount(ep, temp); new.br_startoff = del_endoff; temp2 = got_endoff - del_endoff; @@ -3315,8 +3334,8 @@ xfs_bmap_del_extent( } } } - xfs_bmap_trace_post_update(fname, "0", ip, idx, whichfork); - xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 1, &new, NULL, + XFS_BMAP_TRACE_POST_UPDATE("0", ip, idx, whichfork); + XFS_BMAP_TRACE_INSERT("0", ip, idx + 1, 1, &new, NULL, whichfork); xfs_iext_insert(ifp, idx + 1, 1, &new); ifp->if_lastex = idx + 1; @@ -3556,9 +3575,6 @@ xfs_bmap_local_to_extents( { int error; /* error return value */ int flags; /* logging flags returned */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_local_to_extents"; -#endif xfs_ifork_t *ifp; /* inode fork pointer */ /* @@ -3613,7 +3629,7 @@ xfs_bmap_local_to_extents( xfs_iext_add(ifp, 0, 1); ep = xfs_iext_get_ext(ifp, 0); xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM); - xfs_bmap_trace_post_update(fname, "new", ip, 0, whichfork); + XFS_BMAP_TRACE_POST_UPDATE("new", ip, 0, whichfork); XFS_IFORK_NEXT_SET(ip, whichfork, 1); ip->i_d.di_nblocks = 1; XFS_TRANS_MOD_DQUOT_BYINO(args.mp, tp, ip, @@ -3736,7 +3752,7 @@ ktrace_t *xfs_bmap_trace_buf; STATIC void xfs_bmap_trace_addentry( int opcode, /* operation */ - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(ies) */ @@ -3795,7 +3811,7 @@ xfs_bmap_trace_addentry( */ STATIC void xfs_bmap_trace_delete( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(entries) deleted */ @@ -3817,7 +3833,7 @@ xfs_bmap_trace_delete( */ STATIC void xfs_bmap_trace_insert( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry(entries) inserted */ @@ -3846,7 +3862,7 @@ xfs_bmap_trace_insert( */ STATIC void xfs_bmap_trace_post_update( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry updated */ @@ -3864,7 +3880,7 @@ xfs_bmap_trace_post_update( */ STATIC void xfs_bmap_trace_pre_update( - char *fname, /* function name */ + const char *fname, /* function name */ char *desc, /* operation description */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t idx, /* index of entry to be updated */ @@ -4481,9 +4497,6 @@ xfs_bmap_read_extents( xfs_buf_t *bp; /* buffer for "block" */ int error; /* error return value */ xfs_exntfmt_t exntf; /* XFS_EXTFMT_NOSTATE, if checking */ -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_bmap_read_extents"; -#endif xfs_extnum_t i, j; /* index into the extents list */ xfs_ifork_t *ifp; /* fork structure */ int level; /* btree level, for checking */ @@ -4600,7 +4613,7 @@ xfs_bmap_read_extents( } ASSERT(i == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))); ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork)); - xfs_bmap_trace_exlist(fname, ip, i, whichfork); + XFS_BMAP_TRACE_EXLIST(ip, i, whichfork); return 0; error0: xfs_trans_brelse(tp, bp); @@ -4613,7 +4626,7 @@ error0: */ void xfs_bmap_trace_exlist( - char *fname, /* function name */ + const char *fname, /* function name */ xfs_inode_t *ip, /* incore inode pointer */ xfs_extnum_t cnt, /* count of entries in the list */ int whichfork) /* data or attr fork */ @@ -4628,7 +4641,7 @@ xfs_bmap_trace_exlist( for (idx = 0; idx < cnt; idx++) { ep = xfs_iext_get_ext(ifp, idx); xfs_bmbt_get_all(ep, &s); - xfs_bmap_trace_insert(fname, "exlist", ip, idx, 1, &s, NULL, + XFS_BMAP_TRACE_INSERT("exlist", ip, idx, 1, &s, NULL, whichfork); } } @@ -4868,12 +4881,7 @@ xfs_bmapi( xfs_extlen_t extsz; /* Figure out the extent size, adjust alen */ - if (rt) { - if (!(extsz = ip->i_d.di_extsize)) - extsz = mp->m_sb.sb_rextsize; - } else { - extsz = ip->i_d.di_extsize; - } + extsz = xfs_get_extsz_hint(ip); if (extsz) { error = xfs_bmap_extsize_align(mp, &got, &prev, extsz, @@ -5219,10 +5227,10 @@ xfs_bmapi( * Else go on to the next record. */ ep = xfs_iext_get_ext(ifp, ++lastx); - if (lastx >= nextents) { + prev = got; + if (lastx >= nextents) eof = 1; - prev = got; - } else + else xfs_bmbt_get_all(ep, &got); } ifp->if_lastex = lastx; @@ -5813,8 +5821,7 @@ xfs_getbmap( ip->i_d.di_format != XFS_DINODE_FMT_LOCAL) return XFS_ERROR(EINVAL); if (whichfork == XFS_DATA_FORK) { - if ((ip->i_d.di_extsize && (ip->i_d.di_flags & - (XFS_DIFLAG_REALTIME|XFS_DIFLAG_EXTSIZE))) || + if (xfs_get_extsz_hint(ip) || ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)){ prealloced = 1; fixlen = XFS_MAXIOFFSET(mp); diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index 4f24c7e..524b1c9 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h @@ -144,12 +144,14 @@ extern ktrace_t *xfs_bmap_trace_buf; */ void xfs_bmap_trace_exlist( - char *fname, /* function name */ + const char *fname, /* function name */ struct xfs_inode *ip, /* incore inode pointer */ xfs_extnum_t cnt, /* count of entries in list */ int whichfork); /* data or attr fork */ +#define XFS_BMAP_TRACE_EXLIST(ip,c,w) \ + xfs_bmap_trace_exlist(__FUNCTION__,ip,c,w) #else -#define xfs_bmap_trace_exlist(f,ip,c,w) +#define XFS_BMAP_TRACE_EXLIST(ip,c,w) #endif /* diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c index 0bf192f..89b891f 100644 --- a/fs/xfs/xfs_bmap_btree.c +++ b/fs/xfs/xfs_bmap_btree.c @@ -76,7 +76,7 @@ static char EXIT[] = "exit"; */ STATIC void xfs_bmbt_trace_enter( - char *func, + const char *func, xfs_btree_cur_t *cur, char *s, int type, @@ -117,7 +117,7 @@ xfs_bmbt_trace_enter( */ STATIC void xfs_bmbt_trace_argbi( - char *func, + const char *func, xfs_btree_cur_t *cur, xfs_buf_t *b, int i, @@ -134,7 +134,7 @@ xfs_bmbt_trace_argbi( */ STATIC void xfs_bmbt_trace_argbii( - char *func, + const char *func, xfs_btree_cur_t *cur, xfs_buf_t *b, int i0, @@ -153,7 +153,7 @@ xfs_bmbt_trace_argbii( */ STATIC void xfs_bmbt_trace_argfffi( - char *func, + const char *func, xfs_btree_cur_t *cur, xfs_dfiloff_t o, xfs_dfsbno_t b, @@ -172,7 +172,7 @@ xfs_bmbt_trace_argfffi( */ STATIC void xfs_bmbt_trace_argi( - char *func, + const char *func, xfs_btree_cur_t *cur, int i, int line) @@ -188,7 +188,7 @@ xfs_bmbt_trace_argi( */ STATIC void xfs_bmbt_trace_argifk( - char *func, + const char *func, xfs_btree_cur_t *cur, int i, xfs_fsblock_t f, @@ -206,7 +206,7 @@ xfs_bmbt_trace_argifk( */ STATIC void xfs_bmbt_trace_argifr( - char *func, + const char *func, xfs_btree_cur_t *cur, int i, xfs_fsblock_t f, @@ -235,7 +235,7 @@ xfs_bmbt_trace_argifr( */ STATIC void xfs_bmbt_trace_argik( - char *func, + const char *func, xfs_btree_cur_t *cur, int i, xfs_bmbt_key_t *k, @@ -255,7 +255,7 @@ xfs_bmbt_trace_argik( */ STATIC void xfs_bmbt_trace_cursor( - char *func, + const char *func, xfs_btree_cur_t *cur, char *s, int line) @@ -274,21 +274,21 @@ xfs_bmbt_trace_cursor( } #define XFS_BMBT_TRACE_ARGBI(c,b,i) \ - xfs_bmbt_trace_argbi(fname, c, b, i, __LINE__) + xfs_bmbt_trace_argbi(__FUNCTION__, c, b, i, __LINE__) #define XFS_BMBT_TRACE_ARGBII(c,b,i,j) \ - xfs_bmbt_trace_argbii(fname, c, b, i, j, __LINE__) + xfs_bmbt_trace_argbii(__FUNCTION__, c, b, i, j, __LINE__) #define XFS_BMBT_TRACE_ARGFFFI(c,o,b,i,j) \ - xfs_bmbt_trace_argfffi(fname, c, o, b, i, j, __LINE__) + xfs_bmbt_trace_argfffi(__FUNCTION__, c, o, b, i, j, __LINE__) #define XFS_BMBT_TRACE_ARGI(c,i) \ - xfs_bmbt_trace_argi(fname, c, i, __LINE__) + xfs_bmbt_trace_argi(__FUNCTION__, c, i, __LINE__) #define XFS_BMBT_TRACE_ARGIFK(c,i,f,s) \ - xfs_bmbt_trace_argifk(fname, c, i, f, s, __LINE__) + xfs_bmbt_trace_argifk(__FUNCTION__, c, i, f, s, __LINE__) #define XFS_BMBT_TRACE_ARGIFR(c,i,f,r) \ - xfs_bmbt_trace_argifr(fname, c, i, f, r, __LINE__) + xfs_bmbt_trace_argifr(__FUNCTION__, c, i, f, r, __LINE__) #define XFS_BMBT_TRACE_ARGIK(c,i,k) \ - xfs_bmbt_trace_argik(fname, c, i, k, __LINE__) + xfs_bmbt_trace_argik(__FUNCTION__, c, i, k, __LINE__) #define XFS_BMBT_TRACE_CURSOR(c,s) \ - xfs_bmbt_trace_cursor(fname, c, s, __LINE__) + xfs_bmbt_trace_cursor(__FUNCTION__, c, s, __LINE__) #else #define XFS_BMBT_TRACE_ARGBI(c,b,i) #define XFS_BMBT_TRACE_ARGBII(c,b,i,j) @@ -318,9 +318,6 @@ xfs_bmbt_delrec( xfs_fsblock_t bno; /* fs-relative block number */ xfs_buf_t *bp; /* buffer for block */ int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_delrec"; -#endif int i; /* loop counter */ int j; /* temp state */ xfs_bmbt_key_t key; /* bmap btree key */ @@ -694,9 +691,6 @@ xfs_bmbt_insrec( xfs_bmbt_block_t *block; /* bmap btree block */ xfs_buf_t *bp; /* buffer for block */ int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_insrec"; -#endif int i; /* loop index */ xfs_bmbt_key_t key; /* bmap btree key */ xfs_bmbt_key_t *kp=NULL; /* pointer to bmap btree key */ @@ -881,9 +875,6 @@ xfs_bmbt_killroot( #ifdef DEBUG int error; #endif -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_killroot"; -#endif int i; xfs_bmbt_key_t *kp; xfs_inode_t *ip; @@ -973,9 +964,6 @@ xfs_bmbt_log_keys( int kfirst, int klast) { -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_log_keys"; -#endif xfs_trans_t *tp; XFS_BMBT_TRACE_CURSOR(cur, ENTRY); @@ -1012,9 +1000,6 @@ xfs_bmbt_log_ptrs( int pfirst, int plast) { -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_log_ptrs"; -#endif xfs_trans_t *tp; XFS_BMBT_TRACE_CURSOR(cur, ENTRY); @@ -1055,9 +1040,6 @@ xfs_bmbt_lookup( xfs_daddr_t d; xfs_sfiloff_t diff; int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_lookup"; -#endif xfs_fsblock_t fsbno=0; int high; int i; @@ -1195,9 +1177,6 @@ xfs_bmbt_lshift( int *stat) /* success/failure */ { int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_lshift"; -#endif #ifdef DEBUG int i; /* loop counter */ #endif @@ -1331,9 +1310,6 @@ xfs_bmbt_rshift( int *stat) /* success/failure */ { int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_rshift"; -#endif int i; /* loop counter */ xfs_bmbt_key_t key; /* bmap btree key */ xfs_buf_t *lbp; /* left buffer pointer */ @@ -1492,9 +1468,6 @@ xfs_bmbt_split( { xfs_alloc_arg_t args; /* block allocation args */ int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_split"; -#endif int i; /* loop counter */ xfs_fsblock_t lbno; /* left sibling block number */ xfs_buf_t *lbp; /* left buffer pointer */ @@ -1641,9 +1614,6 @@ xfs_bmbt_updkey( #ifdef DEBUG int error; #endif -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_updkey"; -#endif xfs_bmbt_key_t *kp; int ptr; @@ -1712,9 +1682,6 @@ xfs_bmbt_decrement( xfs_bmbt_block_t *block; xfs_buf_t *bp; int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_decrement"; -#endif xfs_fsblock_t fsbno; int lev; xfs_mount_t *mp; @@ -1785,9 +1752,6 @@ xfs_bmbt_delete( int *stat) /* success/failure */ { int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_delete"; -#endif int i; int level; @@ -2000,9 +1964,6 @@ xfs_bmbt_increment( xfs_bmbt_block_t *block; xfs_buf_t *bp; int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_increment"; -#endif xfs_fsblock_t fsbno; int lev; xfs_mount_t *mp; @@ -2080,9 +2041,6 @@ xfs_bmbt_insert( int *stat) /* success/failure */ { int error; /* error return value */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_insert"; -#endif int i; int level; xfs_fsblock_t nbno; @@ -2142,9 +2100,6 @@ xfs_bmbt_log_block( int fields) { int first; -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_log_block"; -#endif int last; xfs_trans_t *tp; static const short offsets[] = { @@ -2181,9 +2136,6 @@ xfs_bmbt_log_recs( { xfs_bmbt_block_t *block; int first; -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_log_recs"; -#endif int last; xfs_bmbt_rec_t *rp; xfs_trans_t *tp; @@ -2245,9 +2197,6 @@ xfs_bmbt_newroot( xfs_bmbt_key_t *ckp; /* child key pointer */ xfs_bmbt_ptr_t *cpp; /* child ptr pointer */ int error; /* error return code */ -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_newroot"; -#endif #ifdef DEBUG int i; /* loop counter */ #endif @@ -2630,9 +2579,6 @@ xfs_bmbt_update( xfs_bmbt_block_t *block; xfs_buf_t *bp; int error; -#ifdef XFS_BMBT_TRACE - static char fname[] = "xfs_bmbt_update"; -#endif xfs_bmbt_key_t key; int ptr; xfs_bmbt_rec_t *rp; diff --git a/fs/xfs/xfs_btree.h b/fs/xfs/xfs_btree.h index 4e27d55..6e40a0a 100644 --- a/fs/xfs/xfs_btree.h +++ b/fs/xfs/xfs_btree.h @@ -444,30 +444,14 @@ xfs_btree_setbuf( /* * Min and max functions for extlen, agblock, fileoff, and filblks types. */ -#define XFS_EXTLEN_MIN(a,b) \ - ((xfs_extlen_t)(a) < (xfs_extlen_t)(b) ? \ - (xfs_extlen_t)(a) : (xfs_extlen_t)(b)) -#define XFS_EXTLEN_MAX(a,b) \ - ((xfs_extlen_t)(a) > (xfs_extlen_t)(b) ? \ - (xfs_extlen_t)(a) : (xfs_extlen_t)(b)) -#define XFS_AGBLOCK_MIN(a,b) \ - ((xfs_agblock_t)(a) < (xfs_agblock_t)(b) ? \ - (xfs_agblock_t)(a) : (xfs_agblock_t)(b)) -#define XFS_AGBLOCK_MAX(a,b) \ - ((xfs_agblock_t)(a) > (xfs_agblock_t)(b) ? \ - (xfs_agblock_t)(a) : (xfs_agblock_t)(b)) -#define XFS_FILEOFF_MIN(a,b) \ - ((xfs_fileoff_t)(a) < (xfs_fileoff_t)(b) ? \ - (xfs_fileoff_t)(a) : (xfs_fileoff_t)(b)) -#define XFS_FILEOFF_MAX(a,b) \ - ((xfs_fileoff_t)(a) > (xfs_fileoff_t)(b) ? \ - (xfs_fileoff_t)(a) : (xfs_fileoff_t)(b)) -#define XFS_FILBLKS_MIN(a,b) \ - ((xfs_filblks_t)(a) < (xfs_filblks_t)(b) ? \ - (xfs_filblks_t)(a) : (xfs_filblks_t)(b)) -#define XFS_FILBLKS_MAX(a,b) \ - ((xfs_filblks_t)(a) > (xfs_filblks_t)(b) ? \ - (xfs_filblks_t)(a) : (xfs_filblks_t)(b)) +#define XFS_EXTLEN_MIN(a,b) min_t(xfs_extlen_t, (a), (b)) +#define XFS_EXTLEN_MAX(a,b) max_t(xfs_extlen_t, (a), (b)) +#define XFS_AGBLOCK_MIN(a,b) min_t(xfs_agblock_t, (a), (b)) +#define XFS_AGBLOCK_MAX(a,b) max_t(xfs_agblock_t, (a), (b)) +#define XFS_FILEOFF_MIN(a,b) min_t(xfs_fileoff_t, (a), (b)) +#define XFS_FILEOFF_MAX(a,b) max_t(xfs_fileoff_t, (a), (b)) +#define XFS_FILBLKS_MIN(a,b) min_t(xfs_filblks_t, (a), (b)) +#define XFS_FILBLKS_MAX(a,b) max_t(xfs_filblks_t, (a), (b)) #define XFS_FSB_SANITY_CHECK(mp,fsb) \ (XFS_FSB_TO_AGNO(mp, fsb) < mp->m_sb.sb_agcount && \ diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 6c1bddc..b0667cb 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -580,8 +580,8 @@ xfs_buf_item_unlock( * If the buf item isn't tracking any data, free it. * Otherwise, if XFS_BLI_HOLD is set clear it. */ - if (xfs_count_bits(bip->bli_format.blf_data_map, - bip->bli_format.blf_map_size, 0) == 0) { + if (xfs_bitmap_empty(bip->bli_format.blf_data_map, + bip->bli_format.blf_map_size)) { xfs_buf_item_relse(bp); } else if (hold) { bip->bli_flags &= ~XFS_BLI_HOLD; diff --git a/fs/xfs/xfs_clnt.h b/fs/xfs/xfs_clnt.h index 5b7eb81..f89196c 100644 --- a/fs/xfs/xfs_clnt.h +++ b/fs/xfs/xfs_clnt.h @@ -99,5 +99,7 @@ struct xfs_mount_args { */ #define XFSMNT2_COMPAT_IOSIZE 0x00000001 /* don't report large preferred * I/O size in stat(2) */ +#define XFSMNT2_FILESTREAMS 0x00000002 /* enable the filestreams + * allocator */ #endif /* __XFS_CLNT_H__ */ diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h index b338269..fefd011 100644 --- a/fs/xfs/xfs_dinode.h +++ b/fs/xfs/xfs_dinode.h @@ -257,6 +257,7 @@ typedef enum xfs_dinode_fmt #define XFS_DIFLAG_EXTSIZE_BIT 11 /* inode extent size allocator hint */ #define XFS_DIFLAG_EXTSZINHERIT_BIT 12 /* inherit inode extent size */ #define XFS_DIFLAG_NODEFRAG_BIT 13 /* do not reorganize/defragment */ +#define XFS_DIFLAG_FILESTREAM_BIT 14 /* use filestream allocator */ #define XFS_DIFLAG_REALTIME (1 << XFS_DIFLAG_REALTIME_BIT) #define XFS_DIFLAG_PREALLOC (1 << XFS_DIFLAG_PREALLOC_BIT) #define XFS_DIFLAG_NEWRTBM (1 << XFS_DIFLAG_NEWRTBM_BIT) @@ -271,12 +272,13 @@ typedef enum xfs_dinode_fmt #define XFS_DIFLAG_EXTSIZE (1 << XFS_DIFLAG_EXTSIZE_BIT) #define XFS_DIFLAG_EXTSZINHERIT (1 << XFS_DIFLAG_EXTSZINHERIT_BIT) #define XFS_DIFLAG_NODEFRAG (1 << XFS_DIFLAG_NODEFRAG_BIT) +#define XFS_DIFLAG_FILESTREAM (1 << XFS_DIFLAG_FILESTREAM_BIT) #define XFS_DIFLAG_ANY \ (XFS_DIFLAG_REALTIME | XFS_DIFLAG_PREALLOC | XFS_DIFLAG_NEWRTBM | \ XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND | XFS_DIFLAG_SYNC | \ XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | XFS_DIFLAG_RTINHERIT | \ XFS_DIFLAG_PROJINHERIT | XFS_DIFLAG_NOSYMLINKS | XFS_DIFLAG_EXTSIZE | \ - XFS_DIFLAG_EXTSZINHERIT | XFS_DIFLAG_NODEFRAG) + XFS_DIFLAG_EXTSZINHERIT | XFS_DIFLAG_NODEFRAG | XFS_DIFLAG_FILESTREAM) #endif /* __XFS_DINODE_H__ */ diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index 8e8e527..29e0919 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -55,9 +55,9 @@ xfs_dir_mount( XFS_MAX_BLOCKSIZE); mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog); mp->m_dirblkfsbs = 1 << mp->m_sb.sb_dirblklog; - mp->m_dirdatablk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_DATA_FIRSTDB(mp)); - mp->m_dirleafblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_LEAF_FIRSTDB(mp)); - mp->m_dirfreeblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_FREE_FIRSTDB(mp)); + mp->m_dirdatablk = xfs_dir2_db_to_da(mp, XFS_DIR2_DATA_FIRSTDB(mp)); + mp->m_dirleafblk = xfs_dir2_db_to_da(mp, XFS_DIR2_LEAF_FIRSTDB(mp)); + mp->m_dirfreeblk = xfs_dir2_db_to_da(mp, XFS_DIR2_FREE_FIRSTDB(mp)); mp->m_attr_node_ents = (mp->m_sb.sb_blocksize - (uint)sizeof(xfs_da_node_hdr_t)) / (uint)sizeof(xfs_da_node_entry_t); @@ -554,7 +554,7 @@ xfs_dir2_grow_inode( */ if (mapp != &map) kmem_free(mapp, sizeof(*mapp) * count); - *dbp = XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)bno); + *dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno); /* * Update file's size if this is the data space and it grew. */ @@ -706,7 +706,7 @@ xfs_dir2_shrink_inode( dp = args->dp; mp = dp->i_mount; tp = args->trans; - da = XFS_DIR2_DB_TO_DA(mp, db); + da = xfs_dir2_db_to_da(mp, db); /* * Unmap the fsblock(s). */ @@ -742,7 +742,7 @@ xfs_dir2_shrink_inode( /* * If the block isn't the last one in the directory, we're done. */ - if (dp->i_d.di_size > XFS_DIR2_DB_OFF_TO_BYTE(mp, db + 1, 0)) + if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(mp, db + 1, 0)) return 0; bno = da; if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) { diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c index 3accc1d..e4df1aa 100644 --- a/fs/xfs/xfs_dir2_block.c +++ b/fs/xfs/xfs_dir2_block.c @@ -115,13 +115,13 @@ xfs_dir2_block_addname( xfs_da_brelse(tp, bp); return XFS_ERROR(EFSCORRUPTED); } - len = XFS_DIR2_DATA_ENTSIZE(args->namelen); + len = xfs_dir2_data_entsize(args->namelen); /* * Set up pointers to parts of the block. */ bf = block->hdr.bestfree; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * No stale entries? Need space for entry and new leaf. */ @@ -396,7 +396,7 @@ xfs_dir2_block_addname( * Fill in the leaf entry. */ blp[mid].hashval = cpu_to_be32(args->hashval); - blp[mid].address = cpu_to_be32(XFS_DIR2_BYTE_TO_DATAPTR(mp, + blp[mid].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, (char *)dep - (char *)block)); xfs_dir2_block_log_leaf(tp, bp, lfloglow, lfloghigh); /* @@ -411,7 +411,7 @@ xfs_dir2_block_addname( dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, args->namelen); - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)block); /* * Clean up the bestfree array and log the header, tail, and entry. @@ -455,7 +455,7 @@ xfs_dir2_block_getdents( /* * If the block number in the offset is out of range, we're done. */ - if (XFS_DIR2_DATAPTR_TO_DB(mp, uio->uio_offset) > mp->m_dirdatablk) { + if (xfs_dir2_dataptr_to_db(mp, uio->uio_offset) > mp->m_dirdatablk) { *eofp = 1; return 0; } @@ -471,15 +471,15 @@ xfs_dir2_block_getdents( * Extract the byte offset we start at from the seek pointer. * We'll skip entries before this. */ - wantoff = XFS_DIR2_DATAPTR_TO_OFF(mp, uio->uio_offset); + wantoff = xfs_dir2_dataptr_to_off(mp, uio->uio_offset); block = bp->data; xfs_dir2_data_check(dp, bp); /* * Set up values for the loop. */ - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); + btp = xfs_dir2_block_tail_p(mp, block); ptr = (char *)block->u; - endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp); + endptr = (char *)xfs_dir2_block_leaf_p(btp); p.dbp = dbp; p.put = put; p.uio = uio; @@ -502,7 +502,7 @@ xfs_dir2_block_getdents( /* * Bump pointer for the next iteration. */ - ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen); + ptr += xfs_dir2_data_entsize(dep->namelen); /* * The entry is before the desired starting point, skip it. */ @@ -513,7 +513,7 @@ xfs_dir2_block_getdents( */ p.namelen = dep->namelen; - p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + p.cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, ptr - (char *)block); p.ino = be64_to_cpu(dep->inumber); #if XFS_BIG_INUMS @@ -531,7 +531,7 @@ xfs_dir2_block_getdents( */ if (!p.done) { uio->uio_offset = - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, (char *)dep - (char *)block); xfs_da_brelse(tp, bp); return error; @@ -545,7 +545,7 @@ xfs_dir2_block_getdents( *eofp = 1; uio->uio_offset = - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk + 1, 0); + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0); xfs_da_brelse(tp, bp); @@ -569,8 +569,8 @@ xfs_dir2_block_log_leaf( mp = tp->t_mountp; block = bp->data; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); xfs_da_log_buf(tp, bp, (uint)((char *)&blp[first] - (char *)block), (uint)((char *)&blp[last + 1] - (char *)block - 1)); } @@ -589,7 +589,7 @@ xfs_dir2_block_log_tail( mp = tp->t_mountp; block = bp->data; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); + btp = xfs_dir2_block_tail_p(mp, block); xfs_da_log_buf(tp, bp, (uint)((char *)btp - (char *)block), (uint)((char *)(btp + 1) - (char *)block - 1)); } @@ -623,13 +623,13 @@ xfs_dir2_block_lookup( mp = dp->i_mount; block = bp->data; xfs_dir2_data_check(dp, bp); - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Get the offset from the leaf entry, to point to the data. */ dep = (xfs_dir2_data_entry_t *) - ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(blp[ent].address))); + ((char *)block + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address))); /* * Fill in inode number, release the block. */ @@ -675,8 +675,8 @@ xfs_dir2_block_lookup_int( ASSERT(bp != NULL); block = bp->data; xfs_dir2_data_check(dp, bp); - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Loop doing a binary search for our hash value. * Find our entry, ENOENT if it's not there. @@ -713,7 +713,7 @@ xfs_dir2_block_lookup_int( * Get pointer to the entry from the leaf. */ dep = (xfs_dir2_data_entry_t *) - ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, addr)); + ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); /* * Compare, if it's right give back buffer & entry number. */ @@ -768,20 +768,20 @@ xfs_dir2_block_removename( tp = args->trans; mp = dp->i_mount; block = bp->data; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Point to the data entry using the leaf entry. */ dep = (xfs_dir2_data_entry_t *) - ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(blp[ent].address))); + ((char *)block + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address))); /* * Mark the data entry's space free. */ needlog = needscan = 0; xfs_dir2_data_make_free(tp, bp, (xfs_dir2_data_aoff_t)((char *)dep - (char *)block), - XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan); + xfs_dir2_data_entsize(dep->namelen), &needlog, &needscan); /* * Fix up the block tail. */ @@ -843,13 +843,13 @@ xfs_dir2_block_replace( dp = args->dp; mp = dp->i_mount; block = bp->data; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Point to the data entry we need to change. */ dep = (xfs_dir2_data_entry_t *) - ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(blp[ent].address))); + ((char *)block + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address))); ASSERT(be64_to_cpu(dep->inumber) != args->inumber); /* * Change the inode number to the new value. @@ -912,7 +912,7 @@ xfs_dir2_leaf_to_block( mp = dp->i_mount; leaf = lbp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAF1_MAGIC); - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); /* * If there are data blocks other than the first one, take this * opportunity to remove trailing empty data blocks that may have @@ -920,7 +920,7 @@ xfs_dir2_leaf_to_block( * These will show up in the leaf bests table. */ while (dp->i_d.di_size > mp->m_dirblksize) { - bestsp = XFS_DIR2_LEAF_BESTS_P(ltp); + bestsp = xfs_dir2_leaf_bests_p(ltp); if (be16_to_cpu(bestsp[be32_to_cpu(ltp->bestcount) - 1]) == mp->m_dirblksize - (uint)sizeof(block->hdr)) { if ((error = @@ -974,14 +974,14 @@ xfs_dir2_leaf_to_block( /* * Initialize the block tail. */ - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); + btp = xfs_dir2_block_tail_p(mp, block); btp->count = cpu_to_be32(be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale)); btp->stale = 0; xfs_dir2_block_log_tail(tp, dbp); /* * Initialize the block leaf area. We compact out stale entries. */ - lep = XFS_DIR2_BLOCK_LEAF_P(btp); + lep = xfs_dir2_block_leaf_p(btp); for (from = to = 0; from < be16_to_cpu(leaf->hdr.count); from++) { if (be32_to_cpu(leaf->ents[from].address) == XFS_DIR2_NULL_DATAPTR) continue; @@ -1067,7 +1067,7 @@ xfs_dir2_sf_to_block( ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * Copy the directory into the stack buffer. * Then pitch the incore inode data so we can make extents. @@ -1119,10 +1119,10 @@ xfs_dir2_sf_to_block( /* * Fill in the tail. */ - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); + btp = xfs_dir2_block_tail_p(mp, block); btp->count = cpu_to_be32(sfp->hdr.count + 2); /* ., .. */ btp->stale = 0; - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + blp = xfs_dir2_block_leaf_p(btp); endoffset = (uint)((char *)blp - (char *)block); /* * Remove the freespace, we'll manage it. @@ -1138,25 +1138,25 @@ xfs_dir2_sf_to_block( dep->inumber = cpu_to_be64(dp->i_ino); dep->namelen = 1; dep->name[0] = '.'; - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)block); xfs_dir2_data_log_entry(tp, bp, dep); blp[0].hashval = cpu_to_be32(xfs_dir_hash_dot); - blp[0].address = cpu_to_be32(XFS_DIR2_BYTE_TO_DATAPTR(mp, + blp[0].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, (char *)dep - (char *)block)); /* * Create entry for .. */ dep = (xfs_dir2_data_entry_t *) ((char *)block + XFS_DIR2_DATA_DOTDOT_OFFSET); - dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); + dep->inumber = cpu_to_be64(xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent)); dep->namelen = 2; dep->name[0] = dep->name[1] = '.'; - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)block); xfs_dir2_data_log_entry(tp, bp, dep); blp[1].hashval = cpu_to_be32(xfs_dir_hash_dotdot); - blp[1].address = cpu_to_be32(XFS_DIR2_BYTE_TO_DATAPTR(mp, + blp[1].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, (char *)dep - (char *)block)); offset = XFS_DIR2_DATA_FIRST_OFFSET; /* @@ -1165,7 +1165,7 @@ xfs_dir2_sf_to_block( if ((i = 0) == sfp->hdr.count) sfep = NULL; else - sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + sfep = xfs_dir2_sf_firstentry(sfp); /* * Need to preserve the existing offset values in the sf directory. * Insert holes (unused entries) where necessary. @@ -1177,7 +1177,7 @@ xfs_dir2_sf_to_block( if (sfep == NULL) newoffset = endoffset; else - newoffset = XFS_DIR2_SF_GET_OFFSET(sfep); + newoffset = xfs_dir2_sf_get_offset(sfep); /* * There should be a hole here, make one. */ @@ -1186,7 +1186,7 @@ xfs_dir2_sf_to_block( ((char *)block + offset); dup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG); dup->length = cpu_to_be16(newoffset - offset); - *XFS_DIR2_DATA_UNUSED_TAG_P(dup) = cpu_to_be16( + *xfs_dir2_data_unused_tag_p(dup) = cpu_to_be16( ((char *)dup - (char *)block)); xfs_dir2_data_log_unused(tp, bp, dup); (void)xfs_dir2_data_freeinsert((xfs_dir2_data_t *)block, @@ -1198,22 +1198,22 @@ xfs_dir2_sf_to_block( * Copy a real entry. */ dep = (xfs_dir2_data_entry_t *)((char *)block + newoffset); - dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp, - XFS_DIR2_SF_INUMBERP(sfep))); + dep->inumber = cpu_to_be64(xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep))); dep->namelen = sfep->namelen; memcpy(dep->name, sfep->name, dep->namelen); - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)block); xfs_dir2_data_log_entry(tp, bp, dep); blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname( (char *)sfep->name, sfep->namelen)); - blp[2 + i].address = cpu_to_be32(XFS_DIR2_BYTE_TO_DATAPTR(mp, + blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp, (char *)dep - (char *)block)); offset = (int)((char *)(tagp + 1) - (char *)block); if (++i == sfp->hdr.count) sfep = NULL; else - sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); + sfep = xfs_dir2_sf_nextentry(sfp, sfep); } /* Done with the temporary buffer */ kmem_free(buf, buf_len); diff --git a/fs/xfs/xfs_dir2_block.h b/fs/xfs/xfs_dir2_block.h index 6722eff..e7c2606 100644 --- a/fs/xfs/xfs_dir2_block.h +++ b/fs/xfs/xfs_dir2_block.h @@ -60,7 +60,6 @@ typedef struct xfs_dir2_block { /* * Pointer to the leaf header embedded in a data block (1-block format) */ -#define XFS_DIR2_BLOCK_TAIL_P(mp,block) xfs_dir2_block_tail_p(mp,block) static inline xfs_dir2_block_tail_t * xfs_dir2_block_tail_p(struct xfs_mount *mp, xfs_dir2_block_t *block) { @@ -71,7 +70,6 @@ xfs_dir2_block_tail_p(struct xfs_mount *mp, xfs_dir2_block_t *block) /* * Pointer to the leaf entries embedded in a data block (1-block format) */ -#define XFS_DIR2_BLOCK_LEAF_P(btp) xfs_dir2_block_leaf_p(btp) static inline struct xfs_dir2_leaf_entry * xfs_dir2_block_leaf_p(xfs_dir2_block_tail_t *btp) { diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index c211c37..7ebe295 100644 --- a/fs/xfs/xfs_dir2_data.c +++ b/fs/xfs/xfs_dir2_data.c @@ -72,8 +72,8 @@ xfs_dir2_data_check( bf = d->hdr.bestfree; p = (char *)d->u; if (be32_to_cpu(d->hdr.magic) == XFS_DIR2_BLOCK_MAGIC) { - btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d); - lep = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, (xfs_dir2_block_t *)d); + lep = xfs_dir2_block_leaf_p(btp); endp = (char *)lep; } else endp = (char *)d + mp->m_dirblksize; @@ -107,7 +107,7 @@ xfs_dir2_data_check( */ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { ASSERT(lastfree == 0); - ASSERT(be16_to_cpu(*XFS_DIR2_DATA_UNUSED_TAG_P(dup)) == + ASSERT(be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) == (char *)dup - (char *)d); dfp = xfs_dir2_data_freefind(d, dup); if (dfp) { @@ -131,12 +131,12 @@ xfs_dir2_data_check( dep = (xfs_dir2_data_entry_t *)p; ASSERT(dep->namelen != 0); ASSERT(xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber)) == 0); - ASSERT(be16_to_cpu(*XFS_DIR2_DATA_ENTRY_TAG_P(dep)) == + ASSERT(be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep)) == (char *)dep - (char *)d); count++; lastfree = 0; if (be32_to_cpu(d->hdr.magic) == XFS_DIR2_BLOCK_MAGIC) { - addr = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, (xfs_dir2_data_aoff_t) ((char *)dep - (char *)d)); hash = xfs_da_hashname((char *)dep->name, dep->namelen); @@ -147,7 +147,7 @@ xfs_dir2_data_check( } ASSERT(i < be32_to_cpu(btp->count)); } - p += XFS_DIR2_DATA_ENTSIZE(dep->namelen); + p += xfs_dir2_data_entsize(dep->namelen); } /* * Need to have seen all the entries and all the bestfree slots. @@ -346,8 +346,8 @@ xfs_dir2_data_freescan( */ p = (char *)d->u; if (be32_to_cpu(d->hdr.magic) == XFS_DIR2_BLOCK_MAGIC) { - btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d); - endp = (char *)XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, (xfs_dir2_block_t *)d); + endp = (char *)xfs_dir2_block_leaf_p(btp); } else endp = (char *)d + mp->m_dirblksize; /* @@ -360,7 +360,7 @@ xfs_dir2_data_freescan( */ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { ASSERT((char *)dup - (char *)d == - be16_to_cpu(*XFS_DIR2_DATA_UNUSED_TAG_P(dup))); + be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup))); xfs_dir2_data_freeinsert(d, dup, loghead); p += be16_to_cpu(dup->length); } @@ -370,8 +370,8 @@ xfs_dir2_data_freescan( else { dep = (xfs_dir2_data_entry_t *)p; ASSERT((char *)dep - (char *)d == - be16_to_cpu(*XFS_DIR2_DATA_ENTRY_TAG_P(dep))); - p += XFS_DIR2_DATA_ENTSIZE(dep->namelen); + be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep))); + p += xfs_dir2_data_entsize(dep->namelen); } } } @@ -402,7 +402,7 @@ xfs_dir2_data_init( /* * Get the buffer set up for the block. */ - error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, blkno), -1, &bp, + error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, blkno), -1, &bp, XFS_DATA_FORK); if (error) { return error; @@ -427,7 +427,7 @@ xfs_dir2_data_init( t=mp->m_dirblksize - (uint)sizeof(d->hdr); d->hdr.bestfree[0].length = cpu_to_be16(t); dup->length = cpu_to_be16(t); - *XFS_DIR2_DATA_UNUSED_TAG_P(dup) = cpu_to_be16((char *)dup - (char *)d); + *xfs_dir2_data_unused_tag_p(dup) = cpu_to_be16((char *)dup - (char *)d); /* * Log it and return it. */ @@ -452,7 +452,7 @@ xfs_dir2_data_log_entry( ASSERT(be32_to_cpu(d->hdr.magic) == XFS_DIR2_DATA_MAGIC || be32_to_cpu(d->hdr.magic) == XFS_DIR2_BLOCK_MAGIC); xfs_da_log_buf(tp, bp, (uint)((char *)dep - (char *)d), - (uint)((char *)(XFS_DIR2_DATA_ENTRY_TAG_P(dep) + 1) - + (uint)((char *)(xfs_dir2_data_entry_tag_p(dep) + 1) - (char *)d - 1)); } @@ -497,8 +497,8 @@ xfs_dir2_data_log_unused( * Log the end (tag) of the unused entry. */ xfs_da_log_buf(tp, bp, - (uint)((char *)XFS_DIR2_DATA_UNUSED_TAG_P(dup) - (char *)d), - (uint)((char *)XFS_DIR2_DATA_UNUSED_TAG_P(dup) - (char *)d + + (uint)((char *)xfs_dir2_data_unused_tag_p(dup) - (char *)d), + (uint)((char *)xfs_dir2_data_unused_tag_p(dup) - (char *)d + sizeof(xfs_dir2_data_off_t) - 1)); } @@ -535,8 +535,8 @@ xfs_dir2_data_make_free( xfs_dir2_block_tail_t *btp; /* block tail */ ASSERT(be32_to_cpu(d->hdr.magic) == XFS_DIR2_BLOCK_MAGIC); - btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d); - endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, (xfs_dir2_block_t *)d); + endptr = (char *)xfs_dir2_block_leaf_p(btp); } /* * If this isn't the start of the block, then back up to @@ -587,7 +587,7 @@ xfs_dir2_data_make_free( * Fix up the new big freespace. */ be16_add(&prevdup->length, len + be16_to_cpu(postdup->length)); - *XFS_DIR2_DATA_UNUSED_TAG_P(prevdup) = + *xfs_dir2_data_unused_tag_p(prevdup) = cpu_to_be16((char *)prevdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, prevdup); if (!needscan) { @@ -621,7 +621,7 @@ xfs_dir2_data_make_free( else if (prevdup) { dfp = xfs_dir2_data_freefind(d, prevdup); be16_add(&prevdup->length, len); - *XFS_DIR2_DATA_UNUSED_TAG_P(prevdup) = + *xfs_dir2_data_unused_tag_p(prevdup) = cpu_to_be16((char *)prevdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, prevdup); /* @@ -649,7 +649,7 @@ xfs_dir2_data_make_free( newdup = (xfs_dir2_data_unused_t *)((char *)d + offset); newdup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG); newdup->length = cpu_to_be16(len + be16_to_cpu(postdup->length)); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup) = + *xfs_dir2_data_unused_tag_p(newdup) = cpu_to_be16((char *)newdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup); /* @@ -676,7 +676,7 @@ xfs_dir2_data_make_free( newdup = (xfs_dir2_data_unused_t *)((char *)d + offset); newdup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG); newdup->length = cpu_to_be16(len); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup) = + *xfs_dir2_data_unused_tag_p(newdup) = cpu_to_be16((char *)newdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup); (void)xfs_dir2_data_freeinsert(d, newdup, needlogp); @@ -712,7 +712,7 @@ xfs_dir2_data_use_free( ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG); ASSERT(offset >= (char *)dup - (char *)d); ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)d); - ASSERT((char *)dup - (char *)d == be16_to_cpu(*XFS_DIR2_DATA_UNUSED_TAG_P(dup))); + ASSERT((char *)dup - (char *)d == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup))); /* * Look up the entry in the bestfree table. */ @@ -745,7 +745,7 @@ xfs_dir2_data_use_free( newdup = (xfs_dir2_data_unused_t *)((char *)d + offset + len); newdup->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG); newdup->length = cpu_to_be16(oldlen - len); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup) = + *xfs_dir2_data_unused_tag_p(newdup) = cpu_to_be16((char *)newdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup); /* @@ -772,7 +772,7 @@ xfs_dir2_data_use_free( else if (matchback) { newdup = dup; newdup->length = cpu_to_be16(((char *)d + offset) - (char *)newdup); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup) = + *xfs_dir2_data_unused_tag_p(newdup) = cpu_to_be16((char *)newdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup); /* @@ -799,13 +799,13 @@ xfs_dir2_data_use_free( else { newdup = dup; newdup->length = cpu_to_be16(((char *)d + offset) - (char *)newdup); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup) = + *xfs_dir2_data_unused_tag_p(newdup) = cpu_to_be16((char *)newdup - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup); newdup2 = (xfs_dir2_data_unused_t *)((char *)d + offset + len); newdup2->freetag = cpu_to_be16(XFS_DIR2_DATA_FREE_TAG); newdup2->length = cpu_to_be16(oldlen - len - be16_to_cpu(newdup->length)); - *XFS_DIR2_DATA_UNUSED_TAG_P(newdup2) = + *xfs_dir2_data_unused_tag_p(newdup2) = cpu_to_be16((char *)newdup2 - (char *)d); xfs_dir2_data_log_unused(tp, bp, newdup2); /* diff --git a/fs/xfs/xfs_dir2_data.h b/fs/xfs/xfs_dir2_data.h index c94c909..b816e02 100644 --- a/fs/xfs/xfs_dir2_data.h +++ b/fs/xfs/xfs_dir2_data.h @@ -44,7 +44,7 @@ struct xfs_trans; #define XFS_DIR2_DATA_SPACE 0 #define XFS_DIR2_DATA_OFFSET (XFS_DIR2_DATA_SPACE * XFS_DIR2_SPACE_SIZE) #define XFS_DIR2_DATA_FIRSTDB(mp) \ - XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_DATA_OFFSET) + xfs_dir2_byte_to_db(mp, XFS_DIR2_DATA_OFFSET) /* * Offsets of . and .. in data space (always block 0) @@ -52,9 +52,9 @@ struct xfs_trans; #define XFS_DIR2_DATA_DOT_OFFSET \ ((xfs_dir2_data_aoff_t)sizeof(xfs_dir2_data_hdr_t)) #define XFS_DIR2_DATA_DOTDOT_OFFSET \ - (XFS_DIR2_DATA_DOT_OFFSET + XFS_DIR2_DATA_ENTSIZE(1)) + (XFS_DIR2_DATA_DOT_OFFSET + xfs_dir2_data_entsize(1)) #define XFS_DIR2_DATA_FIRST_OFFSET \ - (XFS_DIR2_DATA_DOTDOT_OFFSET + XFS_DIR2_DATA_ENTSIZE(2)) + (XFS_DIR2_DATA_DOTDOT_OFFSET + xfs_dir2_data_entsize(2)) /* * Structures. @@ -123,7 +123,6 @@ typedef struct xfs_dir2_data { /* * Size of a data entry. */ -#define XFS_DIR2_DATA_ENTSIZE(n) xfs_dir2_data_entsize(n) static inline int xfs_dir2_data_entsize(int n) { return (int)roundup(offsetof(xfs_dir2_data_entry_t, name[0]) + (n) + \ @@ -133,19 +132,16 @@ static inline int xfs_dir2_data_entsize(int n) /* * Pointer to an entry's tag word. */ -#define XFS_DIR2_DATA_ENTRY_TAG_P(dep) xfs_dir2_data_entry_tag_p(dep) static inline __be16 * xfs_dir2_data_entry_tag_p(xfs_dir2_data_entry_t *dep) { return (__be16 *)((char *)dep + - XFS_DIR2_DATA_ENTSIZE(dep->namelen) - sizeof(__be16)); + xfs_dir2_data_entsize(dep->namelen) - sizeof(__be16)); } /* * Pointer to a freespace's tag word. */ -#define XFS_DIR2_DATA_UNUSED_TAG_P(dup) \ - xfs_dir2_data_unused_tag_p(dup) static inline __be16 * xfs_dir2_data_unused_tag_p(xfs_dir2_data_unused_t *dup) { diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index db14ea7..1b73c9a 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -92,7 +92,7 @@ xfs_dir2_block_to_leaf( if ((error = xfs_da_grow_inode(args, &blkno))) { return error; } - ldb = XFS_DIR2_DA_TO_DB(mp, blkno); + ldb = xfs_dir2_da_to_db(mp, blkno); ASSERT(ldb == XFS_DIR2_LEAF_FIRSTDB(mp)); /* * Initialize the leaf block, get a buffer for it. @@ -104,8 +104,8 @@ xfs_dir2_block_to_leaf( leaf = lbp->data; block = dbp->data; xfs_dir2_data_check(dp, dbp); - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Set the counts in the leaf header. */ @@ -137,9 +137,9 @@ xfs_dir2_block_to_leaf( /* * Set up leaf tail and bests table. */ - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); ltp->bestcount = cpu_to_be32(1); - bestsp = XFS_DIR2_LEAF_BESTS_P(ltp); + bestsp = xfs_dir2_leaf_bests_p(ltp); bestsp[0] = block->hdr.bestfree[0].length; /* * Log the data header and leaf bests table. @@ -209,9 +209,9 @@ xfs_dir2_leaf_addname( */ index = xfs_dir2_leaf_search_hash(args, lbp); leaf = lbp->data; - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); - bestsp = XFS_DIR2_LEAF_BESTS_P(ltp); - length = XFS_DIR2_DATA_ENTSIZE(args->namelen); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); + bestsp = xfs_dir2_leaf_bests_p(ltp); + length = xfs_dir2_data_entsize(args->namelen); /* * See if there are any entries with the same hash value * and space in their block for the new entry. @@ -223,7 +223,7 @@ xfs_dir2_leaf_addname( index++, lep++) { if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR) continue; - i = XFS_DIR2_DATAPTR_TO_DB(mp, be32_to_cpu(lep->address)); + i = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); ASSERT(i < be32_to_cpu(ltp->bestcount)); ASSERT(be16_to_cpu(bestsp[i]) != NULLDATAOFF); if (be16_to_cpu(bestsp[i]) >= length) { @@ -378,7 +378,7 @@ xfs_dir2_leaf_addname( */ else { if ((error = - xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, use_block), + xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, use_block), -1, &dbp, XFS_DATA_FORK))) { xfs_da_brelse(tp, lbp); return error; @@ -407,7 +407,7 @@ xfs_dir2_leaf_addname( dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, dep->namelen); - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)data); /* * Need to scan fix up the bestfree table. @@ -529,7 +529,7 @@ xfs_dir2_leaf_addname( * Fill in the new leaf entry. */ lep->hashval = cpu_to_be32(args->hashval); - lep->address = cpu_to_be32(XFS_DIR2_DB_OFF_TO_DATAPTR(mp, use_block, + lep->address = cpu_to_be32(xfs_dir2_db_off_to_dataptr(mp, use_block, be16_to_cpu(*tagp))); /* * Log the leaf fields and give up the buffers. @@ -567,13 +567,13 @@ xfs_dir2_leaf_check( * Should factor in the size of the bests table as well. * We can deduce a value for that from di_size. */ - ASSERT(be16_to_cpu(leaf->hdr.count) <= XFS_DIR2_MAX_LEAF_ENTS(mp)); - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ASSERT(be16_to_cpu(leaf->hdr.count) <= xfs_dir2_max_leaf_ents(mp)); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); /* * Leaves and bests don't overlap. */ ASSERT((char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] <= - (char *)XFS_DIR2_LEAF_BESTS_P(ltp)); + (char *)xfs_dir2_leaf_bests_p(ltp)); /* * Check hash value order, count stale entries. */ @@ -815,12 +815,12 @@ xfs_dir2_leaf_getdents( * Inside the loop we keep the main offset value as a byte offset * in the directory file. */ - curoff = XFS_DIR2_DATAPTR_TO_BYTE(mp, uio->uio_offset); + curoff = xfs_dir2_dataptr_to_byte(mp, uio->uio_offset); /* * Force this conversion through db so we truncate the offset * down to get the start of the data block. */ - map_off = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_BYTE_TO_DB(mp, curoff)); + map_off = xfs_dir2_db_to_da(mp, xfs_dir2_byte_to_db(mp, curoff)); /* * Loop over directory entries until we reach the end offset. * Get more blocks and readahead as necessary. @@ -870,7 +870,7 @@ xfs_dir2_leaf_getdents( */ if (1 + ra_want > map_blocks && map_off < - XFS_DIR2_BYTE_TO_DA(mp, XFS_DIR2_LEAF_OFFSET)) { + xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET)) { /* * Get more bmaps, fill in after the ones * we already have in the table. @@ -878,7 +878,7 @@ xfs_dir2_leaf_getdents( nmap = map_size - map_valid; error = xfs_bmapi(tp, dp, map_off, - XFS_DIR2_BYTE_TO_DA(mp, + xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET) - map_off, XFS_BMAPI_METADATA, NULL, 0, &map[map_valid], &nmap, NULL, NULL); @@ -903,7 +903,7 @@ xfs_dir2_leaf_getdents( map[map_valid + nmap - 1].br_blockcount; else map_off = - XFS_DIR2_BYTE_TO_DA(mp, + xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET); /* * Look for holes in the mapping, and @@ -931,14 +931,14 @@ xfs_dir2_leaf_getdents( * No valid mappings, so no more data blocks. */ if (!map_valid) { - curoff = XFS_DIR2_DA_TO_BYTE(mp, map_off); + curoff = xfs_dir2_da_to_byte(mp, map_off); break; } /* * Read the directory block starting at the first * mapping. */ - curdb = XFS_DIR2_DA_TO_DB(mp, map->br_startoff); + curdb = xfs_dir2_da_to_db(mp, map->br_startoff); error = xfs_da_read_buf(tp, dp, map->br_startoff, map->br_blockcount >= mp->m_dirblkfsbs ? XFS_FSB_TO_DADDR(mp, map->br_startblock) : @@ -1014,7 +1014,7 @@ xfs_dir2_leaf_getdents( /* * Having done a read, we need to set a new offset. */ - newoff = XFS_DIR2_DB_OFF_TO_BYTE(mp, curdb, 0); + newoff = xfs_dir2_db_off_to_byte(mp, curdb, 0); /* * Start of the current block. */ @@ -1024,7 +1024,7 @@ xfs_dir2_leaf_getdents( * Make sure we're in the right block. */ else if (curoff > newoff) - ASSERT(XFS_DIR2_BYTE_TO_DB(mp, curoff) == + ASSERT(xfs_dir2_byte_to_db(mp, curoff) == curdb); data = bp->data; xfs_dir2_data_check(dp, bp); @@ -1032,7 +1032,7 @@ xfs_dir2_leaf_getdents( * Find our position in the block. */ ptr = (char *)&data->u; - byteoff = XFS_DIR2_BYTE_TO_OFF(mp, curoff); + byteoff = xfs_dir2_byte_to_off(mp, curoff); /* * Skip past the header. */ @@ -1054,15 +1054,15 @@ xfs_dir2_leaf_getdents( } dep = (xfs_dir2_data_entry_t *)ptr; length = - XFS_DIR2_DATA_ENTSIZE(dep->namelen); + xfs_dir2_data_entsize(dep->namelen); ptr += length; } /* * Now set our real offset. */ curoff = - XFS_DIR2_DB_OFF_TO_BYTE(mp, - XFS_DIR2_BYTE_TO_DB(mp, curoff), + xfs_dir2_db_off_to_byte(mp, + xfs_dir2_byte_to_db(mp, curoff), (char *)ptr - (char *)data); if (ptr >= (char *)data + mp->m_dirblksize) { continue; @@ -1091,9 +1091,9 @@ xfs_dir2_leaf_getdents( p->namelen = dep->namelen; - length = XFS_DIR2_DATA_ENTSIZE(p->namelen); + length = xfs_dir2_data_entsize(p->namelen); - p->cook = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff + length); + p->cook = xfs_dir2_byte_to_dataptr(mp, curoff + length); p->ino = be64_to_cpu(dep->inumber); #if XFS_BIG_INUMS @@ -1121,10 +1121,10 @@ xfs_dir2_leaf_getdents( * All done. Set output offset value to current offset. */ *eofp = eof; - if (curoff > XFS_DIR2_DATAPTR_TO_BYTE(mp, XFS_DIR2_MAX_DATAPTR)) + if (curoff > xfs_dir2_dataptr_to_byte(mp, XFS_DIR2_MAX_DATAPTR)) uio->uio_offset = XFS_DIR2_MAX_DATAPTR; else - uio->uio_offset = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff); + uio->uio_offset = xfs_dir2_byte_to_dataptr(mp, curoff); kmem_free(map, map_size * sizeof(*map)); kmem_free(p, sizeof(*p)); if (bp) @@ -1159,7 +1159,7 @@ xfs_dir2_leaf_init( /* * Get the buffer for the block. */ - error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, bno), -1, &bp, + error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, bno), -1, &bp, XFS_DATA_FORK); if (error) { return error; @@ -1181,7 +1181,7 @@ xfs_dir2_leaf_init( * the block. */ if (magic == XFS_DIR2_LEAF1_MAGIC) { - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); ltp->bestcount = 0; xfs_dir2_leaf_log_tail(tp, bp); } @@ -1206,9 +1206,9 @@ xfs_dir2_leaf_log_bests( leaf = bp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAF1_MAGIC); - ltp = XFS_DIR2_LEAF_TAIL_P(tp->t_mountp, leaf); - firstb = XFS_DIR2_LEAF_BESTS_P(ltp) + first; - lastb = XFS_DIR2_LEAF_BESTS_P(ltp) + last; + ltp = xfs_dir2_leaf_tail_p(tp->t_mountp, leaf); + firstb = xfs_dir2_leaf_bests_p(ltp) + first; + lastb = xfs_dir2_leaf_bests_p(ltp) + last; xfs_da_log_buf(tp, bp, (uint)((char *)firstb - (char *)leaf), (uint)((char *)lastb - (char *)leaf + sizeof(*lastb) - 1)); } @@ -1268,7 +1268,7 @@ xfs_dir2_leaf_log_tail( mp = tp->t_mountp; leaf = bp->data; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAF1_MAGIC); - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); xfs_da_log_buf(tp, bp, (uint)((char *)ltp - (char *)leaf), (uint)(mp->m_dirblksize - 1)); } @@ -1312,7 +1312,7 @@ xfs_dir2_leaf_lookup( */ dep = (xfs_dir2_data_entry_t *) ((char *)dbp->data + - XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, be32_to_cpu(lep->address))); + xfs_dir2_dataptr_to_off(dp->i_mount, be32_to_cpu(lep->address))); /* * Return the found inode number. */ @@ -1381,7 +1381,7 @@ xfs_dir2_leaf_lookup_int( /* * Get the new data block number. */ - newdb = XFS_DIR2_DATAPTR_TO_DB(mp, be32_to_cpu(lep->address)); + newdb = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); /* * If it's not the same as the old data block number, * need to pitch the old one and read the new one. @@ -1391,7 +1391,7 @@ xfs_dir2_leaf_lookup_int( xfs_da_brelse(tp, dbp); if ((error = xfs_da_read_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, newdb), -1, &dbp, + xfs_dir2_db_to_da(mp, newdb), -1, &dbp, XFS_DATA_FORK))) { xfs_da_brelse(tp, lbp); return error; @@ -1404,7 +1404,7 @@ xfs_dir2_leaf_lookup_int( */ dep = (xfs_dir2_data_entry_t *) ((char *)dbp->data + - XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(lep->address))); + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); /* * If it matches then return it. */ @@ -1469,20 +1469,20 @@ xfs_dir2_leaf_removename( * Point to the leaf entry, use that to point to the data entry. */ lep = &leaf->ents[index]; - db = XFS_DIR2_DATAPTR_TO_DB(mp, be32_to_cpu(lep->address)); + db = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); dep = (xfs_dir2_data_entry_t *) - ((char *)data + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(lep->address))); + ((char *)data + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); needscan = needlog = 0; oldbest = be16_to_cpu(data->hdr.bestfree[0].length); - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); - bestsp = XFS_DIR2_LEAF_BESTS_P(ltp); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); + bestsp = xfs_dir2_leaf_bests_p(ltp); ASSERT(be16_to_cpu(bestsp[db]) == oldbest); /* * Mark the former data entry unused. */ xfs_dir2_data_make_free(tp, dbp, (xfs_dir2_data_aoff_t)((char *)dep - (char *)data), - XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan); + xfs_dir2_data_entsize(dep->namelen), &needlog, &needscan); /* * We just mark the leaf entry stale by putting a null in it. */ @@ -1602,7 +1602,7 @@ xfs_dir2_leaf_replace( */ dep = (xfs_dir2_data_entry_t *) ((char *)dbp->data + - XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, be32_to_cpu(lep->address))); + xfs_dir2_dataptr_to_off(dp->i_mount, be32_to_cpu(lep->address))); ASSERT(args->inumber != be64_to_cpu(dep->inumber)); /* * Put the new inode number in, log it. @@ -1698,7 +1698,7 @@ xfs_dir2_leaf_trim_data( /* * Read the offending data block. We need its buffer. */ - if ((error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, db), -1, &dbp, + if ((error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, db), -1, &dbp, XFS_DATA_FORK))) { return error; } @@ -1712,7 +1712,7 @@ xfs_dir2_leaf_trim_data( */ leaf = lbp->data; - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); ASSERT(be16_to_cpu(data->hdr.bestfree[0].length) == mp->m_dirblksize - (uint)sizeof(data->hdr)); ASSERT(db == be32_to_cpu(ltp->bestcount) - 1); @@ -1727,7 +1727,7 @@ xfs_dir2_leaf_trim_data( /* * Eliminate the last bests entry from the table. */ - bestsp = XFS_DIR2_LEAF_BESTS_P(ltp); + bestsp = xfs_dir2_leaf_bests_p(ltp); be32_add(<p->bestcount, -1); memmove(&bestsp[1], &bestsp[0], be32_to_cpu(ltp->bestcount) * sizeof(*bestsp)); xfs_dir2_leaf_log_tail(tp, lbp); @@ -1838,12 +1838,12 @@ xfs_dir2_node_to_leaf( /* * Set up the leaf tail from the freespace block. */ - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); ltp->bestcount = free->hdr.nvalid; /* * Set up the leaf bests table. */ - memcpy(XFS_DIR2_LEAF_BESTS_P(ltp), free->bests, + memcpy(xfs_dir2_leaf_bests_p(ltp), free->bests, be32_to_cpu(ltp->bestcount) * sizeof(leaf->bests[0])); xfs_dir2_leaf_log_bests(tp, lbp, 0, be32_to_cpu(ltp->bestcount) - 1); xfs_dir2_leaf_log_tail(tp, lbp); diff --git a/fs/xfs/xfs_dir2_leaf.h b/fs/xfs/xfs_dir2_leaf.h index f57ca116..70c97f3 100644 --- a/fs/xfs/xfs_dir2_leaf.h +++ b/fs/xfs/xfs_dir2_leaf.h @@ -32,7 +32,7 @@ struct xfs_trans; #define XFS_DIR2_LEAF_SPACE 1 #define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE) #define XFS_DIR2_LEAF_FIRSTDB(mp) \ - XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_LEAF_OFFSET) + xfs_dir2_byte_to_db(mp, XFS_DIR2_LEAF_OFFSET) /* * Offset in data space of a data entry. @@ -82,7 +82,6 @@ typedef struct xfs_dir2_leaf { * DB blocks here are logical directory block numbers, not filesystem blocks. */ -#define XFS_DIR2_MAX_LEAF_ENTS(mp) xfs_dir2_max_leaf_ents(mp) static inline int xfs_dir2_max_leaf_ents(struct xfs_mount *mp) { return (int)(((mp)->m_dirblksize - (uint)sizeof(xfs_dir2_leaf_hdr_t)) / @@ -92,7 +91,6 @@ static inline int xfs_dir2_max_leaf_ents(struct xfs_mount *mp) /* * Get address of the bestcount field in the single-leaf block. */ -#define XFS_DIR2_LEAF_TAIL_P(mp,lp) xfs_dir2_leaf_tail_p(mp, lp) static inline xfs_dir2_leaf_tail_t * xfs_dir2_leaf_tail_p(struct xfs_mount *mp, xfs_dir2_leaf_t *lp) { @@ -104,7 +102,6 @@ xfs_dir2_leaf_tail_p(struct xfs_mount *mp, xfs_dir2_leaf_t *lp) /* * Get address of the bests array in the single-leaf block. */ -#define XFS_DIR2_LEAF_BESTS_P(ltp) xfs_dir2_leaf_bests_p(ltp) static inline __be16 * xfs_dir2_leaf_bests_p(xfs_dir2_leaf_tail_t *ltp) { @@ -114,7 +111,6 @@ xfs_dir2_leaf_bests_p(xfs_dir2_leaf_tail_t *ltp) /* * Convert dataptr to byte in file space */ -#define XFS_DIR2_DATAPTR_TO_BYTE(mp,dp) xfs_dir2_dataptr_to_byte(mp, dp) static inline xfs_dir2_off_t xfs_dir2_dataptr_to_byte(struct xfs_mount *mp, xfs_dir2_dataptr_t dp) { @@ -124,7 +120,6 @@ xfs_dir2_dataptr_to_byte(struct xfs_mount *mp, xfs_dir2_dataptr_t dp) /* * Convert byte in file space to dataptr. It had better be aligned. */ -#define XFS_DIR2_BYTE_TO_DATAPTR(mp,by) xfs_dir2_byte_to_dataptr(mp,by) static inline xfs_dir2_dataptr_t xfs_dir2_byte_to_dataptr(struct xfs_mount *mp, xfs_dir2_off_t by) { @@ -134,7 +129,6 @@ xfs_dir2_byte_to_dataptr(struct xfs_mount *mp, xfs_dir2_off_t by) /* * Convert byte in space to (DB) block */ -#define XFS_DIR2_BYTE_TO_DB(mp,by) xfs_dir2_byte_to_db(mp, by) static inline xfs_dir2_db_t xfs_dir2_byte_to_db(struct xfs_mount *mp, xfs_dir2_off_t by) { @@ -145,17 +139,15 @@ xfs_dir2_byte_to_db(struct xfs_mount *mp, xfs_dir2_off_t by) /* * Convert dataptr to a block number */ -#define XFS_DIR2_DATAPTR_TO_DB(mp,dp) xfs_dir2_dataptr_to_db(mp, dp) static inline xfs_dir2_db_t xfs_dir2_dataptr_to_db(struct xfs_mount *mp, xfs_dir2_dataptr_t dp) { - return XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_DATAPTR_TO_BYTE(mp, dp)); + return xfs_dir2_byte_to_db(mp, xfs_dir2_dataptr_to_byte(mp, dp)); } /* * Convert byte in space to offset in a block */ -#define XFS_DIR2_BYTE_TO_OFF(mp,by) xfs_dir2_byte_to_off(mp, by) static inline xfs_dir2_data_aoff_t xfs_dir2_byte_to_off(struct xfs_mount *mp, xfs_dir2_off_t by) { @@ -166,18 +158,15 @@ xfs_dir2_byte_to_off(struct xfs_mount *mp, xfs_dir2_off_t by) /* * Convert dataptr to a byte offset in a block */ -#define XFS_DIR2_DATAPTR_TO_OFF(mp,dp) xfs_dir2_dataptr_to_off(mp, dp) static inline xfs_dir2_data_aoff_t xfs_dir2_dataptr_to_off(struct xfs_mount *mp, xfs_dir2_dataptr_t dp) { - return XFS_DIR2_BYTE_TO_OFF(mp, XFS_DIR2_DATAPTR_TO_BYTE(mp, dp)); + return xfs_dir2_byte_to_off(mp, xfs_dir2_dataptr_to_byte(mp, dp)); } /* * Convert block and offset to byte in space */ -#define XFS_DIR2_DB_OFF_TO_BYTE(mp,db,o) \ - xfs_dir2_db_off_to_byte(mp, db, o) static inline xfs_dir2_off_t xfs_dir2_db_off_to_byte(struct xfs_mount *mp, xfs_dir2_db_t db, xfs_dir2_data_aoff_t o) @@ -189,7 +178,6 @@ xfs_dir2_db_off_to_byte(struct xfs_mount *mp, xfs_dir2_db_t db, /* * Convert block (DB) to block (dablk) */ -#define XFS_DIR2_DB_TO_DA(mp,db) xfs_dir2_db_to_da(mp, db) static inline xfs_dablk_t xfs_dir2_db_to_da(struct xfs_mount *mp, xfs_dir2_db_t db) { @@ -199,29 +187,25 @@ xfs_dir2_db_to_da(struct xfs_mount *mp, xfs_dir2_db_t db) /* * Convert byte in space to (DA) block */ -#define XFS_DIR2_BYTE_TO_DA(mp,by) xfs_dir2_byte_to_da(mp, by) static inline xfs_dablk_t xfs_dir2_byte_to_da(struct xfs_mount *mp, xfs_dir2_off_t by) { - return XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_BYTE_TO_DB(mp, by)); + return xfs_dir2_db_to_da(mp, xfs_dir2_byte_to_db(mp, by)); } /* * Convert block and offset to dataptr */ -#define XFS_DIR2_DB_OFF_TO_DATAPTR(mp,db,o) \ - xfs_dir2_db_off_to_dataptr(mp, db, o) static inline xfs_dir2_dataptr_t xfs_dir2_db_off_to_dataptr(struct xfs_mount *mp, xfs_dir2_db_t db, xfs_dir2_data_aoff_t o) { - return XFS_DIR2_BYTE_TO_DATAPTR(mp, XFS_DIR2_DB_OFF_TO_BYTE(mp, db, o)); + return xfs_dir2_byte_to_dataptr(mp, xfs_dir2_db_off_to_byte(mp, db, o)); } /* * Convert block (dablk) to block (DB) */ -#define XFS_DIR2_DA_TO_DB(mp,da) xfs_dir2_da_to_db(mp, da) static inline xfs_dir2_db_t xfs_dir2_da_to_db(struct xfs_mount *mp, xfs_dablk_t da) { @@ -231,11 +215,10 @@ xfs_dir2_da_to_db(struct xfs_mount *mp, xfs_dablk_t da) /* * Convert block (dablk) to byte offset in space */ -#define XFS_DIR2_DA_TO_BYTE(mp,da) xfs_dir2_da_to_byte(mp, da) static inline xfs_dir2_off_t xfs_dir2_da_to_byte(struct xfs_mount *mp, xfs_dablk_t da) { - return XFS_DIR2_DB_OFF_TO_BYTE(mp, XFS_DIR2_DA_TO_DB(mp, da), 0); + return xfs_dir2_db_off_to_byte(mp, xfs_dir2_da_to_db(mp, da), 0); } /* diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index d083c38..91c61d9 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c @@ -136,14 +136,14 @@ xfs_dir2_leaf_to_node( /* * Get the buffer for the new freespace block. */ - if ((error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, fdb), -1, &fbp, + if ((error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, fdb), -1, &fbp, XFS_DATA_FORK))) { return error; } ASSERT(fbp != NULL); free = fbp->data; leaf = lbp->data; - ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf); + ltp = xfs_dir2_leaf_tail_p(mp, leaf); /* * Initialize the freespace block header. */ @@ -155,7 +155,7 @@ xfs_dir2_leaf_to_node( * Copy freespace entries from the leaf block to the new block. * Count active entries. */ - for (i = n = 0, from = XFS_DIR2_LEAF_BESTS_P(ltp), to = free->bests; + for (i = n = 0, from = xfs_dir2_leaf_bests_p(ltp), to = free->bests; i < be32_to_cpu(ltp->bestcount); i++, from++, to++) { if ((off = be16_to_cpu(*from)) != NULLDATAOFF) n++; @@ -215,7 +215,7 @@ xfs_dir2_leafn_add( * a compact. */ - if (be16_to_cpu(leaf->hdr.count) == XFS_DIR2_MAX_LEAF_ENTS(mp)) { + if (be16_to_cpu(leaf->hdr.count) == xfs_dir2_max_leaf_ents(mp)) { if (!leaf->hdr.stale) return XFS_ERROR(ENOSPC); compact = be16_to_cpu(leaf->hdr.stale) > 1; @@ -327,7 +327,7 @@ xfs_dir2_leafn_add( * Insert the new entry, log everything. */ lep->hashval = cpu_to_be32(args->hashval); - lep->address = cpu_to_be32(XFS_DIR2_DB_OFF_TO_DATAPTR(mp, + lep->address = cpu_to_be32(xfs_dir2_db_off_to_dataptr(mp, args->blkno, args->index)); xfs_dir2_leaf_log_header(tp, bp); xfs_dir2_leaf_log_ents(tp, bp, lfloglow, lfloghigh); @@ -352,7 +352,7 @@ xfs_dir2_leafn_check( leaf = bp->data; mp = dp->i_mount; ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC); - ASSERT(be16_to_cpu(leaf->hdr.count) <= XFS_DIR2_MAX_LEAF_ENTS(mp)); + ASSERT(be16_to_cpu(leaf->hdr.count) <= xfs_dir2_max_leaf_ents(mp)); for (i = stale = 0; i < be16_to_cpu(leaf->hdr.count); i++) { if (i + 1 < be16_to_cpu(leaf->hdr.count)) { ASSERT(be32_to_cpu(leaf->ents[i].hashval) <= @@ -440,7 +440,7 @@ xfs_dir2_leafn_lookup_int( if (args->addname) { curfdb = curbp ? state->extrablk.blkno : -1; curdb = -1; - length = XFS_DIR2_DATA_ENTSIZE(args->namelen); + length = xfs_dir2_data_entsize(args->namelen); if ((free = (curbp ? curbp->data : NULL))) ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC); } @@ -465,7 +465,7 @@ xfs_dir2_leafn_lookup_int( /* * Pull the data block number from the entry. */ - newdb = XFS_DIR2_DATAPTR_TO_DB(mp, be32_to_cpu(lep->address)); + newdb = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); /* * For addname, we're looking for a place to put the new entry. * We want to use a data block with an entry of equal @@ -482,7 +482,7 @@ xfs_dir2_leafn_lookup_int( * Convert the data block to the free block * holding its freespace information. */ - newfdb = XFS_DIR2_DB_TO_FDB(mp, newdb); + newfdb = xfs_dir2_db_to_fdb(mp, newdb); /* * If it's not the one we have in hand, * read it in. @@ -497,7 +497,7 @@ xfs_dir2_leafn_lookup_int( * Read the free block. */ if ((error = xfs_da_read_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, + xfs_dir2_db_to_da(mp, newfdb), -1, &curbp, XFS_DATA_FORK))) { @@ -517,7 +517,7 @@ xfs_dir2_leafn_lookup_int( /* * Get the index for our entry. */ - fi = XFS_DIR2_DB_TO_FDINDEX(mp, curdb); + fi = xfs_dir2_db_to_fdindex(mp, curdb); /* * If it has room, return it. */ @@ -561,7 +561,7 @@ xfs_dir2_leafn_lookup_int( */ if ((error = xfs_da_read_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, newdb), -1, + xfs_dir2_db_to_da(mp, newdb), -1, &curbp, XFS_DATA_FORK))) { return error; } @@ -573,7 +573,7 @@ xfs_dir2_leafn_lookup_int( */ dep = (xfs_dir2_data_entry_t *) ((char *)curbp->data + - XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(lep->address))); + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address))); /* * Compare the entry, return it if it matches. */ @@ -876,9 +876,9 @@ xfs_dir2_leafn_remove( /* * Extract the data block and offset from the entry. */ - db = XFS_DIR2_DATAPTR_TO_DB(mp, be32_to_cpu(lep->address)); + db = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address)); ASSERT(dblk->blkno == db); - off = XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(lep->address)); + off = xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)); ASSERT(dblk->index == off); /* * Kill the leaf entry by marking it stale. @@ -898,7 +898,7 @@ xfs_dir2_leafn_remove( longest = be16_to_cpu(data->hdr.bestfree[0].length); needlog = needscan = 0; xfs_dir2_data_make_free(tp, dbp, off, - XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan); + xfs_dir2_data_entsize(dep->namelen), &needlog, &needscan); /* * Rescan the data block freespaces for bestfree. * Log the data block header if needed. @@ -924,8 +924,8 @@ xfs_dir2_leafn_remove( * Convert the data block number to a free block, * read in the free block. */ - fdb = XFS_DIR2_DB_TO_FDB(mp, db); - if ((error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, fdb), + fdb = xfs_dir2_db_to_fdb(mp, db); + if ((error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, fdb), -1, &fbp, XFS_DATA_FORK))) { return error; } @@ -937,7 +937,7 @@ xfs_dir2_leafn_remove( /* * Calculate which entry we need to fix. */ - findex = XFS_DIR2_DB_TO_FDINDEX(mp, db); + findex = xfs_dir2_db_to_fdindex(mp, db); longest = be16_to_cpu(data->hdr.bestfree[0].length); /* * If the data block is now empty we can get rid of it @@ -1073,7 +1073,7 @@ xfs_dir2_leafn_split( /* * Initialize the new leaf block. */ - error = xfs_dir2_leaf_init(args, XFS_DIR2_DA_TO_DB(mp, blkno), + error = xfs_dir2_leaf_init(args, xfs_dir2_da_to_db(mp, blkno), &newblk->bp, XFS_DIR2_LEAFN_MAGIC); if (error) { return error; @@ -1385,7 +1385,7 @@ xfs_dir2_node_addname_int( dp = args->dp; mp = dp->i_mount; tp = args->trans; - length = XFS_DIR2_DATA_ENTSIZE(args->namelen); + length = xfs_dir2_data_entsize(args->namelen); /* * If we came in with a freespace block that means that lookup * found an entry with our hash value. This is the freespace @@ -1438,7 +1438,7 @@ xfs_dir2_node_addname_int( if ((error = xfs_bmap_last_offset(tp, dp, &fo, XFS_DATA_FORK))) return error; - lastfbno = XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)fo); + lastfbno = xfs_dir2_da_to_db(mp, (xfs_dablk_t)fo); fbno = ifbno; } /* @@ -1474,7 +1474,7 @@ xfs_dir2_node_addname_int( * to avoid it. */ if ((error = xfs_da_read_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, fbno), -2, &fbp, + xfs_dir2_db_to_da(mp, fbno), -2, &fbp, XFS_DATA_FORK))) { return error; } @@ -1550,9 +1550,9 @@ xfs_dir2_node_addname_int( * Get the freespace block corresponding to the data block * that was just allocated. */ - fbno = XFS_DIR2_DB_TO_FDB(mp, dbno); + fbno = xfs_dir2_db_to_fdb(mp, dbno); if (unlikely(error = xfs_da_read_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, fbno), -2, &fbp, + xfs_dir2_db_to_da(mp, fbno), -2, &fbp, XFS_DATA_FORK))) { xfs_da_buf_done(dbp); return error; @@ -1567,14 +1567,14 @@ xfs_dir2_node_addname_int( return error; } - if (unlikely(XFS_DIR2_DB_TO_FDB(mp, dbno) != fbno)) { + if (unlikely(xfs_dir2_db_to_fdb(mp, dbno) != fbno)) { cmn_err(CE_ALERT, "xfs_dir2_node_addname_int: dir ino " "%llu needed freesp block %lld for\n" " data block %lld, got %lld\n" " ifbno %llu lastfbno %d\n", (unsigned long long)dp->i_ino, - (long long)XFS_DIR2_DB_TO_FDB(mp, dbno), + (long long)xfs_dir2_db_to_fdb(mp, dbno), (long long)dbno, (long long)fbno, (unsigned long long)ifbno, lastfbno); if (fblk) { @@ -1598,7 +1598,7 @@ xfs_dir2_node_addname_int( * Get a buffer for the new block. */ if ((error = xfs_da_get_buf(tp, dp, - XFS_DIR2_DB_TO_DA(mp, fbno), + xfs_dir2_db_to_da(mp, fbno), -1, &fbp, XFS_DATA_FORK))) { return error; } @@ -1623,7 +1623,7 @@ xfs_dir2_node_addname_int( /* * Set the freespace block index from the data block number. */ - findex = XFS_DIR2_DB_TO_FDINDEX(mp, dbno); + findex = xfs_dir2_db_to_fdindex(mp, dbno); /* * If it's after the end of the current entries in the * freespace block, extend that table. @@ -1669,7 +1669,7 @@ xfs_dir2_node_addname_int( * Read the data block in. */ if (unlikely( - error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, dbno), + error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, dbno), -1, &dbp, XFS_DATA_FORK))) { if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL) xfs_da_buf_done(fbp); @@ -1698,7 +1698,7 @@ xfs_dir2_node_addname_int( dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, dep->namelen); - tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); + tagp = xfs_dir2_data_entry_tag_p(dep); *tagp = cpu_to_be16((char *)dep - (char *)data); xfs_dir2_data_log_entry(tp, dbp, dep); /* @@ -1904,7 +1904,7 @@ xfs_dir2_node_replace( ASSERT(be32_to_cpu(data->hdr.magic) == XFS_DIR2_DATA_MAGIC); dep = (xfs_dir2_data_entry_t *) ((char *)data + - XFS_DIR2_DATAPTR_TO_OFF(state->mp, be32_to_cpu(lep->address))); + xfs_dir2_dataptr_to_off(state->mp, be32_to_cpu(lep->address))); ASSERT(inum != be64_to_cpu(dep->inumber)); /* * Fill in the new inode number and log the entry. @@ -1980,7 +1980,7 @@ xfs_dir2_node_trim_free( * Blow the block away. */ if ((error = - xfs_dir2_shrink_inode(args, XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)fo), + xfs_dir2_shrink_inode(args, xfs_dir2_da_to_db(mp, (xfs_dablk_t)fo), bp))) { /* * Can't fail with ENOSPC since that only happens with no diff --git a/fs/xfs/xfs_dir2_node.h b/fs/xfs/xfs_dir2_node.h index c7c870e..dde72db 100644 --- a/fs/xfs/xfs_dir2_node.h +++ b/fs/xfs/xfs_dir2_node.h @@ -36,7 +36,7 @@ struct xfs_trans; #define XFS_DIR2_FREE_SPACE 2 #define XFS_DIR2_FREE_OFFSET (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE) #define XFS_DIR2_FREE_FIRSTDB(mp) \ - XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_FREE_OFFSET) + xfs_dir2_byte_to_db(mp, XFS_DIR2_FREE_OFFSET) #define XFS_DIR2_FREE_MAGIC 0x58443246 /* XD2F */ @@ -60,7 +60,6 @@ typedef struct xfs_dir2_free { /* * Convert data space db to the corresponding free db. */ -#define XFS_DIR2_DB_TO_FDB(mp,db) xfs_dir2_db_to_fdb(mp, db) static inline xfs_dir2_db_t xfs_dir2_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db) { @@ -70,7 +69,6 @@ xfs_dir2_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db) /* * Convert data space db to the corresponding index in a free db. */ -#define XFS_DIR2_DB_TO_FDINDEX(mp,db) xfs_dir2_db_to_fdindex(mp, db) static inline int xfs_dir2_db_to_fdindex(struct xfs_mount *mp, xfs_dir2_db_t db) { diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c index 0cd77b1..38fc4f2 100644 --- a/fs/xfs/xfs_dir2_sf.c +++ b/fs/xfs/xfs_dir2_sf.c @@ -89,8 +89,8 @@ xfs_dir2_block_sfsize( mp = dp->i_mount; count = i8count = namelen = 0; - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); - blp = XFS_DIR2_BLOCK_LEAF_P(btp); + btp = xfs_dir2_block_tail_p(mp, block); + blp = xfs_dir2_block_leaf_p(btp); /* * Iterate over the block's data entries by using the leaf pointers. @@ -102,7 +102,7 @@ xfs_dir2_block_sfsize( * Calculate the pointer to the entry at hand. */ dep = (xfs_dir2_data_entry_t *) - ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, addr)); + ((char *)block + xfs_dir2_dataptr_to_off(mp, addr)); /* * Detect . and .., so we can special-case them. * . is not included in sf directories. @@ -124,7 +124,7 @@ xfs_dir2_block_sfsize( /* * Calculate the new size, see if we should give up yet. */ - size = XFS_DIR2_SF_HDR_SIZE(i8count) + /* header */ + size = xfs_dir2_sf_hdr_size(i8count) + /* header */ count + /* namelen */ count * (uint)sizeof(xfs_dir2_sf_off_t) + /* offset */ namelen + /* name */ @@ -139,7 +139,7 @@ xfs_dir2_block_sfsize( */ sfhp->count = count; sfhp->i8count = i8count; - XFS_DIR2_SF_PUT_INUMBER((xfs_dir2_sf_t *)sfhp, &parent, &sfhp->parent); + xfs_dir2_sf_put_inumber((xfs_dir2_sf_t *)sfhp, &parent, &sfhp->parent); return size; } @@ -199,15 +199,15 @@ xfs_dir2_block_to_sf( * Copy the header into the newly allocate local space. */ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - memcpy(sfp, sfhp, XFS_DIR2_SF_HDR_SIZE(sfhp->i8count)); + memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); dp->i_d.di_size = size; /* * Set up to loop over the block's entries. */ - btp = XFS_DIR2_BLOCK_TAIL_P(mp, block); + btp = xfs_dir2_block_tail_p(mp, block); ptr = (char *)block->u; - endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp); - sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + endptr = (char *)xfs_dir2_block_leaf_p(btp); + sfep = xfs_dir2_sf_firstentry(sfp); /* * Loop over the active and unused entries. * Stop when we reach the leaf/tail portion of the block. @@ -233,22 +233,22 @@ xfs_dir2_block_to_sf( else if (dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.') ASSERT(be64_to_cpu(dep->inumber) == - XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); + xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent)); /* * Normal entry, copy it into shortform. */ else { sfep->namelen = dep->namelen; - XFS_DIR2_SF_PUT_OFFSET(sfep, + xfs_dir2_sf_put_offset(sfep, (xfs_dir2_data_aoff_t) ((char *)dep - (char *)block)); memcpy(sfep->name, dep->name, dep->namelen); temp = be64_to_cpu(dep->inumber); - XFS_DIR2_SF_PUT_INUMBER(sfp, &temp, - XFS_DIR2_SF_INUMBERP(sfep)); - sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); + xfs_dir2_sf_put_inumber(sfp, &temp, + xfs_dir2_sf_inumberp(sfep)); + sfep = xfs_dir2_sf_nextentry(sfp, sfep); } - ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen); + ptr += xfs_dir2_data_entsize(dep->namelen); } ASSERT((char *)sfep - (char *)sfp == size); xfs_dir2_sf_check(args); @@ -294,11 +294,11 @@ xfs_dir2_sf_addname( ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * Compute entry (and change in) size. */ - add_entsize = XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen); + add_entsize = xfs_dir2_sf_entsize_byname(sfp, args->namelen); incr_isize = add_entsize; objchange = 0; #if XFS_BIG_INUMS @@ -392,7 +392,7 @@ xfs_dir2_sf_addname_easy( /* * Grow the in-inode space. */ - xfs_idata_realloc(dp, XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen), + xfs_idata_realloc(dp, xfs_dir2_sf_entsize_byname(sfp, args->namelen), XFS_DATA_FORK); /* * Need to set up again due to realloc of the inode data. @@ -403,10 +403,10 @@ xfs_dir2_sf_addname_easy( * Fill in the new entry. */ sfep->namelen = args->namelen; - XFS_DIR2_SF_PUT_OFFSET(sfep, offset); + xfs_dir2_sf_put_offset(sfep, offset); memcpy(sfep->name, args->name, sfep->namelen); - XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, - XFS_DIR2_SF_INUMBERP(sfep)); + xfs_dir2_sf_put_inumber(sfp, &args->inumber, + xfs_dir2_sf_inumberp(sfep)); /* * Update the header and inode. */ @@ -463,14 +463,14 @@ xfs_dir2_sf_addname_hard( * If it's going to end up at the end then oldsfep will point there. */ for (offset = XFS_DIR2_DATA_FIRST_OFFSET, - oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp), - add_datasize = XFS_DIR2_DATA_ENTSIZE(args->namelen), + oldsfep = xfs_dir2_sf_firstentry(oldsfp), + add_datasize = xfs_dir2_data_entsize(args->namelen), eof = (char *)oldsfep == &buf[old_isize]; !eof; - offset = new_offset + XFS_DIR2_DATA_ENTSIZE(oldsfep->namelen), - oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep), + offset = new_offset + xfs_dir2_data_entsize(oldsfep->namelen), + oldsfep = xfs_dir2_sf_nextentry(oldsfp, oldsfep), eof = (char *)oldsfep == &buf[old_isize]) { - new_offset = XFS_DIR2_SF_GET_OFFSET(oldsfep); + new_offset = xfs_dir2_sf_get_offset(oldsfep); if (offset + add_datasize <= new_offset) break; } @@ -495,10 +495,10 @@ xfs_dir2_sf_addname_hard( * Fill in the new entry, and update the header counts. */ sfep->namelen = args->namelen; - XFS_DIR2_SF_PUT_OFFSET(sfep, offset); + xfs_dir2_sf_put_offset(sfep, offset); memcpy(sfep->name, args->name, sfep->namelen); - XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, - XFS_DIR2_SF_INUMBERP(sfep)); + xfs_dir2_sf_put_inumber(sfp, &args->inumber, + xfs_dir2_sf_inumberp(sfep)); sfp->hdr.count++; #if XFS_BIG_INUMS if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange) @@ -508,7 +508,7 @@ xfs_dir2_sf_addname_hard( * If there's more left to copy, do that. */ if (!eof) { - sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); + sfep = xfs_dir2_sf_nextentry(sfp, sfep); memcpy(sfep, oldsfep, old_isize - nbytes); } kmem_free(buf, old_isize); @@ -544,9 +544,9 @@ xfs_dir2_sf_addname_pick( mp = dp->i_mount; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - size = XFS_DIR2_DATA_ENTSIZE(args->namelen); + size = xfs_dir2_data_entsize(args->namelen); offset = XFS_DIR2_DATA_FIRST_OFFSET; - sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + sfep = xfs_dir2_sf_firstentry(sfp); holefit = 0; /* * Loop over sf entries. @@ -555,10 +555,10 @@ xfs_dir2_sf_addname_pick( */ for (i = 0; i < sfp->hdr.count; i++) { if (!holefit) - holefit = offset + size <= XFS_DIR2_SF_GET_OFFSET(sfep); - offset = XFS_DIR2_SF_GET_OFFSET(sfep) + - XFS_DIR2_DATA_ENTSIZE(sfep->namelen); - sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); + holefit = offset + size <= xfs_dir2_sf_get_offset(sfep); + offset = xfs_dir2_sf_get_offset(sfep) + + xfs_dir2_data_entsize(sfep->namelen); + sfep = xfs_dir2_sf_nextentry(sfp, sfep); } /* * Calculate data bytes used excluding the new entry, if this @@ -617,18 +617,18 @@ xfs_dir2_sf_check( sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; offset = XFS_DIR2_DATA_FIRST_OFFSET; - ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); + ino = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); i8count = ino > XFS_DIR2_MAX_SHORT_INUM; - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { - ASSERT(XFS_DIR2_SF_GET_OFFSET(sfep) >= offset); - ino = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { + ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset); + ino = xfs_dir2_sf_get_inumber(sfp, xfs_dir2_sf_inumberp(sfep)); i8count += ino > XFS_DIR2_MAX_SHORT_INUM; offset = - XFS_DIR2_SF_GET_OFFSET(sfep) + - XFS_DIR2_DATA_ENTSIZE(sfep->namelen); + xfs_dir2_sf_get_offset(sfep) + + xfs_dir2_data_entsize(sfep->namelen); } ASSERT(i8count == sfp->hdr.i8count); ASSERT(XFS_BIG_INUMS || i8count == 0); @@ -671,7 +671,7 @@ xfs_dir2_sf_create( ASSERT(dp->i_df.if_flags & XFS_IFINLINE); ASSERT(dp->i_df.if_bytes == 0); i8count = pino > XFS_DIR2_MAX_SHORT_INUM; - size = XFS_DIR2_SF_HDR_SIZE(i8count); + size = xfs_dir2_sf_hdr_size(i8count); /* * Make a buffer for the data. */ @@ -684,7 +684,7 @@ xfs_dir2_sf_create( /* * Now can put in the inode number, since i8count is set. */ - XFS_DIR2_SF_PUT_INUMBER(sfp, &pino, &sfp->hdr.parent); + xfs_dir2_sf_put_inumber(sfp, &pino, &sfp->hdr.parent); sfp->hdr.count = 0; dp->i_d.di_size = size; xfs_dir2_sf_check(args); @@ -727,12 +727,12 @@ xfs_dir2_sf_getdents( sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * If the block number in the offset is out of range, we're done. */ - if (XFS_DIR2_DATAPTR_TO_DB(mp, dir_offset) > mp->m_dirdatablk) { + if (xfs_dir2_dataptr_to_db(mp, dir_offset) > mp->m_dirdatablk) { *eofp = 1; return 0; } @@ -747,9 +747,9 @@ xfs_dir2_sf_getdents( * Put . entry unless we're starting past it. */ if (dir_offset <= - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOT_OFFSET)) { - p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, 0, + p.cook = xfs_dir2_db_off_to_dataptr(mp, 0, XFS_DIR2_DATA_DOTDOT_OFFSET); p.ino = dp->i_ino; #if XFS_BIG_INUMS @@ -762,7 +762,7 @@ xfs_dir2_sf_getdents( if (!p.done) { uio->uio_offset = - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOT_OFFSET); return error; } @@ -772,11 +772,11 @@ xfs_dir2_sf_getdents( * Put .. entry unless we're starting past it. */ if (dir_offset <= - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOTDOT_OFFSET)) { - p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + p.cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR2_DATA_FIRST_OFFSET); - p.ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); + p.ino = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif @@ -787,7 +787,7 @@ xfs_dir2_sf_getdents( if (!p.done) { uio->uio_offset = - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, XFS_DIR2_DATA_DOTDOT_OFFSET); return error; } @@ -796,23 +796,23 @@ xfs_dir2_sf_getdents( /* * Loop while there are more entries and put'ing works. */ - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { - off = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, - XFS_DIR2_SF_GET_OFFSET(sfep)); + off = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, + xfs_dir2_sf_get_offset(sfep)); if (dir_offset > off) continue; p.namelen = sfep->namelen; - p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, - XFS_DIR2_SF_GET_OFFSET(sfep) + - XFS_DIR2_DATA_ENTSIZE(p.namelen)); + p.cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk, + xfs_dir2_sf_get_offset(sfep) + + xfs_dir2_data_entsize(p.namelen)); - p.ino = XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep)); + p.ino = xfs_dir2_sf_get_inumber(sfp, xfs_dir2_sf_inumberp(sfep)); #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif @@ -832,7 +832,7 @@ xfs_dir2_sf_getdents( *eofp = 1; uio->uio_offset = - XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk + 1, 0); + xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0); return 0; } @@ -865,7 +865,7 @@ xfs_dir2_sf_lookup( ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * Special case for . */ @@ -878,21 +878,21 @@ xfs_dir2_sf_lookup( */ if (args->namelen == 2 && args->name[0] == '.' && args->name[1] == '.') { - args->inumber = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); + args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); return XFS_ERROR(EEXIST); } /* * Loop over all the entries trying to match ours. */ - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(args->name, sfep->name, args->namelen) == 0) { args->inumber = - XFS_DIR2_SF_GET_INUMBER(sfp, - XFS_DIR2_SF_INUMBERP(sfep)); + xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep)); return XFS_ERROR(EEXIST); } } @@ -934,19 +934,19 @@ xfs_dir2_sf_removename( ASSERT(dp->i_df.if_bytes == oldsize); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(oldsize >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(oldsize >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); /* * Loop over the old directory entries. * Find the one we're deleting. */ - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(sfep->name, args->name, args->namelen) == 0) { - ASSERT(XFS_DIR2_SF_GET_INUMBER(sfp, - XFS_DIR2_SF_INUMBERP(sfep)) == + ASSERT(xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep)) == args->inumber); break; } @@ -961,7 +961,7 @@ xfs_dir2_sf_removename( * Calculate sizes. */ byteoff = (int)((char *)sfep - (char *)sfp); - entsize = XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen); + entsize = xfs_dir2_sf_entsize_byname(sfp, args->namelen); newsize = oldsize - entsize; /* * Copy the part if any after the removed entry, sliding it down. @@ -1027,7 +1027,7 @@ xfs_dir2_sf_replace( ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(dp->i_df.if_u1.if_data != NULL); sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; - ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)); + ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->hdr.i8count)); #if XFS_BIG_INUMS /* * New inode number is large, and need to convert to 8-byte inodes. @@ -1067,28 +1067,28 @@ xfs_dir2_sf_replace( if (args->namelen == 2 && args->name[0] == '.' && args->name[1] == '.') { #if XFS_BIG_INUMS || defined(DEBUG) - ino = XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent); + ino = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent); ASSERT(args->inumber != ino); #endif - XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, &sfp->hdr.parent); + xfs_dir2_sf_put_inumber(sfp, &args->inumber, &sfp->hdr.parent); } /* * Normal entry, look for the name. */ else { - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) { if (sfep->namelen == args->namelen && sfep->name[0] == args->name[0] && memcmp(args->name, sfep->name, args->namelen) == 0) { #if XFS_BIG_INUMS || defined(DEBUG) - ino = XFS_DIR2_SF_GET_INUMBER(sfp, - XFS_DIR2_SF_INUMBERP(sfep)); + ino = xfs_dir2_sf_get_inumber(sfp, + xfs_dir2_sf_inumberp(sfep)); ASSERT(args->inumber != ino); #endif - XFS_DIR2_SF_PUT_INUMBER(sfp, &args->inumber, - XFS_DIR2_SF_INUMBERP(sfep)); + xfs_dir2_sf_put_inumber(sfp, &args->inumber, + xfs_dir2_sf_inumberp(sfep)); break; } } @@ -1189,22 +1189,22 @@ xfs_dir2_sf_toino4( */ sfp->hdr.count = oldsfp->hdr.count; sfp->hdr.i8count = 0; - ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, &oldsfp->hdr.parent); - XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, &sfp->hdr.parent); + ino = xfs_dir2_sf_get_inumber(oldsfp, &oldsfp->hdr.parent); + xfs_dir2_sf_put_inumber(sfp, &ino, &sfp->hdr.parent); /* * Copy the entries field by field. */ - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp), - oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), + oldsfep = xfs_dir2_sf_firstentry(oldsfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep), - oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep), + oldsfep = xfs_dir2_sf_nextentry(oldsfp, oldsfep)) { sfep->namelen = oldsfep->namelen; sfep->offset = oldsfep->offset; memcpy(sfep->name, oldsfep->name, sfep->namelen); - ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, - XFS_DIR2_SF_INUMBERP(oldsfep)); - XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, XFS_DIR2_SF_INUMBERP(sfep)); + ino = xfs_dir2_sf_get_inumber(oldsfp, + xfs_dir2_sf_inumberp(oldsfep)); + xfs_dir2_sf_put_inumber(sfp, &ino, xfs_dir2_sf_inumberp(sfep)); } /* * Clean up the inode. @@ -1266,22 +1266,22 @@ xfs_dir2_sf_toino8( */ sfp->hdr.count = oldsfp->hdr.count; sfp->hdr.i8count = 1; - ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, &oldsfp->hdr.parent); - XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, &sfp->hdr.parent); + ino = xfs_dir2_sf_get_inumber(oldsfp, &oldsfp->hdr.parent); + xfs_dir2_sf_put_inumber(sfp, &ino, &sfp->hdr.parent); /* * Copy the entries field by field. */ - for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp), - oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp); + for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), + oldsfep = xfs_dir2_sf_firstentry(oldsfp); i < sfp->hdr.count; - i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep), - oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep)) { + i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep), + oldsfep = xfs_dir2_sf_nextentry(oldsfp, oldsfep)) { sfep->namelen = oldsfep->namelen; sfep->offset = oldsfep->offset; memcpy(sfep->name, oldsfep->name, sfep->namelen); - ino = XFS_DIR2_SF_GET_INUMBER(oldsfp, - XFS_DIR2_SF_INUMBERP(oldsfep)); - XFS_DIR2_SF_PUT_INUMBER(sfp, &ino, XFS_DIR2_SF_INUMBERP(sfep)); + ino = xfs_dir2_sf_get_inumber(oldsfp, + xfs_dir2_sf_inumberp(oldsfep)); + xfs_dir2_sf_put_inumber(sfp, &ino, xfs_dir2_sf_inumberp(sfep)); } /* * Clean up the inode. diff --git a/fs/xfs/xfs_dir2_sf.h b/fs/xfs/xfs_dir2_sf.h index 42f015b..11e5032 100644 --- a/fs/xfs/xfs_dir2_sf.h +++ b/fs/xfs/xfs_dir2_sf.h @@ -90,7 +90,6 @@ typedef struct xfs_dir2_sf { xfs_dir2_sf_entry_t list[1]; /* shortform entries */ } xfs_dir2_sf_t; -#define XFS_DIR2_SF_HDR_SIZE(i8count) xfs_dir2_sf_hdr_size(i8count) static inline int xfs_dir2_sf_hdr_size(int i8count) { return ((uint)sizeof(xfs_dir2_sf_hdr_t) - \ @@ -98,14 +97,11 @@ static inline int xfs_dir2_sf_hdr_size(int i8count) ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t))); } -#define XFS_DIR2_SF_INUMBERP(sfep) xfs_dir2_sf_inumberp(sfep) static inline xfs_dir2_inou_t *xfs_dir2_sf_inumberp(xfs_dir2_sf_entry_t *sfep) { return (xfs_dir2_inou_t *)&(sfep)->name[(sfep)->namelen]; } -#define XFS_DIR2_SF_GET_INUMBER(sfp, from) \ - xfs_dir2_sf_get_inumber(sfp, from) static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp, xfs_dir2_inou_t *from) { @@ -114,8 +110,6 @@ xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp, xfs_dir2_inou_t *from) (xfs_intino_t)XFS_GET_DIR_INO8((from)->i8)); } -#define XFS_DIR2_SF_PUT_INUMBER(sfp,from,to) \ - xfs_dir2_sf_put_inumber(sfp,from,to) static inline void xfs_dir2_sf_put_inumber(xfs_dir2_sf_t *sfp, xfs_ino_t *from, xfs_dir2_inou_t *to) { @@ -125,24 +119,18 @@ static inline void xfs_dir2_sf_put_inumber(xfs_dir2_sf_t *sfp, xfs_ino_t *from, XFS_PUT_DIR_INO8(*(from), (to)->i8); } -#define XFS_DIR2_SF_GET_OFFSET(sfep) \ - xfs_dir2_sf_get_offset(sfep) static inline xfs_dir2_data_aoff_t xfs_dir2_sf_get_offset(xfs_dir2_sf_entry_t *sfep) { return INT_GET_UNALIGNED_16_BE(&(sfep)->offset.i); } -#define XFS_DIR2_SF_PUT_OFFSET(sfep,off) \ - xfs_dir2_sf_put_offset(sfep,off) static inline void xfs_dir2_sf_put_offset(xfs_dir2_sf_entry_t *sfep, xfs_dir2_data_aoff_t off) { INT_SET_UNALIGNED_16_BE(&(sfep)->offset.i, off); } -#define XFS_DIR2_SF_ENTSIZE_BYNAME(sfp,len) \ - xfs_dir2_sf_entsize_byname(sfp,len) static inline int xfs_dir2_sf_entsize_byname(xfs_dir2_sf_t *sfp, int len) { return ((uint)sizeof(xfs_dir2_sf_entry_t) - 1 + (len) - \ @@ -150,8 +138,6 @@ static inline int xfs_dir2_sf_entsize_byname(xfs_dir2_sf_t *sfp, int len) ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t))); } -#define XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp,sfep) \ - xfs_dir2_sf_entsize_byentry(sfp,sfep) static inline int xfs_dir2_sf_entsize_byentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep) { @@ -160,19 +146,17 @@ xfs_dir2_sf_entsize_byentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep) ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t))); } -#define XFS_DIR2_SF_FIRSTENTRY(sfp) xfs_dir2_sf_firstentry(sfp) static inline xfs_dir2_sf_entry_t *xfs_dir2_sf_firstentry(xfs_dir2_sf_t *sfp) { return ((xfs_dir2_sf_entry_t *) \ - ((char *)(sfp) + XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count))); + ((char *)(sfp) + xfs_dir2_sf_hdr_size(sfp->hdr.i8count))); } -#define XFS_DIR2_SF_NEXTENTRY(sfp,sfep) xfs_dir2_sf_nextentry(sfp,sfep) static inline xfs_dir2_sf_entry_t * xfs_dir2_sf_nextentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep) { return ((xfs_dir2_sf_entry_t *) \ - ((char *)(sfep) + XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp,sfep))); + ((char *)(sfep) + xfs_dir2_sf_entsize_byentry(sfp,sfep))); } /* diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c new file mode 100644 index 0000000..ce22786 --- /dev/null +++ b/fs/xfs/xfs_filestream.c @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2006-2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "xfs.h" +#include "xfs_bmap_btree.h" +#include "xfs_inum.h" +#include "xfs_dir2.h" +#include "xfs_dir2_sf.h" +#include "xfs_attr_sf.h" +#include "xfs_dinode.h" +#include "xfs_inode.h" +#include "xfs_ag.h" +#include "xfs_dmapi.h" +#include "xfs_log.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_mount.h" +#include "xfs_bmap.h" +#include "xfs_alloc.h" +#include "xfs_utils.h" +#include "xfs_mru_cache.h" +#include "xfs_filestream.h" + +#ifdef XFS_FILESTREAMS_TRACE + +ktrace_t *xfs_filestreams_trace_buf; + +STATIC void +xfs_filestreams_trace( + xfs_mount_t *mp, /* mount point */ + int type, /* type of trace */ + const char *func, /* source function */ + int line, /* source line number */ + __psunsigned_t arg0, + __psunsigned_t arg1, + __psunsigned_t arg2, + __psunsigned_t arg3, + __psunsigned_t arg4, + __psunsigned_t arg5) +{ + ktrace_enter(xfs_filestreams_trace_buf, + (void *)(__psint_t)(type | (line << 16)), + (void *)func, + (void *)(__psunsigned_t)current_pid(), + (void *)mp, + (void *)(__psunsigned_t)arg0, + (void *)(__psunsigned_t)arg1, + (void *)(__psunsigned_t)arg2, + (void *)(__psunsigned_t)arg3, + (void *)(__psunsigned_t)arg4, + (void *)(__psunsigned_t)arg5, + NULL, NULL, NULL, NULL, NULL, NULL); +} + +#define TRACE0(mp,t) TRACE6(mp,t,0,0,0,0,0,0) +#define TRACE1(mp,t,a0) TRACE6(mp,t,a0,0,0,0,0,0) +#define TRACE2(mp,t,a0,a1) TRACE6(mp,t,a0,a1,0,0,0,0) +#define TRACE3(mp,t,a0,a1,a2) TRACE6(mp,t,a0,a1,a2,0,0,0) +#define TRACE4(mp,t,a0,a1,a2,a3) TRACE6(mp,t,a0,a1,a2,a3,0,0) +#define TRACE5(mp,t,a0,a1,a2,a3,a4) TRACE6(mp,t,a0,a1,a2,a3,a4,0) +#define TRACE6(mp,t,a0,a1,a2,a3,a4,a5) \ + xfs_filestreams_trace(mp, t, __FUNCTION__, __LINE__, \ + (__psunsigned_t)a0, (__psunsigned_t)a1, \ + (__psunsigned_t)a2, (__psunsigned_t)a3, \ + (__psunsigned_t)a4, (__psunsigned_t)a5) + +#define TRACE_AG_SCAN(mp, ag, ag2) \ + TRACE2(mp, XFS_FSTRM_KTRACE_AGSCAN, ag, ag2); +#define TRACE_AG_PICK1(mp, max_ag, maxfree) \ + TRACE2(mp, XFS_FSTRM_KTRACE_AGPICK1, max_ag, maxfree); +#define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) \ + TRACE6(mp, XFS_FSTRM_KTRACE_AGPICK2, ag, ag2, \ + cnt, free, scan, flag) +#define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) \ + TRACE5(mp, XFS_FSTRM_KTRACE_UPDATE, ip, ag, cnt, ag2, cnt2) +#define TRACE_FREE(mp, ip, pip, ag, cnt) \ + TRACE4(mp, XFS_FSTRM_KTRACE_FREE, ip, pip, ag, cnt) +#define TRACE_LOOKUP(mp, ip, pip, ag, cnt) \ + TRACE4(mp, XFS_FSTRM_KTRACE_ITEM_LOOKUP, ip, pip, ag, cnt) +#define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) \ + TRACE4(mp, XFS_FSTRM_KTRACE_ASSOCIATE, ip, pip, ag, cnt) +#define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) \ + TRACE6(mp, XFS_FSTRM_KTRACE_MOVEAG, ip, pip, oag, ocnt, nag, ncnt) +#define TRACE_ORPHAN(mp, ip, ag) \ + TRACE2(mp, XFS_FSTRM_KTRACE_ORPHAN, ip, ag); + + +#else +#define TRACE_AG_SCAN(mp, ag, ag2) +#define TRACE_AG_PICK1(mp, max_ag, maxfree) +#define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) +#define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) +#define TRACE_FREE(mp, ip, pip, ag, cnt) +#define TRACE_LOOKUP(mp, ip, pip, ag, cnt) +#define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) +#define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) +#define TRACE_ORPHAN(mp, ip, ag) +#endif + +static kmem_zone_t *item_zone; + +/* + * Structure for associating a file or a directory with an allocation group. + * The parent directory pointer is only needed for files, but since there will + * generally be vastly more files than directories in the cache, using the same + * data structure simplifies the code with very little memory overhead. + */ +typedef struct fstrm_item +{ + xfs_agnumber_t ag; /* AG currently in use for the file/directory. */ + xfs_inode_t *ip; /* inode self-pointer. */ + xfs_inode_t *pip; /* Parent directory inode pointer. */ +} fstrm_item_t; + + +/* + * Scan the AGs starting at startag looking for an AG that isn't in use and has + * at least minlen blocks free. + */ +static int +_xfs_filestream_pick_ag( + xfs_mount_t *mp, + xfs_agnumber_t startag, + xfs_agnumber_t *agp, + int flags, + xfs_extlen_t minlen) +{ + int err, trylock, nscan; + xfs_extlen_t delta, longest, need, free, minfree, maxfree = 0; + xfs_agnumber_t ag, max_ag = NULLAGNUMBER; + struct xfs_perag *pag; + + /* 2% of an AG's blocks must be free for it to be chosen. */ + minfree = mp->m_sb.sb_agblocks / 50; + + ag = startag; + *agp = NULLAGNUMBER; + + /* For the first pass, don't sleep trying to init the per-AG. */ + trylock = XFS_ALLOC_FLAG_TRYLOCK; + + for (nscan = 0; 1; nscan++) { + + TRACE_AG_SCAN(mp, ag, xfs_filestream_peek_ag(mp, ag)); + + pag = mp->m_perag + ag; + + if (!pag->pagf_init) { + err = xfs_alloc_pagf_init(mp, NULL, ag, trylock); + if (err && !trylock) + return err; + } + + /* Might fail sometimes during the 1st pass with trylock set. */ + if (!pag->pagf_init) + goto next_ag; + + /* Keep track of the AG with the most free blocks. */ + if (pag->pagf_freeblks > maxfree) { + maxfree = pag->pagf_freeblks; + max_ag = ag; + } + + /* + * The AG reference count does two things: it enforces mutual + * exclusion when examining the suitability of an AG in this + * loop, and it guards against two filestreams being established + * in the same AG as each other. + */ + if (xfs_filestream_get_ag(mp, ag) > 1) { + xfs_filestream_put_ag(mp, ag); + goto next_ag; + } + + need = XFS_MIN_FREELIST_PAG(pag, mp); + delta = need > pag->pagf_flcount ? need - pag->pagf_flcount : 0; + longest = (pag->pagf_longest > delta) ? + (pag->pagf_longest - delta) : + (pag->pagf_flcount > 0 || pag->pagf_longest > 0); + + if (((minlen && longest >= minlen) || + (!minlen && pag->pagf_freeblks >= minfree)) && + (!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) || + (flags & XFS_PICK_LOWSPACE))) { + + /* Break out, retaining the reference on the AG. */ + free = pag->pagf_freeblks; + *agp = ag; + break; + } + + /* Drop the reference on this AG, it's not usable. */ + xfs_filestream_put_ag(mp, ag); +next_ag: + /* Move to the next AG, wrapping to AG 0 if necessary. */ + if (++ag >= mp->m_sb.sb_agcount) + ag = 0; + + /* If a full pass of the AGs hasn't been done yet, continue. */ + if (ag != startag) + continue; + + /* Allow sleeping in xfs_alloc_pagf_init() on the 2nd pass. */ + if (trylock != 0) { + trylock = 0; + continue; + } + + /* Finally, if lowspace wasn't set, set it for the 3rd pass. */ + if (!(flags & XFS_PICK_LOWSPACE)) { + flags |= XFS_PICK_LOWSPACE; + continue; + } + + /* + * Take the AG with the most free space, regardless of whether + * it's already in use by another filestream. + */ + if (max_ag != NULLAGNUMBER) { + xfs_filestream_get_ag(mp, max_ag); + TRACE_AG_PICK1(mp, max_ag, maxfree); + free = maxfree; + *agp = max_ag; + break; + } + + /* take AG 0 if none matched */ + TRACE_AG_PICK1(mp, max_ag, maxfree); + *agp = 0; + return 0; + } + + TRACE_AG_PICK2(mp, startag, *agp, xfs_filestream_peek_ag(mp, *agp), + free, nscan, flags); + + return 0; +} + +/* + * Set the allocation group number for a file or a directory, updating inode + * references and per-AG references as appropriate. Must be called with the + * m_peraglock held in read mode. + */ +static int +_xfs_filestream_update_ag( + xfs_inode_t *ip, + xfs_inode_t *pip, + xfs_agnumber_t ag) +{ + int err = 0; + xfs_mount_t *mp; + xfs_mru_cache_t *cache; + fstrm_item_t *item; + xfs_agnumber_t old_ag; + xfs_inode_t *old_pip; + + /* + * Either ip is a regular file and pip is a directory, or ip is a + * directory and pip is NULL. + */ + ASSERT(ip && (((ip->i_d.di_mode & S_IFREG) && pip && + (pip->i_d.di_mode & S_IFDIR)) || + ((ip->i_d.di_mode & S_IFDIR) && !pip))); + + mp = ip->i_mount; + cache = mp->m_filestream; + + item = xfs_mru_cache_lookup(cache, ip->i_ino); + if (item) { + ASSERT(item->ip == ip); + old_ag = item->ag; + item->ag = ag; + old_pip = item->pip; + item->pip = pip; + xfs_mru_cache_done(cache); + + /* + * If the AG has changed, drop the old ref and take a new one, + * effectively transferring the reference from old to new AG. + */ + if (ag != old_ag) { + xfs_filestream_put_ag(mp, old_ag); + xfs_filestream_get_ag(mp, ag); + } + + /* + * If ip is a file and its pip has changed, drop the old ref and + * take a new one. + */ + if (pip && pip != old_pip) { + IRELE(old_pip); + IHOLD(pip); + } + + TRACE_UPDATE(mp, ip, old_ag, xfs_filestream_peek_ag(mp, old_ag), + ag, xfs_filestream_peek_ag(mp, ag)); + return 0; + } + + item = kmem_zone_zalloc(item_zone, KM_MAYFAIL); + if (!item) + return ENOMEM; + + item->ag = ag; + item->ip = ip; + item->pip = pip; + + err = xfs_mru_cache_insert(cache, ip->i_ino, item); + if (err) { + kmem_zone_free(item_zone, item); + return err; + } + + /* Take a reference on the AG. */ + xfs_filestream_get_ag(mp, ag); + + /* + * Take a reference on the inode itself regardless of whether it's a + * regular file or a directory. + */ + IHOLD(ip); + + /* + * In the case of a regular file, take a reference on the parent inode + * as well to ensure it remains in-core. + */ + if (pip) + IHOLD(pip); + + TRACE_UPDATE(mp, ip, ag, xfs_filestream_peek_ag(mp, ag), + ag, xfs_filestream_peek_ag(mp, ag)); + + return 0; +} + +/* xfs_fstrm_free_func(): callback for freeing cached stream items. */ +void +xfs_fstrm_free_func( + xfs_ino_t ino, + fstrm_item_t *item) +{ + xfs_inode_t *ip = item->ip; + int ref; + + ASSERT(ip->i_ino == ino); + + xfs_iflags_clear(ip, XFS_IFILESTREAM); + + /* Drop the reference taken on the AG when the item was added. */ + ref = xfs_filestream_put_ag(ip->i_mount, item->ag); + + ASSERT(ref >= 0); + TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, + xfs_filestream_peek_ag(ip->i_mount, item->ag)); + + /* + * _xfs_filestream_update_ag() always takes a reference on the inode + * itself, whether it's a file or a directory. Release it here. + * This can result in the inode being freed and so we must + * not hold any inode locks when freeing filesstreams objects + * otherwise we can deadlock here. + */ + IRELE(ip); + + /* + * In the case of a regular file, _xfs_filestream_update_ag() also + * takes a ref on the parent inode to keep it in-core. Release that + * too. + */ + if (item->pip) + IRELE(item->pip); + + /* Finally, free the memory allocated for the item. */ + kmem_zone_free(item_zone, item); +} + +/* + * xfs_filestream_init() is called at xfs initialisation time to set up the + * memory zone that will be used for filestream data structure allocation. + */ +int +xfs_filestream_init(void) +{ + item_zone = kmem_zone_init(sizeof(fstrm_item_t), "fstrm_item"); +#ifdef XFS_FILESTREAMS_TRACE + xfs_filestreams_trace_buf = ktrace_alloc(XFS_FSTRM_KTRACE_SIZE, KM_SLEEP); +#endif + return item_zone ? 0 : -ENOMEM; +} + +/* + * xfs_filestream_uninit() is called at xfs termination time to destroy the + * memory zone that was used for filestream data structure allocation. + */ +void +xfs_filestream_uninit(void) +{ +#ifdef XFS_FILESTREAMS_TRACE + ktrace_free(xfs_filestreams_trace_buf); +#endif + kmem_zone_destroy(item_zone); +} + +/* + * xfs_filestream_mount() is called when a file system is mounted with the + * filestream option. It is responsible for allocating the data structures + * needed to track the new file system's file streams. + */ +int +xfs_filestream_mount( + xfs_mount_t *mp) +{ + int err; + unsigned int lifetime, grp_count; + + /* + * The filestream timer tunable is currently fixed within the range of + * one second to four minutes, with five seconds being the default. The + * group count is somewhat arbitrary, but it'd be nice to adhere to the + * timer tunable to within about 10 percent. This requires at least 10 + * groups. + */ + lifetime = xfs_fstrm_centisecs * 10; + grp_count = 10; + + err = xfs_mru_cache_create(&mp->m_filestream, lifetime, grp_count, + (xfs_mru_cache_free_func_t)xfs_fstrm_free_func); + + return err; +} + +/* + * xfs_filestream_unmount() is called when a file system that was mounted with + * the filestream option is unmounted. It drains the data structures created + * to track the file system's file streams and frees all the memory that was + * allocated. + */ +void +xfs_filestream_unmount( + xfs_mount_t *mp) +{ + xfs_mru_cache_destroy(mp->m_filestream); +} + +/* + * If the mount point's m_perag array is going to be reallocated, all + * outstanding cache entries must be flushed to avoid accessing reference count + * addresses that have been freed. The call to xfs_filestream_flush() must be + * made inside the block that holds the m_peraglock in write mode to do the + * reallocation. + */ +void +xfs_filestream_flush( + xfs_mount_t *mp) +{ + /* point in time flush, so keep the reaper running */ + xfs_mru_cache_flush(mp->m_filestream, 1); +} + +/* + * Return the AG of the filestream the file or directory belongs to, or + * NULLAGNUMBER otherwise. + */ +xfs_agnumber_t +xfs_filestream_lookup_ag( + xfs_inode_t *ip) +{ + xfs_mru_cache_t *cache; + fstrm_item_t *item; + xfs_agnumber_t ag; + int ref; + + if (!(ip->i_d.di_mode & (S_IFREG | S_IFDIR))) { + ASSERT(0); + return NULLAGNUMBER; + } + + cache = ip->i_mount->m_filestream; + item = xfs_mru_cache_lookup(cache, ip->i_ino); + if (!item) { + TRACE_LOOKUP(ip->i_mount, ip, NULL, NULLAGNUMBER, 0); + return NULLAGNUMBER; + } + + ASSERT(ip == item->ip); + ag = item->ag; + ref = xfs_filestream_peek_ag(ip->i_mount, ag); + xfs_mru_cache_done(cache); + + TRACE_LOOKUP(ip->i_mount, ip, item->pip, ag, ref); + return ag; +} + +/* + * xfs_filestream_associate() should only be called to associate a regular file + * with its parent directory. Calling it with a child directory isn't + * appropriate because filestreams don't apply to entire directory hierarchies. + * Creating a file in a child directory of an existing filestream directory + * starts a new filestream with its own allocation group association. + * + * Returns < 0 on error, 0 if successful association occurred, > 0 if + * we failed to get an association because of locking issues. + */ +int +xfs_filestream_associate( + xfs_inode_t *pip, + xfs_inode_t *ip) +{ + xfs_mount_t *mp; + xfs_mru_cache_t *cache; + fstrm_item_t *item; + xfs_agnumber_t ag, rotorstep, startag; + int err = 0; + + ASSERT(pip->i_d.di_mode & S_IFDIR); + ASSERT(ip->i_d.di_mode & S_IFREG); + if (!(pip->i_d.di_mode & S_IFDIR) || !(ip->i_d.di_mode & S_IFREG)) + return -EINVAL; + + mp = pip->i_mount; + cache = mp->m_filestream; + down_read(&mp->m_peraglock); + + /* + * We have a problem, Houston. + * + * Taking the iolock here violates inode locking order - we already + * hold the ilock. Hence if we block getting this lock we may never + * wake. Unfortunately, that means if we can't get the lock, we're + * screwed in terms of getting a stream association - we can't spin + * waiting for the lock because someone else is waiting on the lock we + * hold and we cannot drop that as we are in a transaction here. + * + * Lucky for us, this inversion is rarely a problem because it's a + * directory inode that we are trying to lock here and that means the + * only place that matters is xfs_sync_inodes() and SYNC_DELWRI is + * used. i.e. freeze, remount-ro, quotasync or unmount. + * + * So, if we can't get the iolock without sleeping then just give up + */ + if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL)) { + up_read(&mp->m_peraglock); + return 1; + } + + /* If the parent directory is already in the cache, use its AG. */ + item = xfs_mru_cache_lookup(cache, pip->i_ino); + if (item) { + ASSERT(item->ip == pip); + ag = item->ag; + xfs_mru_cache_done(cache); + + TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag)); + err = _xfs_filestream_update_ag(ip, pip, ag); + + goto exit; + } + + /* + * Set the starting AG using the rotor for inode32, otherwise + * use the directory inode's AG. + */ + if (mp->m_flags & XFS_MOUNT_32BITINODES) { + rotorstep = xfs_rotorstep; + startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount; + mp->m_agfrotor = (mp->m_agfrotor + 1) % + (mp->m_sb.sb_agcount * rotorstep); + } else + startag = XFS_INO_TO_AGNO(mp, pip->i_ino); + + /* Pick a new AG for the parent inode starting at startag. */ + err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0); + if (err || ag == NULLAGNUMBER) + goto exit_did_pick; + + /* Associate the parent inode with the AG. */ + err = _xfs_filestream_update_ag(pip, NULL, ag); + if (err) + goto exit_did_pick; + + /* Associate the file inode with the AG. */ + err = _xfs_filestream_update_ag(ip, pip, ag); + if (err) + goto exit_did_pick; + + TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag)); + +exit_did_pick: + /* + * If _xfs_filestream_pick_ag() returned a valid AG, remove the + * reference it took on it, since the file and directory will have taken + * their own now if they were successfully cached. + */ + if (ag != NULLAGNUMBER) + xfs_filestream_put_ag(mp, ag); + +exit: + xfs_iunlock(pip, XFS_IOLOCK_EXCL); + up_read(&mp->m_peraglock); + return -err; +} + +/* + * Pick a new allocation group for the current file and its file stream. This + * function is called by xfs_bmap_filestreams() with the mount point's per-ag + * lock held. + */ +int +xfs_filestream_new_ag( + xfs_bmalloca_t *ap, + xfs_agnumber_t *agp) +{ + int flags, err; + xfs_inode_t *ip, *pip = NULL; + xfs_mount_t *mp; + xfs_mru_cache_t *cache; + xfs_extlen_t minlen; + fstrm_item_t *dir, *file; + xfs_agnumber_t ag = NULLAGNUMBER; + + ip = ap->ip; + mp = ip->i_mount; + cache = mp->m_filestream; + minlen = ap->alen; + *agp = NULLAGNUMBER; + + /* + * Look for the file in the cache, removing it if it's found. Doing + * this allows it to be held across the dir lookup that follows. + */ + file = xfs_mru_cache_remove(cache, ip->i_ino); + if (file) { + ASSERT(ip == file->ip); + + /* Save the file's parent inode and old AG number for later. */ + pip = file->pip; + ag = file->ag; + + /* Look for the file's directory in the cache. */ + dir = xfs_mru_cache_lookup(cache, pip->i_ino); + if (dir) { + ASSERT(pip == dir->ip); + + /* + * If the directory has already moved on to a new AG, + * use that AG as the new AG for the file. Don't + * forget to twiddle the AG refcounts to match the + * movement. + */ + if (dir->ag != file->ag) { + xfs_filestream_put_ag(mp, file->ag); + xfs_filestream_get_ag(mp, dir->ag); + *agp = file->ag = dir->ag; + } + + xfs_mru_cache_done(cache); + } + + /* + * Put the file back in the cache. If this fails, the free + * function needs to be called to tidy up in the same way as if + * the item had simply expired from the cache. + */ + err = xfs_mru_cache_insert(cache, ip->i_ino, file); + if (err) { + xfs_fstrm_free_func(ip->i_ino, file); + return err; + } + + /* + * If the file's AG was moved to the directory's new AG, there's + * nothing more to be done. + */ + if (*agp != NULLAGNUMBER) { + TRACE_MOVEAG(mp, ip, pip, + ag, xfs_filestream_peek_ag(mp, ag), + *agp, xfs_filestream_peek_ag(mp, *agp)); + return 0; + } + } + + /* + * If the file's parent directory is known, take its iolock in exclusive + * mode to prevent two sibling files from racing each other to migrate + * themselves and their parent to different AGs. + */ + if (pip) + xfs_ilock(pip, XFS_IOLOCK_EXCL); + + /* + * A new AG needs to be found for the file. If the file's parent + * directory is also known, it will be moved to the new AG as well to + * ensure that files created inside it in future use the new AG. + */ + ag = (ag == NULLAGNUMBER) ? 0 : (ag + 1) % mp->m_sb.sb_agcount; + flags = (ap->userdata ? XFS_PICK_USERDATA : 0) | + (ap->low ? XFS_PICK_LOWSPACE : 0); + + err = _xfs_filestream_pick_ag(mp, ag, agp, flags, minlen); + if (err || *agp == NULLAGNUMBER) + goto exit; + + /* + * If the file wasn't found in the file cache, then its parent directory + * inode isn't known. For this to have happened, the file must either + * be pre-existing, or it was created long enough ago that its cache + * entry has expired. This isn't the sort of usage that the filestreams + * allocator is trying to optimise, so there's no point trying to track + * its new AG somehow in the filestream data structures. + */ + if (!pip) { + TRACE_ORPHAN(mp, ip, *agp); + goto exit; + } + + /* Associate the parent inode with the AG. */ + err = _xfs_filestream_update_ag(pip, NULL, *agp); + if (err) + goto exit; + + /* Associate the file inode with the AG. */ + err = _xfs_filestream_update_ag(ip, pip, *agp); + if (err) + goto exit; + + TRACE_MOVEAG(mp, ip, pip, NULLAGNUMBER, 0, + *agp, xfs_filestream_peek_ag(mp, *agp)); + +exit: + /* + * If _xfs_filestream_pick_ag() returned a valid AG, remove the + * reference it took on it, since the file and directory will have taken + * their own now if they were successfully cached. + */ + if (*agp != NULLAGNUMBER) + xfs_filestream_put_ag(mp, *agp); + else + *agp = 0; + + if (pip) + xfs_iunlock(pip, XFS_IOLOCK_EXCL); + + return err; +} + +/* + * Remove an association between an inode and a filestream object. + * Typically this is done on last close of an unlinked file. + */ +void +xfs_filestream_deassociate( + xfs_inode_t *ip) +{ + xfs_mru_cache_t *cache = ip->i_mount->m_filestream; + + xfs_mru_cache_delete(cache, ip->i_ino); +} diff --git a/fs/xfs/xfs_filestream.h b/fs/xfs/xfs_filestream.h new file mode 100644 index 0000000..f655f7d --- /dev/null +++ b/fs/xfs/xfs_filestream.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2006-2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __XFS_FILESTREAM_H__ +#define __XFS_FILESTREAM_H__ + +#ifdef __KERNEL__ + +struct xfs_mount; +struct xfs_inode; +struct xfs_perag; +struct xfs_bmalloca; + +#ifdef XFS_FILESTREAMS_TRACE +#define XFS_FSTRM_KTRACE_INFO 1 +#define XFS_FSTRM_KTRACE_AGSCAN 2 +#define XFS_FSTRM_KTRACE_AGPICK1 3 +#define XFS_FSTRM_KTRACE_AGPICK2 4 +#define XFS_FSTRM_KTRACE_UPDATE 5 +#define XFS_FSTRM_KTRACE_FREE 6 +#define XFS_FSTRM_KTRACE_ITEM_LOOKUP 7 +#define XFS_FSTRM_KTRACE_ASSOCIATE 8 +#define XFS_FSTRM_KTRACE_MOVEAG 9 +#define XFS_FSTRM_KTRACE_ORPHAN 10 + +#define XFS_FSTRM_KTRACE_SIZE 16384 +extern ktrace_t *xfs_filestreams_trace_buf; + +#endif + +/* + * Allocation group filestream associations are tracked with per-ag atomic + * counters. These counters allow _xfs_filestream_pick_ag() to tell whether a + * particular AG already has active filestreams associated with it. The mount + * point's m_peraglock is used to protect these counters from per-ag array + * re-allocation during a growfs operation. When xfs_growfs_data_private() is + * about to reallocate the array, it calls xfs_filestream_flush() with the + * m_peraglock held in write mode. + * + * Since xfs_mru_cache_flush() guarantees that all the free functions for all + * the cache elements have finished executing before it returns, it's safe for + * the free functions to use the atomic counters without m_peraglock protection. + * This allows the implementation of xfs_fstrm_free_func() to be agnostic about + * whether it was called with the m_peraglock held in read mode, write mode or + * not held at all. The race condition this addresses is the following: + * + * - The work queue scheduler fires and pulls a filestream directory cache + * element off the LRU end of the cache for deletion, then gets pre-empted. + * - A growfs operation grabs the m_peraglock in write mode, flushes all the + * remaining items from the cache and reallocates the mount point's per-ag + * array, resetting all the counters to zero. + * - The work queue thread resumes and calls the free function for the element + * it started cleaning up earlier. In the process it decrements the + * filestreams counter for an AG that now has no references. + * + * With a shrinkfs feature, the above scenario could panic the system. + * + * All other uses of the following macros should be protected by either the + * m_peraglock held in read mode, or the cache's internal locking exposed by the + * interval between a call to xfs_mru_cache_lookup() and a call to + * xfs_mru_cache_done(). In addition, the m_peraglock must be held in read mode + * when new elements are added to the cache. + * + * Combined, these locking rules ensure that no associations will ever exist in + * the cache that reference per-ag array elements that have since been + * reallocated. + */ +STATIC_INLINE int +xfs_filestream_peek_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + return atomic_read(&mp->m_perag[agno].pagf_fstrms); +} + +STATIC_INLINE int +xfs_filestream_get_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + return atomic_inc_return(&mp->m_perag[agno].pagf_fstrms); +} + +STATIC_INLINE int +xfs_filestream_put_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + return atomic_dec_return(&mp->m_perag[agno].pagf_fstrms); +} + +/* allocation selection flags */ +typedef enum xfs_fstrm_alloc { + XFS_PICK_USERDATA = 1, + XFS_PICK_LOWSPACE = 2, +} xfs_fstrm_alloc_t; + +/* prototypes for filestream.c */ +int xfs_filestream_init(void); +void xfs_filestream_uninit(void); +int xfs_filestream_mount(struct xfs_mount *mp); +void xfs_filestream_unmount(struct xfs_mount *mp); +void xfs_filestream_flush(struct xfs_mount *mp); +xfs_agnumber_t xfs_filestream_lookup_ag(struct xfs_inode *ip); +int xfs_filestream_associate(struct xfs_inode *dip, struct xfs_inode *ip); +void xfs_filestream_deassociate(struct xfs_inode *ip); +int xfs_filestream_new_ag(struct xfs_bmalloca *ap, xfs_agnumber_t *agp); + + +/* filestreams for the inode? */ +STATIC_INLINE int +xfs_inode_is_filestream( + struct xfs_inode *ip) +{ + return (ip->i_mount->m_flags & XFS_MOUNT_FILESTREAMS) || + xfs_iflags_test(ip, XFS_IFILESTREAM) || + (ip->i_d.di_flags & XFS_DIFLAG_FILESTREAM); +} + +#endif /* __KERNEL__ */ + +#endif /* __XFS_FILESTREAM_H__ */ diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h index 1335449..ec3c9c2 100644 --- a/fs/xfs/xfs_fs.h +++ b/fs/xfs/xfs_fs.h @@ -66,6 +66,7 @@ struct fsxattr { #define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ #define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */ #define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */ +#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ #define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ /* @@ -238,6 +239,7 @@ typedef struct xfs_fsop_resblks { #define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */ #define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */ #define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */ +#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */ /* diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index b599e6b..432e823 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -44,6 +44,7 @@ #include "xfs_trans_space.h" #include "xfs_rtalloc.h" #include "xfs_rw.h" +#include "xfs_filestream.h" /* * File system operations @@ -94,6 +95,8 @@ xfs_fs_geometry( XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) | (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? XFS_FSOP_GEOM_FLAGS_SECTOR : 0) | + (xfs_sb_version_haslazysbcount(&mp->m_sb) ? + XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) | (XFS_SB_VERSION_HASATTR2(&mp->m_sb) ? XFS_FSOP_GEOM_FLAGS_ATTR2 : 0); geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? @@ -140,6 +143,8 @@ xfs_growfs_data_private( pct = in->imaxpct; if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100) return XFS_ERROR(EINVAL); + if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb))) + return error; dpct = pct - mp->m_sb.sb_imax_pct; error = xfs_read_buf(mp, mp->m_ddev_targp, XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), @@ -161,6 +166,7 @@ xfs_growfs_data_private( new = nb - mp->m_sb.sb_dblocks; oagcount = mp->m_sb.sb_agcount; if (nagcount > oagcount) { + xfs_filestream_flush(mp); down_write(&mp->m_peraglock); mp->m_perag = kmem_realloc(mp->m_perag, sizeof(xfs_perag_t) * nagcount, @@ -173,6 +179,7 @@ xfs_growfs_data_private( up_write(&mp->m_peraglock); } tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); + tp->t_flags |= XFS_TRANS_RESERVE; if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp), XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) { xfs_trans_cancel(tp, 0); @@ -328,6 +335,7 @@ xfs_growfs_data_private( be32_add(&agf->agf_length, new); ASSERT(be32_to_cpu(agf->agf_length) == be32_to_cpu(agi->agi_length)); + xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH); /* * Free the new space. */ @@ -494,8 +502,9 @@ xfs_reserve_blocks( unsigned long s; /* If inval is null, report current values and return */ - if (inval == (__uint64_t *)NULL) { + if (!outval) + return EINVAL; outval->resblks = mp->m_resblks; outval->resblks_avail = mp->m_resblks_avail; return 0; @@ -558,8 +567,10 @@ retry: } } out: - outval->resblks = mp->m_resblks; - outval->resblks_avail = mp->m_resblks_avail; + if (outval) { + outval->resblks = mp->m_resblks; + outval->resblks_avail = mp->m_resblks_avail; + } XFS_SB_UNLOCK(mp, s); if (fdblks_delta) { diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index b5feb3e..f943368 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -123,6 +123,7 @@ xfs_ialloc_ag_alloc( int blks_per_cluster; /* fs blocks per inode cluster */ xfs_btree_cur_t *cur; /* inode btree cursor */ xfs_daddr_t d; /* disk addr of buffer */ + xfs_agnumber_t agno; int error; xfs_buf_t *fbuf; /* new free inodes' buffer */ xfs_dinode_t *free; /* new free inode structure */ @@ -302,15 +303,15 @@ xfs_ialloc_ag_alloc( } be32_add(&agi->agi_count, newlen); be32_add(&agi->agi_freecount, newlen); + agno = be32_to_cpu(agi->agi_seqno); down_read(&args.mp->m_peraglock); - args.mp->m_perag[be32_to_cpu(agi->agi_seqno)].pagi_freecount += newlen; + args.mp->m_perag[agno].pagi_freecount += newlen; up_read(&args.mp->m_peraglock); agi->agi_newino = cpu_to_be32(newino); /* * Insert records describing the new inode chunk into the btree. */ - cur = xfs_btree_init_cursor(args.mp, tp, agbp, - be32_to_cpu(agi->agi_seqno), + cur = xfs_btree_init_cursor(args.mp, tp, agbp, agno, XFS_BTNUM_INO, (xfs_inode_t *)0, 0); for (thisino = newino; thisino < newino + newlen; @@ -1387,6 +1388,7 @@ xfs_ialloc_read_agi( pag = &mp->m_perag[agno]; if (!pag->pagi_init) { pag->pagi_freecount = be32_to_cpu(agi->agi_freecount); + pag->pagi_count = be32_to_cpu(agi->agi_count); pag->pagi_init = 1; } else { /* @@ -1410,3 +1412,23 @@ xfs_ialloc_read_agi( *bpp = bp; return 0; } + +/* + * Read in the agi to initialise the per-ag data in the mount structure + */ +int +xfs_ialloc_pagi_init( + xfs_mount_t *mp, /* file system mount structure */ + xfs_trans_t *tp, /* transaction pointer */ + xfs_agnumber_t agno) /* allocation group number */ +{ + xfs_buf_t *bp = NULL; + int error; + + error = xfs_ialloc_read_agi(mp, tp, agno, &bp); + if (error) + return error; + if (bp) + xfs_trans_brelse(tp, bp); + return 0; +} diff --git a/fs/xfs/xfs_ialloc.h b/fs/xfs/xfs_ialloc.h index 7f5debe..97f4040 100644 --- a/fs/xfs/xfs_ialloc.h +++ b/fs/xfs/xfs_ialloc.h @@ -149,6 +149,16 @@ xfs_ialloc_read_agi( xfs_agnumber_t agno, /* allocation group number */ struct xfs_buf **bpp); /* allocation group hdr buf */ +/* + * Read in the allocation group header to initialise the per-ag data + * in the mount structure + */ +int +xfs_ialloc_pagi_init( + struct xfs_mount *mp, /* file system mount structure */ + struct xfs_trans *tp, /* transaction pointer */ + xfs_agnumber_t agno); /* allocation group number */ + #endif /* __KERNEL__ */ #endif /* __XFS_IALLOC_H__ */ diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 3ca5d43..cdc4c28 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -48,7 +48,9 @@ #include "xfs_dir2_trace.h" #include "xfs_quota.h" #include "xfs_acl.h" +#include "xfs_filestream.h" +#include <linux/log2.h> kmem_zone_t *xfs_ifork_zone; kmem_zone_t *xfs_inode_zone; @@ -643,8 +645,7 @@ xfs_iformat_extents( ep->l1 = INT_GET(get_unaligned((__uint64_t*)&dp->l1), ARCH_CONVERT); } - xfs_bmap_trace_exlist("xfs_iformat_extents", ip, nex, - whichfork); + XFS_BMAP_TRACE_EXLIST(ip, nex, whichfork); if (whichfork != XFS_DATA_FORK || XFS_EXTFMT_INODE(ip) == XFS_EXTFMT_NOSTATE) if (unlikely(xfs_check_nostate_extents( @@ -817,6 +818,8 @@ _xfs_dic2xflags( flags |= XFS_XFLAG_EXTSZINHERIT; if (di_flags & XFS_DIFLAG_NODEFRAG) flags |= XFS_XFLAG_NODEFRAG; + if (di_flags & XFS_DIFLAG_FILESTREAM) + flags |= XFS_XFLAG_FILESTREAM; } return flags; @@ -1074,6 +1077,11 @@ xfs_iread_extents( * also returns the [locked] bp pointing to the head of the freelist * as ialloc_context. The caller should hold this buffer across * the commit and pass it back into this routine on the second call. + * + * If we are allocating quota inodes, we do not have a parent inode + * to attach to or associate with (i.e. pip == NULL) because they + * are not linked into the directory structure - they are attached + * directly to the superblock - and so have no parent. */ int xfs_ialloc( @@ -1099,7 +1107,7 @@ xfs_ialloc( * Call the space management code to pick * the on-disk inode to be allocated. */ - error = xfs_dialloc(tp, pip->i_ino, mode, okalloc, + error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc, ialloc_context, call_again, &ino); if (error != 0) { return error; @@ -1150,10 +1158,10 @@ xfs_ialloc( /* * Project ids won't be stored on disk if we are using a version 1 inode. */ - if ( (prid != 0) && (ip->i_d.di_version == XFS_DINODE_VERSION_1)) + if ((prid != 0) && (ip->i_d.di_version == XFS_DINODE_VERSION_1)) xfs_bump_ino_vers2(tp, ip); - if (XFS_INHERIT_GID(pip, vp->v_vfsp)) { + if (pip && XFS_INHERIT_GID(pip, vp->v_vfsp)) { ip->i_d.di_gid = pip->i_d.di_gid; if ((pip->i_d.di_mode & S_ISGID) && (mode & S_IFMT) == S_IFDIR) { ip->i_d.di_mode |= S_ISGID; @@ -1195,8 +1203,16 @@ xfs_ialloc( flags |= XFS_ILOG_DEV; break; case S_IFREG: + if (pip && xfs_inode_is_filestream(pip)) { + error = xfs_filestream_associate(pip, ip); + if (error < 0) + return -error; + if (!error) + xfs_iflags_set(ip, XFS_IFILESTREAM); + } + /* fall through */ case S_IFDIR: - if (unlikely(pip->i_d.di_flags & XFS_DIFLAG_ANY)) { + if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) { uint di_flags = 0; if ((mode & S_IFMT) == S_IFDIR) { @@ -1233,6 +1249,8 @@ xfs_ialloc( if ((pip->i_d.di_flags & XFS_DIFLAG_NODEFRAG) && xfs_inherit_nodefrag) di_flags |= XFS_DIFLAG_NODEFRAG; + if (pip->i_d.di_flags & XFS_DIFLAG_FILESTREAM) + di_flags |= XFS_DIFLAG_FILESTREAM; ip->i_d.di_flags |= di_flags; } /* FALLTHROUGH */ @@ -2875,9 +2893,6 @@ xfs_iextents_copy( int copied; xfs_bmbt_rec_t *dest_ep; xfs_bmbt_rec_t *ep; -#ifdef XFS_BMAP_TRACE - static char fname[] = "xfs_iextents_copy"; -#endif int i; xfs_ifork_t *ifp; int nrecs; @@ -2888,7 +2903,7 @@ xfs_iextents_copy( ASSERT(ifp->if_bytes > 0); nrecs = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); - xfs_bmap_trace_exlist(fname, ip, nrecs, whichfork); + XFS_BMAP_TRACE_EXLIST(ip, nrecs, whichfork); ASSERT(nrecs > 0); /* @@ -4184,7 +4199,7 @@ xfs_iext_realloc_direct( ifp->if_bytes = new_size; return; } - if ((new_size & (new_size - 1)) != 0) { + if (!is_power_of_2(new_size)){ rnew_size = xfs_iroundup(new_size); } if (rnew_size != ifp->if_real_bytes) { @@ -4207,7 +4222,7 @@ xfs_iext_realloc_direct( */ else { new_size += ifp->if_bytes; - if ((new_size & (new_size - 1)) != 0) { + if (!is_power_of_2(new_size)) { rnew_size = xfs_iroundup(new_size); } xfs_iext_inline_to_direct(ifp, rnew_size); diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index f75afec..012dfd4 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -379,6 +379,7 @@ xfs_iflags_test(xfs_inode_t *ip, unsigned short flags) #define XFS_ISTALE 0x0010 /* inode has been staled */ #define XFS_IRECLAIMABLE 0x0020 /* inode can be reclaimed */ #define XFS_INEW 0x0040 +#define XFS_IFILESTREAM 0x0080 /* inode is in a filestream directory */ /* * Flags for inode locking. @@ -414,19 +415,22 @@ xfs_iflags_test(xfs_inode_t *ip, unsigned short flags) * gets a lockdep subclass of 1 and the second lock will have a lockdep * subclass of 0. * - * XFS_I[O]LOCK_INUMORDER - for locking several inodes at the some time + * XFS_LOCK_INUMORDER - for locking several inodes at the some time * with xfs_lock_inodes(). This flag is used as the starting subclass * and each subsequent lock acquired will increment the subclass by one. * So the first lock acquired will have a lockdep subclass of 2, the - * second lock will have a lockdep subclass of 3, and so on. + * second lock will have a lockdep subclass of 3, and so on. It is + * the responsibility of the class builder to shift this to the correct + * portion of the lock_mode lockdep mask. */ +#define XFS_LOCK_PARENT 1 +#define XFS_LOCK_INUMORDER 2 + #define XFS_IOLOCK_SHIFT 16 -#define XFS_IOLOCK_PARENT (1 << XFS_IOLOCK_SHIFT) -#define XFS_IOLOCK_INUMORDER (2 << XFS_IOLOCK_SHIFT) +#define XFS_IOLOCK_PARENT (XFS_LOCK_PARENT << XFS_IOLOCK_SHIFT) #define XFS_ILOCK_SHIFT 24 -#define XFS_ILOCK_PARENT (1 << XFS_ILOCK_SHIFT) -#define XFS_ILOCK_INUMORDER (2 << XFS_ILOCK_SHIFT) +#define XFS_ILOCK_PARENT (XFS_LOCK_PARENT << XFS_ILOCK_SHIFT) #define XFS_IOLOCK_DEP_MASK 0x00ff0000 #define XFS_ILOCK_DEP_MASK 0xff000000 diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 3f2b9f2..bf57b75 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -451,19 +451,14 @@ xfs_iomap_write_direct( return XFS_ERROR(error); rt = XFS_IS_REALTIME_INODE(ip); - if (unlikely(rt)) { - if (!(extsz = ip->i_d.di_extsize)) - extsz = mp->m_sb.sb_rextsize; - } else { - extsz = ip->i_d.di_extsize; - } + extsz = xfs_get_extsz_hint(ip); isize = ip->i_size; if (io->io_new_size > isize) isize = io->io_new_size; - offset_fsb = XFS_B_TO_FSBT(mp, offset); - last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count))); + offset_fsb = XFS_B_TO_FSBT(mp, offset); + last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count))); if ((offset + count) > isize) { error = xfs_iomap_eof_align_last_fsb(mp, io, isize, extsz, &last_fsb); @@ -489,13 +484,13 @@ xfs_iomap_write_direct( if (unlikely(rt)) { resrtextents = qblocks = resaligned; resrtextents /= mp->m_sb.sb_rextsize; - resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0); - quota_flag = XFS_QMOPT_RES_RTBLKS; - } else { - resrtextents = 0; + resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0); + quota_flag = XFS_QMOPT_RES_RTBLKS; + } else { + resrtextents = 0; resblks = qblocks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned); - quota_flag = XFS_QMOPT_RES_REGBLKS; - } + quota_flag = XFS_QMOPT_RES_REGBLKS; + } /* * Allocate and setup the transaction @@ -666,13 +661,7 @@ xfs_iomap_write_delay( if (error) return XFS_ERROR(error); - if (XFS_IS_REALTIME_INODE(ip)) { - if (!(extsz = ip->i_d.di_extsize)) - extsz = mp->m_sb.sb_rextsize; - } else { - extsz = ip->i_d.di_extsize; - } - + extsz = xfs_get_extsz_hint(ip); offset_fsb = XFS_B_TO_FSBT(mp, offset); retry: @@ -788,18 +777,12 @@ xfs_iomap_write_allocate( nimaps = 0; while (nimaps == 0) { tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE); + tp->t_flags |= XFS_TRANS_RESERVE; nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK); error = xfs_trans_reserve(tp, nres, XFS_WRITE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES, XFS_WRITE_LOG_COUNT); - if (error == ENOSPC) { - error = xfs_trans_reserve(tp, 0, - XFS_WRITE_LOG_RES(mp), - 0, - XFS_TRANS_PERM_LOG_RES, - XFS_WRITE_LOG_COUNT); - } if (error) { xfs_trans_cancel(tp, 0); return XFS_ERROR(error); @@ -917,8 +900,8 @@ xfs_iomap_write_unwritten( * from unwritten to real. Do allocations in a loop until * we have covered the range passed in. */ - tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE); + tp->t_flags |= XFS_TRANS_RESERVE; error = xfs_trans_reserve(tp, resblks, XFS_WRITE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES, diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index e725ddd..4c2454b 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -202,6 +202,16 @@ xfs_bulkstat_one_dinode( return 0; } +STATIC int +xfs_bulkstat_one_fmt( + void __user *ubuffer, + const xfs_bstat_t *buffer) +{ + if (copy_to_user(ubuffer, buffer, sizeof(*buffer))) + return -EFAULT; + return sizeof(*buffer); +} + /* * Return stat information for one inode. * Return 0 if ok, else errno. @@ -221,6 +231,7 @@ xfs_bulkstat_one( xfs_bstat_t *buf; /* return buffer */ int error = 0; /* error value */ xfs_dinode_t *dip; /* dinode inode pointer */ + bulkstat_one_fmt_pf formatter = private_data ? : xfs_bulkstat_one_fmt; dip = (xfs_dinode_t *)dibuff; *stat = BULKSTAT_RV_NOTHING; @@ -243,14 +254,15 @@ xfs_bulkstat_one( xfs_bulkstat_one_dinode(mp, ino, dip, buf); } - if (copy_to_user(buffer, buf, sizeof(*buf))) { + error = formatter(buffer, buf); + if (error < 0) { error = EFAULT; goto out_free; } *stat = BULKSTAT_RV_DIDONE; if (ubused) - *ubused = sizeof(*buf); + *ubused = error; out_free: kmem_free(buf, sizeof(*buf)); @@ -748,6 +760,19 @@ xfs_bulkstat_single( return 0; } +int +xfs_inumbers_fmt( + void __user *ubuffer, /* buffer to write to */ + const xfs_inogrp_t *buffer, /* buffer to read from */ + long count, /* # of elements to read */ + long *written) /* # of bytes written */ +{ + if (copy_to_user(ubuffer, buffer, count * sizeof(*buffer))) + return -EFAULT; + *written = count * sizeof(*buffer); + return 0; +} + /* * Return inode number table for the filesystem. */ @@ -756,7 +781,8 @@ xfs_inumbers( xfs_mount_t *mp, /* mount point for filesystem */ xfs_ino_t *lastino, /* last inode returned */ int *count, /* size of buffer/count returned */ - xfs_inogrp_t __user *ubuffer)/* buffer with inode descriptions */ + void __user *ubuffer,/* buffer with inode descriptions */ + inumbers_fmt_pf formatter) { xfs_buf_t *agbp; xfs_agino_t agino; @@ -835,12 +861,12 @@ xfs_inumbers( bufidx++; left--; if (bufidx == bcount) { - if (copy_to_user(ubuffer, buffer, - bufidx * sizeof(*buffer))) { + long written; + if (formatter(ubuffer, buffer, bufidx, &written)) { error = XFS_ERROR(EFAULT); break; } - ubuffer += bufidx; + ubuffer += written; *count += bufidx; bufidx = 0; } @@ -862,8 +888,8 @@ xfs_inumbers( } if (!error) { if (bufidx) { - if (copy_to_user(ubuffer, buffer, - bufidx * sizeof(*buffer))) + long written; + if (formatter(ubuffer, buffer, bufidx, &written)) error = XFS_ERROR(EFAULT); else *count += bufidx; diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h index f25a288..a1f18fc 100644 --- a/fs/xfs/xfs_itable.h +++ b/fs/xfs/xfs_itable.h @@ -69,6 +69,10 @@ xfs_bulkstat_single( char __user *buffer, int *done); +typedef int (*bulkstat_one_fmt_pf)( /* used size in bytes or negative error */ + void __user *ubuffer, /* buffer to write to */ + const xfs_bstat_t *buffer); /* buffer to read from */ + int xfs_bulkstat_one( xfs_mount_t *mp, @@ -86,11 +90,25 @@ xfs_internal_inum( xfs_mount_t *mp, xfs_ino_t ino); +typedef int (*inumbers_fmt_pf)( + void __user *ubuffer, /* buffer to write to */ + const xfs_inogrp_t *buffer, /* buffer to read from */ + long count, /* # of elements to read */ + long *written); /* # of bytes written */ + +int +xfs_inumbers_fmt( + void __user *ubuffer, /* buffer to write to */ + const xfs_inogrp_t *buffer, /* buffer to read from */ + long count, /* # of elements to read */ + long *written); /* # of bytes written */ + int /* error status */ xfs_inumbers( xfs_mount_t *mp, /* mount point for filesystem */ xfs_ino_t *last, /* last inode returned */ int *count, /* size of buffer/count returned */ - xfs_inogrp_t __user *buffer);/* buffer with inode info */ + void __user *buffer, /* buffer with inode info */ + inumbers_fmt_pf formatter); #endif /* __XFS_ITABLE_H__ */ diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index c48bf61..9d4c4fb 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -817,10 +817,8 @@ xfs_log_need_covered(xfs_mount_t *mp) SPLDECL(s); int needed = 0, gen; xlog_t *log = mp->m_log; - bhv_vfs_t *vfsp = XFS_MTOVFS(mp); - if (vfs_test_for_freeze(vfsp) || XFS_FORCED_SHUTDOWN(mp) || - (vfsp->vfs_flag & VFS_RDONLY)) + if (!xfs_fs_writable(mp)) return 0; s = LOG_LOCK(log); @@ -967,14 +965,16 @@ xlog_iodone(xfs_buf_t *bp) } else if (iclog->ic_state & XLOG_STATE_IOERROR) { aborted = XFS_LI_ABORTED; } + + /* log I/O is always issued ASYNC */ + ASSERT(XFS_BUF_ISASYNC(bp)); xlog_state_done_syncing(iclog, aborted); - if (!(XFS_BUF_ISASYNC(bp))) { - /* - * Corresponding psema() will be done in bwrite(). If we don't - * vsema() here, panic. - */ - XFS_BUF_V_IODONESEMA(bp); - } + /* + * do not reference the buffer (bp) here as we could race + * with it being freed after writing the unmount record to the + * log. + */ + } /* xlog_iodone */ /* @@ -1199,11 +1199,18 @@ xlog_alloc_log(xfs_mount_t *mp, *iclogp = (xlog_in_core_t *) kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP); iclog = *iclogp; - iclog->hic_data = (xlog_in_core_2_t *) - kmem_zalloc(iclogsize, KM_SLEEP | KM_LARGE); - iclog->ic_prev = prev_iclog; prev_iclog = iclog; + + bp = xfs_buf_get_noaddr(log->l_iclog_size, mp->m_logdev_targp); + if (!XFS_BUF_CPSEMA(bp)) + ASSERT(0); + XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone); + XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb); + XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1); + iclog->ic_bp = bp; + iclog->hic_data = bp->b_addr; + log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header); head = &iclog->ic_header; @@ -1216,11 +1223,6 @@ xlog_alloc_log(xfs_mount_t *mp, INT_SET(head->h_fmt, ARCH_CONVERT, XLOG_FMT); memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t)); - bp = xfs_buf_get_empty(log->l_iclog_size, mp->m_logdev_targp); - XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone); - XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb); - XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1); - iclog->ic_bp = bp; iclog->ic_size = XFS_BUF_SIZE(bp) - log->l_iclog_hsize; iclog->ic_state = XLOG_STATE_ACTIVE; @@ -1432,7 +1434,7 @@ xlog_sync(xlog_t *log, } else { iclog->ic_bwritecnt = 1; } - XFS_BUF_SET_PTR(bp, (xfs_caddr_t) &(iclog->ic_header), count); + XFS_BUF_SET_COUNT(bp, count); XFS_BUF_SET_FSPRIVATE(bp, iclog); /* save for later */ XFS_BUF_ZEROFLAGS(bp); XFS_BUF_BUSY(bp); @@ -1528,7 +1530,6 @@ xlog_dealloc_log(xlog_t *log) } #endif next_iclog = iclog->ic_next; - kmem_free(iclog->hic_data, log->l_iclog_size); kmem_free(iclog, sizeof(xlog_in_core_t)); iclog = next_iclog; } diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 080fabf..fddbb09 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -927,6 +927,14 @@ xlog_find_tail( ASSIGN_ANY_LSN_HOST(log->l_last_sync_lsn, log->l_curr_cycle, after_umount_blk); *tail_blk = after_umount_blk; + + /* + * Note that the unmount was clean. If the unmount + * was not clean, we need to know this to rebuild the + * superblock counters from the perag headers if we + * have a filesystem using non-persistent counters. + */ + log->l_mp->m_flags |= XFS_MOUNT_WAS_CLEAN; } } diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index a96bde6..a66b398 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -202,6 +202,27 @@ xfs_mount_free( kmem_free(mp, sizeof(xfs_mount_t)); } +/* + * Check size of device based on the (data/realtime) block count. + * Note: this check is used by the growfs code as well as mount. + */ +int +xfs_sb_validate_fsb_count( + xfs_sb_t *sbp, + __uint64_t nblocks) +{ + ASSERT(PAGE_SHIFT >= sbp->sb_blocklog); + ASSERT(sbp->sb_blocklog >= BBSHIFT); + +#if XFS_BIG_BLKNOS /* Limited by ULONG_MAX of page cache index */ + if (nblocks >> (PAGE_CACHE_SHIFT - sbp->sb_blocklog) > ULONG_MAX) + return E2BIG; +#else /* Limited by UINT_MAX of sectors */ + if (nblocks << (sbp->sb_blocklog - BBSHIFT) > UINT_MAX) + return E2BIG; +#endif + return 0; +} /* * Check the validity of the SB found. @@ -284,18 +305,8 @@ xfs_mount_validate_sb( return XFS_ERROR(EFSCORRUPTED); } - ASSERT(PAGE_SHIFT >= sbp->sb_blocklog); - ASSERT(sbp->sb_blocklog >= BBSHIFT); - -#if XFS_BIG_BLKNOS /* Limited by ULONG_MAX of page cache index */ - if (unlikely( - (sbp->sb_dblocks >> (PAGE_SHIFT - sbp->sb_blocklog)) > ULONG_MAX || - (sbp->sb_rblocks >> (PAGE_SHIFT - sbp->sb_blocklog)) > ULONG_MAX)) { -#else /* Limited by UINT_MAX of sectors */ - if (unlikely( - (sbp->sb_dblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX || - (sbp->sb_rblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX)) { -#endif + if (xfs_sb_validate_fsb_count(sbp, sbp->sb_dblocks) || + xfs_sb_validate_fsb_count(sbp, sbp->sb_rblocks)) { xfs_fs_mount_cmn_err(flags, "file system too large to be mounted on this system."); return XFS_ERROR(E2BIG); @@ -632,6 +643,64 @@ xfs_mount_common(xfs_mount_t *mp, xfs_sb_t *sbp) sbp->sb_inopblock); mp->m_ialloc_blks = mp->m_ialloc_inos >> sbp->sb_inopblog; } + +/* + * xfs_initialize_perag_data + * + * Read in each per-ag structure so we can count up the number of + * allocated inodes, free inodes and used filesystem blocks as this + * information is no longer persistent in the superblock. Once we have + * this information, write it into the in-core superblock structure. + */ +STATIC int +xfs_initialize_perag_data(xfs_mount_t *mp, xfs_agnumber_t agcount) +{ + xfs_agnumber_t index; + xfs_perag_t *pag; + xfs_sb_t *sbp = &mp->m_sb; + uint64_t ifree = 0; + uint64_t ialloc = 0; + uint64_t bfree = 0; + uint64_t bfreelst = 0; + uint64_t btree = 0; + int error; + int s; + + for (index = 0; index < agcount; index++) { + /* + * read the agf, then the agi. This gets us + * all the inforamtion we need and populates the + * per-ag structures for us. + */ + error = xfs_alloc_pagf_init(mp, NULL, index, 0); + if (error) + return error; + + error = xfs_ialloc_pagi_init(mp, NULL, index); + if (error) + return error; + pag = &mp->m_perag[index]; + ifree += pag->pagi_freecount; + ialloc += pag->pagi_count; + bfree += pag->pagf_freeblks; + bfreelst += pag->pagf_flcount; + btree += pag->pagf_btreeblks; + } + /* + * Overwrite incore superblock counters with just-read data + */ + s = XFS_SB_LOCK(mp); + sbp->sb_ifree = ifree; + sbp->sb_icount = ialloc; + sbp->sb_fdblocks = bfree + bfreelst + btree; + XFS_SB_UNLOCK(mp, s); + + /* Fixup the per-cpu counters as well. */ + xfs_icsb_reinit_counters(mp); + + return 0; +} + /* * xfs_mountfs * @@ -656,7 +725,7 @@ xfs_mountfs( bhv_vnode_t *rvp = NULL; int readio_log, writeio_log; xfs_daddr_t d; - __uint64_t ret64; + __uint64_t resblks; __int64_t update_flags; uint quotamount, quotaflags; int agno; @@ -773,6 +842,7 @@ xfs_mountfs( */ if ((mfsi_flags & XFS_MFSI_SECOND) == 0 && (mp->m_flags & XFS_MOUNT_NOUUID) == 0) { + __uint64_t ret64; if (xfs_uuid_mount(mp)) { error = XFS_ERROR(EINVAL); goto error1; @@ -976,6 +1046,34 @@ xfs_mountfs( } /* + * Now the log is mounted, we know if it was an unclean shutdown or + * not. If it was, with the first phase of recovery has completed, we + * have consistent AG blocks on disk. We have not recovered EFIs yet, + * but they are recovered transactionally in the second recovery phase + * later. + * + * Hence we can safely re-initialise incore superblock counters from + * the per-ag data. These may not be correct if the filesystem was not + * cleanly unmounted, so we need to wait for recovery to finish before + * doing this. + * + * If the filesystem was cleanly unmounted, then we can trust the + * values in the superblock to be correct and we don't need to do + * anything here. + * + * If we are currently making the filesystem, the initialisation will + * fail as the perag data is in an undefined state. + */ + + if (xfs_sb_version_haslazysbcount(&mp->m_sb) && + !XFS_LAST_UNMOUNT_WAS_CLEAN(mp) && + !mp->m_sb.sb_inprogress) { + error = xfs_initialize_perag_data(mp, sbp->sb_agcount); + if (error) { + goto error2; + } + } + /* * Get and sanity-check the root inode. * Save the pointer to it in the mount structure. */ @@ -1044,6 +1142,23 @@ xfs_mountfs( if ((error = XFS_QM_MOUNT(mp, quotamount, quotaflags, mfsi_flags))) goto error4; + /* + * Now we are mounted, reserve a small amount of unused space for + * privileged transactions. This is needed so that transaction + * space required for critical operations can dip into this pool + * when at ENOSPC. This is needed for operations like create with + * attr, unwritten extent conversion at ENOSPC, etc. Data allocations + * are not allowed to use this reserved space. + * + * We default to 5% or 1024 fsbs of space reserved, whichever is smaller. + * This may drive us straight to ENOSPC on mount, but that implies + * we were already there on the last unmount. + */ + resblks = mp->m_sb.sb_dblocks; + do_div(resblks, 20); + resblks = min_t(__uint64_t, resblks, 1024); + xfs_reserve_blocks(mp, &resblks, NULL); + return 0; error4: @@ -1083,7 +1198,19 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr) #if defined(DEBUG) || defined(INDUCE_IO_ERROR) int64_t fsid; #endif + __uint64_t resblks; + /* + * We can potentially deadlock here if we have an inode cluster + * that has been freed has it's buffer still pinned in memory because + * the transaction is still sitting in a iclog. The stale inodes + * on that buffer will have their flush locks held until the + * transaction hits the disk and the callbacks run. the inode + * flush takes the flush lock unconditionally and with nothing to + * push out the iclog we will never get that unlocked. hence we + * need to force the log first. + */ + xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC); xfs_iflush_all(mp); XFS_QM_DQPURGEALL(mp, XFS_QMOPT_QUOTALL | XFS_QMOPT_UMOUNTING); @@ -1100,10 +1227,26 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr) xfs_binval(mp->m_rtdev_targp); } - xfs_unmountfs_writesb(mp); + /* + * Unreserve any blocks we have so that when we unmount we don't account + * the reserved free space as used. This is really only necessary for + * lazy superblock counting because it trusts the incore superblock + * counters to be aboslutely correct on clean unmount. + * + * We don't bother correcting this elsewhere for lazy superblock + * counting because on mount of an unclean filesystem we reconstruct the + * correct counter value and this is irrelevant. + * + * For non-lazy counter filesystems, this doesn't matter at all because + * we only every apply deltas to the superblock and hence the incore + * value does not matter.... + */ + resblks = 0; + xfs_reserve_blocks(mp, &resblks, NULL); + xfs_log_sbcount(mp, 1); + xfs_unmountfs_writesb(mp); xfs_unmountfs_wait(mp); /* wait for async bufs */ - xfs_log_unmount(mp); /* Done! No more fs ops. */ xfs_freesb(mp); @@ -1150,6 +1293,62 @@ xfs_unmountfs_wait(xfs_mount_t *mp) } int +xfs_fs_writable(xfs_mount_t *mp) +{ + bhv_vfs_t *vfsp = XFS_MTOVFS(mp); + + return !(vfs_test_for_freeze(vfsp) || XFS_FORCED_SHUTDOWN(mp) || + (vfsp->vfs_flag & VFS_RDONLY)); +} + +/* + * xfs_log_sbcount + * + * Called either periodically to keep the on disk superblock values + * roughly up to date or from unmount to make sure the values are + * correct on a clean unmount. + * + * Note this code can be called during the process of freezing, so + * we may need to use the transaction allocator which does not not + * block when the transaction subsystem is in its frozen state. + */ +int +xfs_log_sbcount( + xfs_mount_t *mp, + uint sync) +{ + xfs_trans_t *tp; + int error; + + if (!xfs_fs_writable(mp)) + return 0; + + xfs_icsb_sync_counters(mp); + + /* + * we don't need to do this if we are updating the superblock + * counters on every modification. + */ + if (!xfs_sb_version_haslazysbcount(&mp->m_sb)) + return 0; + + tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_COUNT); + error = xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0, + XFS_DEFAULT_LOG_COUNT); + if (error) { + xfs_trans_cancel(tp, 0); + return error; + } + + xfs_mod_sb(tp, XFS_SB_IFREE | XFS_SB_ICOUNT | XFS_SB_FDBLOCKS); + if (sync) + xfs_trans_set_sync(tp); + xfs_trans_commit(tp, 0); + + return 0; +} + +int xfs_unmountfs_writesb(xfs_mount_t *mp) { xfs_buf_t *sbp; @@ -1160,16 +1359,15 @@ xfs_unmountfs_writesb(xfs_mount_t *mp) * skip superblock write if fs is read-only, or * if we are doing a forced umount. */ - sbp = xfs_getsb(mp, 0); if (!(XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY || XFS_FORCED_SHUTDOWN(mp))) { - xfs_icsb_sync_counters(mp); + sbp = xfs_getsb(mp, 0); + sb = XFS_BUF_TO_SBP(sbp); /* * mark shared-readonly if desired */ - sb = XFS_BUF_TO_SBP(sbp); if (mp->m_mk_sharedro) { if (!(sb->sb_flags & XFS_SBF_READONLY)) sb->sb_flags |= XFS_SBF_READONLY; @@ -1178,6 +1376,7 @@ xfs_unmountfs_writesb(xfs_mount_t *mp) xfs_fs_cmn_err(CE_NOTE, mp, "Unmounting, marking shared read-only"); } + XFS_BUF_UNDONE(sbp); XFS_BUF_UNREAD(sbp); XFS_BUF_UNDELAYWRITE(sbp); @@ -1192,8 +1391,8 @@ xfs_unmountfs_writesb(xfs_mount_t *mp) mp, sbp, XFS_BUF_ADDR(sbp)); if (error && mp->m_mk_sharedro) xfs_fs_cmn_err(CE_ALERT, mp, "Superblock write error detected while unmounting. Filesystem may not be marked shared readonly"); + xfs_buf_relse(sbp); } - xfs_buf_relse(sbp); return error; } diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 82304b9..76ad747 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -66,6 +66,7 @@ struct xfs_bmbt_irec; struct xfs_bmap_free; struct xfs_extdelta; struct xfs_swapext; +struct xfs_mru_cache; extern struct bhv_vfsops xfs_vfsops; extern struct bhv_vnodeops xfs_vnodeops; @@ -424,17 +425,18 @@ typedef struct xfs_mount { struct notifier_block m_icsb_notifier; /* hotplug cpu notifier */ struct mutex m_icsb_mutex; /* balancer sync lock */ #endif + struct xfs_mru_cache *m_filestream; /* per-mount filestream data */ } xfs_mount_t; /* * Flags for m_flags. */ -#define XFS_MOUNT_WSYNC (1ULL << 0) /* for nfs - all metadata ops +#define XFS_MOUNT_WSYNC (1ULL << 0) /* for nfs - all metadata ops must be synchronous except for space allocations */ -#define XFS_MOUNT_INO64 (1ULL << 1) +#define XFS_MOUNT_INO64 (1ULL << 1) /* (1ULL << 2) -- currently unused */ - /* (1ULL << 3) -- currently unused */ +#define XFS_MOUNT_WAS_CLEAN (1ULL << 3) #define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem operations, typically for disk errors in metadata */ @@ -463,6 +465,8 @@ typedef struct xfs_mount { * I/O size in stat() */ #define XFS_MOUNT_NO_PERCPU_SB (1ULL << 23) /* don't use per-cpu superblock counters */ +#define XFS_MOUNT_FILESTREAMS (1ULL << 24) /* enable the filestreams + allocator */ /* @@ -511,6 +515,8 @@ xfs_preferred_iosize(xfs_mount_t *mp) #define XFS_MAXIOFFSET(mp) ((mp)->m_maxioffset) +#define XFS_LAST_UNMOUNT_WAS_CLEAN(mp) \ + ((mp)->m_flags & XFS_MOUNT_WAS_CLEAN) #define XFS_FORCED_SHUTDOWN(mp) ((mp)->m_flags & XFS_MOUNT_FS_SHUTDOWN) #define xfs_force_shutdown(m,f) \ bhv_vfs_force_shutdown((XFS_MTOVFS(m)), f, __FILE__, __LINE__) @@ -602,6 +608,7 @@ typedef struct xfs_mod_sb { extern xfs_mount_t *xfs_mount_init(void); extern void xfs_mod_sb(xfs_trans_t *, __int64_t); +extern int xfs_log_sbcount(xfs_mount_t *, uint); extern void xfs_mount_free(xfs_mount_t *mp, int remove_bhv); extern int xfs_mountfs(struct bhv_vfs *, xfs_mount_t *mp, int); extern void xfs_mountfs_check_barriers(xfs_mount_t *mp); @@ -618,12 +625,14 @@ extern int xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *, extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int); extern int xfs_readsb(xfs_mount_t *, int); extern void xfs_freesb(xfs_mount_t *); +extern int xfs_fs_writable(xfs_mount_t *); extern void xfs_do_force_shutdown(bhv_desc_t *, int, char *, int); extern int xfs_syncsub(xfs_mount_t *, int, int *); extern int xfs_sync_inodes(xfs_mount_t *, int, int *); extern xfs_agnumber_t xfs_initialize_perag(struct bhv_vfs *, xfs_mount_t *, xfs_agnumber_t); extern void xfs_xlatesb(void *, struct xfs_sb *, int, __int64_t); +extern int xfs_sb_validate_fsb_count(struct xfs_sb *, __uint64_t); extern struct xfs_dmops xfs_dmcore_stub; extern struct xfs_qmops xfs_qmcore_stub; diff --git a/fs/xfs/xfs_mru_cache.c b/fs/xfs/xfs_mru_cache.c new file mode 100644 index 0000000..7deb9e3 --- /dev/null +++ b/fs/xfs/xfs_mru_cache.c @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2006-2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "xfs.h" +#include "xfs_mru_cache.h" + +/* + * The MRU Cache data structure consists of a data store, an array of lists and + * a lock to protect its internal state. At initialisation time, the client + * supplies an element lifetime in milliseconds and a group count, as well as a + * function pointer to call when deleting elements. A data structure for + * queueing up work in the form of timed callbacks is also included. + * + * The group count controls how many lists are created, and thereby how finely + * the elements are grouped in time. When reaping occurs, all the elements in + * all the lists whose time has expired are deleted. + * + * To give an example of how this works in practice, consider a client that + * initialises an MRU Cache with a lifetime of ten seconds and a group count of + * five. Five internal lists will be created, each representing a two second + * period in time. When the first element is added, time zero for the data + * structure is initialised to the current time. + * + * All the elements added in the first two seconds are appended to the first + * list. Elements added in the third second go into the second list, and so on. + * If an element is accessed at any point, it is removed from its list and + * inserted at the head of the current most-recently-used list. + * + * The reaper function will have nothing to do until at least twelve seconds + * have elapsed since the first element was added. The reason for this is that + * if it were called at t=11s, there could be elements in the first list that + * have only been inactive for nine seconds, so it still does nothing. If it is + * called anywhere between t=12 and t=14 seconds, it will delete all the + * elements that remain in the first list. It's therefore possible for elements + * to remain in the data store even after they've been inactive for up to + * (t + t/g) seconds, where t is the inactive element lifetime and g is the + * number of groups. + * + * The above example assumes that the reaper function gets called at least once + * every (t/g) seconds. If it is called less frequently, unused elements will + * accumulate in the reap list until the reaper function is eventually called. + * The current implementation uses work queue callbacks to carefully time the + * reaper function calls, so this should happen rarely, if at all. + * + * From a design perspective, the primary reason for the choice of a list array + * representing discrete time intervals is that it's only practical to reap + * expired elements in groups of some appreciable size. This automatically + * introduces a granularity to element lifetimes, so there's no point storing an + * individual timeout with each element that specifies a more precise reap time. + * The bonus is a saving of sizeof(long) bytes of memory per element stored. + * + * The elements could have been stored in just one list, but an array of + * counters or pointers would need to be maintained to allow them to be divided + * up into discrete time groups. More critically, the process of touching or + * removing an element would involve walking large portions of the entire list, + * which would have a detrimental effect on performance. The additional memory + * requirement for the array of list heads is minimal. + * + * When an element is touched or deleted, it needs to be removed from its + * current list. Doubly linked lists are used to make the list maintenance + * portion of these operations O(1). Since reaper timing can be imprecise, + * inserts and lookups can occur when there are no free lists available. When + * this happens, all the elements on the LRU list need to be migrated to the end + * of the reap list. To keep the list maintenance portion of these operations + * O(1) also, list tails need to be accessible without walking the entire list. + * This is the reason why doubly linked list heads are used. + */ + +/* + * An MRU Cache is a dynamic data structure that stores its elements in a way + * that allows efficient lookups, but also groups them into discrete time + * intervals based on insertion time. This allows elements to be efficiently + * and automatically reaped after a fixed period of inactivity. + * + * When a client data pointer is stored in the MRU Cache it needs to be added to + * both the data store and to one of the lists. It must also be possible to + * access each of these entries via the other, i.e. to: + * + * a) Walk a list, removing the corresponding data store entry for each item. + * b) Look up a data store entry, then access its list entry directly. + * + * To achieve both of these goals, each entry must contain both a list entry and + * a key, in addition to the user's data pointer. Note that it's not a good + * idea to have the client embed one of these structures at the top of their own + * data structure, because inserting the same item more than once would most + * likely result in a loop in one of the lists. That's a sure-fire recipe for + * an infinite loop in the code. + */ +typedef struct xfs_mru_cache_elem +{ + struct list_head list_node; + unsigned long key; + void *value; +} xfs_mru_cache_elem_t; + +static kmem_zone_t *xfs_mru_elem_zone; +static struct workqueue_struct *xfs_mru_reap_wq; + +/* + * When inserting, destroying or reaping, it's first necessary to update the + * lists relative to a particular time. In the case of destroying, that time + * will be well in the future to ensure that all items are moved to the reap + * list. In all other cases though, the time will be the current time. + * + * This function enters a loop, moving the contents of the LRU list to the reap + * list again and again until either a) the lists are all empty, or b) time zero + * has been advanced sufficiently to be within the immediate element lifetime. + * + * Case a) above is detected by counting how many groups are migrated and + * stopping when they've all been moved. Case b) is detected by monitoring the + * time_zero field, which is updated as each group is migrated. + * + * The return value is the earliest time that more migration could be needed, or + * zero if there's no need to schedule more work because the lists are empty. + */ +STATIC unsigned long +_xfs_mru_cache_migrate( + xfs_mru_cache_t *mru, + unsigned long now) +{ + unsigned int grp; + unsigned int migrated = 0; + struct list_head *lru_list; + + /* Nothing to do if the data store is empty. */ + if (!mru->time_zero) + return 0; + + /* While time zero is older than the time spanned by all the lists. */ + while (mru->time_zero <= now - mru->grp_count * mru->grp_time) { + + /* + * If the LRU list isn't empty, migrate its elements to the tail + * of the reap list. + */ + lru_list = mru->lists + mru->lru_grp; + if (!list_empty(lru_list)) + list_splice_init(lru_list, mru->reap_list.prev); + + /* + * Advance the LRU group number, freeing the old LRU list to + * become the new MRU list; advance time zero accordingly. + */ + mru->lru_grp = (mru->lru_grp + 1) % mru->grp_count; + mru->time_zero += mru->grp_time; + + /* + * If reaping is so far behind that all the elements on all the + * lists have been migrated to the reap list, it's now empty. + */ + if (++migrated == mru->grp_count) { + mru->lru_grp = 0; + mru->time_zero = 0; + return 0; + } + } + + /* Find the first non-empty list from the LRU end. */ + for (grp = 0; grp < mru->grp_count; grp++) { + + /* Check the grp'th list from the LRU end. */ + lru_list = mru->lists + ((mru->lru_grp + grp) % mru->grp_count); + if (!list_empty(lru_list)) + return mru->time_zero + + (mru->grp_count + grp) * mru->grp_time; + } + + /* All the lists must be empty. */ + mru->lru_grp = 0; + mru->time_zero = 0; + return 0; +} + +/* + * When inserting or doing a lookup, an element needs to be inserted into the + * MRU list. The lists must be migrated first to ensure that they're + * up-to-date, otherwise the new element could be given a shorter lifetime in + * the cache than it should. + */ +STATIC void +_xfs_mru_cache_list_insert( + xfs_mru_cache_t *mru, + xfs_mru_cache_elem_t *elem) +{ + unsigned int grp = 0; + unsigned long now = jiffies; + + /* + * If the data store is empty, initialise time zero, leave grp set to + * zero and start the work queue timer if necessary. Otherwise, set grp + * to the number of group times that have elapsed since time zero. + */ + if (!_xfs_mru_cache_migrate(mru, now)) { + mru->time_zero = now; + if (!mru->next_reap) + mru->next_reap = mru->grp_count * mru->grp_time; + } else { + grp = (now - mru->time_zero) / mru->grp_time; + grp = (mru->lru_grp + grp) % mru->grp_count; + } + + /* Insert the element at the tail of the corresponding list. */ + list_add_tail(&elem->list_node, mru->lists + grp); +} + +/* + * When destroying or reaping, all the elements that were migrated to the reap + * list need to be deleted. For each element this involves removing it from the + * data store, removing it from the reap list, calling the client's free + * function and deleting the element from the element zone. + */ +STATIC void +_xfs_mru_cache_clear_reap_list( + xfs_mru_cache_t *mru) +{ + xfs_mru_cache_elem_t *elem, *next; + struct list_head tmp; + + INIT_LIST_HEAD(&tmp); + list_for_each_entry_safe(elem, next, &mru->reap_list, list_node) { + + /* Remove the element from the data store. */ + radix_tree_delete(&mru->store, elem->key); + + /* + * remove to temp list so it can be freed without + * needing to hold the lock + */ + list_move(&elem->list_node, &tmp); + } + mutex_spinunlock(&mru->lock, 0); + + list_for_each_entry_safe(elem, next, &tmp, list_node) { + + /* Remove the element from the reap list. */ + list_del_init(&elem->list_node); + + /* Call the client's free function with the key and value pointer. */ + mru->free_func(elem->key, elem->value); + + /* Free the element structure. */ + kmem_zone_free(xfs_mru_elem_zone, elem); + } + + mutex_spinlock(&mru->lock); +} + +/* + * We fire the reap timer every group expiry interval so + * we always have a reaper ready to run. This makes shutdown + * and flushing of the reaper easy to do. Hence we need to + * keep when the next reap must occur so we can determine + * at each interval whether there is anything we need to do. + */ +STATIC void +_xfs_mru_cache_reap( + struct work_struct *work) +{ + xfs_mru_cache_t *mru = container_of(work, xfs_mru_cache_t, work.work); + unsigned long now; + + ASSERT(mru && mru->lists); + if (!mru || !mru->lists) + return; + + mutex_spinlock(&mru->lock); + now = jiffies; + if (mru->reap_all || + (mru->next_reap && time_after(now, mru->next_reap))) { + if (mru->reap_all) + now += mru->grp_count * mru->grp_time * 2; + mru->next_reap = _xfs_mru_cache_migrate(mru, now); + _xfs_mru_cache_clear_reap_list(mru); + } + + /* + * the process that triggered the reap_all is responsible + * for restating the periodic reap if it is required. + */ + if (!mru->reap_all) + queue_delayed_work(xfs_mru_reap_wq, &mru->work, mru->grp_time); + mru->reap_all = 0; + mutex_spinunlock(&mru->lock, 0); +} + +int +xfs_mru_cache_init(void) +{ + xfs_mru_elem_zone = kmem_zone_init(sizeof(xfs_mru_cache_elem_t), + "xfs_mru_cache_elem"); + if (!xfs_mru_elem_zone) + return ENOMEM; + + xfs_mru_reap_wq = create_singlethread_workqueue("xfs_mru_cache"); + if (!xfs_mru_reap_wq) { + kmem_zone_destroy(xfs_mru_elem_zone); + return ENOMEM; + } + + return 0; +} + +void +xfs_mru_cache_uninit(void) +{ + destroy_workqueue(xfs_mru_reap_wq); + kmem_zone_destroy(xfs_mru_elem_zone); +} + +/* + * To initialise a struct xfs_mru_cache pointer, call xfs_mru_cache_create() + * with the address of the pointer, a lifetime value in milliseconds, a group + * count and a free function to use when deleting elements. This function + * returns 0 if the initialisation was successful. + */ +int +xfs_mru_cache_create( + xfs_mru_cache_t **mrup, + unsigned int lifetime_ms, + unsigned int grp_count, + xfs_mru_cache_free_func_t free_func) +{ + xfs_mru_cache_t *mru = NULL; + int err = 0, grp; + unsigned int grp_time; + + if (mrup) + *mrup = NULL; + + if (!mrup || !grp_count || !lifetime_ms || !free_func) + return EINVAL; + + if (!(grp_time = msecs_to_jiffies(lifetime_ms) / grp_count)) + return EINVAL; + + if (!(mru = kmem_zalloc(sizeof(*mru), KM_SLEEP))) + return ENOMEM; + + /* An extra list is needed to avoid reaping up to a grp_time early. */ + mru->grp_count = grp_count + 1; + mru->lists = kmem_alloc(mru->grp_count * sizeof(*mru->lists), KM_SLEEP); + + if (!mru->lists) { + err = ENOMEM; + goto exit; + } + + for (grp = 0; grp < mru->grp_count; grp++) + INIT_LIST_HEAD(mru->lists + grp); + + /* + * We use GFP_KERNEL radix tree preload and do inserts under a + * spinlock so GFP_ATOMIC is appropriate for the radix tree itself. + */ + INIT_RADIX_TREE(&mru->store, GFP_ATOMIC); + INIT_LIST_HEAD(&mru->reap_list); + spinlock_init(&mru->lock, "xfs_mru_cache"); + INIT_DELAYED_WORK(&mru->work, _xfs_mru_cache_reap); + + mru->grp_time = grp_time; + mru->free_func = free_func; + + /* start up the reaper event */ + mru->next_reap = 0; + mru->reap_all = 0; + queue_delayed_work(xfs_mru_reap_wq, &mru->work, mru->grp_time); + + *mrup = mru; + +exit: + if (err && mru && mru->lists) + kmem_free(mru->lists, mru->grp_count * sizeof(*mru->lists)); + if (err && mru) + kmem_free(mru, sizeof(*mru)); + + return err; +} + +/* + * Call xfs_mru_cache_flush() to flush out all cached entries, calling their + * free functions as they're deleted. When this function returns, the caller is + * guaranteed that all the free functions for all the elements have finished + * executing. + * + * While we are flushing, we stop the periodic reaper event from triggering. + * Normally, we want to restart this periodic event, but if we are shutting + * down the cache we do not want it restarted. hence the restart parameter + * where 0 = do not restart reaper and 1 = restart reaper. + */ +void +xfs_mru_cache_flush( + xfs_mru_cache_t *mru, + int restart) +{ + if (!mru || !mru->lists) + return; + + cancel_rearming_delayed_workqueue(xfs_mru_reap_wq, &mru->work); + + mutex_spinlock(&mru->lock); + mru->reap_all = 1; + mutex_spinunlock(&mru->lock, 0); + + queue_work(xfs_mru_reap_wq, &mru->work.work); + flush_workqueue(xfs_mru_reap_wq); + + mutex_spinlock(&mru->lock); + WARN_ON_ONCE(mru->reap_all != 0); + mru->reap_all = 0; + if (restart) + queue_delayed_work(xfs_mru_reap_wq, &mru->work, mru->grp_time); + mutex_spinunlock(&mru->lock, 0); +} + +void +xfs_mru_cache_destroy( + xfs_mru_cache_t *mru) +{ + if (!mru || !mru->lists) + return; + + /* we don't want the reaper to restart here */ + xfs_mru_cache_flush(mru, 0); + + kmem_free(mru->lists, mru->grp_count * sizeof(*mru->lists)); + kmem_free(mru, sizeof(*mru)); +} + +/* + * To insert an element, call xfs_mru_cache_insert() with the data store, the + * element's key and the client data pointer. This function returns 0 on + * success or ENOMEM if memory for the data element couldn't be allocated. + */ +int +xfs_mru_cache_insert( + xfs_mru_cache_t *mru, + unsigned long key, + void *value) +{ + xfs_mru_cache_elem_t *elem; + + ASSERT(mru && mru->lists); + if (!mru || !mru->lists) + return EINVAL; + + elem = kmem_zone_zalloc(xfs_mru_elem_zone, KM_SLEEP); + if (!elem) + return ENOMEM; + + if (radix_tree_preload(GFP_KERNEL)) { + kmem_zone_free(xfs_mru_elem_zone, elem); + return ENOMEM; + } + + INIT_LIST_HEAD(&elem->list_node); + elem->key = key; + elem->value = value; + + mutex_spinlock(&mru->lock); + + radix_tree_insert(&mru->store, key, elem); + radix_tree_preload_end(); + _xfs_mru_cache_list_insert(mru, elem); + + mutex_spinunlock(&mru->lock, 0); + + return 0; +} + +/* + * To remove an element without calling the free function, call + * xfs_mru_cache_remove() with the data store and the element's key. On success + * the client data pointer for the removed element is returned, otherwise this + * function will return a NULL pointer. + */ +void * +xfs_mru_cache_remove( + xfs_mru_cache_t *mru, + unsigned long key) +{ + xfs_mru_cache_elem_t *elem; + void *value = NULL; + + ASSERT(mru && mru->lists); + if (!mru || !mru->lists) + return NULL; + + mutex_spinlock(&mru->lock); + elem = radix_tree_delete(&mru->store, key); + if (elem) { + value = elem->value; + list_del(&elem->list_node); + } + + mutex_spinunlock(&mru->lock, 0); + + if (elem) + kmem_zone_free(xfs_mru_elem_zone, elem); + + return value; +} + +/* + * To remove and element and call the free function, call xfs_mru_cache_delete() + * with the data store and the element's key. + */ +void +xfs_mru_cache_delete( + xfs_mru_cache_t *mru, + unsigned long key) +{ + void *value = xfs_mru_cache_remove(mru, key); + + if (value) + mru->free_func(key, value); +} + +/* + * To look up an element using its key, call xfs_mru_cache_lookup() with the + * data store and the element's key. If found, the element will be moved to the + * head of the MRU list to indicate that it's been touched. + * + * The internal data structures are protected by a spinlock that is STILL HELD + * when this function returns. Call xfs_mru_cache_done() to release it. Note + * that it is not safe to call any function that might sleep in the interim. + * + * The implementation could have used reference counting to avoid this + * restriction, but since most clients simply want to get, set or test a member + * of the returned data structure, the extra per-element memory isn't warranted. + * + * If the element isn't found, this function returns NULL and the spinlock is + * released. xfs_mru_cache_done() should NOT be called when this occurs. + */ +void * +xfs_mru_cache_lookup( + xfs_mru_cache_t *mru, + unsigned long key) +{ + xfs_mru_cache_elem_t *elem; + + ASSERT(mru && mru->lists); + if (!mru || !mru->lists) + return NULL; + + mutex_spinlock(&mru->lock); + elem = radix_tree_lookup(&mru->store, key); + if (elem) { + list_del(&elem->list_node); + _xfs_mru_cache_list_insert(mru, elem); + } + else + mutex_spinunlock(&mru->lock, 0); + + return elem ? elem->value : NULL; +} + +/* + * To look up an element using its key, but leave its location in the internal + * lists alone, call xfs_mru_cache_peek(). If the element isn't found, this + * function returns NULL. + * + * See the comments above the declaration of the xfs_mru_cache_lookup() function + * for important locking information pertaining to this call. + */ +void * +xfs_mru_cache_peek( + xfs_mru_cache_t *mru, + unsigned long key) +{ + xfs_mru_cache_elem_t *elem; + + ASSERT(mru && mru->lists); + if (!mru || !mru->lists) + return NULL; + + mutex_spinlock(&mru->lock); + elem = radix_tree_lookup(&mru->store, key); + if (!elem) + mutex_spinunlock(&mru->lock, 0); + + return elem ? elem->value : NULL; +} + +/* + * To release the internal data structure spinlock after having performed an + * xfs_mru_cache_lookup() or an xfs_mru_cache_peek(), call xfs_mru_cache_done() + * with the data store pointer. + */ +void +xfs_mru_cache_done( + xfs_mru_cache_t *mru) +{ + mutex_spinunlock(&mru->lock, 0); +} diff --git a/fs/xfs/xfs_mru_cache.h b/fs/xfs/xfs_mru_cache.h new file mode 100644 index 0000000..624fd10 --- /dev/null +++ b/fs/xfs/xfs_mru_cache.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2007 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __XFS_MRU_CACHE_H__ +#define __XFS_MRU_CACHE_H__ + + +/* Function pointer type for callback to free a client's data pointer. */ +typedef void (*xfs_mru_cache_free_func_t)(unsigned long, void*); + +typedef struct xfs_mru_cache +{ + struct radix_tree_root store; /* Core storage data structure. */ + struct list_head *lists; /* Array of lists, one per grp. */ + struct list_head reap_list; /* Elements overdue for reaping. */ + spinlock_t lock; /* Lock to protect this struct. */ + unsigned int grp_count; /* Number of discrete groups. */ + unsigned int grp_time; /* Time period spanned by grps. */ + unsigned int lru_grp; /* Group containing time zero. */ + unsigned long time_zero; /* Time first element was added. */ + unsigned long next_reap; /* Time that the reaper should + next do something. */ + unsigned int reap_all; /* if set, reap all lists */ + xfs_mru_cache_free_func_t free_func; /* Function pointer for freeing. */ + struct delayed_work work; /* Workqueue data for reaping. */ +} xfs_mru_cache_t; + +int xfs_mru_cache_init(void); +void xfs_mru_cache_uninit(void); +int xfs_mru_cache_create(struct xfs_mru_cache **mrup, unsigned int lifetime_ms, + unsigned int grp_count, + xfs_mru_cache_free_func_t free_func); +void xfs_mru_cache_flush(xfs_mru_cache_t *mru, int restart); +void xfs_mru_cache_destroy(struct xfs_mru_cache *mru); +int xfs_mru_cache_insert(struct xfs_mru_cache *mru, unsigned long key, + void *value); +void * xfs_mru_cache_remove(struct xfs_mru_cache *mru, unsigned long key); +void xfs_mru_cache_delete(struct xfs_mru_cache *mru, unsigned long key); +void *xfs_mru_cache_lookup(struct xfs_mru_cache *mru, unsigned long key); +void *xfs_mru_cache_peek(struct xfs_mru_cache *mru, unsigned long key); +void xfs_mru_cache_done(struct xfs_mru_cache *mru); + +#endif /* __XFS_MRU_CACHE_H__ */ diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index b3a5f07..47082c0 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -1882,11 +1882,13 @@ xfs_growfs_rt( (nrblocks = in->newblocks) <= sbp->sb_rblocks || (sbp->sb_rblocks && (in->extsize != sbp->sb_rextsize))) return XFS_ERROR(EINVAL); + if ((error = xfs_sb_validate_fsb_count(sbp, nrblocks))) + return error; /* * Read in the last block of the device, make sure it exists. */ error = xfs_read_buf(mp, mp->m_rtdev_targp, - XFS_FSB_TO_BB(mp, in->newblocks - 1), + XFS_FSB_TO_BB(mp, nrblocks - 1), XFS_FSB_TO_BB(mp, 1), 0, &bp); if (error) return error; diff --git a/fs/xfs/xfs_rw.h b/fs/xfs/xfs_rw.h index 188b296..fcf28db 100644 --- a/fs/xfs/xfs_rw.h +++ b/fs/xfs/xfs_rw.h @@ -72,6 +72,34 @@ xfs_fsb_to_db_io(struct xfs_iocore *io, xfs_fsblock_t fsb) } /* + * Flags for xfs_free_eofblocks + */ +#define XFS_FREE_EOF_LOCK (1<<0) +#define XFS_FREE_EOF_NOLOCK (1<<1) + + +/* + * helper function to extract extent size hint from inode + */ +STATIC_INLINE xfs_extlen_t +xfs_get_extsz_hint( + xfs_inode_t *ip) +{ + xfs_extlen_t extsz; + + if (unlikely(ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) { + extsz = (ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE) + ? ip->i_d.di_extsize + : ip->i_mount->m_sb.sb_rextsize; + ASSERT(extsz); + } else { + extsz = (ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE) + ? ip->i_d.di_extsize : 0; + } + return extsz; +} + +/* * Prototypes for functions in xfs_rw.c. */ extern int xfs_write_clear_setuid(struct xfs_inode *ip); @@ -91,10 +119,12 @@ extern void xfs_ioerror_alert(char *func, struct xfs_mount *mp, extern int xfs_rwlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); extern void xfs_rwunlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); extern int xfs_setattr(bhv_desc_t *, bhv_vattr_t *vap, int flags, - cred_t *credp); + cred_t *credp); extern int xfs_change_file_space(bhv_desc_t *bdp, int cmd, xfs_flock64_t *bf, - xfs_off_t offset, cred_t *credp, int flags); + xfs_off_t offset, cred_t *credp, int flags); extern int xfs_set_dmattrs(bhv_desc_t *bdp, u_int evmask, u_int16_t state, - cred_t *credp); + cred_t *credp); +extern int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip, + int flags); #endif /* __XFS_RW_H__ */ diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h index 467854b..ef42537 100644 --- a/fs/xfs/xfs_sb.h +++ b/fs/xfs/xfs_sb.h @@ -74,12 +74,13 @@ struct xfs_mount; */ #define XFS_SB_VERSION2_REALFBITS 0x00ffffff /* Mask: features */ #define XFS_SB_VERSION2_RESERVED1BIT 0x00000001 -#define XFS_SB_VERSION2_RESERVED2BIT 0x00000002 +#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */ #define XFS_SB_VERSION2_RESERVED4BIT 0x00000004 #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ #define XFS_SB_VERSION2_OKREALFBITS \ - (XFS_SB_VERSION2_ATTR2BIT) + (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ + XFS_SB_VERSION2_ATTR2BIT) #define XFS_SB_VERSION2_OKSASHFBITS \ (0) #define XFS_SB_VERSION2_OKREALBITS \ @@ -181,6 +182,9 @@ typedef enum { #define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN) #define XFS_SB_UNIT XFS_SB_MVAL(UNIT) #define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH) +#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT) +#define XFS_SB_IFREE XFS_SB_MVAL(IFREE) +#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS) #define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2) #define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT) #define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1) @@ -188,7 +192,7 @@ typedef enum { (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \ - XFS_SB_FEATURES2) + XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2) /* @@ -414,6 +418,12 @@ static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp) * ((sbp)->sb_features2 & XFS_SB_VERSION2_FUNBIT) */ +static inline int xfs_sb_version_haslazysbcount(xfs_sb_t *sbp) +{ + return (XFS_SB_VERSION_HASMOREBITS(sbp) && \ + ((sbp)->sb_features2 & XFS_SB_VERSION2_LAZYSBCOUNTBIT)); +} + #define XFS_SB_VERSION_HASATTR2(sbp) xfs_sb_version_hasattr2(sbp) static inline int xfs_sb_version_hasattr2(xfs_sb_t *sbp) { diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index cc2d609..356d662 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -427,6 +427,14 @@ undo_blocks: * * Mark the transaction structure to indicate that the superblock * needs to be updated before committing. + * + * Because we may not be keeping track of allocated/free inodes and + * used filesystem blocks in the superblock, we do not mark the + * superblock dirty in this transaction if we modify these fields. + * We still need to update the transaction deltas so that they get + * applied to the incore superblock, but we don't want them to + * cause the superblock to get locked and logged if these are the + * only fields in the superblock that the transaction modifies. */ void xfs_trans_mod_sb( @@ -434,13 +442,19 @@ xfs_trans_mod_sb( uint field, int64_t delta) { + uint32_t flags = (XFS_TRANS_DIRTY|XFS_TRANS_SB_DIRTY); + xfs_mount_t *mp = tp->t_mountp; switch (field) { case XFS_TRANS_SB_ICOUNT: tp->t_icount_delta += delta; + if (xfs_sb_version_haslazysbcount(&mp->m_sb)) + flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_IFREE: tp->t_ifree_delta += delta; + if (xfs_sb_version_haslazysbcount(&mp->m_sb)) + flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_FDBLOCKS: /* @@ -453,6 +467,8 @@ xfs_trans_mod_sb( ASSERT(tp->t_blk_res_used <= tp->t_blk_res); } tp->t_fdblocks_delta += delta; + if (xfs_sb_version_haslazysbcount(&mp->m_sb)) + flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_RES_FDBLOCKS: /* @@ -462,6 +478,8 @@ xfs_trans_mod_sb( */ ASSERT(delta < 0); tp->t_res_fdblocks_delta += delta; + if (xfs_sb_version_haslazysbcount(&mp->m_sb)) + flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_FREXTENTS: /* @@ -515,7 +533,7 @@ xfs_trans_mod_sb( return; } - tp->t_flags |= (XFS_TRANS_SB_DIRTY | XFS_TRANS_DIRTY); + tp->t_flags |= flags; } /* @@ -544,18 +562,23 @@ xfs_trans_apply_sb_deltas( (tp->t_ag_freeblks_delta + tp->t_ag_flist_delta + tp->t_ag_btree_delta)); - if (tp->t_icount_delta != 0) { - INT_MOD(sbp->sb_icount, ARCH_CONVERT, tp->t_icount_delta); - } - if (tp->t_ifree_delta != 0) { - INT_MOD(sbp->sb_ifree, ARCH_CONVERT, tp->t_ifree_delta); - } + /* + * Only update the superblock counters if we are logging them + */ + if (!xfs_sb_version_haslazysbcount(&(tp->t_mountp->m_sb))) { + if (tp->t_icount_delta != 0) { + INT_MOD(sbp->sb_icount, ARCH_CONVERT, tp->t_icount_delta); + } + if (tp->t_ifree_delta != 0) { + INT_MOD(sbp->sb_ifree, ARCH_CONVERT, tp->t_ifree_delta); + } - if (tp->t_fdblocks_delta != 0) { - INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_fdblocks_delta); - } - if (tp->t_res_fdblocks_delta != 0) { - INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_res_fdblocks_delta); + if (tp->t_fdblocks_delta != 0) { + INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_fdblocks_delta); + } + if (tp->t_res_fdblocks_delta != 0) { + INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_res_fdblocks_delta); + } } if (tp->t_frextents_delta != 0) { @@ -615,11 +638,23 @@ xfs_trans_apply_sb_deltas( } /* - * xfs_trans_unreserve_and_mod_sb() is called to release unused - * reservations and apply superblock counter changes to the in-core - * superblock. + * xfs_trans_unreserve_and_mod_sb() is called to release unused reservations + * and apply superblock counter changes to the in-core superblock. The + * t_res_fdblocks_delta and t_res_frextents_delta fields are explicitly NOT + * applied to the in-core superblock. The idea is that that has already been + * done. * * This is done efficiently with a single call to xfs_mod_incore_sb_batch(). + * However, we have to ensure that we only modify each superblock field only + * once because the application of the delta values may not be atomic. That can + * lead to ENOSPC races occurring if we have two separate modifcations of the + * free space counter to put back the entire reservation and then take away + * what we used. + * + * If we are not logging superblock counters, then the inode allocated/free and + * used block counts are not updated in the on disk superblock. In this case, + * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we + * still need to update the incore superblock with the changes. */ STATIC void xfs_trans_unreserve_and_mod_sb( @@ -627,40 +662,49 @@ xfs_trans_unreserve_and_mod_sb( { xfs_mod_sb_t msb[14]; /* If you add cases, add entries */ xfs_mod_sb_t *msbp; + xfs_mount_t *mp = tp->t_mountp; /* REFERENCED */ int error; int rsvd; + int64_t blkdelta = 0; + int64_t rtxdelta = 0; msbp = msb; rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; - /* - * Release any reserved blocks. Any that were allocated - * will be taken back again by fdblocks_delta below. - */ - if (tp->t_blk_res > 0) { + /* calculate free blocks delta */ + if (tp->t_blk_res > 0) + blkdelta = tp->t_blk_res; + + if ((tp->t_fdblocks_delta != 0) && + (xfs_sb_version_haslazysbcount(&mp->m_sb) || + (tp->t_flags & XFS_TRANS_SB_DIRTY))) + blkdelta += tp->t_fdblocks_delta; + + if (blkdelta != 0) { msbp->msb_field = XFS_SBS_FDBLOCKS; - msbp->msb_delta = tp->t_blk_res; + msbp->msb_delta = blkdelta; msbp++; } - /* - * Release any reserved real time extents . Any that were - * allocated will be taken back again by frextents_delta below. - */ - if (tp->t_rtx_res > 0) { + /* calculate free realtime extents delta */ + if (tp->t_rtx_res > 0) + rtxdelta = tp->t_rtx_res; + + if ((tp->t_frextents_delta != 0) && + (tp->t_flags & XFS_TRANS_SB_DIRTY)) + rtxdelta += tp->t_frextents_delta; + + if (rtxdelta != 0) { msbp->msb_field = XFS_SBS_FREXTENTS; - msbp->msb_delta = tp->t_rtx_res; + msbp->msb_delta = rtxdelta; msbp++; } - /* - * Apply any superblock modifications to the in-core version. - * The t_res_fdblocks_delta and t_res_frextents_delta fields are - * explicitly NOT applied to the in-core superblock. - * The idea is that that has already been done. - */ - if (tp->t_flags & XFS_TRANS_SB_DIRTY) { + /* apply remaining deltas */ + + if (xfs_sb_version_haslazysbcount(&mp->m_sb) || + (tp->t_flags & XFS_TRANS_SB_DIRTY)) { if (tp->t_icount_delta != 0) { msbp->msb_field = XFS_SBS_ICOUNT; msbp->msb_delta = tp->t_icount_delta; @@ -671,16 +715,9 @@ xfs_trans_unreserve_and_mod_sb( msbp->msb_delta = tp->t_ifree_delta; msbp++; } - if (tp->t_fdblocks_delta != 0) { - msbp->msb_field = XFS_SBS_FDBLOCKS; - msbp->msb_delta = tp->t_fdblocks_delta; - msbp++; - } - if (tp->t_frextents_delta != 0) { - msbp->msb_field = XFS_SBS_FREXTENTS; - msbp->msb_delta = tp->t_frextents_delta; - msbp++; - } + } + + if (tp->t_flags & XFS_TRANS_SB_DIRTY) { if (tp->t_dblocks_delta != 0) { msbp->msb_field = XFS_SBS_DBLOCKS; msbp->msb_delta = tp->t_dblocks_delta; diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 7dfcc45..0e26e72 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -94,7 +94,8 @@ typedef struct xfs_trans_header { #define XFS_TRANS_GROWFSRT_ZERO 38 #define XFS_TRANS_GROWFSRT_FREE 39 #define XFS_TRANS_SWAPEXT 40 -#define XFS_TRANS_TYPE_MAX 40 +#define XFS_TRANS_SB_COUNT 41 +#define XFS_TRANS_TYPE_MAX 41 /* new transaction types need to be reflected in xfs_logprint(8) */ diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c index 65c5612..11f5ea2 100644 --- a/fs/xfs/xfs_vfsops.c +++ b/fs/xfs/xfs_vfsops.c @@ -51,6 +51,8 @@ #include "xfs_acl.h" #include "xfs_attr.h" #include "xfs_clnt.h" +#include "xfs_mru_cache.h" +#include "xfs_filestream.h" #include "xfs_fsops.h" STATIC int xfs_sync(bhv_desc_t *, int, cred_t *); @@ -81,6 +83,8 @@ xfs_init(void) xfs_dabuf_zone = kmem_zone_init(sizeof(xfs_dabuf_t), "xfs_dabuf"); xfs_ifork_zone = kmem_zone_init(sizeof(xfs_ifork_t), "xfs_ifork"); xfs_acl_zone_init(xfs_acl_zone, "xfs_acl"); + xfs_mru_cache_init(); + xfs_filestream_init(); /* * The size of the zone allocated buf log item is the maximum @@ -164,6 +168,8 @@ xfs_cleanup(void) xfs_cleanup_procfs(); xfs_sysctl_unregister(); xfs_refcache_destroy(); + xfs_filestream_uninit(); + xfs_mru_cache_uninit(); xfs_acl_zone_destroy(xfs_acl_zone); #ifdef XFS_DIR2_TRACE @@ -320,6 +326,9 @@ xfs_start_flags( else mp->m_flags &= ~XFS_MOUNT_BARRIER; + if (ap->flags2 & XFSMNT2_FILESTREAMS) + mp->m_flags |= XFS_MOUNT_FILESTREAMS; + return 0; } @@ -518,6 +527,9 @@ xfs_mount( if (mp->m_flags & XFS_MOUNT_BARRIER) xfs_mountfs_check_barriers(mp); + if ((error = xfs_filestream_mount(mp))) + goto error2; + error = XFS_IOINIT(vfsp, args, flags); if (error) goto error2; @@ -575,6 +587,13 @@ xfs_unmount( */ xfs_refcache_purge_mp(mp); + /* + * Blow away any referenced inode in the filestreams cache. + * This can and will cause log traffic as inodes go inactive + * here. + */ + xfs_filestream_unmount(mp); + XFS_bflush(mp->m_ddev_targp); error = xfs_unmount_flush(mp, 0); if (error) @@ -640,7 +659,7 @@ xfs_quiesce_fs( * we can write the unmount record. */ do { - xfs_syncsub(mp, SYNC_REMOUNT|SYNC_ATTR|SYNC_WAIT, NULL); + xfs_syncsub(mp, SYNC_INODE_QUIESCE, NULL); pincount = xfs_flush_buftarg(mp->m_ddev_targp, 1); if (!pincount) { delay(50); @@ -651,6 +670,30 @@ xfs_quiesce_fs( return 0; } +/* + * Second stage of a quiesce. The data is already synced, now we have to take + * care of the metadata. New transactions are already blocked, so we need to + * wait for any remaining transactions to drain out before proceding. + */ +STATIC void +xfs_attr_quiesce( + xfs_mount_t *mp) +{ + /* wait for all modifications to complete */ + while (atomic_read(&mp->m_active_trans) > 0) + delay(100); + + /* flush inodes and push all remaining buffers out to disk */ + xfs_quiesce_fs(mp); + + ASSERT_ALWAYS(atomic_read(&mp->m_active_trans) == 0); + + /* Push the superblock and write an unmount record */ + xfs_log_sbcount(mp, 1); + xfs_log_unmount_write(mp); + xfs_unmountfs_writesb(mp); +} + STATIC int xfs_mntupdate( bhv_desc_t *bdp, @@ -670,10 +713,9 @@ xfs_mntupdate( mp->m_flags &= ~XFS_MOUNT_BARRIER; } } else if (!(vfsp->vfs_flag & VFS_RDONLY)) { /* rw -> ro */ - bhv_vfs_sync(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL); - xfs_quiesce_fs(mp); - xfs_log_unmount_write(mp); - xfs_unmountfs_writesb(mp); + xfs_filestream_flush(mp); + bhv_vfs_sync(vfsp, SYNC_DATA_QUIESCE, NULL); + xfs_attr_quiesce(mp); vfsp->vfs_flag |= VFS_RDONLY; } return 0; @@ -887,6 +929,9 @@ xfs_sync( { xfs_mount_t *mp = XFS_BHVTOM(bdp); + if (flags & SYNC_IOWAIT) + xfs_filestream_flush(mp); + return xfs_syncsub(mp, flags, NULL); } @@ -1128,58 +1173,41 @@ xfs_sync_inodes( * in the inode list. */ - if ((flags & SYNC_CLOSE) && (vp != NULL)) { - /* - * This is the shutdown case. We just need to - * flush and invalidate all the pages associated - * with the inode. Drop the inode lock since - * we can't hold it across calls to the buffer - * cache. - * - * We don't set the VREMAPPING bit in the vnode - * here, because we don't hold the vnode lock - * exclusively. It doesn't really matter, though, - * because we only come here when we're shutting - * down anyway. - */ - xfs_iunlock(ip, XFS_ILOCK_SHARED); - - if (XFS_FORCED_SHUTDOWN(mp)) { - bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF); - } else { - error = bhv_vop_flushinval_pages(vp, 0, -1, FI_REMAPF); + /* + * If we have to flush data or wait for I/O completion + * we need to drop the ilock that we currently hold. + * If we need to drop the lock, insert a marker if we + * have not already done so. + */ + if ((flags & (SYNC_CLOSE|SYNC_IOWAIT)) || + ((flags & SYNC_DELWRI) && VN_DIRTY(vp))) { + if (mount_locked) { + IPOINTER_INSERT(ip, mp); } + xfs_iunlock(ip, XFS_ILOCK_SHARED); - xfs_ilock(ip, XFS_ILOCK_SHARED); - - } else if ((flags & SYNC_DELWRI) && (vp != NULL)) { - if (VN_DIRTY(vp)) { - /* We need to have dropped the lock here, - * so insert a marker if we have not already - * done so. - */ - if (mount_locked) { - IPOINTER_INSERT(ip, mp); - } - - /* - * Drop the inode lock since we can't hold it - * across calls to the buffer cache. - */ - xfs_iunlock(ip, XFS_ILOCK_SHARED); + if (flags & SYNC_CLOSE) { + /* Shutdown case. Flush and invalidate. */ + if (XFS_FORCED_SHUTDOWN(mp)) + bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF); + else + error = bhv_vop_flushinval_pages(vp, 0, + -1, FI_REMAPF); + } else if ((flags & SYNC_DELWRI) && VN_DIRTY(vp)) { error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, fflag, FI_NONE); - xfs_ilock(ip, XFS_ILOCK_SHARED); } + /* + * When freezing, we need to wait ensure all I/O (including direct + * I/O) is complete to ensure no further data modification can take + * place after this point + */ + if (flags & SYNC_IOWAIT) + vn_iowait(vp); + + xfs_ilock(ip, XFS_ILOCK_SHARED); } - /* - * When freezing, we need to wait ensure all I/O (including direct - * I/O) is complete to ensure no further data modification can take - * place after this point - */ - if (flags & SYNC_IOWAIT) - vn_iowait(vp); if (flags & SYNC_BDFLUSH) { if ((flags & SYNC_ATTR) && @@ -1514,6 +1542,15 @@ xfs_syncsub( } /* + * If asked, update the disk superblock with incore counter values if we + * are using non-persistent counters so that they don't get too far out + * of sync if we crash or get a forced shutdown. We don't want to force + * this to disk, just get a transaction into the iclogs.... + */ + if (flags & SYNC_SUPER) + xfs_log_sbcount(mp, 0); + + /* * Now check to see if the log needs a "dummy" transaction. */ @@ -1645,6 +1682,7 @@ xfs_vget( * in stat(). */ #define MNTOPT_ATTR2 "attr2" /* do use attr2 attribute format */ #define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */ +#define MNTOPT_FILESTREAM "filestreams" /* use filestreams allocator */ STATIC unsigned long suffix_strtoul(char *s, char **endp, unsigned int base) @@ -1831,6 +1869,8 @@ xfs_parseargs( args->flags |= XFSMNT_ATTR2; } else if (!strcmp(this_char, MNTOPT_NOATTR2)) { args->flags &= ~XFSMNT_ATTR2; + } else if (!strcmp(this_char, MNTOPT_FILESTREAM)) { + args->flags2 |= XFSMNT2_FILESTREAMS; } else if (!strcmp(this_char, "osyncisdsync")) { /* no-op, this is now the default */ cmn_err(CE_WARN, @@ -1959,9 +1999,9 @@ xfs_showargs( } /* - * Second stage of a freeze. The data is already frozen, now we have to take - * care of the metadata. New transactions are already blocked, so we need to - * wait for any remaining transactions to drain out before proceding. + * Second stage of a freeze. The data is already frozen so we only + * need to take care of themetadata. Once that's done write a dummy + * record to dirty the log in case of a crash while frozen. */ STATIC void xfs_freeze( @@ -1969,18 +2009,7 @@ xfs_freeze( { xfs_mount_t *mp = XFS_BHVTOM(bdp); - /* wait for all modifications to complete */ - while (atomic_read(&mp->m_active_trans) > 0) - delay(100); - - /* flush inodes and push all remaining buffers out to disk */ - xfs_quiesce_fs(mp); - - ASSERT_ALWAYS(atomic_read(&mp->m_active_trans) == 0); - - /* Push the superblock and write an unmount record */ - xfs_log_unmount_write(mp); - xfs_unmountfs_writesb(mp); + xfs_attr_quiesce(mp); xfs_fs_log_dummy(mp); } diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 70bc82f..1a5ad8c 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -51,6 +51,7 @@ #include "xfs_refcache.h" #include "xfs_trans_space.h" #include "xfs_log_priv.h" +#include "xfs_filestream.h" STATIC int xfs_open( @@ -77,36 +78,6 @@ xfs_open( return 0; } -STATIC int -xfs_close( - bhv_desc_t *bdp, - int flags, - lastclose_t lastclose, - cred_t *credp) -{ - bhv_vnode_t *vp = BHV_TO_VNODE(bdp); - xfs_inode_t *ip = XFS_BHVTOI(bdp); - - if (XFS_FORCED_SHUTDOWN(ip->i_mount)) - return XFS_ERROR(EIO); - - if (lastclose != L_TRUE || !VN_ISREG(vp)) - return 0; - - /* - * If we previously truncated this file and removed old data in - * the process, we want to initiate "early" writeout on the last - * close. This is an attempt to combat the notorious NULL files - * problem which is particularly noticable from a truncate down, - * buffered (re-)write (delalloc), followed by a crash. What we - * are effectively doing here is significantly reducing the time - * window where we'd otherwise be exposed to that problem. - */ - if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0) - return bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE); - return 0; -} - /* * xfs_getattr */ @@ -183,9 +154,8 @@ xfs_getattr( * realtime extent size or the realtime volume's * extent size. */ - vap->va_blocksize = ip->i_d.di_extsize ? - (ip->i_d.di_extsize << mp->m_sb.sb_blocklog) : - (mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog); + vap->va_blocksize = + xfs_get_extsz_hint(ip) << mp->m_sb.sb_blocklog; } break; } @@ -619,7 +589,30 @@ xfs_setattr( code = xfs_igrow_start(ip, vap->va_size, credp); } xfs_iunlock(ip, XFS_ILOCK_EXCL); - vn_iowait(vp); /* wait for the completion of any pending DIOs */ + + /* + * We are going to log the inode size change in this + * transaction so any previous writes that are beyond the on + * disk EOF and the new EOF that have not been written out need + * to be written here. If we do not write the data out, we + * expose ourselves to the null files problem. + * + * Only flush from the on disk size to the smaller of the in + * memory file size or the new size as that's the range we + * really care about here and prevents waiting for other data + * not within the range we care about here. + */ + if (!code && + (ip->i_size != ip->i_d.di_size) && + (vap->va_size > ip->i_d.di_size)) { + code = bhv_vop_flush_pages(XFS_ITOV(ip), + ip->i_d.di_size, vap->va_size, + XFS_B_ASYNC, FI_NONE); + } + + /* wait for all I/O to complete */ + vn_iowait(vp); + if (!code) code = xfs_itruncate_data(ip, vap->va_size); if (code) { @@ -814,6 +807,8 @@ xfs_setattr( di_flags |= XFS_DIFLAG_PROJINHERIT; if (vap->va_xflags & XFS_XFLAG_NODEFRAG) di_flags |= XFS_DIFLAG_NODEFRAG; + if (vap->va_xflags & XFS_XFLAG_FILESTREAM) + di_flags |= XFS_DIFLAG_FILESTREAM; if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) { if (vap->va_xflags & XFS_XFLAG_RTINHERIT) di_flags |= XFS_DIFLAG_RTINHERIT; @@ -1201,13 +1196,15 @@ xfs_fsync( } /* - * This is called by xfs_inactive to free any blocks beyond eof, - * when the link count isn't zero. + * This is called by xfs_inactive to free any blocks beyond eof + * when the link count isn't zero and by xfs_dm_punch_hole() when + * punching a hole to EOF. */ -STATIC int -xfs_inactive_free_eofblocks( +int +xfs_free_eofblocks( xfs_mount_t *mp, - xfs_inode_t *ip) + xfs_inode_t *ip, + int flags) { xfs_trans_t *tp; int error; @@ -1216,6 +1213,7 @@ xfs_inactive_free_eofblocks( xfs_filblks_t map_len; int nimaps; xfs_bmbt_irec_t imap; + int use_iolock = (flags & XFS_FREE_EOF_LOCK); /* * Figure out if there are any blocks beyond the end @@ -1256,11 +1254,14 @@ xfs_inactive_free_eofblocks( * cache and we can't * do that within a transaction. */ - xfs_ilock(ip, XFS_IOLOCK_EXCL); + if (use_iolock) + xfs_ilock(ip, XFS_IOLOCK_EXCL); error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, ip->i_size); if (error) { - xfs_iunlock(ip, XFS_IOLOCK_EXCL); + xfs_trans_cancel(tp, 0); + if (use_iolock) + xfs_iunlock(ip, XFS_IOLOCK_EXCL); return error; } @@ -1297,7 +1298,8 @@ xfs_inactive_free_eofblocks( error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); } - xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); + xfs_iunlock(ip, (use_iolock ? (XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL) + : XFS_ILOCK_EXCL)); } return error; } @@ -1560,6 +1562,31 @@ xfs_release( if (vp->v_vfsp->vfs_flag & VFS_RDONLY) return 0; + if (!XFS_FORCED_SHUTDOWN(mp)) { + /* + * If we are using filestreams, and we have an unlinked + * file that we are processing the last close on, then nothing + * will be able to reopen and write to this file. Purge this + * inode from the filestreams cache so that it doesn't delay + * teardown of the inode. + */ + if ((ip->i_d.di_nlink == 0) && xfs_inode_is_filestream(ip)) + xfs_filestream_deassociate(ip); + + /* + * If we previously truncated this file and removed old data + * in the process, we want to initiate "early" writeout on + * the last close. This is an attempt to combat the notorious + * NULL files problem which is particularly noticable from a + * truncate down, buffered (re-)write (delalloc), followed by + * a crash. What we are effectively doing here is + * significantly reducing the time window where we'd otherwise + * be exposed to that problem. + */ + if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0) + bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE); + } + #ifdef HAVE_REFCACHE /* If we are in the NFS reference cache then don't do this now */ if (ip->i_refcache) @@ -1573,7 +1600,8 @@ xfs_release( (ip->i_df.if_flags & XFS_IFEXTENTS)) && (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) { - if ((error = xfs_inactive_free_eofblocks(mp, ip))) + error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK); + if (error) return error; /* Update linux inode block count after free above */ vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp, @@ -1654,7 +1682,8 @@ xfs_inactive( (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) || (ip->i_delayed_blks != 0)))) { - if ((error = xfs_inactive_free_eofblocks(mp, ip))) + error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK); + if (error) return VN_INACTIVE_CACHE; /* Update linux inode block count after free above */ vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp, @@ -1680,6 +1709,7 @@ xfs_inactive( error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, 0); if (error) { + xfs_trans_cancel(tp, 0); xfs_iunlock(ip, XFS_IOLOCK_EXCL); return VN_INACTIVE_CACHE; } @@ -2217,9 +2247,9 @@ static inline int xfs_lock_inumorder(int lock_mode, int subclass) { if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)) - lock_mode |= (subclass + XFS_IOLOCK_INUMORDER) << XFS_IOLOCK_SHIFT; + lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_IOLOCK_SHIFT; if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) - lock_mode |= (subclass + XFS_ILOCK_INUMORDER) << XFS_ILOCK_SHIFT; + lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_ILOCK_SHIFT; return lock_mode; } @@ -2546,6 +2576,15 @@ xfs_remove( */ xfs_refcache_purge_ip(ip); + /* + * If we are using filestreams, kill the stream association. + * If the file is still open it may get a new one but that + * will get killed on last close in xfs_close() so we don't + * have to worry about that. + */ + if (link_zero && xfs_inode_is_filestream(ip)) + xfs_filestream_deassociate(ip); + vn_trace_exit(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address); /* @@ -4047,22 +4086,16 @@ xfs_alloc_file_space( if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); - rt = XFS_IS_REALTIME_INODE(ip); - if (unlikely(rt)) { - if (!(extsz = ip->i_d.di_extsize)) - extsz = mp->m_sb.sb_rextsize; - } else { - extsz = ip->i_d.di_extsize; - } - if ((error = XFS_QM_DQATTACH(mp, ip, 0))) return error; if (len <= 0) return XFS_ERROR(EINVAL); + rt = XFS_IS_REALTIME_INODE(ip); + extsz = xfs_get_extsz_hint(ip); + count = len; - error = 0; imapp = &imaps[0]; nimaps = 1; bmapi_flag = XFS_BMAPI_WRITE | (alloc_type ? XFS_BMAPI_PREALLOC : 0); @@ -4424,9 +4457,12 @@ xfs_free_file_space( while (!error && !done) { /* - * allocate and setup the transaction + * allocate and setup the transaction. Allow this + * transaction to dip into the reserve blocks to ensure + * the freeing of the space succeeds at ENOSPC. */ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT); + tp->t_flags |= XFS_TRANS_RESERVE; error = xfs_trans_reserve(tp, resblks, XFS_WRITE_LOG_RES(mp), @@ -4678,7 +4714,6 @@ xfs_change_file_space( bhv_vnodeops_t xfs_vnodeops = { BHV_IDENTITY_INIT(VN_BHV_XFS,VNODE_POSITION_XFS), .vop_open = xfs_open, - .vop_close = xfs_close, .vop_read = xfs_read, #ifdef HAVE_SPLICE .vop_splice_read = xfs_splice_read, |