diff options
Diffstat (limited to 'fs')
115 files changed, 11330 insertions, 822 deletions
diff --git a/fs/9p/9p.c b/fs/9p/9p.c new file mode 100644 index 000000000000..e847f504a47c --- /dev/null +++ b/fs/9p/9p.c | |||
| @@ -0,0 +1,359 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/9p.c | ||
| 3 | * | ||
| 4 | * This file contains functions 9P2000 functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/errno.h> | ||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/idr.h> | ||
| 32 | |||
| 33 | #include "debug.h" | ||
| 34 | #include "v9fs.h" | ||
| 35 | #include "9p.h" | ||
| 36 | #include "mux.h" | ||
| 37 | |||
| 38 | /** | ||
| 39 | * v9fs_t_version - negotiate protocol parameters with sever | ||
| 40 | * @v9ses: 9P2000 session information | ||
| 41 | * @msize: requested max size packet | ||
| 42 | * @version: requested version.extension string | ||
| 43 | * @fcall: pointer to response fcall pointer | ||
| 44 | * | ||
| 45 | */ | ||
| 46 | |||
| 47 | int | ||
| 48 | v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, | ||
| 49 | char *version, struct v9fs_fcall **fcall) | ||
| 50 | { | ||
| 51 | struct v9fs_fcall msg; | ||
| 52 | |||
| 53 | dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version); | ||
| 54 | msg.id = TVERSION; | ||
| 55 | msg.params.tversion.msize = msize; | ||
| 56 | msg.params.tversion.version = version; | ||
| 57 | |||
| 58 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * v9fs_t_attach - mount the server | ||
| 63 | * @v9ses: 9P2000 session information | ||
| 64 | * @uname: user name doing the attach | ||
| 65 | * @aname: remote name being attached to | ||
| 66 | * @fid: mount fid to attatch to root node | ||
| 67 | * @afid: authentication fid (in this case result key) | ||
| 68 | * @fcall: pointer to response fcall pointer | ||
| 69 | * | ||
| 70 | */ | ||
| 71 | |||
| 72 | int | ||
| 73 | v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, | ||
| 74 | u32 fid, u32 afid, struct v9fs_fcall **fcall) | ||
| 75 | { | ||
| 76 | struct v9fs_fcall msg; | ||
| 77 | |||
| 78 | dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname, | ||
| 79 | aname, fid, afid); | ||
| 80 | msg.id = TATTACH; | ||
| 81 | msg.params.tattach.fid = fid; | ||
| 82 | msg.params.tattach.afid = afid; | ||
| 83 | msg.params.tattach.uname = uname; | ||
| 84 | msg.params.tattach.aname = aname; | ||
| 85 | |||
| 86 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * v9fs_t_clunk - release a fid (finish a transaction) | ||
| 91 | * @v9ses: 9P2000 session information | ||
| 92 | * @fid: fid to release | ||
| 93 | * @fcall: pointer to response fcall pointer | ||
| 94 | * | ||
| 95 | */ | ||
| 96 | |||
| 97 | int | ||
| 98 | v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, | ||
| 99 | struct v9fs_fcall **fcall) | ||
| 100 | { | ||
| 101 | struct v9fs_fcall msg; | ||
| 102 | |||
| 103 | dprintk(DEBUG_9P, "fid %d\n", fid); | ||
| 104 | msg.id = TCLUNK; | ||
| 105 | msg.params.tclunk.fid = fid; | ||
| 106 | |||
| 107 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * v9fs_v9fs_t_flush - flush a pending transaction | ||
| 112 | * @v9ses: 9P2000 session information | ||
| 113 | * @tag: tid to release | ||
| 114 | * | ||
| 115 | */ | ||
| 116 | |||
| 117 | int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) | ||
| 118 | { | ||
| 119 | struct v9fs_fcall msg; | ||
| 120 | |||
| 121 | dprintk(DEBUG_9P, "oldtag %d\n", tag); | ||
| 122 | msg.id = TFLUSH; | ||
| 123 | msg.params.tflush.oldtag = tag; | ||
| 124 | return v9fs_mux_rpc(v9ses, &msg, NULL); | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * v9fs_t_stat - read a file's meta-data | ||
| 129 | * @v9ses: 9P2000 session information | ||
| 130 | * @fid: fid pointing to file or directory to get info about | ||
| 131 | * @fcall: pointer to response fcall | ||
| 132 | * | ||
| 133 | */ | ||
| 134 | |||
| 135 | int | ||
| 136 | v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) | ||
| 137 | { | ||
| 138 | struct v9fs_fcall msg; | ||
| 139 | |||
| 140 | dprintk(DEBUG_9P, "fid %d\n", fid); | ||
| 141 | if (fcall) | ||
| 142 | *fcall = NULL; | ||
| 143 | |||
| 144 | msg.id = TSTAT; | ||
| 145 | msg.params.tstat.fid = fid; | ||
| 146 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * v9fs_t_wstat - write a file's meta-data | ||
| 151 | * @v9ses: 9P2000 session information | ||
| 152 | * @fid: fid pointing to file or directory to write info about | ||
| 153 | * @stat: metadata | ||
| 154 | * @fcall: pointer to response fcall | ||
| 155 | * | ||
| 156 | */ | ||
| 157 | |||
| 158 | int | ||
| 159 | v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, | ||
| 160 | struct v9fs_stat *stat, struct v9fs_fcall **fcall) | ||
| 161 | { | ||
| 162 | struct v9fs_fcall msg; | ||
| 163 | |||
| 164 | dprintk(DEBUG_9P, "fid %d length %d\n", fid, (int)stat->length); | ||
| 165 | msg.id = TWSTAT; | ||
| 166 | msg.params.twstat.fid = fid; | ||
| 167 | msg.params.twstat.stat = stat; | ||
| 168 | |||
| 169 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * v9fs_t_walk - walk a fid to a new file or directory | ||
| 174 | * @v9ses: 9P2000 session information | ||
| 175 | * @fid: fid to walk | ||
| 176 | * @newfid: new fid (for clone operations) | ||
| 177 | * @name: path to walk fid to | ||
| 178 | * @fcall: pointer to response fcall | ||
| 179 | * | ||
| 180 | */ | ||
| 181 | |||
| 182 | /* TODO: support multiple walk */ | ||
| 183 | |||
| 184 | int | ||
| 185 | v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, | ||
| 186 | char *name, struct v9fs_fcall **fcall) | ||
| 187 | { | ||
| 188 | struct v9fs_fcall msg; | ||
| 189 | |||
| 190 | dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name); | ||
| 191 | msg.id = TWALK; | ||
| 192 | msg.params.twalk.fid = fid; | ||
| 193 | msg.params.twalk.newfid = newfid; | ||
| 194 | |||
| 195 | if (name) { | ||
| 196 | msg.params.twalk.nwname = 1; | ||
| 197 | msg.params.twalk.wnames = &name; | ||
| 198 | } else { | ||
| 199 | msg.params.twalk.nwname = 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * v9fs_t_open - open a file | ||
| 207 | * | ||
| 208 | * @v9ses - 9P2000 session information | ||
| 209 | * @fid - fid to open | ||
| 210 | * @mode - mode to open file (R, RW, etc) | ||
| 211 | * @fcall - pointer to response fcall | ||
| 212 | * | ||
| 213 | */ | ||
| 214 | |||
| 215 | int | ||
| 216 | v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, | ||
| 217 | struct v9fs_fcall **fcall) | ||
| 218 | { | ||
| 219 | struct v9fs_fcall msg; | ||
| 220 | long errorno = -1; | ||
| 221 | |||
| 222 | dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); | ||
| 223 | msg.id = TOPEN; | ||
| 224 | msg.params.topen.fid = fid; | ||
| 225 | msg.params.topen.mode = mode; | ||
| 226 | |||
| 227 | errorno = v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 228 | |||
| 229 | return errorno; | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * v9fs_t_remove - remove a file or directory | ||
| 234 | * @v9ses: 9P2000 session information | ||
| 235 | * @fid: fid to remove | ||
| 236 | * @fcall: pointer to response fcall | ||
| 237 | * | ||
| 238 | */ | ||
| 239 | |||
| 240 | int | ||
| 241 | v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, | ||
| 242 | struct v9fs_fcall **fcall) | ||
| 243 | { | ||
| 244 | struct v9fs_fcall msg; | ||
| 245 | |||
| 246 | dprintk(DEBUG_9P, "fid %d\n", fid); | ||
| 247 | msg.id = TREMOVE; | ||
| 248 | msg.params.tremove.fid = fid; | ||
| 249 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * v9fs_t_create - create a file or directory | ||
| 254 | * @v9ses: 9P2000 session information | ||
| 255 | * @fid: fid to create | ||
| 256 | * @name: name of the file or directory to create | ||
| 257 | * @perm: permissions to create with | ||
| 258 | * @mode: mode to open file (R, RW, etc) | ||
| 259 | * @fcall: pointer to response fcall | ||
| 260 | * | ||
| 261 | */ | ||
| 262 | |||
| 263 | int | ||
| 264 | v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, | ||
| 265 | u32 perm, u8 mode, struct v9fs_fcall **fcall) | ||
| 266 | { | ||
| 267 | struct v9fs_fcall msg; | ||
| 268 | |||
| 269 | dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n", | ||
| 270 | fid, name, perm, mode); | ||
| 271 | |||
| 272 | msg.id = TCREATE; | ||
| 273 | msg.params.tcreate.fid = fid; | ||
| 274 | msg.params.tcreate.name = name; | ||
| 275 | msg.params.tcreate.perm = perm; | ||
| 276 | msg.params.tcreate.mode = mode; | ||
| 277 | |||
| 278 | return v9fs_mux_rpc(v9ses, &msg, fcall); | ||
| 279 | } | ||
| 280 | |||
| 281 | /** | ||
| 282 | * v9fs_t_read - read data | ||
| 283 | * @v9ses: 9P2000 session information | ||
| 284 | * @fid: fid to read from | ||
| 285 | * @offset: offset to start read at | ||
| 286 | * @count: how many bytes to read | ||
| 287 | * @fcall: pointer to response fcall (with data) | ||
| 288 | * | ||
| 289 | */ | ||
| 290 | |||
| 291 | int | ||
| 292 | v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, | ||
| 293 | u32 count, struct v9fs_fcall **fcall) | ||
| 294 | { | ||
| 295 | struct v9fs_fcall msg; | ||
| 296 | struct v9fs_fcall *rc = NULL; | ||
| 297 | long errorno = -1; | ||
| 298 | |||
| 299 | dprintk(DEBUG_9P, "fid %d offset 0x%lx count 0x%x\n", fid, | ||
| 300 | (long unsigned int)offset, count); | ||
| 301 | msg.id = TREAD; | ||
| 302 | msg.params.tread.fid = fid; | ||
| 303 | msg.params.tread.offset = offset; | ||
| 304 | msg.params.tread.count = count; | ||
| 305 | errorno = v9fs_mux_rpc(v9ses, &msg, &rc); | ||
| 306 | |||
| 307 | if (!errorno) { | ||
| 308 | errorno = rc->params.rread.count; | ||
| 309 | dump_data(rc->params.rread.data, rc->params.rread.count); | ||
| 310 | } | ||
| 311 | |||
| 312 | if (fcall) | ||
| 313 | *fcall = rc; | ||
| 314 | else | ||
| 315 | kfree(rc); | ||
| 316 | |||
| 317 | return errorno; | ||
| 318 | } | ||
| 319 | |||
| 320 | /** | ||
| 321 | * v9fs_t_write - write data | ||
| 322 | * @v9ses: 9P2000 session information | ||
| 323 | * @fid: fid to write to | ||
| 324 | * @offset: offset to start write at | ||
| 325 | * @count: how many bytes to write | ||
| 326 | * @fcall: pointer to response fcall | ||
| 327 | * | ||
| 328 | */ | ||
| 329 | |||
| 330 | int | ||
| 331 | v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, | ||
| 332 | u64 offset, u32 count, void *data, struct v9fs_fcall **fcall) | ||
| 333 | { | ||
| 334 | struct v9fs_fcall msg; | ||
| 335 | struct v9fs_fcall *rc = NULL; | ||
| 336 | long errorno = -1; | ||
| 337 | |||
| 338 | dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid, | ||
| 339 | (unsigned long long)offset, count); | ||
| 340 | dump_data(data, count); | ||
| 341 | |||
| 342 | msg.id = TWRITE; | ||
| 343 | msg.params.twrite.fid = fid; | ||
| 344 | msg.params.twrite.offset = offset; | ||
| 345 | msg.params.twrite.count = count; | ||
| 346 | msg.params.twrite.data = data; | ||
| 347 | |||
| 348 | errorno = v9fs_mux_rpc(v9ses, &msg, &rc); | ||
| 349 | |||
| 350 | if (!errorno) | ||
| 351 | errorno = rc->params.rwrite.count; | ||
| 352 | |||
| 353 | if (fcall) | ||
| 354 | *fcall = rc; | ||
| 355 | else | ||
| 356 | kfree(rc); | ||
| 357 | |||
| 358 | return errorno; | ||
| 359 | } | ||
diff --git a/fs/9p/9p.h b/fs/9p/9p.h new file mode 100644 index 000000000000..f55424216be2 --- /dev/null +++ b/fs/9p/9p.h | |||
| @@ -0,0 +1,341 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/9p.h | ||
| 3 | * | ||
| 4 | * 9P protocol definitions. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | /* Message Types */ | ||
| 28 | enum { | ||
| 29 | TVERSION = 100, | ||
| 30 | RVERSION, | ||
| 31 | TAUTH = 102, | ||
| 32 | RAUTH, | ||
| 33 | TATTACH = 104, | ||
| 34 | RATTACH, | ||
| 35 | TERROR = 106, | ||
| 36 | RERROR, | ||
| 37 | TFLUSH = 108, | ||
| 38 | RFLUSH, | ||
| 39 | TWALK = 110, | ||
| 40 | RWALK, | ||
| 41 | TOPEN = 112, | ||
| 42 | ROPEN, | ||
| 43 | TCREATE = 114, | ||
| 44 | RCREATE, | ||
| 45 | TREAD = 116, | ||
| 46 | RREAD, | ||
| 47 | TWRITE = 118, | ||
| 48 | RWRITE, | ||
| 49 | TCLUNK = 120, | ||
| 50 | RCLUNK, | ||
| 51 | TREMOVE = 122, | ||
| 52 | RREMOVE, | ||
| 53 | TSTAT = 124, | ||
| 54 | RSTAT, | ||
| 55 | TWSTAT = 126, | ||
| 56 | RWSTAT, | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* modes */ | ||
| 60 | enum { | ||
| 61 | V9FS_OREAD = 0x00, | ||
| 62 | V9FS_OWRITE = 0x01, | ||
| 63 | V9FS_ORDWR = 0x02, | ||
| 64 | V9FS_OEXEC = 0x03, | ||
| 65 | V9FS_OEXCL = 0x04, | ||
| 66 | V9FS_OTRUNC = 0x10, | ||
| 67 | V9FS_OREXEC = 0x20, | ||
| 68 | V9FS_ORCLOSE = 0x40, | ||
| 69 | V9FS_OAPPEND = 0x80, | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* permissions */ | ||
| 73 | enum { | ||
| 74 | V9FS_DMDIR = 0x80000000, | ||
| 75 | V9FS_DMAPPEND = 0x40000000, | ||
| 76 | V9FS_DMEXCL = 0x20000000, | ||
| 77 | V9FS_DMMOUNT = 0x10000000, | ||
| 78 | V9FS_DMAUTH = 0x08000000, | ||
| 79 | V9FS_DMTMP = 0x04000000, | ||
| 80 | V9FS_DMSYMLINK = 0x02000000, | ||
| 81 | V9FS_DMLINK = 0x01000000, | ||
| 82 | /* 9P2000.u extensions */ | ||
| 83 | V9FS_DMDEVICE = 0x00800000, | ||
| 84 | V9FS_DMNAMEDPIPE = 0x00200000, | ||
| 85 | V9FS_DMSOCKET = 0x00100000, | ||
| 86 | V9FS_DMSETUID = 0x00080000, | ||
| 87 | V9FS_DMSETGID = 0x00040000, | ||
| 88 | }; | ||
| 89 | |||
| 90 | /* qid.types */ | ||
| 91 | enum { | ||
| 92 | V9FS_QTDIR = 0x80, | ||
| 93 | V9FS_QTAPPEND = 0x40, | ||
| 94 | V9FS_QTEXCL = 0x20, | ||
| 95 | V9FS_QTMOUNT = 0x10, | ||
| 96 | V9FS_QTAUTH = 0x08, | ||
| 97 | V9FS_QTTMP = 0x04, | ||
| 98 | V9FS_QTSYMLINK = 0x02, | ||
| 99 | V9FS_QTLINK = 0x01, | ||
| 100 | V9FS_QTFILE = 0x00, | ||
| 101 | }; | ||
| 102 | |||
| 103 | /* ample room for Twrite/Rread header (iounit) */ | ||
| 104 | #define V9FS_IOHDRSZ 24 | ||
| 105 | |||
| 106 | /* qids are the unique ID for a file (like an inode */ | ||
| 107 | struct v9fs_qid { | ||
| 108 | u8 type; | ||
| 109 | u32 version; | ||
| 110 | u64 path; | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* Plan 9 file metadata (stat) structure */ | ||
| 114 | struct v9fs_stat { | ||
| 115 | u16 size; | ||
| 116 | u16 type; | ||
| 117 | u32 dev; | ||
| 118 | struct v9fs_qid qid; | ||
| 119 | u32 mode; | ||
| 120 | u32 atime; | ||
| 121 | u32 mtime; | ||
| 122 | u64 length; | ||
| 123 | char *name; | ||
| 124 | char *uid; | ||
| 125 | char *gid; | ||
| 126 | char *muid; | ||
| 127 | char *extension; /* 9p2000.u extensions */ | ||
| 128 | u32 n_uid; /* 9p2000.u extensions */ | ||
| 129 | u32 n_gid; /* 9p2000.u extensions */ | ||
| 130 | u32 n_muid; /* 9p2000.u extensions */ | ||
| 131 | char data[0]; | ||
| 132 | }; | ||
| 133 | |||
| 134 | /* Structures for Protocol Operations */ | ||
| 135 | |||
| 136 | struct Tversion { | ||
| 137 | u32 msize; | ||
| 138 | char *version; | ||
| 139 | }; | ||
| 140 | |||
| 141 | struct Rversion { | ||
| 142 | u32 msize; | ||
| 143 | char *version; | ||
| 144 | }; | ||
| 145 | |||
| 146 | struct Tauth { | ||
| 147 | u32 afid; | ||
| 148 | char *uname; | ||
| 149 | char *aname; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct Rauth { | ||
| 153 | struct v9fs_qid qid; | ||
| 154 | }; | ||
| 155 | |||
| 156 | struct Rerror { | ||
| 157 | char *error; | ||
| 158 | u32 errno; /* 9p2000.u extension */ | ||
| 159 | }; | ||
| 160 | |||
| 161 | struct Tflush { | ||
| 162 | u32 oldtag; | ||
| 163 | }; | ||
| 164 | |||
| 165 | struct Rflush { | ||
| 166 | }; | ||
| 167 | |||
| 168 | struct Tattach { | ||
| 169 | u32 fid; | ||
| 170 | u32 afid; | ||
| 171 | char *uname; | ||
| 172 | char *aname; | ||
| 173 | }; | ||
| 174 | |||
| 175 | struct Rattach { | ||
| 176 | struct v9fs_qid qid; | ||
| 177 | }; | ||
| 178 | |||
| 179 | struct Twalk { | ||
| 180 | u32 fid; | ||
| 181 | u32 newfid; | ||
| 182 | u32 nwname; | ||
| 183 | char **wnames; | ||
| 184 | }; | ||
| 185 | |||
| 186 | struct Rwalk { | ||
| 187 | u32 nwqid; | ||
| 188 | struct v9fs_qid *wqids; | ||
| 189 | }; | ||
| 190 | |||
| 191 | struct Topen { | ||
| 192 | u32 fid; | ||
| 193 | u8 mode; | ||
| 194 | }; | ||
| 195 | |||
| 196 | struct Ropen { | ||
| 197 | struct v9fs_qid qid; | ||
| 198 | u32 iounit; | ||
| 199 | }; | ||
| 200 | |||
| 201 | struct Tcreate { | ||
| 202 | u32 fid; | ||
| 203 | char *name; | ||
| 204 | u32 perm; | ||
| 205 | u8 mode; | ||
| 206 | }; | ||
| 207 | |||
| 208 | struct Rcreate { | ||
| 209 | struct v9fs_qid qid; | ||
| 210 | u32 iounit; | ||
| 211 | }; | ||
| 212 | |||
| 213 | struct Tread { | ||
| 214 | u32 fid; | ||
| 215 | u64 offset; | ||
| 216 | u32 count; | ||
| 217 | }; | ||
| 218 | |||
| 219 | struct Rread { | ||
| 220 | u32 count; | ||
| 221 | u8 *data; | ||
| 222 | }; | ||
| 223 | |||
| 224 | struct Twrite { | ||
| 225 | u32 fid; | ||
| 226 | u64 offset; | ||
| 227 | u32 count; | ||
| 228 | u8 *data; | ||
| 229 | }; | ||
| 230 | |||
| 231 | struct Rwrite { | ||
| 232 | u32 count; | ||
| 233 | }; | ||
| 234 | |||
| 235 | struct Tclunk { | ||
| 236 | u32 fid; | ||
| 237 | }; | ||
| 238 | |||
| 239 | struct Rclunk { | ||
| 240 | }; | ||
| 241 | |||
| 242 | struct Tremove { | ||
| 243 | u32 fid; | ||
| 244 | }; | ||
| 245 | |||
| 246 | struct Rremove { | ||
| 247 | }; | ||
| 248 | |||
| 249 | struct Tstat { | ||
| 250 | u32 fid; | ||
| 251 | }; | ||
| 252 | |||
| 253 | struct Rstat { | ||
| 254 | struct v9fs_stat *stat; | ||
| 255 | }; | ||
| 256 | |||
| 257 | struct Twstat { | ||
| 258 | u32 fid; | ||
| 259 | struct v9fs_stat *stat; | ||
| 260 | }; | ||
| 261 | |||
| 262 | struct Rwstat { | ||
| 263 | }; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * fcall is the primary packet structure | ||
| 267 | * | ||
| 268 | */ | ||
| 269 | |||
| 270 | struct v9fs_fcall { | ||
| 271 | u32 size; | ||
| 272 | u8 id; | ||
| 273 | u16 tag; | ||
| 274 | |||
| 275 | union { | ||
| 276 | struct Tversion tversion; | ||
| 277 | struct Rversion rversion; | ||
| 278 | struct Tauth tauth; | ||
| 279 | struct Rauth rauth; | ||
| 280 | struct Rerror rerror; | ||
| 281 | struct Tflush tflush; | ||
| 282 | struct Rflush rflush; | ||
| 283 | struct Tattach tattach; | ||
| 284 | struct Rattach rattach; | ||
| 285 | struct Twalk twalk; | ||
| 286 | struct Rwalk rwalk; | ||
| 287 | struct Topen topen; | ||
| 288 | struct Ropen ropen; | ||
| 289 | struct Tcreate tcreate; | ||
| 290 | struct Rcreate rcreate; | ||
| 291 | struct Tread tread; | ||
| 292 | struct Rread rread; | ||
| 293 | struct Twrite twrite; | ||
| 294 | struct Rwrite rwrite; | ||
| 295 | struct Tclunk tclunk; | ||
| 296 | struct Rclunk rclunk; | ||
| 297 | struct Tremove tremove; | ||
| 298 | struct Rremove rremove; | ||
| 299 | struct Tstat tstat; | ||
| 300 | struct Rstat rstat; | ||
| 301 | struct Twstat twstat; | ||
| 302 | struct Rwstat rwstat; | ||
| 303 | } params; | ||
| 304 | }; | ||
| 305 | |||
| 306 | #define FCALL_ERROR(fcall) (fcall ? fcall->params.rerror.error : "") | ||
| 307 | |||
| 308 | int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, | ||
| 309 | char *version, struct v9fs_fcall **rcall); | ||
| 310 | |||
| 311 | int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, | ||
| 312 | u32 fid, u32 afid, struct v9fs_fcall **rcall); | ||
| 313 | |||
| 314 | int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, | ||
| 315 | struct v9fs_fcall **rcall); | ||
| 316 | |||
| 317 | int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag); | ||
| 318 | |||
| 319 | int v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, | ||
| 320 | struct v9fs_fcall **rcall); | ||
| 321 | |||
| 322 | int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, | ||
| 323 | struct v9fs_stat *stat, struct v9fs_fcall **rcall); | ||
| 324 | |||
| 325 | int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, | ||
| 326 | char *name, struct v9fs_fcall **rcall); | ||
| 327 | |||
| 328 | int v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, | ||
| 329 | struct v9fs_fcall **rcall); | ||
| 330 | |||
| 331 | int v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, | ||
| 332 | struct v9fs_fcall **rcall); | ||
| 333 | |||
| 334 | int v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, | ||
| 335 | u32 perm, u8 mode, struct v9fs_fcall **rcall); | ||
| 336 | |||
| 337 | int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, | ||
| 338 | u64 offset, u32 count, struct v9fs_fcall **rcall); | ||
| 339 | |||
| 340 | int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, | ||
| 341 | u32 count, void *data, struct v9fs_fcall **rcall); | ||
diff --git a/fs/9p/Makefile b/fs/9p/Makefile new file mode 100644 index 000000000000..e4e4ffe5a7dc --- /dev/null +++ b/fs/9p/Makefile | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | obj-$(CONFIG_9P_FS) := 9p2000.o | ||
| 2 | |||
| 3 | 9p2000-objs := \ | ||
| 4 | vfs_super.o \ | ||
| 5 | vfs_inode.o \ | ||
| 6 | vfs_file.o \ | ||
| 7 | vfs_dir.o \ | ||
| 8 | vfs_dentry.o \ | ||
| 9 | error.o \ | ||
| 10 | mux.o \ | ||
| 11 | trans_fd.o \ | ||
| 12 | trans_sock.o \ | ||
| 13 | 9p.o \ | ||
| 14 | conv.o \ | ||
| 15 | v9fs.o \ | ||
| 16 | fid.o | ||
| 17 | |||
diff --git a/fs/9p/conv.c b/fs/9p/conv.c new file mode 100644 index 000000000000..1554731bd653 --- /dev/null +++ b/fs/9p/conv.c | |||
| @@ -0,0 +1,693 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/conv.c | ||
| 3 | * | ||
| 4 | * 9P protocol conversion functions | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/errno.h> | ||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/idr.h> | ||
| 32 | |||
| 33 | #include "debug.h" | ||
| 34 | #include "v9fs.h" | ||
| 35 | #include "9p.h" | ||
| 36 | #include "conv.h" | ||
| 37 | |||
| 38 | /* | ||
| 39 | * Buffer to help with string parsing | ||
| 40 | */ | ||
| 41 | struct cbuf { | ||
| 42 | unsigned char *sp; | ||
| 43 | unsigned char *p; | ||
| 44 | unsigned char *ep; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static inline void buf_init(struct cbuf *buf, void *data, int datalen) | ||
| 48 | { | ||
| 49 | buf->sp = buf->p = data; | ||
| 50 | buf->ep = data + datalen; | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline int buf_check_overflow(struct cbuf *buf) | ||
| 54 | { | ||
| 55 | return buf->p > buf->ep; | ||
| 56 | } | ||
| 57 | |||
| 58 | static inline void buf_check_size(struct cbuf *buf, int len) | ||
| 59 | { | ||
| 60 | if (buf->p+len > buf->ep) { | ||
| 61 | if (buf->p < buf->ep) { | ||
| 62 | eprintk(KERN_ERR, "buffer overflow\n"); | ||
| 63 | buf->p = buf->ep + 1; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | static inline void *buf_alloc(struct cbuf *buf, int len) | ||
| 69 | { | ||
| 70 | void *ret = NULL; | ||
| 71 | |||
| 72 | buf_check_size(buf, len); | ||
| 73 | ret = buf->p; | ||
| 74 | buf->p += len; | ||
| 75 | |||
| 76 | return ret; | ||
| 77 | } | ||
| 78 | |||
| 79 | static inline void buf_put_int8(struct cbuf *buf, u8 val) | ||
| 80 | { | ||
| 81 | buf_check_size(buf, 1); | ||
| 82 | |||
| 83 | buf->p[0] = val; | ||
| 84 | buf->p++; | ||
| 85 | } | ||
| 86 | |||
| 87 | static inline void buf_put_int16(struct cbuf *buf, u16 val) | ||
| 88 | { | ||
| 89 | buf_check_size(buf, 2); | ||
| 90 | |||
| 91 | *(__le16 *) buf->p = cpu_to_le16(val); | ||
| 92 | buf->p += 2; | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void buf_put_int32(struct cbuf *buf, u32 val) | ||
| 96 | { | ||
| 97 | buf_check_size(buf, 4); | ||
| 98 | |||
| 99 | *(__le32 *)buf->p = cpu_to_le32(val); | ||
| 100 | buf->p += 4; | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline void buf_put_int64(struct cbuf *buf, u64 val) | ||
| 104 | { | ||
| 105 | buf_check_size(buf, 8); | ||
| 106 | |||
| 107 | *(__le64 *)buf->p = cpu_to_le64(val); | ||
| 108 | buf->p += 8; | ||
| 109 | } | ||
| 110 | |||
| 111 | static inline void buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) | ||
| 112 | { | ||
| 113 | buf_check_size(buf, slen + 2); | ||
| 114 | |||
| 115 | buf_put_int16(buf, slen); | ||
| 116 | memcpy(buf->p, s, slen); | ||
| 117 | buf->p += slen; | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline void buf_put_string(struct cbuf *buf, const char *s) | ||
| 121 | { | ||
| 122 | buf_put_stringn(buf, s, strlen(s)); | ||
| 123 | } | ||
| 124 | |||
| 125 | static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen) | ||
| 126 | { | ||
| 127 | buf_check_size(buf, datalen); | ||
| 128 | |||
| 129 | memcpy(buf->p, data, datalen); | ||
| 130 | buf->p += datalen; | ||
| 131 | } | ||
| 132 | |||
| 133 | static inline u8 buf_get_int8(struct cbuf *buf) | ||
| 134 | { | ||
| 135 | u8 ret = 0; | ||
| 136 | |||
| 137 | buf_check_size(buf, 1); | ||
| 138 | ret = buf->p[0]; | ||
| 139 | |||
| 140 | buf->p++; | ||
| 141 | |||
| 142 | return ret; | ||
| 143 | } | ||
| 144 | |||
| 145 | static inline u16 buf_get_int16(struct cbuf *buf) | ||
| 146 | { | ||
| 147 | u16 ret = 0; | ||
| 148 | |||
| 149 | buf_check_size(buf, 2); | ||
| 150 | ret = le16_to_cpu(*(__le16 *)buf->p); | ||
| 151 | |||
| 152 | buf->p += 2; | ||
| 153 | |||
| 154 | return ret; | ||
| 155 | } | ||
| 156 | |||
| 157 | static inline u32 buf_get_int32(struct cbuf *buf) | ||
| 158 | { | ||
| 159 | u32 ret = 0; | ||
| 160 | |||
| 161 | buf_check_size(buf, 4); | ||
| 162 | ret = le32_to_cpu(*(__le32 *)buf->p); | ||
| 163 | |||
| 164 | buf->p += 4; | ||
| 165 | |||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | static inline u64 buf_get_int64(struct cbuf *buf) | ||
| 170 | { | ||
| 171 | u64 ret = 0; | ||
| 172 | |||
| 173 | buf_check_size(buf, 8); | ||
| 174 | ret = le64_to_cpu(*(__le64 *)buf->p); | ||
| 175 | |||
| 176 | buf->p += 8; | ||
| 177 | |||
| 178 | return ret; | ||
| 179 | } | ||
| 180 | |||
| 181 | static inline int | ||
| 182 | buf_get_string(struct cbuf *buf, char *data, unsigned int datalen) | ||
| 183 | { | ||
| 184 | |||
| 185 | u16 len = buf_get_int16(buf); | ||
| 186 | buf_check_size(buf, len); | ||
| 187 | if (len + 1 > datalen) | ||
| 188 | return 0; | ||
| 189 | |||
| 190 | memcpy(data, buf->p, len); | ||
| 191 | data[len] = 0; | ||
| 192 | buf->p += len; | ||
| 193 | |||
| 194 | return len + 1; | ||
| 195 | } | ||
| 196 | |||
| 197 | static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) | ||
| 198 | { | ||
| 199 | char *ret = NULL; | ||
| 200 | int n = buf_get_string(buf, sbuf->p, sbuf->ep - sbuf->p); | ||
| 201 | |||
| 202 | if (n > 0) { | ||
| 203 | ret = sbuf->p; | ||
| 204 | sbuf->p += n; | ||
| 205 | } | ||
| 206 | |||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | |||
| 210 | static inline int buf_get_data(struct cbuf *buf, void *data, int datalen) | ||
| 211 | { | ||
| 212 | buf_check_size(buf, datalen); | ||
| 213 | |||
| 214 | memcpy(data, buf->p, datalen); | ||
| 215 | buf->p += datalen; | ||
| 216 | |||
| 217 | return datalen; | ||
| 218 | } | ||
| 219 | |||
| 220 | static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, | ||
| 221 | int datalen) | ||
| 222 | { | ||
| 223 | char *ret = NULL; | ||
| 224 | int n = 0; | ||
| 225 | |||
| 226 | buf_check_size(dbuf, datalen); | ||
| 227 | |||
| 228 | n = buf_get_data(buf, dbuf->p, datalen); | ||
| 229 | |||
| 230 | if (n > 0) { | ||
| 231 | ret = dbuf->p; | ||
| 232 | dbuf->p += n; | ||
| 233 | } | ||
| 234 | |||
| 235 | return ret; | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * v9fs_size_stat - calculate the size of a variable length stat struct | ||
| 240 | * @v9ses: session information | ||
| 241 | * @stat: metadata (stat) structure | ||
| 242 | * | ||
| 243 | */ | ||
| 244 | |||
| 245 | static int v9fs_size_stat(struct v9fs_session_info *v9ses, | ||
| 246 | struct v9fs_stat *stat) | ||
| 247 | { | ||
| 248 | int size = 0; | ||
| 249 | |||
| 250 | if (stat == NULL) { | ||
| 251 | eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n"); | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | size = /* 2 + *//* size[2] */ | ||
| 256 | 2 + /* type[2] */ | ||
| 257 | 4 + /* dev[4] */ | ||
| 258 | 1 + /* qid.type[1] */ | ||
| 259 | 4 + /* qid.vers[4] */ | ||
| 260 | 8 + /* qid.path[8] */ | ||
| 261 | 4 + /* mode[4] */ | ||
| 262 | 4 + /* atime[4] */ | ||
| 263 | 4 + /* mtime[4] */ | ||
| 264 | 8 + /* length[8] */ | ||
| 265 | 8; /* minimum sum of string lengths */ | ||
| 266 | |||
| 267 | if (stat->name) | ||
| 268 | size += strlen(stat->name); | ||
| 269 | if (stat->uid) | ||
| 270 | size += strlen(stat->uid); | ||
| 271 | if (stat->gid) | ||
| 272 | size += strlen(stat->gid); | ||
| 273 | if (stat->muid) | ||
| 274 | size += strlen(stat->muid); | ||
| 275 | |||
| 276 | if (v9ses->extended) { | ||
| 277 | size += 4 + /* n_uid[4] */ | ||
| 278 | 4 + /* n_gid[4] */ | ||
| 279 | 4 + /* n_muid[4] */ | ||
| 280 | 2; /* string length of extension[4] */ | ||
| 281 | if (stat->extension) | ||
| 282 | size += strlen(stat->extension); | ||
| 283 | } | ||
| 284 | |||
| 285 | return size; | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * serialize_stat - safely format a stat structure for transmission | ||
| 290 | * @v9ses: session info | ||
| 291 | * @stat: metadata (stat) structure | ||
| 292 | * @bufp: buffer to serialize structure into | ||
| 293 | * | ||
| 294 | */ | ||
| 295 | |||
| 296 | static int | ||
| 297 | serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, | ||
| 298 | struct cbuf *bufp) | ||
| 299 | { | ||
| 300 | buf_put_int16(bufp, stat->size); | ||
| 301 | buf_put_int16(bufp, stat->type); | ||
| 302 | buf_put_int32(bufp, stat->dev); | ||
| 303 | buf_put_int8(bufp, stat->qid.type); | ||
| 304 | buf_put_int32(bufp, stat->qid.version); | ||
| 305 | buf_put_int64(bufp, stat->qid.path); | ||
| 306 | buf_put_int32(bufp, stat->mode); | ||
| 307 | buf_put_int32(bufp, stat->atime); | ||
| 308 | buf_put_int32(bufp, stat->mtime); | ||
| 309 | buf_put_int64(bufp, stat->length); | ||
| 310 | |||
| 311 | buf_put_string(bufp, stat->name); | ||
| 312 | buf_put_string(bufp, stat->uid); | ||
| 313 | buf_put_string(bufp, stat->gid); | ||
| 314 | buf_put_string(bufp, stat->muid); | ||
| 315 | |||
| 316 | if (v9ses->extended) { | ||
| 317 | buf_put_string(bufp, stat->extension); | ||
| 318 | buf_put_int32(bufp, stat->n_uid); | ||
| 319 | buf_put_int32(bufp, stat->n_gid); | ||
| 320 | buf_put_int32(bufp, stat->n_muid); | ||
| 321 | } | ||
| 322 | |||
| 323 | if (buf_check_overflow(bufp)) | ||
| 324 | return 0; | ||
| 325 | |||
| 326 | return stat->size; | ||
| 327 | } | ||
| 328 | |||
| 329 | /** | ||
| 330 | * deserialize_stat - safely decode a recieved metadata (stat) structure | ||
| 331 | * @v9ses: session info | ||
| 332 | * @bufp: buffer to deserialize | ||
| 333 | * @stat: metadata (stat) structure | ||
| 334 | * @dbufp: buffer to deserialize variable strings into | ||
| 335 | * | ||
| 336 | */ | ||
| 337 | |||
| 338 | static inline int | ||
| 339 | deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, | ||
| 340 | struct v9fs_stat *stat, struct cbuf *dbufp) | ||
| 341 | { | ||
| 342 | |||
| 343 | stat->size = buf_get_int16(bufp); | ||
| 344 | stat->type = buf_get_int16(bufp); | ||
| 345 | stat->dev = buf_get_int32(bufp); | ||
| 346 | stat->qid.type = buf_get_int8(bufp); | ||
| 347 | stat->qid.version = buf_get_int32(bufp); | ||
| 348 | stat->qid.path = buf_get_int64(bufp); | ||
| 349 | stat->mode = buf_get_int32(bufp); | ||
| 350 | stat->atime = buf_get_int32(bufp); | ||
| 351 | stat->mtime = buf_get_int32(bufp); | ||
| 352 | stat->length = buf_get_int64(bufp); | ||
| 353 | stat->name = buf_get_stringb(bufp, dbufp); | ||
| 354 | stat->uid = buf_get_stringb(bufp, dbufp); | ||
| 355 | stat->gid = buf_get_stringb(bufp, dbufp); | ||
| 356 | stat->muid = buf_get_stringb(bufp, dbufp); | ||
| 357 | |||
| 358 | if (v9ses->extended) { | ||
| 359 | stat->extension = buf_get_stringb(bufp, dbufp); | ||
| 360 | stat->n_uid = buf_get_int32(bufp); | ||
| 361 | stat->n_gid = buf_get_int32(bufp); | ||
| 362 | stat->n_muid = buf_get_int32(bufp); | ||
| 363 | } | ||
| 364 | |||
| 365 | if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) | ||
| 366 | return 0; | ||
| 367 | |||
| 368 | return stat->size + 2; | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * deserialize_statb - wrapper for decoding a received metadata structure | ||
| 373 | * @v9ses: session info | ||
| 374 | * @bufp: buffer to deserialize | ||
| 375 | * @dbufp: buffer to deserialize variable strings into | ||
| 376 | * | ||
| 377 | */ | ||
| 378 | |||
| 379 | static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info | ||
| 380 | *v9ses, struct cbuf *bufp, | ||
| 381 | struct cbuf *dbufp) | ||
| 382 | { | ||
| 383 | struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat)); | ||
| 384 | |||
| 385 | if (ret) { | ||
| 386 | int n = deserialize_stat(v9ses, bufp, ret, dbufp); | ||
| 387 | if (n <= 0) | ||
| 388 | return NULL; | ||
| 389 | } | ||
| 390 | |||
| 391 | return ret; | ||
| 392 | } | ||
| 393 | |||
| 394 | /** | ||
| 395 | * v9fs_deserialize_stat - decode a received metadata structure | ||
| 396 | * @v9ses: session info | ||
| 397 | * @buf: buffer to deserialize | ||
| 398 | * @buflen: length of received buffer | ||
| 399 | * @stat: metadata structure to decode into | ||
| 400 | * @statlen: length of destination metadata structure | ||
| 401 | * | ||
| 402 | */ | ||
| 403 | |||
| 404 | int | ||
| 405 | v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf, | ||
| 406 | u32 buflen, struct v9fs_stat *stat, u32 statlen) | ||
| 407 | { | ||
| 408 | struct cbuf buffer; | ||
| 409 | struct cbuf *bufp = &buffer; | ||
| 410 | struct cbuf dbuffer; | ||
| 411 | struct cbuf *dbufp = &dbuffer; | ||
| 412 | |||
| 413 | buf_init(bufp, buf, buflen); | ||
| 414 | buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat), | ||
| 415 | statlen - sizeof(struct v9fs_stat)); | ||
| 416 | |||
| 417 | return deserialize_stat(v9ses, bufp, stat, dbufp); | ||
| 418 | } | ||
| 419 | |||
| 420 | static inline int | ||
| 421 | v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) | ||
| 422 | { | ||
| 423 | int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */ | ||
| 424 | int i = 0; | ||
| 425 | |||
| 426 | switch (fcall->id) { | ||
| 427 | default: | ||
| 428 | eprintk(KERN_ERR, "bad msg type %d\n", fcall->id); | ||
| 429 | return 0; | ||
| 430 | case TVERSION: /* msize[4] version[s] */ | ||
| 431 | size += 4 + 2 + strlen(fcall->params.tversion.version); | ||
| 432 | break; | ||
| 433 | case TAUTH: /* afid[4] uname[s] aname[s] */ | ||
| 434 | size += 4 + 2 + strlen(fcall->params.tauth.uname) + | ||
| 435 | 2 + strlen(fcall->params.tauth.aname); | ||
| 436 | break; | ||
| 437 | case TFLUSH: /* oldtag[2] */ | ||
| 438 | size += 2; | ||
| 439 | break; | ||
| 440 | case TATTACH: /* fid[4] afid[4] uname[s] aname[s] */ | ||
| 441 | size += 4 + 4 + 2 + strlen(fcall->params.tattach.uname) + | ||
| 442 | 2 + strlen(fcall->params.tattach.aname); | ||
| 443 | break; | ||
| 444 | case TWALK: /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ | ||
| 445 | size += 4 + 4 + 2; | ||
| 446 | /* now compute total for the array of names */ | ||
| 447 | for (i = 0; i < fcall->params.twalk.nwname; i++) | ||
| 448 | size += 2 + strlen(fcall->params.twalk.wnames[i]); | ||
| 449 | break; | ||
| 450 | case TOPEN: /* fid[4] mode[1] */ | ||
| 451 | size += 4 + 1; | ||
| 452 | break; | ||
| 453 | case TCREATE: /* fid[4] name[s] perm[4] mode[1] */ | ||
| 454 | size += 4 + 2 + strlen(fcall->params.tcreate.name) + 4 + 1; | ||
| 455 | break; | ||
| 456 | case TREAD: /* fid[4] offset[8] count[4] */ | ||
| 457 | size += 4 + 8 + 4; | ||
| 458 | break; | ||
| 459 | case TWRITE: /* fid[4] offset[8] count[4] data[count] */ | ||
| 460 | size += 4 + 8 + 4 + fcall->params.twrite.count; | ||
| 461 | break; | ||
| 462 | case TCLUNK: /* fid[4] */ | ||
| 463 | size += 4; | ||
| 464 | break; | ||
| 465 | case TREMOVE: /* fid[4] */ | ||
| 466 | size += 4; | ||
| 467 | break; | ||
| 468 | case TSTAT: /* fid[4] */ | ||
| 469 | size += 4; | ||
| 470 | break; | ||
| 471 | case TWSTAT: /* fid[4] stat[n] */ | ||
| 472 | fcall->params.twstat.stat->size = | ||
| 473 | v9fs_size_stat(v9ses, fcall->params.twstat.stat); | ||
| 474 | size += 4 + 2 + 2 + fcall->params.twstat.stat->size; | ||
| 475 | } | ||
| 476 | return size; | ||
| 477 | } | ||
| 478 | |||
| 479 | /* | ||
| 480 | * v9fs_serialize_fcall - marshall fcall struct into a packet | ||
| 481 | * @v9ses: session information | ||
| 482 | * @fcall: structure to convert | ||
| 483 | * @data: buffer to serialize fcall into | ||
| 484 | * @datalen: length of buffer to serialize fcall into | ||
| 485 | * | ||
| 486 | */ | ||
| 487 | |||
| 488 | int | ||
| 489 | v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, | ||
| 490 | void *data, u32 datalen) | ||
| 491 | { | ||
| 492 | int i = 0; | ||
| 493 | struct v9fs_stat *stat = NULL; | ||
| 494 | struct cbuf buffer; | ||
| 495 | struct cbuf *bufp = &buffer; | ||
| 496 | |||
| 497 | buf_init(bufp, data, datalen); | ||
| 498 | |||
| 499 | if (!fcall) { | ||
| 500 | eprintk(KERN_ERR, "no fcall\n"); | ||
| 501 | return -EINVAL; | ||
| 502 | } | ||
| 503 | |||
| 504 | fcall->size = v9fs_size_fcall(v9ses, fcall); | ||
| 505 | |||
| 506 | buf_put_int32(bufp, fcall->size); | ||
| 507 | buf_put_int8(bufp, fcall->id); | ||
| 508 | buf_put_int16(bufp, fcall->tag); | ||
| 509 | |||
| 510 | dprintk(DEBUG_CONV, "size %d id %d tag %d\n", fcall->size, fcall->id, | ||
| 511 | fcall->tag); | ||
| 512 | |||
| 513 | /* now encode it */ | ||
| 514 | switch (fcall->id) { | ||
| 515 | default: | ||
| 516 | eprintk(KERN_ERR, "bad msg type: %d\n", fcall->id); | ||
| 517 | return -EPROTO; | ||
| 518 | case TVERSION: | ||
| 519 | buf_put_int32(bufp, fcall->params.tversion.msize); | ||
| 520 | buf_put_string(bufp, fcall->params.tversion.version); | ||
| 521 | break; | ||
| 522 | case TAUTH: | ||
| 523 | buf_put_int32(bufp, fcall->params.tauth.afid); | ||
| 524 | buf_put_string(bufp, fcall->params.tauth.uname); | ||
| 525 | buf_put_string(bufp, fcall->params.tauth.aname); | ||
| 526 | break; | ||
| 527 | case TFLUSH: | ||
| 528 | buf_put_int16(bufp, fcall->params.tflush.oldtag); | ||
| 529 | break; | ||
| 530 | case TATTACH: | ||
| 531 | buf_put_int32(bufp, fcall->params.tattach.fid); | ||
| 532 | buf_put_int32(bufp, fcall->params.tattach.afid); | ||
| 533 | buf_put_string(bufp, fcall->params.tattach.uname); | ||
| 534 | buf_put_string(bufp, fcall->params.tattach.aname); | ||
| 535 | break; | ||
| 536 | case TWALK: | ||
| 537 | buf_put_int32(bufp, fcall->params.twalk.fid); | ||
| 538 | buf_put_int32(bufp, fcall->params.twalk.newfid); | ||
| 539 | buf_put_int16(bufp, fcall->params.twalk.nwname); | ||
| 540 | for (i = 0; i < fcall->params.twalk.nwname; i++) | ||
| 541 | buf_put_string(bufp, fcall->params.twalk.wnames[i]); | ||
| 542 | break; | ||
| 543 | case TOPEN: | ||
| 544 | buf_put_int32(bufp, fcall->params.topen.fid); | ||
| 545 | buf_put_int8(bufp, fcall->params.topen.mode); | ||
| 546 | break; | ||
| 547 | case TCREATE: | ||
| 548 | buf_put_int32(bufp, fcall->params.tcreate.fid); | ||
| 549 | buf_put_string(bufp, fcall->params.tcreate.name); | ||
| 550 | buf_put_int32(bufp, fcall->params.tcreate.perm); | ||
| 551 | buf_put_int8(bufp, fcall->params.tcreate.mode); | ||
| 552 | break; | ||
| 553 | case TREAD: | ||
| 554 | buf_put_int32(bufp, fcall->params.tread.fid); | ||
| 555 | buf_put_int64(bufp, fcall->params.tread.offset); | ||
| 556 | buf_put_int32(bufp, fcall->params.tread.count); | ||
| 557 | break; | ||
| 558 | case TWRITE: | ||
| 559 | buf_put_int32(bufp, fcall->params.twrite.fid); | ||
| 560 | buf_put_int64(bufp, fcall->params.twrite.offset); | ||
| 561 | buf_put_int32(bufp, fcall->params.twrite.count); | ||
| 562 | buf_put_data(bufp, fcall->params.twrite.data, | ||
| 563 | fcall->params.twrite.count); | ||
| 564 | break; | ||
| 565 | case TCLUNK: | ||
| 566 | buf_put_int32(bufp, fcall->params.tclunk.fid); | ||
| 567 | break; | ||
| 568 | case TREMOVE: | ||
| 569 | buf_put_int32(bufp, fcall->params.tremove.fid); | ||
| 570 | break; | ||
| 571 | case TSTAT: | ||
| 572 | buf_put_int32(bufp, fcall->params.tstat.fid); | ||
| 573 | break; | ||
| 574 | case TWSTAT: | ||
| 575 | buf_put_int32(bufp, fcall->params.twstat.fid); | ||
| 576 | stat = fcall->params.twstat.stat; | ||
| 577 | |||
| 578 | buf_put_int16(bufp, stat->size + 2); | ||
| 579 | serialize_stat(v9ses, stat, bufp); | ||
| 580 | break; | ||
| 581 | } | ||
| 582 | |||
| 583 | if (buf_check_overflow(bufp)) | ||
| 584 | return -EIO; | ||
| 585 | |||
| 586 | return fcall->size; | ||
| 587 | } | ||
| 588 | |||
| 589 | /** | ||
| 590 | * deserialize_fcall - unmarshal a response | ||
| 591 | * @v9ses: session information | ||
| 592 | * @msgsize: size of rcall message | ||
| 593 | * @buf: recieved buffer | ||
| 594 | * @buflen: length of received buffer | ||
| 595 | * @rcall: fcall structure to populate | ||
| 596 | * @rcalllen: length of fcall structure to populate | ||
| 597 | * | ||
| 598 | */ | ||
| 599 | |||
| 600 | int | ||
| 601 | v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, | ||
| 602 | void *buf, u32 buflen, struct v9fs_fcall *rcall, | ||
| 603 | int rcalllen) | ||
| 604 | { | ||
| 605 | |||
| 606 | struct cbuf buffer; | ||
| 607 | struct cbuf *bufp = &buffer; | ||
| 608 | struct cbuf dbuffer; | ||
| 609 | struct cbuf *dbufp = &dbuffer; | ||
| 610 | int i = 0; | ||
| 611 | |||
| 612 | buf_init(bufp, buf, buflen); | ||
| 613 | buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall), | ||
| 614 | rcalllen - sizeof(struct v9fs_fcall)); | ||
| 615 | |||
| 616 | rcall->size = msgsize; | ||
| 617 | rcall->id = buf_get_int8(bufp); | ||
| 618 | rcall->tag = buf_get_int16(bufp); | ||
| 619 | |||
| 620 | dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id, | ||
| 621 | rcall->tag); | ||
| 622 | switch (rcall->id) { | ||
| 623 | default: | ||
| 624 | eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id); | ||
| 625 | return -EPROTO; | ||
| 626 | case RVERSION: | ||
| 627 | rcall->params.rversion.msize = buf_get_int32(bufp); | ||
| 628 | rcall->params.rversion.version = buf_get_stringb(bufp, dbufp); | ||
| 629 | break; | ||
| 630 | case RFLUSH: | ||
| 631 | break; | ||
| 632 | case RATTACH: | ||
| 633 | rcall->params.rattach.qid.type = buf_get_int8(bufp); | ||
| 634 | rcall->params.rattach.qid.version = buf_get_int32(bufp); | ||
| 635 | rcall->params.rattach.qid.path = buf_get_int64(bufp); | ||
| 636 | break; | ||
| 637 | case RWALK: | ||
| 638 | rcall->params.rwalk.nwqid = buf_get_int16(bufp); | ||
| 639 | rcall->params.rwalk.wqids = buf_alloc(bufp, | ||
| 640 | rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); | ||
| 641 | if (rcall->params.rwalk.wqids) | ||
| 642 | for (i = 0; i < rcall->params.rwalk.nwqid; i++) { | ||
| 643 | rcall->params.rwalk.wqids[i].type = | ||
| 644 | buf_get_int8(bufp); | ||
| 645 | rcall->params.rwalk.wqids[i].version = | ||
| 646 | buf_get_int16(bufp); | ||
| 647 | rcall->params.rwalk.wqids[i].path = | ||
| 648 | buf_get_int64(bufp); | ||
| 649 | } | ||
| 650 | break; | ||
| 651 | case ROPEN: | ||
| 652 | rcall->params.ropen.qid.type = buf_get_int8(bufp); | ||
| 653 | rcall->params.ropen.qid.version = buf_get_int32(bufp); | ||
| 654 | rcall->params.ropen.qid.path = buf_get_int64(bufp); | ||
| 655 | rcall->params.ropen.iounit = buf_get_int32(bufp); | ||
| 656 | break; | ||
| 657 | case RCREATE: | ||
| 658 | rcall->params.rcreate.qid.type = buf_get_int8(bufp); | ||
| 659 | rcall->params.rcreate.qid.version = buf_get_int32(bufp); | ||
| 660 | rcall->params.rcreate.qid.path = buf_get_int64(bufp); | ||
| 661 | rcall->params.rcreate.iounit = buf_get_int32(bufp); | ||
| 662 | break; | ||
| 663 | case RREAD: | ||
| 664 | rcall->params.rread.count = buf_get_int32(bufp); | ||
| 665 | rcall->params.rread.data = buf_get_datab(bufp, dbufp, | ||
| 666 | rcall->params.rread.count); | ||
| 667 | break; | ||
| 668 | case RWRITE: | ||
| 669 | rcall->params.rwrite.count = buf_get_int32(bufp); | ||
| 670 | break; | ||
| 671 | case RCLUNK: | ||
| 672 | break; | ||
| 673 | case RREMOVE: | ||
| 674 | break; | ||
| 675 | case RSTAT: | ||
| 676 | buf_get_int16(bufp); | ||
| 677 | rcall->params.rstat.stat = | ||
| 678 | deserialize_statb(v9ses, bufp, dbufp); | ||
| 679 | break; | ||
| 680 | case RWSTAT: | ||
| 681 | break; | ||
| 682 | case RERROR: | ||
| 683 | rcall->params.rerror.error = buf_get_stringb(bufp, dbufp); | ||
| 684 | if (v9ses->extended) | ||
| 685 | rcall->params.rerror.errno = buf_get_int16(bufp); | ||
| 686 | break; | ||
| 687 | } | ||
| 688 | |||
| 689 | if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) | ||
| 690 | return -EIO; | ||
| 691 | |||
| 692 | return rcall->size; | ||
| 693 | } | ||
diff --git a/fs/9p/conv.h b/fs/9p/conv.h new file mode 100644 index 000000000000..ee849613c61a --- /dev/null +++ b/fs/9p/conv.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/conv.h | ||
| 3 | * | ||
| 4 | * 9P protocol conversion definitions | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | int v9fs_deserialize_stat(struct v9fs_session_info *, void *buf, | ||
| 28 | u32 buflen, struct v9fs_stat *stat, u32 statlen); | ||
| 29 | int v9fs_serialize_fcall(struct v9fs_session_info *, struct v9fs_fcall *tcall, | ||
| 30 | void *buf, u32 buflen); | ||
| 31 | int v9fs_deserialize_fcall(struct v9fs_session_info *, u32 msglen, | ||
| 32 | void *buf, u32 buflen, struct v9fs_fcall *rcall, | ||
| 33 | int rcalllen); | ||
| 34 | |||
| 35 | /* this one is actually in error.c right now */ | ||
| 36 | int v9fs_errstr2errno(char *errstr); | ||
diff --git a/fs/9p/debug.h b/fs/9p/debug.h new file mode 100644 index 000000000000..4445f06919d9 --- /dev/null +++ b/fs/9p/debug.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/debug.h - V9FS Debug Definitions | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 5 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to: | ||
| 19 | * Free Software Foundation | ||
| 20 | * 51 Franklin Street, Fifth Floor | ||
| 21 | * Boston, MA 02111-1301 USA | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #define DEBUG_ERROR (1<<0) | ||
| 26 | #define DEBUG_CURRENT (1<<1) | ||
| 27 | #define DEBUG_9P (1<<2) | ||
| 28 | #define DEBUG_VFS (1<<3) | ||
| 29 | #define DEBUG_CONV (1<<4) | ||
| 30 | #define DEBUG_MUX (1<<5) | ||
| 31 | #define DEBUG_TRANS (1<<6) | ||
| 32 | #define DEBUG_SLABS (1<<7) | ||
| 33 | |||
| 34 | #define DEBUG_DUMP_PKT 0 | ||
| 35 | |||
| 36 | extern int v9fs_debug_level; | ||
| 37 | |||
| 38 | #define dprintk(level, format, arg...) \ | ||
| 39 | do { \ | ||
| 40 | if((v9fs_debug_level & level)==level) \ | ||
| 41 | printk(KERN_NOTICE "-- %s (%d): " \ | ||
| 42 | format , __FUNCTION__, current->pid , ## arg); \ | ||
| 43 | } while(0) | ||
| 44 | |||
| 45 | #define eprintk(level, format, arg...) \ | ||
| 46 | do { \ | ||
| 47 | printk(level "v9fs: %s (%d): " \ | ||
| 48 | format , __FUNCTION__, current->pid , ## arg); \ | ||
| 49 | } while(0) | ||
| 50 | |||
| 51 | #if DEBUG_DUMP_PKT | ||
| 52 | static inline void dump_data(const unsigned char *data, unsigned int datalen) | ||
| 53 | { | ||
| 54 | int i, j; | ||
| 55 | int len = datalen; | ||
| 56 | |||
| 57 | printk(KERN_DEBUG "data "); | ||
| 58 | for (i = 0; i < len; i += 4) { | ||
| 59 | for (j = 0; (j < 4) && (i + j < len); j++) | ||
| 60 | printk(KERN_DEBUG "%02x", data[i + j]); | ||
| 61 | printk(KERN_DEBUG " "); | ||
| 62 | } | ||
| 63 | printk(KERN_DEBUG "\n"); | ||
| 64 | } | ||
| 65 | #else /* DEBUG_DUMP_PKT */ | ||
| 66 | static inline void dump_data(const unsigned char *data, unsigned int datalen) | ||
| 67 | { | ||
| 68 | |||
| 69 | } | ||
| 70 | #endif /* DEBUG_DUMP_PKT */ | ||
diff --git a/fs/9p/error.c b/fs/9p/error.c new file mode 100644 index 000000000000..fee5d19179c5 --- /dev/null +++ b/fs/9p/error.c | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/error.c | ||
| 3 | * | ||
| 4 | * Error string handling | ||
| 5 | * | ||
| 6 | * Plan 9 uses error strings, Unix uses error numbers. These functions | ||
| 7 | * try to help manage that and provide for dynamically adding error | ||
| 8 | * mappings. | ||
| 9 | * | ||
| 10 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 11 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; either version 2 of the License, or | ||
| 16 | * (at your option) any later version. | ||
| 17 | * | ||
| 18 | * This program is distributed in the hope that it will be useful, | ||
| 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 21 | * GNU General Public License for more details. | ||
| 22 | * | ||
| 23 | * You should have received a copy of the GNU General Public License | ||
| 24 | * along with this program; if not, write to: | ||
| 25 | * Free Software Foundation | ||
| 26 | * 51 Franklin Street, Fifth Floor | ||
| 27 | * Boston, MA 02111-1301 USA | ||
| 28 | * | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <linux/config.h> | ||
| 32 | #include <linux/module.h> | ||
| 33 | |||
| 34 | #include <linux/list.h> | ||
| 35 | #include <linux/jhash.h> | ||
| 36 | |||
| 37 | #include "debug.h" | ||
| 38 | #include "error.h" | ||
| 39 | |||
| 40 | /** | ||
| 41 | * v9fs_error_init - preload | ||
| 42 | * @errstr: error string | ||
| 43 | * | ||
| 44 | */ | ||
| 45 | |||
| 46 | int v9fs_error_init(void) | ||
| 47 | { | ||
| 48 | struct errormap *c; | ||
| 49 | int bucket; | ||
| 50 | |||
| 51 | /* initialize hash table */ | ||
| 52 | for (bucket = 0; bucket < ERRHASHSZ; bucket++) | ||
| 53 | INIT_HLIST_HEAD(&hash_errmap[bucket]); | ||
| 54 | |||
| 55 | /* load initial error map into hash table */ | ||
| 56 | for (c = errmap; c->name != NULL; c++) { | ||
| 57 | bucket = jhash(c->name, strlen(c->name), 0) % ERRHASHSZ; | ||
| 58 | INIT_HLIST_NODE(&c->list); | ||
| 59 | hlist_add_head(&c->list, &hash_errmap[bucket]); | ||
| 60 | } | ||
| 61 | |||
| 62 | return 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * errstr2errno - convert error string to error number | ||
| 67 | * @errstr: error string | ||
| 68 | * | ||
| 69 | */ | ||
| 70 | |||
| 71 | int v9fs_errstr2errno(char *errstr) | ||
| 72 | { | ||
| 73 | int errno = 0; | ||
| 74 | struct hlist_node *p = NULL; | ||
| 75 | struct errormap *c = NULL; | ||
| 76 | int bucket = jhash(errstr, strlen(errstr), 0) % ERRHASHSZ; | ||
| 77 | |||
| 78 | hlist_for_each_entry(c, p, &hash_errmap[bucket], list) { | ||
| 79 | if (!strcmp(c->name, errstr)) { | ||
| 80 | errno = c->val; | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | if (errno == 0) { | ||
| 86 | /* TODO: if error isn't found, add it dynamically */ | ||
| 87 | printk(KERN_ERR "%s: errstr :%s: not found\n", __FUNCTION__, | ||
| 88 | errstr); | ||
| 89 | errno = 1; | ||
| 90 | } | ||
| 91 | |||
| 92 | return -errno; | ||
| 93 | } | ||
diff --git a/fs/9p/error.h b/fs/9p/error.h new file mode 100644 index 000000000000..78f89acf7c9a --- /dev/null +++ b/fs/9p/error.h | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/error.h | ||
| 3 | * | ||
| 4 | * Huge Nasty Error Table | ||
| 5 | * | ||
| 6 | * Plan 9 uses error strings, Unix uses error numbers. This table tries to | ||
| 7 | * match UNIX strings and Plan 9 strings to unix error numbers. It is used | ||
| 8 | * to preload the dynamic error table which can also track user-specific error | ||
| 9 | * strings. | ||
| 10 | * | ||
| 11 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 12 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify | ||
| 15 | * it under the terms of the GNU General Public License as published by | ||
| 16 | * the Free Software Foundation; either version 2 of the License, or | ||
| 17 | * (at your option) any later version. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope that it will be useful, | ||
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | * GNU General Public License for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License | ||
| 25 | * along with this program; if not, write to: | ||
| 26 | * Free Software Foundation | ||
| 27 | * 51 Franklin Street, Fifth Floor | ||
| 28 | * Boston, MA 02111-1301 USA | ||
| 29 | * | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <asm/errno.h> | ||
| 34 | |||
| 35 | struct errormap { | ||
| 36 | char *name; | ||
| 37 | int val; | ||
| 38 | |||
| 39 | struct hlist_node list; | ||
| 40 | }; | ||
| 41 | |||
| 42 | #define ERRHASHSZ 32 | ||
| 43 | static struct hlist_head hash_errmap[ERRHASHSZ]; | ||
| 44 | |||
| 45 | /* FixMe - reduce to a reasonable size */ | ||
| 46 | static struct errormap errmap[] = { | ||
| 47 | {"Operation not permitted", EPERM}, | ||
| 48 | {"wstat prohibited", EPERM}, | ||
| 49 | {"No such file or directory", ENOENT}, | ||
| 50 | {"directory entry not found", ENOENT}, | ||
| 51 | {"file not found", ENOENT}, | ||
| 52 | {"Interrupted system call", EINTR}, | ||
| 53 | {"Input/output error", EIO}, | ||
| 54 | {"No such device or address", ENXIO}, | ||
| 55 | {"Argument list too long", E2BIG}, | ||
| 56 | {"Bad file descriptor", EBADF}, | ||
| 57 | {"Resource temporarily unavailable", EAGAIN}, | ||
| 58 | {"Cannot allocate memory", ENOMEM}, | ||
| 59 | {"Permission denied", EACCES}, | ||
| 60 | {"Bad address", EFAULT}, | ||
| 61 | {"Block device required", ENOTBLK}, | ||
| 62 | {"Device or resource busy", EBUSY}, | ||
| 63 | {"File exists", EEXIST}, | ||
| 64 | {"Invalid cross-device link", EXDEV}, | ||
| 65 | {"No such device", ENODEV}, | ||
| 66 | {"Not a directory", ENOTDIR}, | ||
| 67 | {"Is a directory", EISDIR}, | ||
| 68 | {"Invalid argument", EINVAL}, | ||
| 69 | {"Too many open files in system", ENFILE}, | ||
| 70 | {"Too many open files", EMFILE}, | ||
| 71 | {"Text file busy", ETXTBSY}, | ||
| 72 | {"File too large", EFBIG}, | ||
| 73 | {"No space left on device", ENOSPC}, | ||
| 74 | {"Illegal seek", ESPIPE}, | ||
| 75 | {"Read-only file system", EROFS}, | ||
| 76 | {"Too many links", EMLINK}, | ||
| 77 | {"Broken pipe", EPIPE}, | ||
| 78 | {"Numerical argument out of domain", EDOM}, | ||
| 79 | {"Numerical result out of range", ERANGE}, | ||
| 80 | {"Resource deadlock avoided", EDEADLK}, | ||
| 81 | {"File name too long", ENAMETOOLONG}, | ||
| 82 | {"No locks available", ENOLCK}, | ||
| 83 | {"Function not implemented", ENOSYS}, | ||
| 84 | {"Directory not empty", ENOTEMPTY}, | ||
| 85 | {"Too many levels of symbolic links", ELOOP}, | ||
| 86 | {"No message of desired type", ENOMSG}, | ||
| 87 | {"Identifier removed", EIDRM}, | ||
| 88 | {"No data available", ENODATA}, | ||
| 89 | {"Machine is not on the network", ENONET}, | ||
| 90 | {"Package not installed", ENOPKG}, | ||
| 91 | {"Object is remote", EREMOTE}, | ||
| 92 | {"Link has been severed", ENOLINK}, | ||
| 93 | {"Communication error on send", ECOMM}, | ||
| 94 | {"Protocol error", EPROTO}, | ||
| 95 | {"Bad message", EBADMSG}, | ||
| 96 | {"File descriptor in bad state", EBADFD}, | ||
| 97 | {"Streams pipe error", ESTRPIPE}, | ||
| 98 | {"Too many users", EUSERS}, | ||
| 99 | {"Socket operation on non-socket", ENOTSOCK}, | ||
| 100 | {"Message too long", EMSGSIZE}, | ||
| 101 | {"Protocol not available", ENOPROTOOPT}, | ||
| 102 | {"Protocol not supported", EPROTONOSUPPORT}, | ||
| 103 | {"Socket type not supported", ESOCKTNOSUPPORT}, | ||
| 104 | {"Operation not supported", EOPNOTSUPP}, | ||
| 105 | {"Protocol family not supported", EPFNOSUPPORT}, | ||
| 106 | {"Network is down", ENETDOWN}, | ||
| 107 | {"Network is unreachable", ENETUNREACH}, | ||
| 108 | {"Network dropped connection on reset", ENETRESET}, | ||
| 109 | {"Software caused connection abort", ECONNABORTED}, | ||
| 110 | {"Connection reset by peer", ECONNRESET}, | ||
| 111 | {"No buffer space available", ENOBUFS}, | ||
| 112 | {"Transport endpoint is already connected", EISCONN}, | ||
| 113 | {"Transport endpoint is not connected", ENOTCONN}, | ||
| 114 | {"Cannot send after transport endpoint shutdown", ESHUTDOWN}, | ||
| 115 | {"Connection timed out", ETIMEDOUT}, | ||
| 116 | {"Connection refused", ECONNREFUSED}, | ||
| 117 | {"Host is down", EHOSTDOWN}, | ||
| 118 | {"No route to host", EHOSTUNREACH}, | ||
| 119 | {"Operation already in progress", EALREADY}, | ||
| 120 | {"Operation now in progress", EINPROGRESS}, | ||
| 121 | {"Is a named type file", EISNAM}, | ||
| 122 | {"Remote I/O error", EREMOTEIO}, | ||
| 123 | {"Disk quota exceeded", EDQUOT}, | ||
| 124 | /* errors from fossil, vacfs, and u9fs */ | ||
| 125 | {"fid unknown or out of range", EBADF}, | ||
| 126 | {"permission denied", EACCES}, | ||
| 127 | {"file does not exist", ENOENT}, | ||
| 128 | {"authentication failed", ECONNREFUSED}, | ||
| 129 | {"bad offset in directory read", ESPIPE}, | ||
| 130 | {"bad use of fid", EBADF}, | ||
| 131 | {"wstat can't convert between files and directories", EPERM}, | ||
| 132 | {"directory is not empty", ENOTEMPTY}, | ||
| 133 | {"file exists", EEXIST}, | ||
| 134 | {"file already exists", EEXIST}, | ||
| 135 | {"file or directory already exists", EEXIST}, | ||
| 136 | {"fid already in use", EBADF}, | ||
| 137 | {"file in use", ETXTBSY}, | ||
| 138 | {"i/o error", EIO}, | ||
| 139 | {"file already open for I/O", ETXTBSY}, | ||
| 140 | {"illegal mode", EINVAL}, | ||
| 141 | {"illegal name", ENAMETOOLONG}, | ||
| 142 | {"not a directory", ENOTDIR}, | ||
| 143 | {"not a member of proposed group", EPERM}, | ||
| 144 | {"not owner", EACCES}, | ||
| 145 | {"only owner can change group in wstat", EACCES}, | ||
| 146 | {"read only file system", EROFS}, | ||
| 147 | {"no access to special file", EPERM}, | ||
| 148 | {"i/o count too large", EIO}, | ||
| 149 | {"unknown group", EINVAL}, | ||
| 150 | {"unknown user", EINVAL}, | ||
| 151 | {"bogus wstat buffer", EPROTO}, | ||
| 152 | {"exclusive use file already open", EAGAIN}, | ||
| 153 | {"corrupted directory entry", EIO}, | ||
| 154 | {"corrupted file entry", EIO}, | ||
| 155 | {"corrupted block label", EIO}, | ||
| 156 | {"corrupted meta data", EIO}, | ||
| 157 | {"illegal offset", EINVAL}, | ||
| 158 | {"illegal path element", ENOENT}, | ||
| 159 | {"root of file system is corrupted", EIO}, | ||
| 160 | {"corrupted super block", EIO}, | ||
| 161 | {"protocol botch", EPROTO}, | ||
| 162 | {"file system is full", ENOSPC}, | ||
| 163 | {"file is in use", EAGAIN}, | ||
| 164 | {"directory entry is not allocated", ENOENT}, | ||
| 165 | {"file is read only", EROFS}, | ||
| 166 | {"file has been removed", EIDRM}, | ||
| 167 | {"only support truncation to zero length", EPERM}, | ||
| 168 | {"cannot remove root", EPERM}, | ||
| 169 | {"file too big", EFBIG}, | ||
| 170 | {"venti i/o error", EIO}, | ||
| 171 | /* these are not errors */ | ||
| 172 | {"u9fs rhostsauth: no authentication required", 0}, | ||
| 173 | {"u9fs authnone: no authentication required", 0}, | ||
| 174 | {NULL, -1} | ||
| 175 | }; | ||
| 176 | |||
| 177 | extern int v9fs_error_init(void); | ||
| 178 | extern int v9fs_errstr2errno(char *errstr); | ||
diff --git a/fs/9p/fid.c b/fs/9p/fid.c new file mode 100644 index 000000000000..821c9c4d76aa --- /dev/null +++ b/fs/9p/fid.c | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | /* | ||
| 2 | * V9FS FID Management | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to: | ||
| 18 | * Free Software Foundation | ||
| 19 | * 51 Franklin Street, Fifth Floor | ||
| 20 | * Boston, MA 02111-1301 USA | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/config.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/errno.h> | ||
| 27 | #include <linux/fs.h> | ||
| 28 | #include <linux/idr.h> | ||
| 29 | |||
| 30 | #include "debug.h" | ||
| 31 | #include "v9fs.h" | ||
| 32 | #include "9p.h" | ||
| 33 | #include "v9fs_vfs.h" | ||
| 34 | #include "transport.h" | ||
| 35 | #include "mux.h" | ||
| 36 | #include "conv.h" | ||
| 37 | #include "fid.h" | ||
| 38 | |||
| 39 | /** | ||
| 40 | * v9fs_fid_insert - add a fid to a dentry | ||
| 41 | * @fid: fid to add | ||
| 42 | * @dentry: dentry that it is being added to | ||
| 43 | * | ||
| 44 | */ | ||
| 45 | |||
| 46 | static int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry) | ||
| 47 | { | ||
| 48 | struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; | ||
| 49 | dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid, | ||
| 50 | dentry->d_iname, dentry); | ||
| 51 | if (dentry->d_fsdata == NULL) { | ||
| 52 | dentry->d_fsdata = | ||
| 53 | kmalloc(sizeof(struct list_head), GFP_KERNEL); | ||
| 54 | if (dentry->d_fsdata == NULL) { | ||
| 55 | dprintk(DEBUG_ERROR, "Out of memory\n"); | ||
| 56 | return -ENOMEM; | ||
| 57 | } | ||
| 58 | fid_list = (struct list_head *)dentry->d_fsdata; | ||
| 59 | INIT_LIST_HEAD(fid_list); /* Initialize list head */ | ||
| 60 | } | ||
| 61 | |||
| 62 | fid->uid = current->uid; | ||
| 63 | fid->pid = current->pid; | ||
| 64 | list_add(&fid->list, fid_list); | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * v9fs_fid_create - allocate a FID structure | ||
| 70 | * @dentry - dentry to link newly created fid to | ||
| 71 | * | ||
| 72 | */ | ||
| 73 | |||
| 74 | struct v9fs_fid *v9fs_fid_create(struct dentry *dentry) | ||
| 75 | { | ||
| 76 | struct v9fs_fid *new; | ||
| 77 | |||
| 78 | new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); | ||
| 79 | if (new == NULL) { | ||
| 80 | dprintk(DEBUG_ERROR, "Out of Memory\n"); | ||
| 81 | return ERR_PTR(-ENOMEM); | ||
| 82 | } | ||
| 83 | |||
| 84 | new->fid = -1; | ||
| 85 | new->fidopen = 0; | ||
| 86 | new->fidcreate = 0; | ||
| 87 | new->fidclunked = 0; | ||
| 88 | new->iounit = 0; | ||
| 89 | |||
| 90 | if (v9fs_fid_insert(new, dentry) == 0) | ||
| 91 | return new; | ||
| 92 | else { | ||
| 93 | dprintk(DEBUG_ERROR, "Problems inserting to dentry\n"); | ||
| 94 | kfree(new); | ||
| 95 | return NULL; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /** | ||
| 100 | * v9fs_fid_destroy - deallocate a FID structure | ||
| 101 | * @fid: fid to destroy | ||
| 102 | * | ||
| 103 | */ | ||
| 104 | |||
| 105 | void v9fs_fid_destroy(struct v9fs_fid *fid) | ||
| 106 | { | ||
| 107 | list_del(&fid->list); | ||
| 108 | kfree(fid); | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * v9fs_fid_lookup - retrieve the right fid from a particular dentry | ||
| 113 | * @dentry: dentry to look for fid in | ||
| 114 | * @type: intent of lookup (operation or traversal) | ||
| 115 | * | ||
| 116 | * search list of fids associated with a dentry for a fid with a matching | ||
| 117 | * thread id or uid. If that fails, look up the dentry's parents to see if you | ||
| 118 | * can find a matching fid. | ||
| 119 | * | ||
| 120 | */ | ||
| 121 | |||
| 122 | struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry, int type) | ||
| 123 | { | ||
| 124 | struct list_head *fid_list = (struct list_head *)dentry->d_fsdata; | ||
| 125 | struct v9fs_fid *current_fid = NULL; | ||
| 126 | struct v9fs_fid *temp = NULL; | ||
| 127 | struct v9fs_fid *return_fid = NULL; | ||
| 128 | int found_parent = 0; | ||
| 129 | int found_user = 0; | ||
| 130 | |||
| 131 | dprintk(DEBUG_9P, " dentry: %s (%p) type %d\n", dentry->d_iname, dentry, | ||
| 132 | type); | ||
| 133 | |||
| 134 | if (fid_list && !list_empty(fid_list)) { | ||
| 135 | list_for_each_entry_safe(current_fid, temp, fid_list, list) { | ||
| 136 | if (current_fid->uid == current->uid) { | ||
| 137 | if (return_fid == NULL) { | ||
| 138 | if ((type == FID_OP) | ||
| 139 | || (!current_fid->fidopen)) { | ||
| 140 | return_fid = current_fid; | ||
| 141 | found_user = 1; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | if (current_fid->pid == current->real_parent->pid) { | ||
| 146 | if ((return_fid == NULL) || (found_parent) | ||
| 147 | || (found_user)) { | ||
| 148 | if ((type == FID_OP) | ||
| 149 | || (!current_fid->fidopen)) { | ||
| 150 | return_fid = current_fid; | ||
| 151 | found_parent = 1; | ||
| 152 | found_user = 0; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | if (current_fid->pid == current->pid) { | ||
| 157 | if ((type == FID_OP) || | ||
| 158 | (!current_fid->fidopen)) { | ||
| 159 | return_fid = current_fid; | ||
| 160 | found_parent = 0; | ||
| 161 | found_user = 0; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | /* we are at the root but didn't match */ | ||
| 168 | if ((!return_fid) && (dentry->d_parent == dentry)) { | ||
| 169 | /* TODO: clone attach with new uid */ | ||
| 170 | return_fid = current_fid; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (!return_fid) { | ||
| 174 | struct dentry *par = current->fs->pwd->d_parent; | ||
| 175 | int count = 1; | ||
| 176 | while (par != NULL) { | ||
| 177 | if (par == dentry) | ||
| 178 | break; | ||
| 179 | count++; | ||
| 180 | if (par == par->d_parent) { | ||
| 181 | dprintk(DEBUG_ERROR, | ||
| 182 | "got to root without finding dentry\n"); | ||
| 183 | break; | ||
| 184 | } | ||
| 185 | par = par->d_parent; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* XXX - there may be some duplication we can get rid of */ | ||
| 189 | if (par == dentry) { | ||
| 190 | /* we need to fid_lookup the starting point */ | ||
| 191 | int fidnum = -1; | ||
| 192 | int oldfid = -1; | ||
| 193 | int result = -1; | ||
| 194 | struct v9fs_session_info *v9ses = | ||
| 195 | v9fs_inode2v9ses(current->fs->pwd->d_inode); | ||
| 196 | |||
| 197 | current_fid = | ||
| 198 | v9fs_fid_lookup(current->fs->pwd, FID_WALK); | ||
| 199 | if (current_fid == NULL) { | ||
| 200 | dprintk(DEBUG_ERROR, | ||
| 201 | "process cwd doesn't have a fid\n"); | ||
| 202 | return return_fid; | ||
| 203 | } | ||
| 204 | oldfid = current_fid->fid; | ||
| 205 | par = current->fs->pwd; | ||
| 206 | /* TODO: take advantage of multiwalk */ | ||
| 207 | |||
| 208 | fidnum = v9fs_get_idpool(&v9ses->fidpool); | ||
| 209 | if (fidnum < 0) { | ||
| 210 | dprintk(DEBUG_ERROR, | ||
| 211 | "could not get a new fid num\n"); | ||
| 212 | return return_fid; | ||
| 213 | } | ||
| 214 | |||
| 215 | while (par != dentry) { | ||
| 216 | result = | ||
| 217 | v9fs_t_walk(v9ses, oldfid, fidnum, "..", | ||
| 218 | NULL); | ||
| 219 | if (result < 0) { | ||
| 220 | dprintk(DEBUG_ERROR, | ||
| 221 | "problem walking to parent\n"); | ||
| 222 | |||
| 223 | break; | ||
| 224 | } | ||
| 225 | oldfid = fidnum; | ||
| 226 | if (par == par->d_parent) { | ||
| 227 | dprintk(DEBUG_ERROR, | ||
| 228 | "can't find dentry\n"); | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | par = par->d_parent; | ||
| 232 | } | ||
| 233 | if (par == dentry) { | ||
| 234 | return_fid = v9fs_fid_create(dentry); | ||
| 235 | return_fid->fid = fidnum; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | return return_fid; | ||
| 241 | } | ||
diff --git a/fs/9p/fid.h b/fs/9p/fid.h new file mode 100644 index 000000000000..7db478ccca36 --- /dev/null +++ b/fs/9p/fid.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * V9FS FID Management | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to: | ||
| 18 | * Free Software Foundation | ||
| 19 | * 51 Franklin Street, Fifth Floor | ||
| 20 | * Boston, MA 02111-1301 USA | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/list.h> | ||
| 25 | |||
| 26 | #define FID_OP 0 | ||
| 27 | #define FID_WALK 1 | ||
| 28 | |||
| 29 | struct v9fs_fid { | ||
| 30 | struct list_head list; /* list of fids associated with a dentry */ | ||
| 31 | struct list_head active; /* XXX - debug */ | ||
| 32 | |||
| 33 | u32 fid; | ||
| 34 | unsigned char fidopen; /* set when fid is opened */ | ||
| 35 | unsigned char fidcreate; /* set when fid was just created */ | ||
| 36 | unsigned char fidclunked; /* set when fid has already been clunked */ | ||
| 37 | |||
| 38 | struct v9fs_qid qid; | ||
| 39 | u32 iounit; | ||
| 40 | |||
| 41 | /* readdir stuff */ | ||
| 42 | int rdir_fpos; | ||
| 43 | loff_t rdir_pos; | ||
| 44 | struct v9fs_fcall *rdir_fcall; | ||
| 45 | |||
| 46 | /* management stuff */ | ||
| 47 | pid_t pid; /* thread associated with this fid */ | ||
| 48 | uid_t uid; /* user associated with this fid */ | ||
| 49 | |||
| 50 | /* private data */ | ||
| 51 | struct file *filp; /* backpointer to File struct for open files */ | ||
| 52 | struct v9fs_session_info *v9ses; /* session info for this FID */ | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry, int type); | ||
| 56 | void v9fs_fid_destroy(struct v9fs_fid *fid); | ||
| 57 | struct v9fs_fid *v9fs_fid_create(struct dentry *); | ||
diff --git a/fs/9p/mux.c b/fs/9p/mux.c new file mode 100644 index 000000000000..8835b576f744 --- /dev/null +++ b/fs/9p/mux.c | |||
| @@ -0,0 +1,475 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/mux.c | ||
| 3 | * | ||
| 4 | * Protocol Multiplexer | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/errno.h> | ||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/kthread.h> | ||
| 32 | #include <linux/idr.h> | ||
| 33 | |||
| 34 | #include "debug.h" | ||
| 35 | #include "v9fs.h" | ||
| 36 | #include "9p.h" | ||
| 37 | #include "transport.h" | ||
| 38 | #include "conv.h" | ||
| 39 | #include "mux.h" | ||
| 40 | |||
| 41 | /** | ||
| 42 | * dprintcond - print condition of session info | ||
| 43 | * @v9ses: session info structure | ||
| 44 | * @req: RPC request structure | ||
| 45 | * | ||
| 46 | */ | ||
| 47 | |||
| 48 | static inline int | ||
| 49 | dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||
| 50 | { | ||
| 51 | dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status, | ||
| 52 | req->rcall); | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * xread - force read of a certain number of bytes | ||
| 58 | * @v9ses: session info structure | ||
| 59 | * @ptr: pointer to buffer | ||
| 60 | * @sz: number of bytes to read | ||
| 61 | * | ||
| 62 | * Chuck Cranor CS-533 project1 | ||
| 63 | */ | ||
| 64 | |||
| 65 | static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz) | ||
| 66 | { | ||
| 67 | int rd = 0; | ||
| 68 | int ret = 0; | ||
| 69 | while (rd < sz) { | ||
| 70 | ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd); | ||
| 71 | if (ret <= 0) { | ||
| 72 | dprintk(DEBUG_ERROR, "xread errno %d\n", ret); | ||
| 73 | return ret; | ||
| 74 | } | ||
| 75 | rd += ret; | ||
| 76 | ptr += ret; | ||
| 77 | } | ||
| 78 | return (rd); | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * read_message - read a full 9P2000 fcall packet | ||
| 83 | * @v9ses: session info structure | ||
| 84 | * @rcall: fcall structure to read into | ||
| 85 | * @rcalllen: size of fcall buffer | ||
| 86 | * | ||
| 87 | */ | ||
| 88 | |||
| 89 | static int | ||
| 90 | read_message(struct v9fs_session_info *v9ses, | ||
| 91 | struct v9fs_fcall *rcall, int rcalllen) | ||
| 92 | { | ||
| 93 | unsigned char buf[4]; | ||
| 94 | void *data; | ||
| 95 | int size = 0; | ||
| 96 | int res = 0; | ||
| 97 | |||
| 98 | res = xread(v9ses, buf, sizeof(buf)); | ||
| 99 | if (res < 0) { | ||
| 100 | dprintk(DEBUG_ERROR, | ||
| 101 | "Reading of count field failed returned: %d\n", res); | ||
| 102 | return res; | ||
| 103 | } | ||
| 104 | |||
| 105 | if (res < 4) { | ||
| 106 | dprintk(DEBUG_ERROR, | ||
| 107 | "Reading of count field failed returned: %d\n", res); | ||
| 108 | return -EIO; | ||
| 109 | } | ||
| 110 | |||
| 111 | size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); | ||
| 112 | dprintk(DEBUG_MUX, "got a packet count: %d\n", size); | ||
| 113 | |||
| 114 | /* adjust for the four bytes of size */ | ||
| 115 | size -= 4; | ||
| 116 | |||
| 117 | if (size > v9ses->maxdata) { | ||
| 118 | dprintk(DEBUG_ERROR, "packet too big: %d\n", size); | ||
| 119 | return -E2BIG; | ||
| 120 | } | ||
| 121 | |||
| 122 | data = kmalloc(size, GFP_KERNEL); | ||
| 123 | if (!data) { | ||
| 124 | eprintk(KERN_WARNING, "out of memory\n"); | ||
| 125 | return -ENOMEM; | ||
| 126 | } | ||
| 127 | |||
| 128 | res = xread(v9ses, data, size); | ||
| 129 | if (res < size) { | ||
| 130 | dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n", | ||
| 131 | res); | ||
| 132 | kfree(data); | ||
| 133 | return res; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* we now have an in-memory string that is the reply. | ||
| 137 | * deserialize it. There is very little to go wrong at this point | ||
| 138 | * save for v9fs_alloc errors. | ||
| 139 | */ | ||
| 140 | res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata, | ||
| 141 | rcall, rcalllen); | ||
| 142 | |||
| 143 | kfree(data); | ||
| 144 | |||
| 145 | if (res < 0) | ||
| 146 | return res; | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * v9fs_recv - receive an RPC response for a particular tag | ||
| 153 | * @v9ses: session info structure | ||
| 154 | * @req: RPC request structure | ||
| 155 | * | ||
| 156 | */ | ||
| 157 | |||
| 158 | static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||
| 159 | { | ||
| 160 | int ret = 0; | ||
| 161 | |||
| 162 | dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag); | ||
| 163 | ret = wait_event_interruptible(v9ses->read_wait, | ||
| 164 | ((v9ses->transport->status != Connected) || | ||
| 165 | (req->rcall != 0) || (req->err < 0) || | ||
| 166 | dprintcond(v9ses, req))); | ||
| 167 | |||
| 168 | dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall); | ||
| 169 | |||
| 170 | spin_lock(&v9ses->muxlock); | ||
| 171 | list_del(&req->next); | ||
| 172 | spin_unlock(&v9ses->muxlock); | ||
| 173 | |||
| 174 | if (req->err < 0) | ||
| 175 | return req->err; | ||
| 176 | |||
| 177 | if (v9ses->transport->status == Disconnected) | ||
| 178 | return -ECONNRESET; | ||
| 179 | |||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * v9fs_send - send a 9P request | ||
| 185 | * @v9ses: session info structure | ||
| 186 | * @req: RPC request to send | ||
| 187 | * | ||
| 188 | */ | ||
| 189 | |||
| 190 | static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) | ||
| 191 | { | ||
| 192 | int ret = -1; | ||
| 193 | void *data = NULL; | ||
| 194 | struct v9fs_fcall *tcall = req->tcall; | ||
| 195 | |||
| 196 | data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); | ||
| 197 | if (!data) | ||
| 198 | return -ENOMEM; | ||
| 199 | |||
| 200 | tcall->size = 0; /* enforce size recalculation */ | ||
| 201 | ret = | ||
| 202 | v9fs_serialize_fcall(v9ses, tcall, data, | ||
| 203 | v9ses->maxdata + V9FS_IOHDRSZ); | ||
| 204 | if (ret < 0) | ||
| 205 | goto free_data; | ||
| 206 | |||
| 207 | spin_lock(&v9ses->muxlock); | ||
| 208 | list_add(&req->next, &v9ses->mux_fcalls); | ||
| 209 | spin_unlock(&v9ses->muxlock); | ||
| 210 | |||
| 211 | dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag, | ||
| 212 | tcall->size); | ||
| 213 | ret = v9ses->transport->write(v9ses->transport, data, tcall->size); | ||
| 214 | |||
| 215 | if (ret != tcall->size) { | ||
| 216 | spin_lock(&v9ses->muxlock); | ||
| 217 | list_del(&req->next); | ||
| 218 | kfree(req->rcall); | ||
| 219 | |||
| 220 | spin_unlock(&v9ses->muxlock); | ||
| 221 | if (ret >= 0) | ||
| 222 | ret = -EREMOTEIO; | ||
| 223 | } else | ||
| 224 | ret = 0; | ||
| 225 | |||
| 226 | free_data: | ||
| 227 | kfree(data); | ||
| 228 | return ret; | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * v9fs_mux_rpc - send a request, receive a response | ||
| 233 | * @v9ses: session info structure | ||
| 234 | * @tcall: fcall to send | ||
| 235 | * @rcall: buffer to place response into | ||
| 236 | * | ||
| 237 | */ | ||
| 238 | |||
| 239 | long | ||
| 240 | v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall, | ||
| 241 | struct v9fs_fcall **rcall) | ||
| 242 | { | ||
| 243 | int tid = -1; | ||
| 244 | struct v9fs_fcall *fcall = NULL; | ||
| 245 | struct v9fs_rpcreq req; | ||
| 246 | int ret = -1; | ||
| 247 | |||
| 248 | if (!v9ses) | ||
| 249 | return -EINVAL; | ||
| 250 | |||
| 251 | if (!v9ses->transport || v9ses->transport->status != Connected) | ||
| 252 | return -EIO; | ||
| 253 | |||
| 254 | if (rcall) | ||
| 255 | *rcall = NULL; | ||
| 256 | |||
| 257 | if (tcall->id != TVERSION) { | ||
| 258 | tid = v9fs_get_idpool(&v9ses->tidpool); | ||
| 259 | if (tid < 0) | ||
| 260 | return -ENOMEM; | ||
| 261 | } | ||
| 262 | |||
| 263 | tcall->tag = tid; | ||
| 264 | |||
| 265 | req.tcall = tcall; | ||
| 266 | req.err = 0; | ||
| 267 | req.rcall = NULL; | ||
| 268 | |||
| 269 | ret = v9fs_send(v9ses, &req); | ||
| 270 | |||
| 271 | if (ret < 0) { | ||
| 272 | if (tcall->id != TVERSION) | ||
| 273 | v9fs_put_idpool(tid, &v9ses->tidpool); | ||
| 274 | dprintk(DEBUG_MUX, "error %d\n", ret); | ||
| 275 | return ret; | ||
| 276 | } | ||
| 277 | |||
| 278 | ret = v9fs_recv(v9ses, &req); | ||
| 279 | |||
| 280 | fcall = req.rcall; | ||
| 281 | |||
| 282 | dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret); | ||
| 283 | if (ret == -ERESTARTSYS) { | ||
| 284 | if (v9ses->transport->status != Disconnected | ||
| 285 | && tcall->id != TFLUSH) { | ||
| 286 | unsigned long flags; | ||
| 287 | |||
| 288 | dprintk(DEBUG_MUX, "flushing the tag: %d\n", | ||
| 289 | tcall->tag); | ||
| 290 | clear_thread_flag(TIF_SIGPENDING); | ||
| 291 | v9fs_t_flush(v9ses, tcall->tag); | ||
| 292 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
| 293 | recalc_sigpending(); | ||
| 294 | spin_unlock_irqrestore(¤t->sighand->siglock, | ||
| 295 | flags); | ||
| 296 | dprintk(DEBUG_MUX, "flushing done\n"); | ||
| 297 | } | ||
| 298 | |||
| 299 | goto release_req; | ||
| 300 | } else if (ret < 0) | ||
| 301 | goto release_req; | ||
| 302 | |||
| 303 | if (!fcall) | ||
| 304 | ret = -EIO; | ||
| 305 | else { | ||
| 306 | if (fcall->id == RERROR) { | ||
| 307 | ret = v9fs_errstr2errno(fcall->params.rerror.error); | ||
| 308 | if (ret == 0) { /* string match failed */ | ||
| 309 | if (fcall->params.rerror.errno) | ||
| 310 | ret = -(fcall->params.rerror.errno); | ||
| 311 | else | ||
| 312 | ret = -ESERVERFAULT; | ||
| 313 | } | ||
| 314 | } else if (fcall->id != tcall->id + 1) { | ||
| 315 | dprintk(DEBUG_ERROR, | ||
| 316 | "fcall mismatch: expected %d, got %d\n", | ||
| 317 | tcall->id + 1, fcall->id); | ||
| 318 | ret = -EIO; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | release_req: | ||
| 323 | if (tcall->id != TVERSION) | ||
| 324 | v9fs_put_idpool(tid, &v9ses->tidpool); | ||
| 325 | if (rcall) | ||
| 326 | *rcall = fcall; | ||
| 327 | else | ||
| 328 | kfree(fcall); | ||
| 329 | |||
| 330 | return ret; | ||
| 331 | } | ||
| 332 | |||
| 333 | /** | ||
| 334 | * v9fs_mux_cancel_requests - cancels all pending requests | ||
| 335 | * | ||
| 336 | * @v9ses: session info structure | ||
| 337 | * @err: error code to return to the requests | ||
| 338 | */ | ||
| 339 | void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err) | ||
| 340 | { | ||
| 341 | struct v9fs_rpcreq *rptr; | ||
| 342 | struct v9fs_rpcreq *rreq; | ||
| 343 | |||
| 344 | dprintk(DEBUG_MUX, " %d\n", err); | ||
| 345 | spin_lock(&v9ses->muxlock); | ||
| 346 | list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { | ||
| 347 | rreq->err = err; | ||
| 348 | } | ||
| 349 | spin_unlock(&v9ses->muxlock); | ||
| 350 | wake_up_all(&v9ses->read_wait); | ||
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * v9fs_recvproc - kproc to handle demultiplexing responses | ||
| 355 | * @data: session info structure | ||
| 356 | * | ||
| 357 | */ | ||
| 358 | |||
| 359 | static int v9fs_recvproc(void *data) | ||
| 360 | { | ||
| 361 | struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data; | ||
| 362 | struct v9fs_fcall *rcall = NULL; | ||
| 363 | struct v9fs_rpcreq *rptr; | ||
| 364 | struct v9fs_rpcreq *req; | ||
| 365 | struct v9fs_rpcreq *rreq; | ||
| 366 | int err = 0; | ||
| 367 | |||
| 368 | allow_signal(SIGKILL); | ||
| 369 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 370 | complete(&v9ses->proccmpl); | ||
| 371 | while (!kthread_should_stop() && err >= 0) { | ||
| 372 | req = rptr = rreq = NULL; | ||
| 373 | |||
| 374 | rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); | ||
| 375 | if (!rcall) { | ||
| 376 | eprintk(KERN_ERR, "no memory for buffers\n"); | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | |||
| 380 | err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ); | ||
| 381 | spin_lock(&v9ses->muxlock); | ||
| 382 | if (err < 0) { | ||
| 383 | list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { | ||
| 384 | rreq->err = err; | ||
| 385 | } | ||
| 386 | if(err != -ERESTARTSYS) | ||
| 387 | eprintk(KERN_ERR, | ||
| 388 | "Transport error while reading message %d\n", err); | ||
| 389 | } else { | ||
| 390 | list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { | ||
| 391 | if (rreq->tcall->tag == rcall->tag) { | ||
| 392 | req = rreq; | ||
| 393 | req->rcall = rcall; | ||
| 394 | break; | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | if (req && (req->tcall->id == TFLUSH)) { | ||
| 400 | struct v9fs_rpcreq *treq = NULL; | ||
| 401 | list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) { | ||
| 402 | if (treq->tcall->tag == | ||
| 403 | req->tcall->params.tflush.oldtag) { | ||
| 404 | list_del(&rptr->next); | ||
| 405 | kfree(treq->rcall); | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | spin_unlock(&v9ses->muxlock); | ||
| 412 | |||
| 413 | if (!req) { | ||
| 414 | if (err >= 0) | ||
| 415 | dprintk(DEBUG_ERROR, | ||
| 416 | "unexpected response: id %d tag %d\n", | ||
| 417 | rcall->id, rcall->tag); | ||
| 418 | |||
| 419 | kfree(rcall); | ||
| 420 | } | ||
| 421 | |||
| 422 | wake_up_all(&v9ses->read_wait); | ||
| 423 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 424 | } | ||
| 425 | |||
| 426 | v9ses->transport->close(v9ses->transport); | ||
| 427 | |||
| 428 | /* Inform all pending processes about the failure */ | ||
| 429 | wake_up_all(&v9ses->read_wait); | ||
| 430 | |||
| 431 | if (signal_pending(current)) | ||
| 432 | complete(&v9ses->proccmpl); | ||
| 433 | |||
| 434 | dprintk(DEBUG_MUX, "recvproc: end\n"); | ||
| 435 | v9ses->recvproc = NULL; | ||
| 436 | |||
| 437 | return err >= 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * v9fs_mux_init - initialize multiplexer (spawn kproc) | ||
| 442 | * @v9ses: session info structure | ||
| 443 | * @dev_name: mount device information (to create unique kproc) | ||
| 444 | * | ||
| 445 | */ | ||
| 446 | |||
| 447 | int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name) | ||
| 448 | { | ||
| 449 | char procname[60]; | ||
| 450 | |||
| 451 | strncpy(procname, dev_name, sizeof(procname)); | ||
| 452 | procname[sizeof(procname) - 1] = 0; | ||
| 453 | |||
| 454 | init_waitqueue_head(&v9ses->read_wait); | ||
| 455 | init_completion(&v9ses->fcread); | ||
| 456 | init_completion(&v9ses->proccmpl); | ||
| 457 | spin_lock_init(&v9ses->muxlock); | ||
| 458 | INIT_LIST_HEAD(&v9ses->mux_fcalls); | ||
| 459 | v9ses->recvproc = NULL; | ||
| 460 | v9ses->curfcall = NULL; | ||
| 461 | |||
| 462 | v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses, | ||
| 463 | "v9fs_recvproc %s", procname); | ||
| 464 | |||
| 465 | if (IS_ERR(v9ses->recvproc)) { | ||
| 466 | eprintk(KERN_ERR, "cannot create receiving thread\n"); | ||
| 467 | v9fs_session_close(v9ses); | ||
| 468 | return -ECONNABORTED; | ||
| 469 | } | ||
| 470 | |||
| 471 | wake_up_process(v9ses->recvproc); | ||
| 472 | wait_for_completion(&v9ses->proccmpl); | ||
| 473 | |||
| 474 | return 0; | ||
| 475 | } | ||
diff --git a/fs/9p/mux.h b/fs/9p/mux.h new file mode 100644 index 000000000000..4994cb10badf --- /dev/null +++ b/fs/9p/mux.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/mux.h | ||
| 3 | * | ||
| 4 | * Multiplexer Definitions | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to: | ||
| 20 | * Free Software Foundation | ||
| 21 | * 51 Franklin Street, Fifth Floor | ||
| 22 | * Boston, MA 02111-1301 USA | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | |||
| 26 | /* structure to manage each RPC transaction */ | ||
| 27 | |||
| 28 | struct v9fs_rpcreq { | ||
| 29 | struct v9fs_fcall *tcall; | ||
| 30 | struct v9fs_fcall *rcall; | ||
| 31 | int err; /* error code if response failed */ | ||
| 32 | |||
| 33 | /* XXX - could we put scatter/gather buffers here? */ | ||
| 34 | |||
| 35 | struct list_head next; | ||
| 36 | }; | ||
| 37 | |||
| 38 | int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name); | ||
| 39 | long v9fs_mux_rpc(struct v9fs_session_info *v9ses, | ||
| 40 | struct v9fs_fcall *tcall, struct v9fs_fcall **rcall); | ||
| 41 | void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err); | ||
diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c new file mode 100644 index 000000000000..63b58ce98ff4 --- /dev/null +++ b/fs/9p/trans_fd.c | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/trans_fd.c | ||
| 3 | * | ||
| 4 | * File Descriptor Transport Layer | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to: | ||
| 20 | * Free Software Foundation | ||
| 21 | * 51 Franklin Street, Fifth Floor | ||
| 22 | * Boston, MA 02111-1301 USA | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/config.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/net.h> | ||
| 29 | #include <linux/ipv6.h> | ||
| 30 | #include <linux/errno.h> | ||
| 31 | #include <linux/kernel.h> | ||
| 32 | #include <linux/un.h> | ||
| 33 | #include <asm/uaccess.h> | ||
| 34 | #include <linux/inet.h> | ||
| 35 | #include <linux/idr.h> | ||
| 36 | #include <linux/file.h> | ||
| 37 | |||
| 38 | #include "debug.h" | ||
| 39 | #include "v9fs.h" | ||
| 40 | #include "transport.h" | ||
| 41 | |||
| 42 | struct v9fs_trans_fd { | ||
| 43 | struct file *in_file; | ||
| 44 | struct file *out_file; | ||
| 45 | }; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * v9fs_fd_recv - receive from a socket | ||
| 49 | * @v9ses: session information | ||
| 50 | * @v: buffer to receive data into | ||
| 51 | * @len: size of receive buffer | ||
| 52 | * | ||
| 53 | */ | ||
| 54 | |||
| 55 | static int v9fs_fd_recv(struct v9fs_transport *trans, void *v, int len) | ||
| 56 | { | ||
| 57 | struct v9fs_trans_fd *ts = trans ? trans->priv : NULL; | ||
| 58 | |||
| 59 | if (!trans || trans->status != Connected || !ts) | ||
| 60 | return -EIO; | ||
| 61 | |||
| 62 | return kernel_read(ts->in_file, ts->in_file->f_pos, v, len); | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * v9fs_fd_send - send to a socket | ||
| 67 | * @v9ses: session information | ||
| 68 | * @v: buffer to send data from | ||
| 69 | * @len: size of send buffer | ||
| 70 | * | ||
| 71 | */ | ||
| 72 | |||
| 73 | static int v9fs_fd_send(struct v9fs_transport *trans, void *v, int len) | ||
| 74 | { | ||
| 75 | struct v9fs_trans_fd *ts = trans ? trans->priv : NULL; | ||
| 76 | mm_segment_t oldfs = get_fs(); | ||
| 77 | int ret = 0; | ||
| 78 | |||
| 79 | if (!trans || trans->status != Connected || !ts) | ||
| 80 | return -EIO; | ||
| 81 | |||
| 82 | set_fs(get_ds()); | ||
| 83 | /* The cast to a user pointer is valid due to the set_fs() */ | ||
| 84 | ret = vfs_write(ts->out_file, (void __user *)v, len, &ts->out_file->f_pos); | ||
| 85 | set_fs(oldfs); | ||
| 86 | |||
| 87 | return ret; | ||
| 88 | } | ||
| 89 | |||
| 90 | /** | ||
| 91 | * v9fs_fd_init - initialize file descriptor transport | ||
| 92 | * @v9ses: session information | ||
| 93 | * @addr: address of server to mount | ||
| 94 | * @data: mount options | ||
| 95 | * | ||
| 96 | */ | ||
| 97 | |||
| 98 | static int | ||
| 99 | v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data) | ||
| 100 | { | ||
| 101 | struct v9fs_trans_fd *ts = NULL; | ||
| 102 | struct v9fs_transport *trans = v9ses->transport; | ||
| 103 | |||
| 104 | if((v9ses->wfdno == ~0) || (v9ses->rfdno == ~0)) { | ||
| 105 | printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n"); | ||
| 106 | return -ENOPROTOOPT; | ||
| 107 | } | ||
| 108 | |||
| 109 | sema_init(&trans->writelock, 1); | ||
| 110 | sema_init(&trans->readlock, 1); | ||
| 111 | |||
| 112 | ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL); | ||
| 113 | |||
| 114 | if (!ts) | ||
| 115 | return -ENOMEM; | ||
| 116 | |||
| 117 | ts->in_file = fget( v9ses->rfdno ); | ||
| 118 | ts->out_file = fget( v9ses->wfdno ); | ||
| 119 | |||
| 120 | if (!ts->in_file || !ts->out_file) { | ||
| 121 | if (ts->in_file) | ||
| 122 | fput(ts->in_file); | ||
| 123 | |||
| 124 | if (ts->out_file) | ||
| 125 | fput(ts->out_file); | ||
| 126 | |||
| 127 | kfree(ts); | ||
| 128 | return -EIO; | ||
| 129 | } | ||
| 130 | |||
| 131 | trans->priv = ts; | ||
| 132 | trans->status = Connected; | ||
| 133 | |||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | |||
| 138 | /** | ||
| 139 | * v9fs_fd_close - shutdown file descriptor | ||
| 140 | * @trans: private socket structure | ||
| 141 | * | ||
| 142 | */ | ||
| 143 | |||
| 144 | static void v9fs_fd_close(struct v9fs_transport *trans) | ||
| 145 | { | ||
| 146 | struct v9fs_trans_fd *ts; | ||
| 147 | |||
| 148 | if (!trans) | ||
| 149 | return; | ||
| 150 | |||
| 151 | trans->status = Disconnected; | ||
| 152 | ts = trans->priv; | ||
| 153 | |||
| 154 | if (!ts) | ||
| 155 | return; | ||
| 156 | |||
| 157 | if (ts->in_file) | ||
| 158 | fput(ts->in_file); | ||
| 159 | |||
| 160 | if (ts->out_file) | ||
| 161 | fput(ts->out_file); | ||
| 162 | |||
| 163 | kfree(ts); | ||
| 164 | } | ||
| 165 | |||
| 166 | struct v9fs_transport v9fs_trans_fd = { | ||
| 167 | .init = v9fs_fd_init, | ||
| 168 | .write = v9fs_fd_send, | ||
| 169 | .read = v9fs_fd_recv, | ||
| 170 | .close = v9fs_fd_close, | ||
| 171 | }; | ||
| 172 | |||
diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c new file mode 100644 index 000000000000..01e26f0013ac --- /dev/null +++ b/fs/9p/trans_sock.c | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/trans_socket.c | ||
| 3 | * | ||
| 4 | * Socket Transport Layer | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> | ||
| 8 | * Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to: | ||
| 22 | * Free Software Foundation | ||
| 23 | * 51 Franklin Street, Fifth Floor | ||
| 24 | * Boston, MA 02111-1301 USA | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/config.h> | ||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/net.h> | ||
| 31 | #include <linux/ipv6.h> | ||
| 32 | #include <linux/errno.h> | ||
| 33 | #include <linux/kernel.h> | ||
| 34 | #include <linux/un.h> | ||
| 35 | #include <asm/uaccess.h> | ||
| 36 | #include <linux/inet.h> | ||
| 37 | #include <linux/idr.h> | ||
| 38 | |||
| 39 | #include "debug.h" | ||
| 40 | #include "v9fs.h" | ||
| 41 | #include "transport.h" | ||
| 42 | |||
| 43 | #define V9FS_PORT 564 | ||
| 44 | |||
| 45 | struct v9fs_trans_sock { | ||
| 46 | struct socket *s; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * v9fs_sock_recv - receive from a socket | ||
| 51 | * @v9ses: session information | ||
| 52 | * @v: buffer to receive data into | ||
| 53 | * @len: size of receive buffer | ||
| 54 | * | ||
| 55 | */ | ||
| 56 | |||
| 57 | static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) | ||
| 58 | { | ||
| 59 | struct msghdr msg; | ||
| 60 | struct kvec iov; | ||
| 61 | int result; | ||
| 62 | mm_segment_t oldfs; | ||
| 63 | struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; | ||
| 64 | |||
| 65 | if (trans->status == Disconnected) | ||
| 66 | return -EREMOTEIO; | ||
| 67 | |||
| 68 | result = -EINVAL; | ||
| 69 | |||
| 70 | oldfs = get_fs(); | ||
| 71 | set_fs(get_ds()); | ||
| 72 | |||
| 73 | iov.iov_base = v; | ||
| 74 | iov.iov_len = len; | ||
| 75 | msg.msg_name = NULL; | ||
| 76 | msg.msg_namelen = 0; | ||
| 77 | msg.msg_iovlen = 1; | ||
| 78 | msg.msg_control = NULL; | ||
| 79 | msg.msg_controllen = 0; | ||
| 80 | msg.msg_namelen = 0; | ||
| 81 | msg.msg_flags = MSG_NOSIGNAL; | ||
| 82 | |||
| 83 | result = kernel_recvmsg(ts->s, &msg, &iov, 1, len, 0); | ||
| 84 | |||
| 85 | dprintk(DEBUG_TRANS, "socket state %d\n", ts->s->state); | ||
| 86 | set_fs(oldfs); | ||
| 87 | |||
| 88 | if (result <= 0) { | ||
| 89 | if (result != -ERESTARTSYS) | ||
| 90 | trans->status = Disconnected; | ||
| 91 | } | ||
| 92 | |||
| 93 | return result; | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * v9fs_sock_send - send to a socket | ||
| 98 | * @v9ses: session information | ||
| 99 | * @v: buffer to send data from | ||
| 100 | * @len: size of send buffer | ||
| 101 | * | ||
| 102 | */ | ||
| 103 | |||
| 104 | static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len) | ||
| 105 | { | ||
| 106 | struct kvec iov; | ||
| 107 | struct msghdr msg; | ||
| 108 | int result = -1; | ||
| 109 | mm_segment_t oldfs; | ||
| 110 | struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; | ||
| 111 | |||
| 112 | dprintk(DEBUG_TRANS, "Sending packet size %d (%x)\n", len, len); | ||
| 113 | dump_data(v, len); | ||
| 114 | |||
| 115 | down(&trans->writelock); | ||
| 116 | |||
| 117 | oldfs = get_fs(); | ||
| 118 | set_fs(get_ds()); | ||
| 119 | iov.iov_base = v; | ||
| 120 | iov.iov_len = len; | ||
| 121 | msg.msg_name = NULL; | ||
| 122 | msg.msg_namelen = 0; | ||
| 123 | msg.msg_iovlen = 1; | ||
| 124 | msg.msg_control = NULL; | ||
| 125 | msg.msg_controllen = 0; | ||
| 126 | msg.msg_namelen = 0; | ||
| 127 | msg.msg_flags = MSG_NOSIGNAL; | ||
| 128 | result = kernel_sendmsg(ts->s, &msg, &iov, 1, len); | ||
| 129 | set_fs(oldfs); | ||
| 130 | |||
| 131 | if (result < 0) { | ||
| 132 | if (result != -ERESTARTSYS) | ||
| 133 | trans->status = Disconnected; | ||
| 134 | } | ||
| 135 | |||
| 136 | up(&trans->writelock); | ||
| 137 | return result; | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * v9fs_tcp_init - initialize TCP socket | ||
| 142 | * @v9ses: session information | ||
| 143 | * @addr: address of server to mount | ||
| 144 | * @data: mount options | ||
| 145 | * | ||
| 146 | */ | ||
| 147 | |||
| 148 | static int | ||
| 149 | v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) | ||
| 150 | { | ||
| 151 | struct socket *csocket = NULL; | ||
| 152 | struct sockaddr_in sin_server; | ||
| 153 | int rc = 0; | ||
| 154 | struct v9fs_trans_sock *ts = NULL; | ||
| 155 | struct v9fs_transport *trans = v9ses->transport; | ||
| 156 | |||
| 157 | sema_init(&trans->writelock, 1); | ||
| 158 | sema_init(&trans->readlock, 1); | ||
| 159 | |||
| 160 | ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); | ||
| 161 | |||
| 162 | if (!ts) | ||
| 163 | return -ENOMEM; | ||
| 164 | |||
| 165 | trans->priv = ts; | ||
| 166 | ts->s = NULL; | ||
| 167 | |||
| 168 | if (!addr) | ||
| 169 | return -EINVAL; | ||
| 170 | |||
| 171 | dprintk(DEBUG_TRANS, "Connecting to %s\n", addr); | ||
| 172 | |||
| 173 | sin_server.sin_family = AF_INET; | ||
| 174 | sin_server.sin_addr.s_addr = in_aton(addr); | ||
| 175 | sin_server.sin_port = htons(v9ses->port); | ||
| 176 | sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket); | ||
| 177 | rc = csocket->ops->connect(csocket, | ||
| 178 | (struct sockaddr *)&sin_server, | ||
| 179 | sizeof(struct sockaddr_in), 0); | ||
| 180 | if (rc < 0) { | ||
| 181 | eprintk(KERN_ERR, | ||
| 182 | "v9fs_trans_tcp: problem connecting socket to %s\n", | ||
| 183 | addr); | ||
| 184 | return rc; | ||
| 185 | } | ||
| 186 | csocket->sk->sk_allocation = GFP_NOIO; | ||
| 187 | ts->s = csocket; | ||
| 188 | trans->status = Connected; | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** | ||
| 194 | * v9fs_unix_init - initialize UNIX domain socket | ||
| 195 | * @v9ses: session information | ||
| 196 | * @dev_name: path to named pipe | ||
| 197 | * @data: mount options | ||
| 198 | * | ||
| 199 | */ | ||
| 200 | |||
| 201 | static int | ||
| 202 | v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, | ||
| 203 | char *data) | ||
| 204 | { | ||
| 205 | int rc; | ||
| 206 | struct socket *csocket; | ||
| 207 | struct sockaddr_un sun_server; | ||
| 208 | struct v9fs_transport *trans; | ||
| 209 | struct v9fs_trans_sock *ts; | ||
| 210 | |||
| 211 | rc = 0; | ||
| 212 | csocket = NULL; | ||
| 213 | trans = v9ses->transport; | ||
| 214 | |||
| 215 | if (strlen(dev_name) > UNIX_PATH_MAX) { | ||
| 216 | eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", | ||
| 217 | dev_name); | ||
| 218 | return -ENOMEM; | ||
| 219 | } | ||
| 220 | |||
| 221 | ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); | ||
| 222 | if (!ts) | ||
| 223 | return -ENOMEM; | ||
| 224 | |||
| 225 | trans->priv = ts; | ||
| 226 | ts->s = NULL; | ||
| 227 | |||
| 228 | sema_init(&trans->writelock, 1); | ||
| 229 | sema_init(&trans->readlock, 1); | ||
| 230 | |||
| 231 | sun_server.sun_family = PF_UNIX; | ||
| 232 | strcpy(sun_server.sun_path, dev_name); | ||
| 233 | sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket); | ||
| 234 | rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server, | ||
| 235 | sizeof(struct sockaddr_un) - 1, 0); /* -1 *is* important */ | ||
| 236 | if (rc < 0) { | ||
| 237 | eprintk(KERN_ERR, | ||
| 238 | "v9fs_trans_unix: problem connecting socket: %s: %d\n", | ||
| 239 | dev_name, rc); | ||
| 240 | return rc; | ||
| 241 | } | ||
| 242 | csocket->sk->sk_allocation = GFP_NOIO; | ||
| 243 | ts->s = csocket; | ||
| 244 | trans->status = Connected; | ||
| 245 | |||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | /** | ||
| 250 | * v9fs_sock_close - shutdown socket | ||
| 251 | * @trans: private socket structure | ||
| 252 | * | ||
| 253 | */ | ||
| 254 | |||
| 255 | static void v9fs_sock_close(struct v9fs_transport *trans) | ||
| 256 | { | ||
| 257 | struct v9fs_trans_sock *ts; | ||
| 258 | |||
| 259 | if (!trans) | ||
| 260 | return; | ||
| 261 | |||
| 262 | ts = trans->priv; | ||
| 263 | |||
| 264 | if ((ts) && (ts->s)) { | ||
| 265 | dprintk(DEBUG_TRANS, "closing the socket %p\n", ts->s); | ||
| 266 | sock_release(ts->s); | ||
| 267 | ts->s = NULL; | ||
| 268 | trans->status = Disconnected; | ||
| 269 | dprintk(DEBUG_TRANS, "socket closed\n"); | ||
| 270 | } | ||
| 271 | |||
| 272 | if (ts) | ||
| 273 | kfree(ts); | ||
| 274 | |||
| 275 | trans->priv = NULL; | ||
| 276 | } | ||
| 277 | |||
| 278 | struct v9fs_transport v9fs_trans_tcp = { | ||
| 279 | .init = v9fs_tcp_init, | ||
| 280 | .write = v9fs_sock_send, | ||
| 281 | .read = v9fs_sock_recv, | ||
| 282 | .close = v9fs_sock_close, | ||
| 283 | }; | ||
| 284 | |||
| 285 | struct v9fs_transport v9fs_trans_unix = { | ||
| 286 | .init = v9fs_unix_init, | ||
| 287 | .write = v9fs_sock_send, | ||
| 288 | .read = v9fs_sock_recv, | ||
| 289 | .close = v9fs_sock_close, | ||
| 290 | }; | ||
diff --git a/fs/9p/transport.h b/fs/9p/transport.h new file mode 100644 index 000000000000..9e9cd418efd5 --- /dev/null +++ b/fs/9p/transport.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/transport.h | ||
| 3 | * | ||
| 4 | * Transport Definition | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to: | ||
| 20 | * Free Software Foundation | ||
| 21 | * 51 Franklin Street, Fifth Floor | ||
| 22 | * Boston, MA 02111-1301 USA | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | |||
| 26 | enum v9fs_transport_status { | ||
| 27 | Connected, | ||
| 28 | Disconnected, | ||
| 29 | Hung, | ||
| 30 | }; | ||
| 31 | |||
| 32 | struct v9fs_transport { | ||
| 33 | enum v9fs_transport_status status; | ||
| 34 | struct semaphore writelock; | ||
| 35 | struct semaphore readlock; | ||
| 36 | void *priv; | ||
| 37 | |||
| 38 | int (*init) (struct v9fs_session_info *, const char *, char *); | ||
| 39 | int (*write) (struct v9fs_transport *, void *, int); | ||
| 40 | int (*read) (struct v9fs_transport *, void *, int); | ||
| 41 | void (*close) (struct v9fs_transport *); | ||
| 42 | }; | ||
| 43 | |||
| 44 | extern struct v9fs_transport v9fs_trans_tcp; | ||
| 45 | extern struct v9fs_transport v9fs_trans_unix; | ||
| 46 | extern struct v9fs_transport v9fs_trans_fd; | ||
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c new file mode 100644 index 000000000000..13bdbbab4387 --- /dev/null +++ b/fs/9p/v9fs.c | |||
| @@ -0,0 +1,452 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/v9fs.c | ||
| 3 | * | ||
| 4 | * This file contains functions assisting in mapping VFS to 9P2000 | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/config.h> | ||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/errno.h> | ||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/parser.h> | ||
| 32 | #include <linux/idr.h> | ||
| 33 | |||
| 34 | #include "debug.h" | ||
| 35 | #include "v9fs.h" | ||
| 36 | #include "9p.h" | ||
| 37 | #include "v9fs_vfs.h" | ||
| 38 | #include "transport.h" | ||
| 39 | #include "mux.h" | ||
| 40 | #include "conv.h" | ||
| 41 | |||
| 42 | /* TODO: sysfs or debugfs interface */ | ||
| 43 | int v9fs_debug_level = 0; /* feature-rific global debug level */ | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Option Parsing (code inspired by NFS code) | ||
| 47 | * | ||
| 48 | */ | ||
| 49 | |||
| 50 | enum { | ||
| 51 | /* Options that take integer arguments */ | ||
| 52 | Opt_port, Opt_msize, Opt_uid, Opt_gid, Opt_afid, Opt_debug, | ||
| 53 | Opt_rfdno, Opt_wfdno, | ||
| 54 | /* String options */ | ||
| 55 | Opt_name, Opt_remotename, | ||
| 56 | /* Options that take no arguments */ | ||
| 57 | Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd, | ||
| 58 | /* Error token */ | ||
| 59 | Opt_err | ||
| 60 | }; | ||
| 61 | |||
| 62 | static match_table_t tokens = { | ||
| 63 | {Opt_port, "port=%u"}, | ||
| 64 | {Opt_msize, "msize=%u"}, | ||
| 65 | {Opt_uid, "uid=%u"}, | ||
| 66 | {Opt_gid, "gid=%u"}, | ||
| 67 | {Opt_afid, "afid=%u"}, | ||
| 68 | {Opt_rfdno, "rfdno=%u"}, | ||
| 69 | {Opt_wfdno, "wfdno=%u"}, | ||
| 70 | {Opt_debug, "debug=%u"}, | ||
| 71 | {Opt_name, "name=%s"}, | ||
| 72 | {Opt_remotename, "aname=%s"}, | ||
| 73 | {Opt_unix, "proto=unix"}, | ||
| 74 | {Opt_tcp, "proto=tcp"}, | ||
| 75 | {Opt_fd, "proto=fd"}, | ||
| 76 | {Opt_tcp, "tcp"}, | ||
| 77 | {Opt_unix, "unix"}, | ||
| 78 | {Opt_fd, "fd"}, | ||
| 79 | {Opt_legacy, "noextend"}, | ||
| 80 | {Opt_nodevmap, "nodevmap"}, | ||
| 81 | {Opt_err, NULL} | ||
| 82 | }; | ||
| 83 | |||
| 84 | /* | ||
| 85 | * Parse option string. | ||
| 86 | */ | ||
| 87 | |||
| 88 | /** | ||
| 89 | * v9fs_parse_options - parse mount options into session structure | ||
| 90 | * @options: options string passed from mount | ||
| 91 | * @v9ses: existing v9fs session information | ||
| 92 | * | ||
| 93 | */ | ||
| 94 | |||
| 95 | static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses) | ||
| 96 | { | ||
| 97 | char *p; | ||
| 98 | substring_t args[MAX_OPT_ARGS]; | ||
| 99 | int option; | ||
| 100 | int ret; | ||
| 101 | |||
| 102 | /* setup defaults */ | ||
| 103 | v9ses->port = V9FS_PORT; | ||
| 104 | v9ses->maxdata = 9000; | ||
| 105 | v9ses->proto = PROTO_TCP; | ||
| 106 | v9ses->extended = 1; | ||
| 107 | v9ses->afid = ~0; | ||
| 108 | v9ses->debug = 0; | ||
| 109 | v9ses->rfdno = ~0; | ||
| 110 | v9ses->wfdno = ~0; | ||
| 111 | |||
| 112 | if (!options) | ||
| 113 | return; | ||
| 114 | |||
| 115 | while ((p = strsep(&options, ",")) != NULL) { | ||
| 116 | int token; | ||
| 117 | if (!*p) | ||
| 118 | continue; | ||
| 119 | token = match_token(p, tokens, args); | ||
| 120 | if (token < Opt_name) { | ||
| 121 | if ((ret = match_int(&args[0], &option)) < 0) { | ||
| 122 | dprintk(DEBUG_ERROR, | ||
| 123 | "integer field, but no integer?\n"); | ||
| 124 | continue; | ||
| 125 | } | ||
| 126 | |||
| 127 | } | ||
| 128 | switch (token) { | ||
| 129 | case Opt_port: | ||
| 130 | v9ses->port = option; | ||
| 131 | break; | ||
| 132 | case Opt_msize: | ||
| 133 | v9ses->maxdata = option; | ||
| 134 | break; | ||
| 135 | case Opt_uid: | ||
| 136 | v9ses->uid = option; | ||
| 137 | break; | ||
| 138 | case Opt_gid: | ||
| 139 | v9ses->gid = option; | ||
| 140 | break; | ||
| 141 | case Opt_afid: | ||
| 142 | v9ses->afid = option; | ||
| 143 | break; | ||
| 144 | case Opt_rfdno: | ||
| 145 | v9ses->rfdno = option; | ||
| 146 | break; | ||
| 147 | case Opt_wfdno: | ||
| 148 | v9ses->wfdno = option; | ||
| 149 | break; | ||
| 150 | case Opt_debug: | ||
| 151 | v9ses->debug = option; | ||
| 152 | break; | ||
| 153 | case Opt_tcp: | ||
| 154 | v9ses->proto = PROTO_TCP; | ||
| 155 | break; | ||
| 156 | case Opt_unix: | ||
| 157 | v9ses->proto = PROTO_UNIX; | ||
| 158 | break; | ||
| 159 | case Opt_fd: | ||
| 160 | v9ses->proto = PROTO_FD; | ||
| 161 | break; | ||
| 162 | case Opt_name: | ||
| 163 | match_strcpy(v9ses->name, &args[0]); | ||
| 164 | break; | ||
| 165 | case Opt_remotename: | ||
| 166 | match_strcpy(v9ses->remotename, &args[0]); | ||
| 167 | break; | ||
| 168 | case Opt_legacy: | ||
| 169 | v9ses->extended = 0; | ||
| 170 | break; | ||
| 171 | case Opt_nodevmap: | ||
| 172 | v9ses->nodev = 1; | ||
| 173 | break; | ||
| 174 | default: | ||
| 175 | continue; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * v9fs_inode2v9ses - safely extract v9fs session info from super block | ||
| 182 | * @inode: inode to extract information from | ||
| 183 | * | ||
| 184 | * Paranoid function to extract v9ses information from superblock, | ||
| 185 | * if anything is missing it will report an error. | ||
| 186 | * | ||
| 187 | */ | ||
| 188 | |||
| 189 | struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode) | ||
| 190 | { | ||
| 191 | return (inode->i_sb->s_fs_info); | ||
| 192 | } | ||
| 193 | |||
| 194 | /** | ||
| 195 | * v9fs_get_idpool - allocate numeric id from pool | ||
| 196 | * @p - pool to allocate from | ||
| 197 | * | ||
| 198 | * XXX - This seems to be an awful generic function, should it be in idr.c with | ||
| 199 | * the lock included in struct idr? | ||
| 200 | */ | ||
| 201 | |||
| 202 | int v9fs_get_idpool(struct v9fs_idpool *p) | ||
| 203 | { | ||
| 204 | int i = 0; | ||
| 205 | int error; | ||
| 206 | |||
| 207 | retry: | ||
| 208 | if (idr_pre_get(&p->pool, GFP_KERNEL) == 0) | ||
| 209 | return 0; | ||
| 210 | |||
| 211 | if (down_interruptible(&p->lock) == -EINTR) { | ||
| 212 | eprintk(KERN_WARNING, "Interrupted while locking\n"); | ||
| 213 | return -1; | ||
| 214 | } | ||
| 215 | |||
| 216 | error = idr_get_new(&p->pool, NULL, &i); | ||
| 217 | up(&p->lock); | ||
| 218 | |||
| 219 | if (error == -EAGAIN) | ||
| 220 | goto retry; | ||
| 221 | else if (error) | ||
| 222 | return -1; | ||
| 223 | |||
| 224 | return i; | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * v9fs_put_idpool - release numeric id from pool | ||
| 229 | * @p - pool to allocate from | ||
| 230 | * | ||
| 231 | * XXX - This seems to be an awful generic function, should it be in idr.c with | ||
| 232 | * the lock included in struct idr? | ||
| 233 | */ | ||
| 234 | |||
| 235 | void v9fs_put_idpool(int id, struct v9fs_idpool *p) | ||
| 236 | { | ||
| 237 | if (down_interruptible(&p->lock) == -EINTR) { | ||
| 238 | eprintk(KERN_WARNING, "Interrupted while locking\n"); | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | idr_remove(&p->pool, id); | ||
| 242 | up(&p->lock); | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * v9fs_session_init - initialize session | ||
| 247 | * @v9ses: session information structure | ||
| 248 | * @dev_name: device being mounted | ||
| 249 | * @data: options | ||
| 250 | * | ||
| 251 | */ | ||
| 252 | |||
| 253 | int | ||
| 254 | v9fs_session_init(struct v9fs_session_info *v9ses, | ||
| 255 | const char *dev_name, char *data) | ||
| 256 | { | ||
| 257 | struct v9fs_fcall *fcall = NULL; | ||
| 258 | struct v9fs_transport *trans_proto; | ||
| 259 | int n = 0; | ||
| 260 | int newfid = -1; | ||
| 261 | int retval = -EINVAL; | ||
| 262 | |||
| 263 | v9ses->name = __getname(); | ||
| 264 | if (!v9ses->name) | ||
| 265 | return -ENOMEM; | ||
| 266 | |||
| 267 | v9ses->remotename = __getname(); | ||
| 268 | if (!v9ses->remotename) { | ||
| 269 | putname(v9ses->name); | ||
| 270 | return -ENOMEM; | ||
| 271 | } | ||
| 272 | |||
| 273 | strcpy(v9ses->name, V9FS_DEFUSER); | ||
| 274 | strcpy(v9ses->remotename, V9FS_DEFANAME); | ||
| 275 | |||
| 276 | v9fs_parse_options(data, v9ses); | ||
| 277 | |||
| 278 | /* set global debug level */ | ||
| 279 | v9fs_debug_level = v9ses->debug; | ||
| 280 | |||
| 281 | /* id pools that are session-dependent: FIDs and TIDs */ | ||
| 282 | idr_init(&v9ses->fidpool.pool); | ||
| 283 | init_MUTEX(&v9ses->fidpool.lock); | ||
| 284 | idr_init(&v9ses->tidpool.pool); | ||
| 285 | init_MUTEX(&v9ses->tidpool.lock); | ||
| 286 | |||
| 287 | |||
| 288 | switch (v9ses->proto) { | ||
| 289 | case PROTO_TCP: | ||
| 290 | trans_proto = &v9fs_trans_tcp; | ||
| 291 | break; | ||
| 292 | case PROTO_UNIX: | ||
| 293 | trans_proto = &v9fs_trans_unix; | ||
| 294 | *v9ses->remotename = 0; | ||
| 295 | break; | ||
| 296 | case PROTO_FD: | ||
| 297 | trans_proto = &v9fs_trans_fd; | ||
| 298 | *v9ses->remotename = 0; | ||
| 299 | break; | ||
| 300 | default: | ||
| 301 | printk(KERN_ERR "v9fs: Bad mount protocol %d\n", v9ses->proto); | ||
| 302 | retval = -ENOPROTOOPT; | ||
| 303 | goto SessCleanUp; | ||
| 304 | }; | ||
| 305 | |||
| 306 | v9ses->transport = trans_proto; | ||
| 307 | |||
| 308 | if ((retval = v9ses->transport->init(v9ses, dev_name, data)) < 0) { | ||
| 309 | eprintk(KERN_ERR, "problem initializing transport\n"); | ||
| 310 | goto SessCleanUp; | ||
| 311 | } | ||
| 312 | |||
| 313 | v9ses->inprogress = 0; | ||
| 314 | v9ses->shutdown = 0; | ||
| 315 | v9ses->session_hung = 0; | ||
| 316 | |||
| 317 | if ((retval = v9fs_mux_init(v9ses, dev_name)) < 0) { | ||
| 318 | dprintk(DEBUG_ERROR, "problem initializing mux\n"); | ||
| 319 | goto SessCleanUp; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (v9ses->afid == ~0) { | ||
| 323 | if (v9ses->extended) | ||
| 324 | retval = | ||
| 325 | v9fs_t_version(v9ses, v9ses->maxdata, "9P2000.u", | ||
| 326 | &fcall); | ||
| 327 | else | ||
| 328 | retval = v9fs_t_version(v9ses, v9ses->maxdata, "9P2000", | ||
| 329 | &fcall); | ||
| 330 | |||
| 331 | if (retval < 0) { | ||
| 332 | dprintk(DEBUG_ERROR, "v9fs_t_version failed\n"); | ||
| 333 | goto FreeFcall; | ||
| 334 | } | ||
| 335 | |||
| 336 | /* Really should check for 9P1 and report error */ | ||
| 337 | if (!strcmp(fcall->params.rversion.version, "9P2000.u")) { | ||
| 338 | dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n"); | ||
| 339 | v9ses->extended = 1; | ||
| 340 | } else { | ||
| 341 | dprintk(DEBUG_9P, "9P2000 legacy mode enabled\n"); | ||
| 342 | v9ses->extended = 0; | ||
| 343 | } | ||
| 344 | |||
| 345 | n = fcall->params.rversion.msize; | ||
| 346 | kfree(fcall); | ||
| 347 | |||
| 348 | if (n < v9ses->maxdata) | ||
| 349 | v9ses->maxdata = n; | ||
| 350 | } | ||
| 351 | |||
| 352 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 353 | if (newfid < 0) { | ||
| 354 | eprintk(KERN_WARNING, "couldn't allocate FID\n"); | ||
| 355 | retval = -ENOMEM; | ||
| 356 | goto SessCleanUp; | ||
| 357 | } | ||
| 358 | /* it is a little bit ugly, but we have to prevent newfid */ | ||
| 359 | /* being the same as afid, so if it is, get a new fid */ | ||
| 360 | if (v9ses->afid != ~0 && newfid == v9ses->afid) { | ||
| 361 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 362 | if (newfid < 0) { | ||
| 363 | eprintk(KERN_WARNING, "couldn't allocate FID\n"); | ||
| 364 | retval = -ENOMEM; | ||
| 365 | goto SessCleanUp; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | if ((retval = | ||
| 370 | v9fs_t_attach(v9ses, v9ses->name, v9ses->remotename, newfid, | ||
| 371 | v9ses->afid, NULL)) | ||
| 372 | < 0) { | ||
| 373 | dprintk(DEBUG_ERROR, "cannot attach\n"); | ||
| 374 | goto SessCleanUp; | ||
| 375 | } | ||
| 376 | |||
| 377 | if (v9ses->afid != ~0) { | ||
| 378 | if (v9fs_t_clunk(v9ses, v9ses->afid, NULL)) | ||
| 379 | dprintk(DEBUG_ERROR, "clunk failed\n"); | ||
| 380 | } | ||
| 381 | |||
| 382 | return newfid; | ||
| 383 | |||
| 384 | FreeFcall: | ||
| 385 | kfree(fcall); | ||
| 386 | |||
| 387 | SessCleanUp: | ||
| 388 | v9fs_session_close(v9ses); | ||
| 389 | return retval; | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * v9fs_session_close - shutdown a session | ||
| 394 | * @v9ses: session information structure | ||
| 395 | * | ||
| 396 | */ | ||
| 397 | |||
| 398 | void v9fs_session_close(struct v9fs_session_info *v9ses) | ||
| 399 | { | ||
| 400 | if (v9ses->recvproc) { | ||
| 401 | send_sig(SIGKILL, v9ses->recvproc, 1); | ||
| 402 | wait_for_completion(&v9ses->proccmpl); | ||
| 403 | } | ||
| 404 | |||
| 405 | if (v9ses->transport) | ||
| 406 | v9ses->transport->close(v9ses->transport); | ||
| 407 | |||
| 408 | putname(v9ses->name); | ||
| 409 | putname(v9ses->remotename); | ||
| 410 | } | ||
| 411 | |||
| 412 | /** | ||
| 413 | * v9fs_session_cancel - mark transport as disconnected | ||
| 414 | * and cancel all pending requests. | ||
| 415 | */ | ||
| 416 | void v9fs_session_cancel(struct v9fs_session_info *v9ses) { | ||
| 417 | v9ses->transport->status = Disconnected; | ||
| 418 | v9fs_mux_cancel_requests(v9ses, -EIO); | ||
| 419 | } | ||
| 420 | |||
| 421 | extern int v9fs_error_init(void); | ||
| 422 | |||
| 423 | /** | ||
| 424 | * v9fs_init - Initialize module | ||
| 425 | * | ||
| 426 | */ | ||
| 427 | |||
| 428 | static int __init init_v9fs(void) | ||
| 429 | { | ||
| 430 | v9fs_error_init(); | ||
| 431 | |||
| 432 | printk(KERN_INFO "Installing v9fs 9P2000 file system support\n"); | ||
| 433 | |||
| 434 | return register_filesystem(&v9fs_fs_type); | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | * v9fs_init - shutdown module | ||
| 439 | * | ||
| 440 | */ | ||
| 441 | |||
| 442 | static void __exit exit_v9fs(void) | ||
| 443 | { | ||
| 444 | unregister_filesystem(&v9fs_fs_type); | ||
| 445 | } | ||
| 446 | |||
| 447 | module_init(init_v9fs) | ||
| 448 | module_exit(exit_v9fs) | ||
| 449 | |||
| 450 | MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); | ||
| 451 | MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>"); | ||
| 452 | MODULE_LICENSE("GPL"); | ||
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h new file mode 100644 index 000000000000..45dcef42bdd6 --- /dev/null +++ b/fs/9p/v9fs.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | /* | ||
| 2 | * V9FS definitions. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 5 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to: | ||
| 19 | * Free Software Foundation | ||
| 20 | * 51 Franklin Street, Fifth Floor | ||
| 21 | * Boston, MA 02111-1301 USA | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Idpool structure provides lock and id management | ||
| 27 | * | ||
| 28 | */ | ||
| 29 | |||
| 30 | struct v9fs_idpool { | ||
| 31 | struct semaphore lock; | ||
| 32 | struct idr pool; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Session structure provides information for an opened session | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | struct v9fs_session_info { | ||
| 41 | /* options */ | ||
| 42 | unsigned int maxdata; | ||
| 43 | unsigned char extended; /* set to 1 if we are using UNIX extensions */ | ||
| 44 | unsigned char nodev; /* set to 1 if no disable device mapping */ | ||
| 45 | unsigned short port; /* port to connect to */ | ||
| 46 | unsigned short debug; /* debug level */ | ||
| 47 | unsigned short proto; /* protocol to use */ | ||
| 48 | unsigned int afid; /* authentication fid */ | ||
| 49 | unsigned int rfdno; /* read file descriptor number */ | ||
| 50 | unsigned int wfdno; /* write file descriptor number */ | ||
| 51 | |||
| 52 | |||
| 53 | char *name; /* user name to mount as */ | ||
| 54 | char *remotename; /* name of remote hierarchy being mounted */ | ||
| 55 | unsigned int uid; /* default uid/muid for legacy support */ | ||
| 56 | unsigned int gid; /* default gid for legacy support */ | ||
| 57 | |||
| 58 | /* book keeping */ | ||
| 59 | struct v9fs_idpool fidpool; /* The FID pool for file descriptors */ | ||
| 60 | struct v9fs_idpool tidpool; /* The TID pool for transactions ids */ | ||
| 61 | |||
| 62 | /* transport information */ | ||
| 63 | struct v9fs_transport *transport; | ||
| 64 | |||
| 65 | int inprogress; /* session in progress => true */ | ||
| 66 | int shutdown; /* session shutting down. no more attaches. */ | ||
| 67 | unsigned char session_hung; | ||
| 68 | |||
| 69 | /* mux private data */ | ||
| 70 | struct v9fs_fcall *curfcall; | ||
| 71 | wait_queue_head_t read_wait; | ||
| 72 | struct completion fcread; | ||
| 73 | struct completion proccmpl; | ||
| 74 | struct task_struct *recvproc; | ||
| 75 | |||
| 76 | spinlock_t muxlock; | ||
| 77 | struct list_head mux_fcalls; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /* possible values of ->proto */ | ||
| 81 | enum { | ||
| 82 | PROTO_TCP, | ||
| 83 | PROTO_UNIX, | ||
| 84 | PROTO_FD, | ||
| 85 | }; | ||
| 86 | |||
| 87 | int v9fs_session_init(struct v9fs_session_info *, const char *, char *); | ||
| 88 | struct v9fs_session_info *v9fs_inode2v9ses(struct inode *); | ||
| 89 | void v9fs_session_close(struct v9fs_session_info *v9ses); | ||
| 90 | int v9fs_get_idpool(struct v9fs_idpool *p); | ||
| 91 | void v9fs_put_idpool(int id, struct v9fs_idpool *p); | ||
| 92 | void v9fs_session_cancel(struct v9fs_session_info *v9ses); | ||
| 93 | |||
| 94 | #define V9FS_MAGIC 0x01021997 | ||
| 95 | |||
| 96 | /* other default globals */ | ||
| 97 | #define V9FS_PORT 564 | ||
| 98 | #define V9FS_DEFUSER "nobody" | ||
| 99 | #define V9FS_DEFANAME "" | ||
| 100 | |||
| 101 | /* inital pool sizes for fids and tags */ | ||
| 102 | #define V9FS_START_FIDS 8192 | ||
| 103 | #define V9FS_START_TIDS 256 | ||
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h new file mode 100644 index 000000000000..2f2cea7ee3e7 --- /dev/null +++ b/fs/9p/v9fs_vfs.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* | ||
| 2 | * V9FS VFS extensions. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 5 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to: | ||
| 19 | * Free Software Foundation | ||
| 20 | * 51 Franklin Street, Fifth Floor | ||
| 21 | * Boston, MA 02111-1301 USA | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | /* plan9 semantics are that created files are implicitly opened. | ||
| 26 | * But linux semantics are that you call create, then open. | ||
| 27 | * the plan9 approach is superior as it provides an atomic | ||
| 28 | * open. | ||
| 29 | * we track the create fid here. When the file is opened, if fidopen is | ||
| 30 | * non-zero, we use the fid and can skip some steps. | ||
| 31 | * there may be a better way to do this, but I don't know it. | ||
| 32 | * one BAD way is to clunk the fid on create, then open it again: | ||
| 33 | * you lose the atomicity of file open | ||
| 34 | */ | ||
| 35 | |||
| 36 | /* special case: | ||
| 37 | * unlink calls remove, which is an implicit clunk. So we have to track | ||
| 38 | * that kind of thing so that we don't try to clunk a dead fid. | ||
| 39 | */ | ||
| 40 | |||
| 41 | extern struct file_system_type v9fs_fs_type; | ||
| 42 | extern struct file_operations v9fs_file_operations; | ||
| 43 | extern struct file_operations v9fs_dir_operations; | ||
| 44 | extern struct dentry_operations v9fs_dentry_operations; | ||
| 45 | |||
| 46 | struct inode *v9fs_get_inode(struct super_block *sb, int mode); | ||
| 47 | ino_t v9fs_qid2ino(struct v9fs_qid *qid); | ||
| 48 | void v9fs_mistat2inode(struct v9fs_stat *, struct inode *, | ||
| 49 | struct super_block *); | ||
| 50 | int v9fs_dir_release(struct inode *inode, struct file *filp); | ||
| 51 | int v9fs_file_open(struct inode *inode, struct file *file); | ||
| 52 | void v9fs_inode2mistat(struct inode *inode, struct v9fs_stat *mistat); | ||
| 53 | void v9fs_dentry_release(struct dentry *); | ||
diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c new file mode 100644 index 000000000000..306c96741f81 --- /dev/null +++ b/fs/9p/vfs_dentry.c | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/vfs_dentry.c | ||
| 3 | * | ||
| 4 | * This file contians vfs dentry ops for the 9P2000 protocol. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/errno.h> | ||
| 29 | #include <linux/fs.h> | ||
| 30 | #include <linux/file.h> | ||
| 31 | #include <linux/pagemap.h> | ||
| 32 | #include <linux/stat.h> | ||
| 33 | #include <linux/string.h> | ||
| 34 | #include <linux/smp_lock.h> | ||
| 35 | #include <linux/inet.h> | ||
| 36 | #include <linux/namei.h> | ||
| 37 | #include <linux/idr.h> | ||
| 38 | |||
| 39 | #include "debug.h" | ||
| 40 | #include "v9fs.h" | ||
| 41 | #include "9p.h" | ||
| 42 | #include "v9fs_vfs.h" | ||
| 43 | #include "conv.h" | ||
| 44 | #include "fid.h" | ||
| 45 | |||
| 46 | /** | ||
| 47 | * v9fs_dentry_validate - VFS dcache hook to validate cache | ||
| 48 | * @dentry: dentry that is being validated | ||
| 49 | * @nd: path data | ||
| 50 | * | ||
| 51 | * dcache really shouldn't be used for 9P2000 as at all due to | ||
| 52 | * potential attached semantics to directory traversal (walk). | ||
| 53 | * | ||
| 54 | * FUTURE: look into how to use dcache to allow multi-stage | ||
| 55 | * walks in Plan 9 & potential for better dcache operation which | ||
| 56 | * would remain valid for Plan 9 semantics. Older versions | ||
| 57 | * had validation via stat for those interested. However, since | ||
| 58 | * stat has the same approximate overhead as walk there really | ||
| 59 | * is no difference. The only improvement would be from a | ||
| 60 | * time-decay cache like NFS has and that undermines the | ||
| 61 | * synchronous nature of 9P2000. | ||
| 62 | * | ||
| 63 | */ | ||
| 64 | |||
| 65 | static int v9fs_dentry_validate(struct dentry *dentry, struct nameidata *nd) | ||
| 66 | { | ||
| 67 | struct dentry *dc = current->fs->pwd; | ||
| 68 | |||
| 69 | dprintk(DEBUG_VFS, "dentry: %s (%p)\n", dentry->d_iname, dentry); | ||
| 70 | if (v9fs_fid_lookup(dentry, FID_OP)) { | ||
| 71 | dprintk(DEBUG_VFS, "VALID\n"); | ||
| 72 | return 1; | ||
| 73 | } | ||
| 74 | |||
| 75 | while (dc != NULL) { | ||
| 76 | if (dc == dentry) { | ||
| 77 | dprintk(DEBUG_VFS, "VALID\n"); | ||
| 78 | return 1; | ||
| 79 | } | ||
| 80 | if (dc == dc->d_parent) | ||
| 81 | break; | ||
| 82 | |||
| 83 | dc = dc->d_parent; | ||
| 84 | } | ||
| 85 | |||
| 86 | dprintk(DEBUG_VFS, "INVALID\n"); | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | /** | ||
| 91 | * v9fs_dentry_release - called when dentry is going to be freed | ||
| 92 | * @dentry: dentry that is being release | ||
| 93 | * | ||
| 94 | */ | ||
| 95 | |||
| 96 | void v9fs_dentry_release(struct dentry *dentry) | ||
| 97 | { | ||
| 98 | dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); | ||
| 99 | |||
| 100 | if (dentry->d_fsdata != NULL) { | ||
| 101 | struct list_head *fid_list = dentry->d_fsdata; | ||
| 102 | struct v9fs_fid *temp = NULL; | ||
| 103 | struct v9fs_fid *current_fid = NULL; | ||
| 104 | struct v9fs_fcall *fcall = NULL; | ||
| 105 | |||
| 106 | list_for_each_entry_safe(current_fid, temp, fid_list, list) { | ||
| 107 | if (v9fs_t_clunk | ||
| 108 | (current_fid->v9ses, current_fid->fid, &fcall)) | ||
| 109 | dprintk(DEBUG_ERROR, "clunk failed: %s\n", | ||
| 110 | FCALL_ERROR(fcall)); | ||
| 111 | |||
| 112 | v9fs_put_idpool(current_fid->fid, | ||
| 113 | ¤t_fid->v9ses->fidpool); | ||
| 114 | |||
| 115 | kfree(fcall); | ||
| 116 | v9fs_fid_destroy(current_fid); | ||
| 117 | } | ||
| 118 | |||
| 119 | kfree(dentry->d_fsdata); /* free the list_head */ | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | struct dentry_operations v9fs_dentry_operations = { | ||
| 124 | .d_revalidate = v9fs_dentry_validate, | ||
| 125 | .d_release = v9fs_dentry_release, | ||
| 126 | }; | ||
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c new file mode 100644 index 000000000000..c478a7384186 --- /dev/null +++ b/fs/9p/vfs_dir.c | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/vfs_dir.c | ||
| 3 | * | ||
| 4 | * This file contains vfs directory ops for the 9P2000 protocol. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/errno.h> | ||
| 29 | #include <linux/fs.h> | ||
| 30 | #include <linux/file.h> | ||
| 31 | #include <linux/stat.h> | ||
| 32 | #include <linux/string.h> | ||
| 33 | #include <linux/smp_lock.h> | ||
| 34 | #include <linux/inet.h> | ||
| 35 | #include <linux/idr.h> | ||
| 36 | |||
| 37 | #include "debug.h" | ||
| 38 | #include "v9fs.h" | ||
| 39 | #include "9p.h" | ||
| 40 | #include "v9fs_vfs.h" | ||
| 41 | #include "conv.h" | ||
| 42 | #include "fid.h" | ||
| 43 | |||
| 44 | /** | ||
| 45 | * dt_type - return file type | ||
| 46 | * @mistat: mistat structure | ||
| 47 | * | ||
| 48 | */ | ||
| 49 | |||
| 50 | static inline int dt_type(struct v9fs_stat *mistat) | ||
| 51 | { | ||
| 52 | unsigned long perm = mistat->mode; | ||
| 53 | int rettype = DT_REG; | ||
| 54 | |||
| 55 | if (perm & V9FS_DMDIR) | ||
| 56 | rettype = DT_DIR; | ||
| 57 | if (perm & V9FS_DMSYMLINK) | ||
| 58 | rettype = DT_LNK; | ||
| 59 | |||
| 60 | return rettype; | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * v9fs_dir_readdir - read a directory | ||
| 65 | * @filep: opened file structure | ||
| 66 | * @dirent: directory structure ??? | ||
| 67 | * @filldir: function to populate directory structure ??? | ||
| 68 | * | ||
| 69 | */ | ||
| 70 | |||
| 71 | static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
| 72 | { | ||
| 73 | struct v9fs_fcall *fcall = NULL; | ||
| 74 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 75 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); | ||
| 76 | struct v9fs_fid *file = filp->private_data; | ||
| 77 | unsigned int i, n; | ||
| 78 | int fid = -1; | ||
| 79 | int ret = 0; | ||
| 80 | struct v9fs_stat *mi = NULL; | ||
| 81 | int over = 0; | ||
| 82 | |||
| 83 | dprintk(DEBUG_VFS, "name %s\n", filp->f_dentry->d_name.name); | ||
| 84 | |||
| 85 | fid = file->fid; | ||
| 86 | |||
| 87 | mi = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 88 | if (!mi) | ||
| 89 | return -ENOMEM; | ||
| 90 | |||
| 91 | if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) { | ||
| 92 | kfree(file->rdir_fcall); | ||
| 93 | file->rdir_fcall = NULL; | ||
| 94 | } | ||
| 95 | |||
| 96 | if (file->rdir_fcall) { | ||
| 97 | n = file->rdir_fcall->params.rread.count; | ||
| 98 | i = file->rdir_fpos; | ||
| 99 | while (i < n) { | ||
| 100 | int s = v9fs_deserialize_stat(v9ses, | ||
| 101 | file->rdir_fcall->params.rread.data + i, | ||
| 102 | n - i, mi, v9ses->maxdata); | ||
| 103 | |||
| 104 | if (s == 0) { | ||
| 105 | dprintk(DEBUG_ERROR, | ||
| 106 | "error while deserializing mistat\n"); | ||
| 107 | ret = -EIO; | ||
| 108 | goto FreeStructs; | ||
| 109 | } | ||
| 110 | |||
| 111 | over = filldir(dirent, mi->name, strlen(mi->name), | ||
| 112 | filp->f_pos, v9fs_qid2ino(&mi->qid), | ||
| 113 | dt_type(mi)); | ||
| 114 | |||
| 115 | if (over) { | ||
| 116 | file->rdir_fpos = i; | ||
| 117 | file->rdir_pos = filp->f_pos; | ||
| 118 | break; | ||
| 119 | } | ||
| 120 | |||
| 121 | i += s; | ||
| 122 | filp->f_pos += s; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (!over) { | ||
| 126 | kfree(file->rdir_fcall); | ||
| 127 | file->rdir_fcall = NULL; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | while (!over) { | ||
| 132 | ret = v9fs_t_read(v9ses, fid, filp->f_pos, | ||
| 133 | v9ses->maxdata-V9FS_IOHDRSZ, &fcall); | ||
| 134 | if (ret < 0) { | ||
| 135 | dprintk(DEBUG_ERROR, "error while reading: %d: %p\n", | ||
| 136 | ret, fcall); | ||
| 137 | goto FreeStructs; | ||
| 138 | } else if (ret == 0) | ||
| 139 | break; | ||
| 140 | |||
| 141 | n = ret; | ||
| 142 | i = 0; | ||
| 143 | while (i < n) { | ||
| 144 | int s = v9fs_deserialize_stat(v9ses, | ||
| 145 | fcall->params.rread.data + i, n - i, mi, | ||
| 146 | v9ses->maxdata); | ||
| 147 | |||
| 148 | if (s == 0) { | ||
| 149 | dprintk(DEBUG_ERROR, | ||
| 150 | "error while deserializing mistat\n"); | ||
| 151 | return -EIO; | ||
| 152 | } | ||
| 153 | |||
| 154 | over = filldir(dirent, mi->name, strlen(mi->name), | ||
| 155 | filp->f_pos, v9fs_qid2ino(&mi->qid), | ||
| 156 | dt_type(mi)); | ||
| 157 | |||
| 158 | if (over) { | ||
| 159 | file->rdir_fcall = fcall; | ||
| 160 | file->rdir_fpos = i; | ||
| 161 | file->rdir_pos = filp->f_pos; | ||
| 162 | fcall = NULL; | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | |||
| 166 | i += s; | ||
| 167 | filp->f_pos += s; | ||
| 168 | } | ||
| 169 | |||
| 170 | kfree(fcall); | ||
| 171 | } | ||
| 172 | |||
| 173 | FreeStructs: | ||
| 174 | kfree(fcall); | ||
| 175 | kfree(mi); | ||
| 176 | return ret; | ||
| 177 | } | ||
| 178 | |||
| 179 | /** | ||
| 180 | * v9fs_dir_release - close a directory | ||
| 181 | * @inode: inode of the directory | ||
| 182 | * @filp: file pointer to a directory | ||
| 183 | * | ||
| 184 | */ | ||
| 185 | |||
| 186 | int v9fs_dir_release(struct inode *inode, struct file *filp) | ||
| 187 | { | ||
| 188 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); | ||
| 189 | struct v9fs_fid *fid = filp->private_data; | ||
| 190 | int fidnum = -1; | ||
| 191 | |||
| 192 | dprintk(DEBUG_VFS, "inode: %p filp: %p fid: %d\n", inode, filp, | ||
| 193 | fid->fid); | ||
| 194 | fidnum = fid->fid; | ||
| 195 | |||
| 196 | filemap_fdatawrite(inode->i_mapping); | ||
| 197 | filemap_fdatawait(inode->i_mapping); | ||
| 198 | |||
| 199 | if (fidnum >= 0) { | ||
| 200 | fid->fidopen--; | ||
| 201 | dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen, | ||
| 202 | fid->fid); | ||
| 203 | |||
| 204 | if (fid->fidopen == 0) { | ||
| 205 | if (v9fs_t_clunk(v9ses, fidnum, NULL)) | ||
| 206 | dprintk(DEBUG_ERROR, "clunk failed\n"); | ||
| 207 | |||
| 208 | v9fs_put_idpool(fid->fid, &v9ses->fidpool); | ||
| 209 | } | ||
| 210 | |||
| 211 | kfree(fid->rdir_fcall); | ||
| 212 | |||
| 213 | filp->private_data = NULL; | ||
| 214 | v9fs_fid_destroy(fid); | ||
| 215 | } | ||
| 216 | |||
| 217 | d_drop(filp->f_dentry); | ||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | struct file_operations v9fs_dir_operations = { | ||
| 222 | .read = generic_read_dir, | ||
| 223 | .readdir = v9fs_dir_readdir, | ||
| 224 | .open = v9fs_file_open, | ||
| 225 | .release = v9fs_dir_release, | ||
| 226 | }; | ||
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c new file mode 100644 index 000000000000..1f8ae7d580ab --- /dev/null +++ b/fs/9p/vfs_file.c | |||
| @@ -0,0 +1,401 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/vfs_file.c | ||
| 3 | * | ||
| 4 | * This file contians vfs file ops for 9P2000. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/errno.h> | ||
| 29 | #include <linux/fs.h> | ||
| 30 | #include <linux/file.h> | ||
| 31 | #include <linux/stat.h> | ||
| 32 | #include <linux/string.h> | ||
| 33 | #include <linux/smp_lock.h> | ||
| 34 | #include <linux/inet.h> | ||
| 35 | #include <linux/version.h> | ||
| 36 | #include <linux/list.h> | ||
| 37 | #include <asm/uaccess.h> | ||
| 38 | #include <linux/idr.h> | ||
| 39 | |||
| 40 | #include "debug.h" | ||
| 41 | #include "v9fs.h" | ||
| 42 | #include "9p.h" | ||
| 43 | #include "v9fs_vfs.h" | ||
| 44 | #include "fid.h" | ||
| 45 | |||
| 46 | /** | ||
| 47 | * v9fs_file_open - open a file (or directory) | ||
| 48 | * @inode: inode to be opened | ||
| 49 | * @file: file being opened | ||
| 50 | * | ||
| 51 | */ | ||
| 52 | |||
| 53 | int v9fs_file_open(struct inode *inode, struct file *file) | ||
| 54 | { | ||
| 55 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); | ||
| 56 | struct v9fs_fid *v9fid = v9fs_fid_lookup(file->f_dentry, FID_WALK); | ||
| 57 | struct v9fs_fid *v9newfid = NULL; | ||
| 58 | struct v9fs_fcall *fcall = NULL; | ||
| 59 | int open_mode = 0; | ||
| 60 | unsigned int iounit = 0; | ||
| 61 | int newfid = -1; | ||
| 62 | long result = -1; | ||
| 63 | |||
| 64 | dprintk(DEBUG_VFS, "inode: %p file: %p v9fid= %p\n", inode, file, | ||
| 65 | v9fid); | ||
| 66 | |||
| 67 | if (!v9fid) { | ||
| 68 | struct dentry *dentry = file->f_dentry; | ||
| 69 | dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); | ||
| 70 | |||
| 71 | /* XXX - some duplication from lookup, generalize later */ | ||
| 72 | /* basically vfs_lookup is too heavy weight */ | ||
| 73 | v9fid = v9fs_fid_lookup(file->f_dentry, FID_OP); | ||
| 74 | if (!v9fid) | ||
| 75 | return -EBADF; | ||
| 76 | |||
| 77 | v9fid = v9fs_fid_lookup(dentry->d_parent, FID_WALK); | ||
| 78 | if (!v9fid) | ||
| 79 | return -EBADF; | ||
| 80 | |||
| 81 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 82 | if (newfid < 0) { | ||
| 83 | eprintk(KERN_WARNING, "newfid fails!\n"); | ||
| 84 | return -ENOSPC; | ||
| 85 | } | ||
| 86 | |||
| 87 | result = | ||
| 88 | v9fs_t_walk(v9ses, v9fid->fid, newfid, | ||
| 89 | (char *)file->f_dentry->d_name.name, NULL); | ||
| 90 | if (result < 0) { | ||
| 91 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 92 | dprintk(DEBUG_ERROR, "rewalk didn't work\n"); | ||
| 93 | return -EBADF; | ||
| 94 | } | ||
| 95 | |||
| 96 | v9fid = v9fs_fid_create(dentry); | ||
| 97 | if (v9fid == NULL) { | ||
| 98 | dprintk(DEBUG_ERROR, "couldn't insert\n"); | ||
| 99 | return -ENOMEM; | ||
| 100 | } | ||
| 101 | v9fid->fid = newfid; | ||
| 102 | } | ||
| 103 | |||
| 104 | if (v9fid->fidcreate) { | ||
| 105 | /* create case */ | ||
| 106 | newfid = v9fid->fid; | ||
| 107 | iounit = v9fid->iounit; | ||
| 108 | v9fid->fidcreate = 0; | ||
| 109 | } else { | ||
| 110 | if (!S_ISDIR(inode->i_mode)) | ||
| 111 | newfid = v9fid->fid; | ||
| 112 | else { | ||
| 113 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 114 | if (newfid < 0) { | ||
| 115 | eprintk(KERN_WARNING, "allocation failed\n"); | ||
| 116 | return -ENOSPC; | ||
| 117 | } | ||
| 118 | /* This would be a somewhat critical clone */ | ||
| 119 | result = | ||
| 120 | v9fs_t_walk(v9ses, v9fid->fid, newfid, NULL, | ||
| 121 | &fcall); | ||
| 122 | if (result < 0) { | ||
| 123 | dprintk(DEBUG_ERROR, "clone error: %s\n", | ||
| 124 | FCALL_ERROR(fcall)); | ||
| 125 | kfree(fcall); | ||
| 126 | return result; | ||
| 127 | } | ||
| 128 | |||
| 129 | v9newfid = v9fs_fid_create(file->f_dentry); | ||
| 130 | v9newfid->fid = newfid; | ||
| 131 | v9newfid->qid = v9fid->qid; | ||
| 132 | v9newfid->iounit = v9fid->iounit; | ||
| 133 | v9newfid->fidopen = 0; | ||
| 134 | v9newfid->fidclunked = 0; | ||
| 135 | v9newfid->v9ses = v9ses; | ||
| 136 | v9fid = v9newfid; | ||
| 137 | kfree(fcall); | ||
| 138 | } | ||
| 139 | |||
| 140 | /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */ | ||
| 141 | /* translate open mode appropriately */ | ||
| 142 | open_mode = file->f_flags & 0x3; | ||
| 143 | |||
| 144 | if (file->f_flags & O_EXCL) | ||
| 145 | open_mode |= V9FS_OEXCL; | ||
| 146 | |||
| 147 | if (v9ses->extended) { | ||
| 148 | if (file->f_flags & O_TRUNC) | ||
| 149 | open_mode |= V9FS_OTRUNC; | ||
| 150 | |||
| 151 | if (file->f_flags & O_APPEND) | ||
| 152 | open_mode |= V9FS_OAPPEND; | ||
| 153 | } | ||
| 154 | |||
| 155 | result = v9fs_t_open(v9ses, newfid, open_mode, &fcall); | ||
| 156 | if (result < 0) { | ||
| 157 | dprintk(DEBUG_ERROR, | ||
| 158 | "open failed, open_mode 0x%x: %s\n", open_mode, | ||
| 159 | FCALL_ERROR(fcall)); | ||
| 160 | kfree(fcall); | ||
| 161 | return result; | ||
| 162 | } | ||
| 163 | |||
| 164 | iounit = fcall->params.ropen.iounit; | ||
| 165 | kfree(fcall); | ||
| 166 | } | ||
| 167 | |||
| 168 | |||
| 169 | file->private_data = v9fid; | ||
| 170 | |||
| 171 | v9fid->rdir_pos = 0; | ||
| 172 | v9fid->rdir_fcall = NULL; | ||
| 173 | v9fid->fidopen = 1; | ||
| 174 | v9fid->filp = file; | ||
| 175 | v9fid->iounit = iounit; | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * v9fs_file_lock - lock a file (or directory) | ||
| 182 | * @inode: inode to be opened | ||
| 183 | * @file: file being opened | ||
| 184 | * | ||
| 185 | * XXX - this looks like a local only lock, we should extend into 9P | ||
| 186 | * by using open exclusive | ||
| 187 | */ | ||
| 188 | |||
| 189 | static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) | ||
| 190 | { | ||
| 191 | int res = 0; | ||
| 192 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 193 | |||
| 194 | dprintk(DEBUG_VFS, "filp: %p lock: %p\n", filp, fl); | ||
| 195 | |||
| 196 | /* No mandatory locks */ | ||
| 197 | if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) | ||
| 198 | return -ENOLCK; | ||
| 199 | |||
| 200 | if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { | ||
| 201 | filemap_fdatawrite(inode->i_mapping); | ||
| 202 | filemap_fdatawait(inode->i_mapping); | ||
| 203 | invalidate_inode_pages(&inode->i_data); | ||
| 204 | } | ||
| 205 | |||
| 206 | return res; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * v9fs_read - read from a file (internal) | ||
| 211 | * @filep: file pointer to read | ||
| 212 | * @data: data buffer to read data into | ||
| 213 | * @count: size of buffer | ||
| 214 | * @offset: offset at which to read data | ||
| 215 | * | ||
| 216 | */ | ||
| 217 | |||
| 218 | static ssize_t | ||
| 219 | v9fs_read(struct file *filp, char *buffer, size_t count, loff_t * offset) | ||
| 220 | { | ||
| 221 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 222 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); | ||
| 223 | struct v9fs_fid *v9f = filp->private_data; | ||
| 224 | struct v9fs_fcall *fcall = NULL; | ||
| 225 | int fid = v9f->fid; | ||
| 226 | int rsize = 0; | ||
| 227 | int result = 0; | ||
| 228 | int total = 0; | ||
| 229 | |||
| 230 | dprintk(DEBUG_VFS, "\n"); | ||
| 231 | |||
| 232 | rsize = v9ses->maxdata - V9FS_IOHDRSZ; | ||
| 233 | if (v9f->iounit != 0 && rsize > v9f->iounit) | ||
| 234 | rsize = v9f->iounit; | ||
| 235 | |||
| 236 | do { | ||
| 237 | if (count < rsize) | ||
| 238 | rsize = count; | ||
| 239 | |||
| 240 | result = v9fs_t_read(v9ses, fid, *offset, rsize, &fcall); | ||
| 241 | |||
| 242 | if (result < 0) { | ||
| 243 | printk(KERN_ERR "9P2000: v9fs_t_read returned %d\n", | ||
| 244 | result); | ||
| 245 | |||
| 246 | kfree(fcall); | ||
| 247 | return total; | ||
| 248 | } else | ||
| 249 | *offset += result; | ||
| 250 | |||
| 251 | /* XXX - extra copy */ | ||
| 252 | memcpy(buffer, fcall->params.rread.data, result); | ||
| 253 | count -= result; | ||
| 254 | buffer += result; | ||
| 255 | total += result; | ||
| 256 | |||
| 257 | kfree(fcall); | ||
| 258 | |||
| 259 | if (result < rsize) | ||
| 260 | break; | ||
| 261 | } while (count); | ||
| 262 | |||
| 263 | return total; | ||
| 264 | } | ||
| 265 | |||
| 266 | /** | ||
| 267 | * v9fs_file_read - read from a file | ||
| 268 | * @filep: file pointer to read | ||
| 269 | * @data: data buffer to read data into | ||
| 270 | * @count: size of buffer | ||
| 271 | * @offset: offset at which to read data | ||
| 272 | * | ||
| 273 | */ | ||
| 274 | |||
| 275 | static ssize_t | ||
| 276 | v9fs_file_read(struct file *filp, char __user * data, size_t count, | ||
| 277 | loff_t * offset) | ||
| 278 | { | ||
| 279 | int retval = -1; | ||
| 280 | int ret = 0; | ||
| 281 | char *buffer; | ||
| 282 | |||
| 283 | buffer = kmalloc(count, GFP_KERNEL); | ||
| 284 | if (!buffer) | ||
| 285 | return -ENOMEM; | ||
| 286 | |||
| 287 | retval = v9fs_read(filp, buffer, count, offset); | ||
| 288 | if (retval > 0) { | ||
| 289 | if ((ret = copy_to_user(data, buffer, retval)) != 0) { | ||
| 290 | dprintk(DEBUG_ERROR, "Problem copying to user %d\n", | ||
| 291 | ret); | ||
| 292 | retval = ret; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | kfree(buffer); | ||
| 297 | |||
| 298 | return retval; | ||
| 299 | } | ||
| 300 | |||
| 301 | /** | ||
| 302 | * v9fs_write - write to a file | ||
| 303 | * @filep: file pointer to write | ||
| 304 | * @data: data buffer to write data from | ||
| 305 | * @count: size of buffer | ||
| 306 | * @offset: offset at which to write data | ||
| 307 | * | ||
| 308 | */ | ||
| 309 | |||
| 310 | static ssize_t | ||
| 311 | v9fs_write(struct file *filp, char *buffer, size_t count, loff_t * offset) | ||
| 312 | { | ||
| 313 | struct inode *inode = filp->f_dentry->d_inode; | ||
| 314 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); | ||
| 315 | struct v9fs_fid *v9fid = filp->private_data; | ||
| 316 | struct v9fs_fcall *fcall; | ||
| 317 | int fid = v9fid->fid; | ||
| 318 | int result = -EIO; | ||
| 319 | int rsize = 0; | ||
| 320 | int total = 0; | ||
| 321 | |||
| 322 | dprintk(DEBUG_VFS, "data %p count %d offset %x\n", buffer, (int)count, | ||
| 323 | (int)*offset); | ||
| 324 | rsize = v9ses->maxdata - V9FS_IOHDRSZ; | ||
| 325 | if (v9fid->iounit != 0 && rsize > v9fid->iounit) | ||
| 326 | rsize = v9fid->iounit; | ||
| 327 | |||
| 328 | dump_data(buffer, count); | ||
| 329 | |||
| 330 | do { | ||
| 331 | if (count < rsize) | ||
| 332 | rsize = count; | ||
| 333 | |||
| 334 | result = | ||
| 335 | v9fs_t_write(v9ses, fid, *offset, rsize, buffer, &fcall); | ||
| 336 | if (result < 0) { | ||
| 337 | eprintk(KERN_ERR, "error while writing: %s(%d)\n", | ||
| 338 | FCALL_ERROR(fcall), result); | ||
| 339 | kfree(fcall); | ||
| 340 | return result; | ||
| 341 | } else | ||
| 342 | *offset += result; | ||
| 343 | |||
| 344 | kfree(fcall); | ||
| 345 | |||
| 346 | if (result != rsize) { | ||
| 347 | eprintk(KERN_ERR, | ||
| 348 | "short write: v9fs_t_write returned %d\n", | ||
| 349 | result); | ||
| 350 | break; | ||
| 351 | } | ||
| 352 | |||
| 353 | count -= result; | ||
| 354 | buffer += result; | ||
| 355 | total += result; | ||
| 356 | } while (count); | ||
| 357 | |||
| 358 | return total; | ||
| 359 | } | ||
| 360 | |||
| 361 | /** | ||
| 362 | * v9fs_file_write - write to a file | ||
| 363 | * @filep: file pointer to write | ||
| 364 | * @data: data buffer to write data from | ||
| 365 | * @count: size of buffer | ||
| 366 | * @offset: offset at which to write data | ||
| 367 | * | ||
| 368 | */ | ||
| 369 | |||
| 370 | static ssize_t | ||
| 371 | v9fs_file_write(struct file *filp, const char __user * data, | ||
| 372 | size_t count, loff_t * offset) | ||
| 373 | { | ||
| 374 | int ret = -1; | ||
| 375 | char *buffer; | ||
| 376 | |||
| 377 | buffer = kmalloc(count, GFP_KERNEL); | ||
| 378 | if (buffer == NULL) | ||
| 379 | return -ENOMEM; | ||
| 380 | |||
| 381 | ret = copy_from_user(buffer, data, count); | ||
| 382 | if (ret) { | ||
| 383 | dprintk(DEBUG_ERROR, "Problem copying from user\n"); | ||
| 384 | ret = -EFAULT; | ||
| 385 | } else { | ||
| 386 | ret = v9fs_write(filp, buffer, count, offset); | ||
| 387 | } | ||
| 388 | |||
| 389 | kfree(buffer); | ||
| 390 | |||
| 391 | return ret; | ||
| 392 | } | ||
| 393 | |||
| 394 | struct file_operations v9fs_file_operations = { | ||
| 395 | .llseek = generic_file_llseek, | ||
| 396 | .read = v9fs_file_read, | ||
| 397 | .write = v9fs_file_write, | ||
| 398 | .open = v9fs_file_open, | ||
| 399 | .release = v9fs_dir_release, | ||
| 400 | .lock = v9fs_file_lock, | ||
| 401 | }; | ||
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c new file mode 100644 index 000000000000..0c13fc600049 --- /dev/null +++ b/fs/9p/vfs_inode.c | |||
| @@ -0,0 +1,1338 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/vfs_inode.c | ||
| 3 | * | ||
| 4 | * This file contains vfs inode ops for the 9P2000 protocol. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 7 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to: | ||
| 21 | * Free Software Foundation | ||
| 22 | * 51 Franklin Street, Fifth Floor | ||
| 23 | * Boston, MA 02111-1301 USA | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/errno.h> | ||
| 29 | #include <linux/fs.h> | ||
| 30 | #include <linux/file.h> | ||
| 31 | #include <linux/pagemap.h> | ||
| 32 | #include <linux/stat.h> | ||
| 33 | #include <linux/string.h> | ||
| 34 | #include <linux/smp_lock.h> | ||
| 35 | #include <linux/inet.h> | ||
| 36 | #include <linux/namei.h> | ||
| 37 | #include <linux/idr.h> | ||
| 38 | |||
| 39 | #include "debug.h" | ||
| 40 | #include "v9fs.h" | ||
| 41 | #include "9p.h" | ||
| 42 | #include "v9fs_vfs.h" | ||
| 43 | #include "conv.h" | ||
| 44 | #include "fid.h" | ||
| 45 | |||
| 46 | static struct inode_operations v9fs_dir_inode_operations; | ||
| 47 | static struct inode_operations v9fs_dir_inode_operations_ext; | ||
| 48 | static struct inode_operations v9fs_file_inode_operations; | ||
| 49 | static struct inode_operations v9fs_symlink_inode_operations; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * unixmode2p9mode - convert unix mode bits to plan 9 | ||
| 53 | * @v9ses: v9fs session information | ||
| 54 | * @mode: mode to convert | ||
| 55 | * | ||
| 56 | */ | ||
| 57 | |||
| 58 | static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode) | ||
| 59 | { | ||
| 60 | int res; | ||
| 61 | res = mode & 0777; | ||
| 62 | if (S_ISDIR(mode)) | ||
| 63 | res |= V9FS_DMDIR; | ||
| 64 | if (v9ses->extended) { | ||
| 65 | if (S_ISLNK(mode)) | ||
| 66 | res |= V9FS_DMSYMLINK; | ||
| 67 | if (v9ses->nodev == 0) { | ||
| 68 | if (S_ISSOCK(mode)) | ||
| 69 | res |= V9FS_DMSOCKET; | ||
| 70 | if (S_ISFIFO(mode)) | ||
| 71 | res |= V9FS_DMNAMEDPIPE; | ||
| 72 | if (S_ISBLK(mode)) | ||
| 73 | res |= V9FS_DMDEVICE; | ||
| 74 | if (S_ISCHR(mode)) | ||
| 75 | res |= V9FS_DMDEVICE; | ||
| 76 | } | ||
| 77 | |||
| 78 | if ((mode & S_ISUID) == S_ISUID) | ||
| 79 | res |= V9FS_DMSETUID; | ||
| 80 | if ((mode & S_ISGID) == S_ISGID) | ||
| 81 | res |= V9FS_DMSETGID; | ||
| 82 | if ((mode & V9FS_DMLINK)) | ||
| 83 | res |= V9FS_DMLINK; | ||
| 84 | } | ||
| 85 | |||
| 86 | return res; | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * p9mode2unixmode- convert plan9 mode bits to unix mode bits | ||
| 91 | * @v9ses: v9fs session information | ||
| 92 | * @mode: mode to convert | ||
| 93 | * | ||
| 94 | */ | ||
| 95 | |||
| 96 | static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) | ||
| 97 | { | ||
| 98 | int res; | ||
| 99 | |||
| 100 | res = mode & 0777; | ||
| 101 | |||
| 102 | if ((mode & V9FS_DMDIR) == V9FS_DMDIR) | ||
| 103 | res |= S_IFDIR; | ||
| 104 | else if ((mode & V9FS_DMSYMLINK) && (v9ses->extended)) | ||
| 105 | res |= S_IFLNK; | ||
| 106 | else if ((mode & V9FS_DMSOCKET) && (v9ses->extended) | ||
| 107 | && (v9ses->nodev == 0)) | ||
| 108 | res |= S_IFSOCK; | ||
| 109 | else if ((mode & V9FS_DMNAMEDPIPE) && (v9ses->extended) | ||
| 110 | && (v9ses->nodev == 0)) | ||
| 111 | res |= S_IFIFO; | ||
| 112 | else if ((mode & V9FS_DMDEVICE) && (v9ses->extended) | ||
| 113 | && (v9ses->nodev == 0)) | ||
| 114 | res |= S_IFBLK; | ||
| 115 | else | ||
| 116 | res |= S_IFREG; | ||
| 117 | |||
| 118 | if (v9ses->extended) { | ||
| 119 | if ((mode & V9FS_DMSETUID) == V9FS_DMSETUID) | ||
| 120 | res |= S_ISUID; | ||
| 121 | |||
| 122 | if ((mode & V9FS_DMSETGID) == V9FS_DMSETGID) | ||
| 123 | res |= S_ISGID; | ||
| 124 | } | ||
| 125 | |||
| 126 | return res; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * v9fs_blank_mistat - helper function to setup a 9P stat structure | ||
| 131 | * @v9ses: 9P session info (for determining extended mode) | ||
| 132 | * @mistat: structure to initialize | ||
| 133 | * | ||
| 134 | */ | ||
| 135 | |||
| 136 | static void | ||
| 137 | v9fs_blank_mistat(struct v9fs_session_info *v9ses, struct v9fs_stat *mistat) | ||
| 138 | { | ||
| 139 | mistat->type = ~0; | ||
| 140 | mistat->dev = ~0; | ||
| 141 | mistat->qid.type = ~0; | ||
| 142 | mistat->qid.version = ~0; | ||
| 143 | *((long long *)&mistat->qid.path) = ~0; | ||
| 144 | mistat->mode = ~0; | ||
| 145 | mistat->atime = ~0; | ||
| 146 | mistat->mtime = ~0; | ||
| 147 | mistat->length = ~0; | ||
| 148 | mistat->name = mistat->data; | ||
| 149 | mistat->uid = mistat->data; | ||
| 150 | mistat->gid = mistat->data; | ||
| 151 | mistat->muid = mistat->data; | ||
| 152 | if (v9ses->extended) { | ||
| 153 | mistat->n_uid = ~0; | ||
| 154 | mistat->n_gid = ~0; | ||
| 155 | mistat->n_muid = ~0; | ||
| 156 | mistat->extension = mistat->data; | ||
| 157 | } | ||
| 158 | *mistat->data = 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * v9fs_mistat2unix - convert mistat to unix stat | ||
| 163 | * @mistat: Plan 9 metadata (mistat) structure | ||
| 164 | * @buf: unix metadata (stat) structure to populate | ||
| 165 | * @sb: superblock | ||
| 166 | * | ||
| 167 | */ | ||
| 168 | |||
| 169 | static void | ||
| 170 | v9fs_mistat2unix(struct v9fs_stat *mistat, struct stat *buf, | ||
| 171 | struct super_block *sb) | ||
| 172 | { | ||
| 173 | struct v9fs_session_info *v9ses = sb ? sb->s_fs_info : NULL; | ||
| 174 | |||
| 175 | buf->st_nlink = 1; | ||
| 176 | |||
| 177 | buf->st_atime = mistat->atime; | ||
| 178 | buf->st_mtime = mistat->mtime; | ||
| 179 | buf->st_ctime = mistat->mtime; | ||
| 180 | |||
| 181 | buf->st_uid = (unsigned short)-1; | ||
| 182 | buf->st_gid = (unsigned short)-1; | ||
| 183 | |||
| 184 | if (v9ses && v9ses->extended) { | ||
| 185 | /* TODO: string to uid mapping via user-space daemon */ | ||
| 186 | if (mistat->n_uid != -1) | ||
| 187 | sscanf(mistat->uid, "%x", (unsigned int *)&buf->st_uid); | ||
| 188 | |||
| 189 | if (mistat->n_gid != -1) | ||
| 190 | sscanf(mistat->gid, "%x", (unsigned int *)&buf->st_gid); | ||
| 191 | } | ||
| 192 | |||
| 193 | if (buf->st_uid == (unsigned short)-1) | ||
| 194 | buf->st_uid = v9ses->uid; | ||
| 195 | if (buf->st_gid == (unsigned short)-1) | ||
| 196 | buf->st_gid = v9ses->gid; | ||
| 197 | |||
| 198 | buf->st_mode = p9mode2unixmode(v9ses, mistat->mode); | ||
| 199 | if ((S_ISBLK(buf->st_mode)) || (S_ISCHR(buf->st_mode))) { | ||
| 200 | char type = 0; | ||
| 201 | int major = -1; | ||
| 202 | int minor = -1; | ||
| 203 | sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); | ||
| 204 | switch (type) { | ||
| 205 | case 'c': | ||
| 206 | buf->st_mode &= ~S_IFBLK; | ||
| 207 | buf->st_mode |= S_IFCHR; | ||
| 208 | break; | ||
| 209 | case 'b': | ||
| 210 | break; | ||
| 211 | default: | ||
| 212 | dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", | ||
| 213 | type, mistat->extension); | ||
| 214 | }; | ||
| 215 | buf->st_rdev = MKDEV(major, minor); | ||
| 216 | } else | ||
| 217 | buf->st_rdev = 0; | ||
| 218 | |||
| 219 | buf->st_size = mistat->length; | ||
| 220 | |||
| 221 | buf->st_blksize = sb->s_blocksize; | ||
| 222 | buf->st_blocks = | ||
| 223 | (buf->st_size + buf->st_blksize - 1) >> sb->s_blocksize_bits; | ||
| 224 | } | ||
| 225 | |||
| 226 | /** | ||
| 227 | * v9fs_get_inode - helper function to setup an inode | ||
| 228 | * @sb: superblock | ||
| 229 | * @mode: mode to setup inode with | ||
| 230 | * | ||
| 231 | */ | ||
| 232 | |||
| 233 | struct inode *v9fs_get_inode(struct super_block *sb, int mode) | ||
| 234 | { | ||
| 235 | struct inode *inode = NULL; | ||
| 236 | struct v9fs_session_info *v9ses = sb->s_fs_info; | ||
| 237 | |||
| 238 | dprintk(DEBUG_VFS, "super block: %p mode: %o\n", sb, mode); | ||
| 239 | |||
| 240 | inode = new_inode(sb); | ||
| 241 | if (inode) { | ||
| 242 | inode->i_mode = mode; | ||
| 243 | inode->i_uid = current->fsuid; | ||
| 244 | inode->i_gid = current->fsgid; | ||
| 245 | inode->i_blksize = sb->s_blocksize; | ||
| 246 | inode->i_blocks = 0; | ||
| 247 | inode->i_rdev = 0; | ||
| 248 | inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; | ||
| 249 | |||
| 250 | switch (mode & S_IFMT) { | ||
| 251 | case S_IFIFO: | ||
| 252 | case S_IFBLK: | ||
| 253 | case S_IFCHR: | ||
| 254 | case S_IFSOCK: | ||
| 255 | if(!v9ses->extended) { | ||
| 256 | dprintk(DEBUG_ERROR, "special files without extended mode\n"); | ||
| 257 | return ERR_PTR(-EINVAL); | ||
| 258 | } | ||
| 259 | init_special_inode(inode, inode->i_mode, | ||
| 260 | inode->i_rdev); | ||
| 261 | break; | ||
| 262 | case S_IFREG: | ||
| 263 | inode->i_op = &v9fs_file_inode_operations; | ||
| 264 | inode->i_fop = &v9fs_file_operations; | ||
| 265 | break; | ||
| 266 | case S_IFLNK: | ||
| 267 | if(!v9ses->extended) { | ||
| 268 | dprintk(DEBUG_ERROR, "extended modes used w/o 9P2000.u\n"); | ||
| 269 | return ERR_PTR(-EINVAL); | ||
| 270 | } | ||
| 271 | inode->i_op = &v9fs_symlink_inode_operations; | ||
| 272 | break; | ||
| 273 | case S_IFDIR: | ||
| 274 | inode->i_nlink++; | ||
| 275 | if(v9ses->extended) | ||
| 276 | inode->i_op = &v9fs_dir_inode_operations_ext; | ||
| 277 | else | ||
| 278 | inode->i_op = &v9fs_dir_inode_operations; | ||
| 279 | inode->i_fop = &v9fs_dir_operations; | ||
| 280 | break; | ||
| 281 | default: | ||
| 282 | dprintk(DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n", | ||
| 283 | mode, mode & S_IFMT); | ||
| 284 | return ERR_PTR(-EINVAL); | ||
| 285 | } | ||
| 286 | } else { | ||
| 287 | eprintk(KERN_WARNING, "Problem allocating inode\n"); | ||
| 288 | return ERR_PTR(-ENOMEM); | ||
| 289 | } | ||
| 290 | return inode; | ||
| 291 | } | ||
| 292 | |||
| 293 | /** | ||
| 294 | * v9fs_create - helper function to create files and directories | ||
| 295 | * @dir: directory inode file is being created in | ||
| 296 | * @file_dentry: dentry file is being created in | ||
| 297 | * @perm: permissions file is being created with | ||
| 298 | * @open_mode: resulting open mode for file | ||
| 299 | * | ||
| 300 | */ | ||
| 301 | |||
| 302 | static int | ||
| 303 | v9fs_create(struct inode *dir, | ||
| 304 | struct dentry *file_dentry, | ||
| 305 | unsigned int perm, unsigned int open_mode) | ||
| 306 | { | ||
| 307 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); | ||
| 308 | struct super_block *sb = dir->i_sb; | ||
| 309 | struct v9fs_fid *dirfid = | ||
| 310 | v9fs_fid_lookup(file_dentry->d_parent, FID_WALK); | ||
| 311 | struct v9fs_fid *fid = NULL; | ||
| 312 | struct inode *file_inode = NULL; | ||
| 313 | struct v9fs_fcall *fcall = NULL; | ||
| 314 | struct v9fs_qid qid; | ||
| 315 | struct stat newstat; | ||
| 316 | int dirfidnum = -1; | ||
| 317 | long newfid = -1; | ||
| 318 | int result = 0; | ||
| 319 | unsigned int iounit = 0; | ||
| 320 | |||
| 321 | perm = unixmode2p9mode(v9ses, perm); | ||
| 322 | |||
| 323 | dprintk(DEBUG_VFS, "dir: %p dentry: %p perm: %o mode: %o\n", dir, | ||
| 324 | file_dentry, perm, open_mode); | ||
| 325 | |||
| 326 | if (!dirfid) | ||
| 327 | return -EBADF; | ||
| 328 | |||
| 329 | dirfidnum = dirfid->fid; | ||
| 330 | if (dirfidnum < 0) { | ||
| 331 | dprintk(DEBUG_ERROR, "No fid for the directory #%lu\n", | ||
| 332 | dir->i_ino); | ||
| 333 | return -EBADF; | ||
| 334 | } | ||
| 335 | |||
| 336 | if (file_dentry->d_inode) { | ||
| 337 | dprintk(DEBUG_ERROR, | ||
| 338 | "Odd. There is an inode for dir %lu, name :%s:\n", | ||
| 339 | dir->i_ino, file_dentry->d_name.name); | ||
| 340 | return -EEXIST; | ||
| 341 | } | ||
| 342 | |||
| 343 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 344 | if (newfid < 0) { | ||
| 345 | eprintk(KERN_WARNING, "no free fids available\n"); | ||
| 346 | return -ENOSPC; | ||
| 347 | } | ||
| 348 | |||
| 349 | result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall); | ||
| 350 | if (result < 0) { | ||
| 351 | dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); | ||
| 352 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 353 | newfid = 0; | ||
| 354 | goto CleanUpFid; | ||
| 355 | } | ||
| 356 | |||
| 357 | kfree(fcall); | ||
| 358 | |||
| 359 | result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name, | ||
| 360 | perm, open_mode, &fcall); | ||
| 361 | if (result < 0) { | ||
| 362 | dprintk(DEBUG_ERROR, "create fails: %s(%d)\n", | ||
| 363 | FCALL_ERROR(fcall), result); | ||
| 364 | |||
| 365 | goto CleanUpFid; | ||
| 366 | } | ||
| 367 | |||
| 368 | iounit = fcall->params.rcreate.iounit; | ||
| 369 | qid = fcall->params.rcreate.qid; | ||
| 370 | kfree(fcall); | ||
| 371 | |||
| 372 | fid = v9fs_fid_create(file_dentry); | ||
| 373 | if (!fid) { | ||
| 374 | result = -ENOMEM; | ||
| 375 | goto CleanUpFid; | ||
| 376 | } | ||
| 377 | |||
| 378 | fid->fid = newfid; | ||
| 379 | fid->fidopen = 0; | ||
| 380 | fid->fidcreate = 1; | ||
| 381 | fid->qid = qid; | ||
| 382 | fid->iounit = iounit; | ||
| 383 | fid->rdir_pos = 0; | ||
| 384 | fid->rdir_fcall = NULL; | ||
| 385 | fid->v9ses = v9ses; | ||
| 386 | |||
| 387 | if ((perm & V9FS_DMSYMLINK) || (perm & V9FS_DMLINK) || | ||
| 388 | (perm & V9FS_DMNAMEDPIPE) || (perm & V9FS_DMSOCKET) || | ||
| 389 | (perm & V9FS_DMDEVICE)) | ||
| 390 | return 0; | ||
| 391 | |||
| 392 | result = v9fs_t_stat(v9ses, newfid, &fcall); | ||
| 393 | if (result < 0) { | ||
| 394 | dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall), | ||
| 395 | result); | ||
| 396 | goto CleanUpFid; | ||
| 397 | } | ||
| 398 | |||
| 399 | v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); | ||
| 400 | |||
| 401 | file_inode = v9fs_get_inode(sb, newstat.st_mode); | ||
| 402 | if ((!file_inode) || IS_ERR(file_inode)) { | ||
| 403 | dprintk(DEBUG_ERROR, "create inode failed\n"); | ||
| 404 | result = -EBADF; | ||
| 405 | goto CleanUpFid; | ||
| 406 | } | ||
| 407 | |||
| 408 | v9fs_mistat2inode(fcall->params.rstat.stat, file_inode, sb); | ||
| 409 | kfree(fcall); | ||
| 410 | d_instantiate(file_dentry, file_inode); | ||
| 411 | |||
| 412 | if (perm & V9FS_DMDIR) { | ||
| 413 | if (v9fs_t_clunk(v9ses, newfid, &fcall)) | ||
| 414 | dprintk(DEBUG_ERROR, "clunk for mkdir failed: %s\n", | ||
| 415 | FCALL_ERROR(fcall)); | ||
| 416 | |||
| 417 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 418 | kfree(fcall); | ||
| 419 | fid->fidopen = 0; | ||
| 420 | fid->fidcreate = 0; | ||
| 421 | d_drop(file_dentry); | ||
| 422 | } | ||
| 423 | |||
| 424 | return 0; | ||
| 425 | |||
| 426 | CleanUpFid: | ||
| 427 | kfree(fcall); | ||
| 428 | |||
| 429 | if (newfid) { | ||
| 430 | if (v9fs_t_clunk(v9ses, newfid, &fcall)) | ||
| 431 | dprintk(DEBUG_ERROR, "clunk failed: %s\n", | ||
| 432 | FCALL_ERROR(fcall)); | ||
| 433 | |||
| 434 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 435 | kfree(fcall); | ||
| 436 | } | ||
| 437 | return result; | ||
| 438 | } | ||
| 439 | |||
| 440 | /** | ||
| 441 | * v9fs_remove - helper function to remove files and directories | ||
| 442 | * @dir: directory inode that is being deleted | ||
| 443 | * @file: dentry that is being deleted | ||
| 444 | * @rmdir: removing a directory | ||
| 445 | * | ||
| 446 | */ | ||
| 447 | |||
| 448 | static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) | ||
| 449 | { | ||
| 450 | struct v9fs_fcall *fcall = NULL; | ||
| 451 | struct super_block *sb = NULL; | ||
| 452 | struct v9fs_session_info *v9ses = NULL; | ||
| 453 | struct v9fs_fid *v9fid = NULL; | ||
| 454 | struct inode *file_inode = NULL; | ||
| 455 | int fid = -1; | ||
| 456 | int result = 0; | ||
| 457 | |||
| 458 | dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, | ||
| 459 | rmdir); | ||
| 460 | |||
| 461 | file_inode = file->d_inode; | ||
| 462 | sb = file_inode->i_sb; | ||
| 463 | v9ses = v9fs_inode2v9ses(file_inode); | ||
| 464 | v9fid = v9fs_fid_lookup(file, FID_OP); | ||
| 465 | |||
| 466 | if (!v9fid) { | ||
| 467 | dprintk(DEBUG_ERROR, | ||
| 468 | "no v9fs_fid\n"); | ||
| 469 | return -EBADF; | ||
| 470 | } | ||
| 471 | |||
| 472 | fid = v9fid->fid; | ||
| 473 | if (fid < 0) { | ||
| 474 | dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n", | ||
| 475 | file_inode->i_ino); | ||
| 476 | return -EBADF; | ||
| 477 | } | ||
| 478 | |||
| 479 | result = v9fs_t_remove(v9ses, fid, &fcall); | ||
| 480 | if (result < 0) | ||
| 481 | dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n", | ||
| 482 | FCALL_ERROR(fcall), result); | ||
| 483 | else { | ||
| 484 | v9fs_put_idpool(fid, &v9ses->fidpool); | ||
| 485 | v9fs_fid_destroy(v9fid); | ||
| 486 | } | ||
| 487 | |||
| 488 | kfree(fcall); | ||
| 489 | return result; | ||
| 490 | } | ||
| 491 | |||
| 492 | /** | ||
| 493 | * v9fs_vfs_create - VFS hook to create files | ||
| 494 | * @inode: directory inode that is being deleted | ||
| 495 | * @dentry: dentry that is being deleted | ||
| 496 | * @perm: create permissions | ||
| 497 | * @nd: path information | ||
| 498 | * | ||
| 499 | */ | ||
| 500 | |||
| 501 | static int | ||
| 502 | v9fs_vfs_create(struct inode *inode, struct dentry *dentry, int perm, | ||
| 503 | struct nameidata *nd) | ||
| 504 | { | ||
| 505 | return v9fs_create(inode, dentry, perm, O_RDWR); | ||
| 506 | } | ||
| 507 | |||
| 508 | /** | ||
| 509 | * v9fs_vfs_mkdir - VFS mkdir hook to create a directory | ||
| 510 | * @inode: inode that is being unlinked | ||
| 511 | * @dentry: dentry that is being unlinked | ||
| 512 | * @mode: mode for new directory | ||
| 513 | * | ||
| 514 | */ | ||
| 515 | |||
| 516 | static int v9fs_vfs_mkdir(struct inode *inode, struct dentry *dentry, int mode) | ||
| 517 | { | ||
| 518 | return v9fs_create(inode, dentry, mode | S_IFDIR, O_RDONLY); | ||
| 519 | } | ||
| 520 | |||
| 521 | /** | ||
| 522 | * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode | ||
| 523 | * @dir: inode that is being walked from | ||
| 524 | * @dentry: dentry that is being walked to? | ||
| 525 | * @nameidata: path data | ||
| 526 | * | ||
| 527 | */ | ||
| 528 | |||
| 529 | static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, | ||
| 530 | struct nameidata *nameidata) | ||
| 531 | { | ||
| 532 | struct super_block *sb; | ||
| 533 | struct v9fs_session_info *v9ses; | ||
| 534 | struct v9fs_fid *dirfid; | ||
| 535 | struct v9fs_fid *fid; | ||
| 536 | struct inode *inode; | ||
| 537 | struct v9fs_fcall *fcall = NULL; | ||
| 538 | struct stat newstat; | ||
| 539 | int dirfidnum = -1; | ||
| 540 | int newfid = -1; | ||
| 541 | int result = 0; | ||
| 542 | |||
| 543 | dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", | ||
| 544 | dir, dentry->d_iname, dentry, nameidata); | ||
| 545 | |||
| 546 | sb = dir->i_sb; | ||
| 547 | v9ses = v9fs_inode2v9ses(dir); | ||
| 548 | dirfid = v9fs_fid_lookup(dentry->d_parent, FID_WALK); | ||
| 549 | |||
| 550 | if (!dirfid) { | ||
| 551 | dprintk(DEBUG_ERROR, "no dirfid\n"); | ||
| 552 | return ERR_PTR(-EINVAL); | ||
| 553 | } | ||
| 554 | |||
| 555 | dirfidnum = dirfid->fid; | ||
| 556 | |||
| 557 | if (dirfidnum < 0) { | ||
| 558 | dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n", | ||
| 559 | dir, dir->i_ino); | ||
| 560 | return ERR_PTR(-EBADF); | ||
| 561 | } | ||
| 562 | |||
| 563 | newfid = v9fs_get_idpool(&v9ses->fidpool); | ||
| 564 | if (newfid < 0) { | ||
| 565 | eprintk(KERN_WARNING, "newfid fails!\n"); | ||
| 566 | return ERR_PTR(-ENOSPC); | ||
| 567 | } | ||
| 568 | |||
| 569 | result = | ||
| 570 | v9fs_t_walk(v9ses, dirfidnum, newfid, (char *)dentry->d_name.name, | ||
| 571 | NULL); | ||
| 572 | if (result < 0) { | ||
| 573 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 574 | if (result == -ENOENT) { | ||
| 575 | d_add(dentry, NULL); | ||
| 576 | dprintk(DEBUG_ERROR, | ||
| 577 | "Return negative dentry %p count %d\n", | ||
| 578 | dentry, atomic_read(&dentry->d_count)); | ||
| 579 | return NULL; | ||
| 580 | } | ||
| 581 | dprintk(DEBUG_ERROR, "walk error:%d\n", result); | ||
| 582 | goto FreeFcall; | ||
| 583 | } | ||
| 584 | |||
| 585 | result = v9fs_t_stat(v9ses, newfid, &fcall); | ||
| 586 | if (result < 0) { | ||
| 587 | dprintk(DEBUG_ERROR, "stat error\n"); | ||
| 588 | goto FreeFcall; | ||
| 589 | } | ||
| 590 | |||
| 591 | v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); | ||
| 592 | inode = v9fs_get_inode(sb, newstat.st_mode); | ||
| 593 | |||
| 594 | if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { | ||
| 595 | eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", | ||
| 596 | PTR_ERR(inode)); | ||
| 597 | |||
| 598 | result = -ENOSPC; | ||
| 599 | goto FreeFcall; | ||
| 600 | } | ||
| 601 | |||
| 602 | inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid); | ||
| 603 | |||
| 604 | fid = v9fs_fid_create(dentry); | ||
| 605 | if (fid == NULL) { | ||
| 606 | dprintk(DEBUG_ERROR, "couldn't insert\n"); | ||
| 607 | result = -ENOMEM; | ||
| 608 | goto FreeFcall; | ||
| 609 | } | ||
| 610 | |||
| 611 | fid->fid = newfid; | ||
| 612 | fid->fidopen = 0; | ||
| 613 | fid->v9ses = v9ses; | ||
| 614 | fid->qid = fcall->params.rstat.stat->qid; | ||
| 615 | |||
| 616 | dentry->d_op = &v9fs_dentry_operations; | ||
| 617 | v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb); | ||
| 618 | |||
| 619 | d_add(dentry, inode); | ||
| 620 | kfree(fcall); | ||
| 621 | |||
| 622 | return NULL; | ||
| 623 | |||
| 624 | FreeFcall: | ||
| 625 | kfree(fcall); | ||
| 626 | return ERR_PTR(result); | ||
| 627 | } | ||
| 628 | |||
| 629 | /** | ||
| 630 | * v9fs_vfs_unlink - VFS unlink hook to delete an inode | ||
| 631 | * @i: inode that is being unlinked | ||
| 632 | * @d: dentry that is being unlinked | ||
| 633 | * | ||
| 634 | */ | ||
| 635 | |||
| 636 | static int v9fs_vfs_unlink(struct inode *i, struct dentry *d) | ||
| 637 | { | ||
| 638 | return v9fs_remove(i, d, 0); | ||
| 639 | } | ||
| 640 | |||
| 641 | /** | ||
| 642 | * v9fs_vfs_rmdir - VFS unlink hook to delete a directory | ||
| 643 | * @i: inode that is being unlinked | ||
| 644 | * @d: dentry that is being unlinked | ||
| 645 | * | ||
| 646 | */ | ||
| 647 | |||
| 648 | static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) | ||
| 649 | { | ||
| 650 | return v9fs_remove(i, d, 1); | ||
| 651 | } | ||
| 652 | |||
| 653 | /** | ||
| 654 | * v9fs_vfs_rename - VFS hook to rename an inode | ||
| 655 | * @old_dir: old dir inode | ||
| 656 | * @old_dentry: old dentry | ||
| 657 | * @new_dir: new dir inode | ||
| 658 | * @new_dentry: new dentry | ||
| 659 | * | ||
| 660 | */ | ||
| 661 | |||
| 662 | static int | ||
| 663 | v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
| 664 | struct inode *new_dir, struct dentry *new_dentry) | ||
| 665 | { | ||
| 666 | struct inode *old_inode = old_dentry->d_inode; | ||
| 667 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); | ||
| 668 | struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry, FID_WALK); | ||
| 669 | struct v9fs_fid *olddirfid = | ||
| 670 | v9fs_fid_lookup(old_dentry->d_parent, FID_WALK); | ||
| 671 | struct v9fs_fid *newdirfid = | ||
| 672 | v9fs_fid_lookup(new_dentry->d_parent, FID_WALK); | ||
| 673 | struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 674 | struct v9fs_fcall *fcall = NULL; | ||
| 675 | int fid = -1; | ||
| 676 | int olddirfidnum = -1; | ||
| 677 | int newdirfidnum = -1; | ||
| 678 | int retval = 0; | ||
| 679 | |||
| 680 | dprintk(DEBUG_VFS, "\n"); | ||
| 681 | |||
| 682 | if (!mistat) | ||
| 683 | return -ENOMEM; | ||
| 684 | |||
| 685 | if ((!oldfid) || (!olddirfid) || (!newdirfid)) { | ||
| 686 | dprintk(DEBUG_ERROR, "problem with arguments\n"); | ||
| 687 | return -EBADF; | ||
| 688 | } | ||
| 689 | |||
| 690 | /* 9P can only handle file rename in the same directory */ | ||
| 691 | if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { | ||
| 692 | dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); | ||
| 693 | retval = -EPERM; | ||
| 694 | goto FreeFcallnBail; | ||
| 695 | } | ||
| 696 | |||
| 697 | fid = oldfid->fid; | ||
| 698 | olddirfidnum = olddirfid->fid; | ||
| 699 | newdirfidnum = newdirfid->fid; | ||
| 700 | |||
| 701 | if (fid < 0) { | ||
| 702 | dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", | ||
| 703 | old_inode->i_ino); | ||
| 704 | retval = -EBADF; | ||
| 705 | goto FreeFcallnBail; | ||
| 706 | } | ||
| 707 | |||
| 708 | v9fs_blank_mistat(v9ses, mistat); | ||
| 709 | |||
| 710 | strcpy(mistat->data + 1, v9ses->name); | ||
| 711 | mistat->name = mistat->data + 1 + strlen(v9ses->name); | ||
| 712 | |||
| 713 | if (new_dentry->d_name.len > | ||
| 714 | (v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) { | ||
| 715 | dprintk(DEBUG_ERROR, "new name too long\n"); | ||
| 716 | goto FreeFcallnBail; | ||
| 717 | } | ||
| 718 | |||
| 719 | strcpy(mistat->name, new_dentry->d_name.name); | ||
| 720 | retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall); | ||
| 721 | |||
| 722 | FreeFcallnBail: | ||
| 723 | kfree(mistat); | ||
| 724 | |||
| 725 | if (retval < 0) | ||
| 726 | dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", | ||
| 727 | FCALL_ERROR(fcall)); | ||
| 728 | |||
| 729 | kfree(fcall); | ||
| 730 | return retval; | ||
| 731 | } | ||
| 732 | |||
| 733 | /** | ||
| 734 | * v9fs_vfs_getattr - retreive file metadata | ||
| 735 | * @mnt - mount information | ||
| 736 | * @dentry - file to get attributes on | ||
| 737 | * @stat - metadata structure to populate | ||
| 738 | * | ||
| 739 | */ | ||
| 740 | |||
| 741 | static int | ||
| 742 | v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
| 743 | struct kstat *stat) | ||
| 744 | { | ||
| 745 | struct v9fs_fcall *fcall = NULL; | ||
| 746 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | ||
| 747 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 748 | int err = -EPERM; | ||
| 749 | |||
| 750 | dprintk(DEBUG_VFS, "dentry: %p\n", dentry); | ||
| 751 | if (!fid) { | ||
| 752 | dprintk(DEBUG_ERROR, | ||
| 753 | "couldn't find fid associated with dentry\n"); | ||
| 754 | return -EBADF; | ||
| 755 | } | ||
| 756 | |||
| 757 | err = v9fs_t_stat(v9ses, fid->fid, &fcall); | ||
| 758 | |||
| 759 | if (err < 0) | ||
| 760 | dprintk(DEBUG_ERROR, "stat error\n"); | ||
| 761 | else { | ||
| 762 | v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode, | ||
| 763 | dentry->d_inode->i_sb); | ||
| 764 | generic_fillattr(dentry->d_inode, stat); | ||
| 765 | } | ||
| 766 | |||
| 767 | kfree(fcall); | ||
| 768 | return err; | ||
| 769 | } | ||
| 770 | |||
| 771 | /** | ||
| 772 | * v9fs_vfs_setattr - set file metadata | ||
| 773 | * @dentry: file whose metadata to set | ||
| 774 | * @iattr: metadata assignment structure | ||
| 775 | * | ||
| 776 | */ | ||
| 777 | |||
| 778 | static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) | ||
| 779 | { | ||
| 780 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | ||
| 781 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 782 | struct v9fs_fcall *fcall = NULL; | ||
| 783 | struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 784 | int res = -EPERM; | ||
| 785 | |||
| 786 | dprintk(DEBUG_VFS, "\n"); | ||
| 787 | |||
| 788 | if (!mistat) | ||
| 789 | return -ENOMEM; | ||
| 790 | |||
| 791 | if (!fid) { | ||
| 792 | dprintk(DEBUG_ERROR, | ||
| 793 | "Couldn't find fid associated with dentry\n"); | ||
| 794 | return -EBADF; | ||
| 795 | } | ||
| 796 | |||
| 797 | v9fs_blank_mistat(v9ses, mistat); | ||
| 798 | if (iattr->ia_valid & ATTR_MODE) | ||
| 799 | mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode); | ||
| 800 | |||
| 801 | if (iattr->ia_valid & ATTR_MTIME) | ||
| 802 | mistat->mtime = iattr->ia_mtime.tv_sec; | ||
| 803 | |||
| 804 | if (iattr->ia_valid & ATTR_ATIME) | ||
| 805 | mistat->atime = iattr->ia_atime.tv_sec; | ||
| 806 | |||
| 807 | if (iattr->ia_valid & ATTR_SIZE) | ||
| 808 | mistat->length = iattr->ia_size; | ||
| 809 | |||
| 810 | if (v9ses->extended) { | ||
| 811 | char *ptr = mistat->data+1; | ||
| 812 | |||
| 813 | if (iattr->ia_valid & ATTR_UID) { | ||
| 814 | mistat->uid = ptr; | ||
| 815 | ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid); | ||
| 816 | mistat->n_uid = iattr->ia_uid; | ||
| 817 | } | ||
| 818 | |||
| 819 | if (iattr->ia_valid & ATTR_GID) { | ||
| 820 | mistat->gid = ptr; | ||
| 821 | ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid); | ||
| 822 | mistat->n_gid = iattr->ia_gid; | ||
| 823 | } | ||
| 824 | } | ||
| 825 | |||
| 826 | res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall); | ||
| 827 | |||
| 828 | if (res < 0) | ||
| 829 | dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall)); | ||
| 830 | |||
| 831 | kfree(mistat); | ||
| 832 | kfree(fcall); | ||
| 833 | |||
| 834 | if (res >= 0) | ||
| 835 | res = inode_setattr(dentry->d_inode, iattr); | ||
| 836 | |||
| 837 | return res; | ||
| 838 | } | ||
| 839 | |||
| 840 | /** | ||
| 841 | * v9fs_mistat2inode - populate an inode structure with mistat info | ||
| 842 | * @mistat: Plan 9 metadata (mistat) structure | ||
| 843 | * @inode: inode to populate | ||
| 844 | * @sb: superblock of filesystem | ||
| 845 | * | ||
| 846 | */ | ||
| 847 | |||
| 848 | void | ||
| 849 | v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, | ||
| 850 | struct super_block *sb) | ||
| 851 | { | ||
| 852 | struct v9fs_session_info *v9ses = sb->s_fs_info; | ||
| 853 | |||
| 854 | inode->i_nlink = 1; | ||
| 855 | |||
| 856 | inode->i_atime.tv_sec = mistat->atime; | ||
| 857 | inode->i_mtime.tv_sec = mistat->mtime; | ||
| 858 | inode->i_ctime.tv_sec = mistat->mtime; | ||
| 859 | |||
| 860 | inode->i_uid = -1; | ||
| 861 | inode->i_gid = -1; | ||
| 862 | |||
| 863 | if (v9ses->extended) { | ||
| 864 | /* TODO: string to uid mapping via user-space daemon */ | ||
| 865 | inode->i_uid = mistat->n_uid; | ||
| 866 | inode->i_gid = mistat->n_gid; | ||
| 867 | |||
| 868 | if (mistat->n_uid == -1) | ||
| 869 | sscanf(mistat->uid, "%x", &inode->i_uid); | ||
| 870 | |||
| 871 | if (mistat->n_gid == -1) | ||
| 872 | sscanf(mistat->gid, "%x", &inode->i_gid); | ||
| 873 | } | ||
| 874 | |||
| 875 | if (inode->i_uid == -1) | ||
| 876 | inode->i_uid = v9ses->uid; | ||
| 877 | if (inode->i_gid == -1) | ||
| 878 | inode->i_gid = v9ses->gid; | ||
| 879 | |||
| 880 | inode->i_mode = p9mode2unixmode(v9ses, mistat->mode); | ||
| 881 | if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { | ||
| 882 | char type = 0; | ||
| 883 | int major = -1; | ||
| 884 | int minor = -1; | ||
| 885 | sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); | ||
| 886 | switch (type) { | ||
| 887 | case 'c': | ||
| 888 | inode->i_mode &= ~S_IFBLK; | ||
| 889 | inode->i_mode |= S_IFCHR; | ||
| 890 | break; | ||
| 891 | case 'b': | ||
| 892 | break; | ||
| 893 | default: | ||
| 894 | dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", | ||
| 895 | type, mistat->extension); | ||
| 896 | }; | ||
| 897 | inode->i_rdev = MKDEV(major, minor); | ||
| 898 | } else | ||
| 899 | inode->i_rdev = 0; | ||
| 900 | |||
| 901 | inode->i_size = mistat->length; | ||
| 902 | |||
| 903 | inode->i_blksize = sb->s_blocksize; | ||
| 904 | inode->i_blocks = | ||
| 905 | (inode->i_size + inode->i_blksize - 1) >> sb->s_blocksize_bits; | ||
| 906 | } | ||
| 907 | |||
| 908 | /** | ||
| 909 | * v9fs_qid2ino - convert qid into inode number | ||
| 910 | * @qid: qid to hash | ||
| 911 | * | ||
| 912 | * BUG: potential for inode number collisions? | ||
| 913 | */ | ||
| 914 | |||
| 915 | ino_t v9fs_qid2ino(struct v9fs_qid *qid) | ||
| 916 | { | ||
| 917 | u64 path = qid->path + 2; | ||
| 918 | ino_t i = 0; | ||
| 919 | |||
| 920 | if (sizeof(ino_t) == sizeof(path)) | ||
| 921 | memcpy(&i, &path, sizeof(ino_t)); | ||
| 922 | else | ||
| 923 | i = (ino_t) (path ^ (path >> 32)); | ||
| 924 | |||
| 925 | return i; | ||
| 926 | } | ||
| 927 | |||
| 928 | /** | ||
| 929 | * v9fs_vfs_symlink - helper function to create symlinks | ||
| 930 | * @dir: directory inode containing symlink | ||
| 931 | * @dentry: dentry for symlink | ||
| 932 | * @symname: symlink data | ||
| 933 | * | ||
| 934 | * See 9P2000.u RFC for more information | ||
| 935 | * | ||
| 936 | */ | ||
| 937 | |||
| 938 | static int | ||
| 939 | v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | ||
| 940 | { | ||
| 941 | int retval = -EPERM; | ||
| 942 | struct v9fs_fid *newfid; | ||
| 943 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); | ||
| 944 | struct v9fs_fcall *fcall = NULL; | ||
| 945 | struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 946 | |||
| 947 | dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, | ||
| 948 | symname); | ||
| 949 | |||
| 950 | if (!mistat) | ||
| 951 | return -ENOMEM; | ||
| 952 | |||
| 953 | if (!v9ses->extended) { | ||
| 954 | dprintk(DEBUG_ERROR, "not extended\n"); | ||
| 955 | goto FreeFcall; | ||
| 956 | } | ||
| 957 | |||
| 958 | /* issue a create */ | ||
| 959 | retval = v9fs_create(dir, dentry, S_IFLNK, 0); | ||
| 960 | if (retval != 0) | ||
| 961 | goto FreeFcall; | ||
| 962 | |||
| 963 | newfid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 964 | |||
| 965 | /* issue a twstat */ | ||
| 966 | v9fs_blank_mistat(v9ses, mistat); | ||
| 967 | strcpy(mistat->data + 1, symname); | ||
| 968 | mistat->extension = mistat->data + 1; | ||
| 969 | retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); | ||
| 970 | if (retval < 0) { | ||
| 971 | dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", | ||
| 972 | FCALL_ERROR(fcall)); | ||
| 973 | goto FreeFcall; | ||
| 974 | } | ||
| 975 | |||
| 976 | kfree(fcall); | ||
| 977 | |||
| 978 | if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { | ||
| 979 | dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", | ||
| 980 | FCALL_ERROR(fcall)); | ||
| 981 | goto FreeFcall; | ||
| 982 | } | ||
| 983 | |||
| 984 | d_drop(dentry); /* FID - will this also clunk? */ | ||
| 985 | |||
| 986 | FreeFcall: | ||
| 987 | kfree(mistat); | ||
| 988 | kfree(fcall); | ||
| 989 | |||
| 990 | return retval; | ||
| 991 | } | ||
| 992 | |||
| 993 | /** | ||
| 994 | * v9fs_readlink - read a symlink's location (internal version) | ||
| 995 | * @dentry: dentry for symlink | ||
| 996 | * @buffer: buffer to load symlink location into | ||
| 997 | * @buflen: length of buffer | ||
| 998 | * | ||
| 999 | */ | ||
| 1000 | |||
| 1001 | static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) | ||
| 1002 | { | ||
| 1003 | int retval = -EPERM; | ||
| 1004 | |||
| 1005 | struct v9fs_fcall *fcall = NULL; | ||
| 1006 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | ||
| 1007 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 1008 | |||
| 1009 | if (!fid) { | ||
| 1010 | dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); | ||
| 1011 | retval = -EBADF; | ||
| 1012 | goto FreeFcall; | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | if (!v9ses->extended) { | ||
| 1016 | retval = -EBADF; | ||
| 1017 | dprintk(DEBUG_ERROR, "not extended\n"); | ||
| 1018 | goto FreeFcall; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); | ||
| 1022 | retval = v9fs_t_stat(v9ses, fid->fid, &fcall); | ||
| 1023 | |||
| 1024 | if (retval < 0) { | ||
| 1025 | dprintk(DEBUG_ERROR, "stat error\n"); | ||
| 1026 | goto FreeFcall; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | if (!fcall) | ||
| 1030 | return -EIO; | ||
| 1031 | |||
| 1032 | if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) { | ||
| 1033 | retval = -EINVAL; | ||
| 1034 | goto FreeFcall; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | /* copy extension buffer into buffer */ | ||
| 1038 | if (strlen(fcall->params.rstat.stat->extension) < buflen) | ||
| 1039 | buflen = strlen(fcall->params.rstat.stat->extension); | ||
| 1040 | |||
| 1041 | memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1); | ||
| 1042 | |||
| 1043 | retval = buflen; | ||
| 1044 | |||
| 1045 | FreeFcall: | ||
| 1046 | kfree(fcall); | ||
| 1047 | |||
| 1048 | return retval; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | /** | ||
| 1052 | * v9fs_vfs_readlink - read a symlink's location | ||
| 1053 | * @dentry: dentry for symlink | ||
| 1054 | * @buf: buffer to load symlink location into | ||
| 1055 | * @buflen: length of buffer | ||
| 1056 | * | ||
| 1057 | */ | ||
| 1058 | |||
| 1059 | static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, | ||
| 1060 | int buflen) | ||
| 1061 | { | ||
| 1062 | int retval; | ||
| 1063 | int ret; | ||
| 1064 | char *link = __getname(); | ||
| 1065 | |||
| 1066 | if (strlen(link) < buflen) | ||
| 1067 | buflen = strlen(link); | ||
| 1068 | |||
| 1069 | dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); | ||
| 1070 | |||
| 1071 | retval = v9fs_readlink(dentry, link, buflen); | ||
| 1072 | |||
| 1073 | if (retval > 0) { | ||
| 1074 | if ((ret = copy_to_user(buffer, link, retval)) != 0) { | ||
| 1075 | dprintk(DEBUG_ERROR, "problem copying to user: %d\n", | ||
| 1076 | ret); | ||
| 1077 | retval = ret; | ||
| 1078 | } | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | putname(link); | ||
| 1082 | return retval; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | /** | ||
| 1086 | * v9fs_vfs_follow_link - follow a symlink path | ||
| 1087 | * @dentry: dentry for symlink | ||
| 1088 | * @nd: nameidata | ||
| 1089 | * | ||
| 1090 | */ | ||
| 1091 | |||
| 1092 | static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
| 1093 | { | ||
| 1094 | int len = 0; | ||
| 1095 | char *link = __getname(); | ||
| 1096 | |||
| 1097 | dprintk(DEBUG_VFS, "%s n", dentry->d_name.name); | ||
| 1098 | |||
| 1099 | if (!link) | ||
| 1100 | link = ERR_PTR(-ENOMEM); | ||
| 1101 | else { | ||
| 1102 | len = v9fs_readlink(dentry, link, strlen(link)); | ||
| 1103 | |||
| 1104 | if (len < 0) { | ||
| 1105 | putname(link); | ||
| 1106 | link = ERR_PTR(len); | ||
| 1107 | } else | ||
| 1108 | link[len] = 0; | ||
| 1109 | } | ||
| 1110 | nd_set_link(nd, link); | ||
| 1111 | |||
| 1112 | return NULL; | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /** | ||
| 1116 | * v9fs_vfs_put_link - release a symlink path | ||
| 1117 | * @dentry: dentry for symlink | ||
| 1118 | * @nd: nameidata | ||
| 1119 | * | ||
| 1120 | */ | ||
| 1121 | |||
| 1122 | static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) | ||
| 1123 | { | ||
| 1124 | char *s = nd_get_link(nd); | ||
| 1125 | |||
| 1126 | dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s); | ||
| 1127 | if (!IS_ERR(s)) | ||
| 1128 | putname(s); | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /** | ||
| 1132 | * v9fs_vfs_link - create a hardlink | ||
| 1133 | * @old_dentry: dentry for file to link to | ||
| 1134 | * @dir: inode destination for new link | ||
| 1135 | * @dentry: dentry for link | ||
| 1136 | * | ||
| 1137 | */ | ||
| 1138 | |||
| 1139 | /* XXX - lots of code dup'd from symlink and creates, | ||
| 1140 | * figure out a better reuse strategy | ||
| 1141 | */ | ||
| 1142 | |||
| 1143 | static int | ||
| 1144 | v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, | ||
| 1145 | struct dentry *dentry) | ||
| 1146 | { | ||
| 1147 | int retval = -EPERM; | ||
| 1148 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); | ||
| 1149 | struct v9fs_fcall *fcall = NULL; | ||
| 1150 | struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 1151 | struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry, FID_OP); | ||
| 1152 | struct v9fs_fid *newfid = NULL; | ||
| 1153 | char *symname = __getname(); | ||
| 1154 | |||
| 1155 | dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, | ||
| 1156 | old_dentry->d_name.name); | ||
| 1157 | |||
| 1158 | if (!v9ses->extended) { | ||
| 1159 | dprintk(DEBUG_ERROR, "not extended\n"); | ||
| 1160 | goto FreeMem; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /* get fid of old_dentry */ | ||
| 1164 | sprintf(symname, "hardlink(%d)\n", oldfid->fid); | ||
| 1165 | |||
| 1166 | /* issue a create */ | ||
| 1167 | retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0); | ||
| 1168 | if (retval != 0) | ||
| 1169 | goto FreeMem; | ||
| 1170 | |||
| 1171 | newfid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 1172 | if (!newfid) { | ||
| 1173 | dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n"); | ||
| 1174 | goto FreeMem; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | /* issue a twstat */ | ||
| 1178 | v9fs_blank_mistat(v9ses, mistat); | ||
| 1179 | strcpy(mistat->data + 1, symname); | ||
| 1180 | mistat->extension = mistat->data + 1; | ||
| 1181 | retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); | ||
| 1182 | if (retval < 0) { | ||
| 1183 | dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", | ||
| 1184 | FCALL_ERROR(fcall)); | ||
| 1185 | goto FreeMem; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | kfree(fcall); | ||
| 1189 | |||
| 1190 | if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { | ||
| 1191 | dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", | ||
| 1192 | FCALL_ERROR(fcall)); | ||
| 1193 | goto FreeMem; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | d_drop(dentry); /* FID - will this also clunk? */ | ||
| 1197 | |||
| 1198 | kfree(fcall); | ||
| 1199 | fcall = NULL; | ||
| 1200 | |||
| 1201 | FreeMem: | ||
| 1202 | kfree(mistat); | ||
| 1203 | kfree(fcall); | ||
| 1204 | putname(symname); | ||
| 1205 | return retval; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | /** | ||
| 1209 | * v9fs_vfs_mknod - create a special file | ||
| 1210 | * @dir: inode destination for new link | ||
| 1211 | * @dentry: dentry for file | ||
| 1212 | * @mode: mode for creation | ||
| 1213 | * @dev_t: device associated with special file | ||
| 1214 | * | ||
| 1215 | */ | ||
| 1216 | |||
| 1217 | static int | ||
| 1218 | v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) | ||
| 1219 | { | ||
| 1220 | int retval = -EPERM; | ||
| 1221 | struct v9fs_fid *newfid; | ||
| 1222 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); | ||
| 1223 | struct v9fs_fcall *fcall = NULL; | ||
| 1224 | struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); | ||
| 1225 | char *symname = __getname(); | ||
| 1226 | |||
| 1227 | dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, | ||
| 1228 | dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); | ||
| 1229 | |||
| 1230 | if (!mistat) | ||
| 1231 | return -ENOMEM; | ||
| 1232 | |||
| 1233 | if (!new_valid_dev(rdev)) { | ||
| 1234 | retval = -EINVAL; | ||
| 1235 | goto FreeMem; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | if (!v9ses->extended) { | ||
| 1239 | dprintk(DEBUG_ERROR, "not extended\n"); | ||
| 1240 | goto FreeMem; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | /* issue a create */ | ||
| 1244 | retval = v9fs_create(dir, dentry, mode, 0); | ||
| 1245 | |||
| 1246 | if (retval != 0) | ||
| 1247 | goto FreeMem; | ||
| 1248 | |||
| 1249 | newfid = v9fs_fid_lookup(dentry, FID_OP); | ||
| 1250 | if (!newfid) { | ||
| 1251 | dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n"); | ||
| 1252 | retval = -EINVAL; | ||
| 1253 | goto FreeMem; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | /* build extension */ | ||
| 1257 | if (S_ISBLK(mode)) | ||
| 1258 | sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev)); | ||
| 1259 | else if (S_ISCHR(mode)) | ||
| 1260 | sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev)); | ||
| 1261 | else if (S_ISFIFO(mode)) | ||
| 1262 | ; /* DO NOTHING */ | ||
| 1263 | else { | ||
| 1264 | retval = -EINVAL; | ||
| 1265 | goto FreeMem; | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | if (!S_ISFIFO(mode)) { | ||
| 1269 | /* issue a twstat */ | ||
| 1270 | v9fs_blank_mistat(v9ses, mistat); | ||
| 1271 | strcpy(mistat->data + 1, symname); | ||
| 1272 | mistat->extension = mistat->data + 1; | ||
| 1273 | retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); | ||
| 1274 | if (retval < 0) { | ||
| 1275 | dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", | ||
| 1276 | FCALL_ERROR(fcall)); | ||
| 1277 | goto FreeMem; | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | /* need to update dcache so we show up */ | ||
| 1282 | kfree(fcall); | ||
| 1283 | |||
| 1284 | if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { | ||
| 1285 | dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", | ||
| 1286 | FCALL_ERROR(fcall)); | ||
| 1287 | goto FreeMem; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | d_drop(dentry); /* FID - will this also clunk? */ | ||
| 1291 | |||
| 1292 | FreeMem: | ||
| 1293 | kfree(mistat); | ||
| 1294 | kfree(fcall); | ||
| 1295 | putname(symname); | ||
| 1296 | |||
| 1297 | return retval; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | static struct inode_operations v9fs_dir_inode_operations_ext = { | ||
| 1301 | .create = v9fs_vfs_create, | ||
| 1302 | .lookup = v9fs_vfs_lookup, | ||
| 1303 | .symlink = v9fs_vfs_symlink, | ||
| 1304 | .link = v9fs_vfs_link, | ||
| 1305 | .unlink = v9fs_vfs_unlink, | ||
| 1306 | .mkdir = v9fs_vfs_mkdir, | ||
| 1307 | .rmdir = v9fs_vfs_rmdir, | ||
| 1308 | .mknod = v9fs_vfs_mknod, | ||
| 1309 | .rename = v9fs_vfs_rename, | ||
| 1310 | .readlink = v9fs_vfs_readlink, | ||
| 1311 | .getattr = v9fs_vfs_getattr, | ||
| 1312 | .setattr = v9fs_vfs_setattr, | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | static struct inode_operations v9fs_dir_inode_operations = { | ||
| 1316 | .create = v9fs_vfs_create, | ||
| 1317 | .lookup = v9fs_vfs_lookup, | ||
| 1318 | .unlink = v9fs_vfs_unlink, | ||
| 1319 | .mkdir = v9fs_vfs_mkdir, | ||
| 1320 | .rmdir = v9fs_vfs_rmdir, | ||
| 1321 | .mknod = v9fs_vfs_mknod, | ||
| 1322 | .rename = v9fs_vfs_rename, | ||
| 1323 | .getattr = v9fs_vfs_getattr, | ||
| 1324 | .setattr = v9fs_vfs_setattr, | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | static struct inode_operations v9fs_file_inode_operations = { | ||
| 1328 | .getattr = v9fs_vfs_getattr, | ||
| 1329 | .setattr = v9fs_vfs_setattr, | ||
| 1330 | }; | ||
| 1331 | |||
| 1332 | static struct inode_operations v9fs_symlink_inode_operations = { | ||
| 1333 | .readlink = v9fs_vfs_readlink, | ||
| 1334 | .follow_link = v9fs_vfs_follow_link, | ||
| 1335 | .put_link = v9fs_vfs_put_link, | ||
| 1336 | .getattr = v9fs_vfs_getattr, | ||
| 1337 | .setattr = v9fs_vfs_setattr, | ||
| 1338 | }; | ||
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c new file mode 100644 index 000000000000..868f350b2c5f --- /dev/null +++ b/fs/9p/vfs_super.c | |||
| @@ -0,0 +1,280 @@ | |||
| 1 | /* | ||
| 2 | * linux/fs/9p/vfs_super.c | ||
| 3 | * | ||
| 4 | * This file contians superblock ops for 9P2000. It is intended that | ||
| 5 | * you mount this file system on directories. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | ||
| 8 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License | ||
| 21 | * along with this program; if not, write to: | ||
| 22 | * Free Software Foundation | ||
| 23 | * 51 Franklin Street, Fifth Floor | ||
| 24 | * Boston, MA 02111-1301 USA | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/config.h> | ||
| 30 | #include <linux/module.h> | ||
| 31 | #include <linux/errno.h> | ||
| 32 | #include <linux/fs.h> | ||
| 33 | #include <linux/file.h> | ||
| 34 | #include <linux/stat.h> | ||
| 35 | #include <linux/string.h> | ||
| 36 | #include <linux/smp_lock.h> | ||
| 37 | #include <linux/inet.h> | ||
| 38 | #include <linux/pagemap.h> | ||
| 39 | #include <linux/seq_file.h> | ||
| 40 | #include <linux/mount.h> | ||
| 41 | #include <linux/idr.h> | ||
| 42 | |||
| 43 | #include "debug.h" | ||
| 44 | #include "v9fs.h" | ||
| 45 | #include "9p.h" | ||
| 46 | #include "v9fs_vfs.h" | ||
| 47 | #include "conv.h" | ||
| 48 | #include "fid.h" | ||
| 49 | |||
| 50 | static void v9fs_clear_inode(struct inode *); | ||
| 51 | static struct super_operations v9fs_super_ops; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * v9fs_clear_inode - release an inode | ||
| 55 | * @inode: inode to release | ||
| 56 | * | ||
| 57 | */ | ||
| 58 | |||
| 59 | static void v9fs_clear_inode(struct inode *inode) | ||
| 60 | { | ||
| 61 | filemap_fdatawrite(inode->i_mapping); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * v9fs_set_super - set the superblock | ||
| 66 | * @s: super block | ||
| 67 | * @data: file system specific data | ||
| 68 | * | ||
| 69 | */ | ||
| 70 | |||
| 71 | static int v9fs_set_super(struct super_block *s, void *data) | ||
| 72 | { | ||
| 73 | s->s_fs_info = data; | ||
| 74 | return set_anon_super(s, data); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * v9fs_fill_super - populate superblock with info | ||
| 79 | * @sb: superblock | ||
| 80 | * @v9ses: session information | ||
| 81 | * | ||
| 82 | */ | ||
| 83 | |||
| 84 | static void | ||
| 85 | v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses, | ||
| 86 | int flags) | ||
| 87 | { | ||
| 88 | sb->s_maxbytes = MAX_LFS_FILESIZE; | ||
| 89 | sb->s_blocksize_bits = fls(v9ses->maxdata - 1); | ||
| 90 | sb->s_blocksize = 1 << sb->s_blocksize_bits; | ||
| 91 | sb->s_magic = V9FS_MAGIC; | ||
| 92 | sb->s_op = &v9fs_super_ops; | ||
| 93 | |||
| 94 | sb->s_flags = flags | MS_ACTIVE | MS_SYNCHRONOUS | MS_DIRSYNC | | ||
| 95 | MS_NODIRATIME | MS_NOATIME; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * v9fs_get_sb - mount a superblock | ||
| 100 | * @fs_type: file system type | ||
| 101 | * @flags: mount flags | ||
| 102 | * @dev_name: device name that was mounted | ||
| 103 | * @data: mount options | ||
| 104 | * | ||
| 105 | */ | ||
| 106 | |||
| 107 | static struct super_block *v9fs_get_sb(struct file_system_type | ||
| 108 | *fs_type, int flags, | ||
| 109 | const char *dev_name, void *data) | ||
| 110 | { | ||
| 111 | struct super_block *sb = NULL; | ||
| 112 | struct v9fs_fcall *fcall = NULL; | ||
| 113 | struct inode *inode = NULL; | ||
| 114 | struct dentry *root = NULL; | ||
| 115 | struct v9fs_session_info *v9ses = NULL; | ||
| 116 | struct v9fs_fid *root_fid = NULL; | ||
| 117 | int mode = S_IRWXUGO | S_ISVTX; | ||
| 118 | uid_t uid = current->fsuid; | ||
| 119 | gid_t gid = current->fsgid; | ||
| 120 | int stat_result = 0; | ||
| 121 | int newfid = 0; | ||
| 122 | int retval = 0; | ||
| 123 | |||
| 124 | dprintk(DEBUG_VFS, " \n"); | ||
| 125 | |||
| 126 | v9ses = kcalloc(1, sizeof(struct v9fs_session_info), GFP_KERNEL); | ||
| 127 | if (!v9ses) | ||
| 128 | return ERR_PTR(-ENOMEM); | ||
| 129 | |||
| 130 | if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { | ||
| 131 | dprintk(DEBUG_ERROR, "problem initiating session\n"); | ||
| 132 | retval = newfid; | ||
| 133 | goto free_session; | ||
| 134 | } | ||
| 135 | |||
| 136 | sb = sget(fs_type, NULL, v9fs_set_super, v9ses); | ||
| 137 | |||
| 138 | v9fs_fill_super(sb, v9ses, flags); | ||
| 139 | |||
| 140 | inode = v9fs_get_inode(sb, S_IFDIR | mode); | ||
| 141 | if (IS_ERR(inode)) { | ||
| 142 | retval = PTR_ERR(inode); | ||
| 143 | goto put_back_sb; | ||
| 144 | } | ||
| 145 | |||
| 146 | inode->i_uid = uid; | ||
| 147 | inode->i_gid = gid; | ||
| 148 | |||
| 149 | root = d_alloc_root(inode); | ||
| 150 | |||
| 151 | if (!root) { | ||
| 152 | retval = -ENOMEM; | ||
| 153 | goto release_inode; | ||
| 154 | } | ||
| 155 | |||
| 156 | sb->s_root = root; | ||
| 157 | |||
| 158 | /* Setup the Root Inode */ | ||
| 159 | root_fid = v9fs_fid_create(root); | ||
| 160 | if (root_fid == NULL) { | ||
| 161 | retval = -ENOMEM; | ||
| 162 | goto release_dentry; | ||
| 163 | } | ||
| 164 | |||
| 165 | root_fid->fidopen = 0; | ||
| 166 | root_fid->v9ses = v9ses; | ||
| 167 | |||
| 168 | stat_result = v9fs_t_stat(v9ses, newfid, &fcall); | ||
| 169 | if (stat_result < 0) { | ||
| 170 | dprintk(DEBUG_ERROR, "stat error\n"); | ||
| 171 | v9fs_t_clunk(v9ses, newfid, NULL); | ||
| 172 | v9fs_put_idpool(newfid, &v9ses->fidpool); | ||
| 173 | } else { | ||
| 174 | root_fid->fid = newfid; | ||
| 175 | root_fid->qid = fcall->params.rstat.stat->qid; | ||
| 176 | root->d_inode->i_ino = | ||
| 177 | v9fs_qid2ino(&fcall->params.rstat.stat->qid); | ||
| 178 | v9fs_mistat2inode(fcall->params.rstat.stat, root->d_inode, sb); | ||
| 179 | } | ||
| 180 | |||
| 181 | kfree(fcall); | ||
| 182 | |||
| 183 | if (stat_result < 0) { | ||
| 184 | retval = stat_result; | ||
| 185 | goto release_dentry; | ||
| 186 | } | ||
| 187 | |||
| 188 | return sb; | ||
| 189 | |||
| 190 | release_dentry: | ||
| 191 | dput(sb->s_root); | ||
| 192 | |||
| 193 | release_inode: | ||
| 194 | iput(inode); | ||
| 195 | |||
| 196 | put_back_sb: | ||
| 197 | up_write(&sb->s_umount); | ||
| 198 | deactivate_super(sb); | ||
| 199 | v9fs_session_close(v9ses); | ||
| 200 | |||
| 201 | free_session: | ||
| 202 | kfree(v9ses); | ||
| 203 | |||
| 204 | return ERR_PTR(retval); | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * v9fs_kill_super - Kill Superblock | ||
| 209 | * @s: superblock | ||
| 210 | * | ||
| 211 | */ | ||
| 212 | |||
| 213 | static void v9fs_kill_super(struct super_block *s) | ||
| 214 | { | ||
| 215 | struct v9fs_session_info *v9ses = s->s_fs_info; | ||
| 216 | |||
| 217 | dprintk(DEBUG_VFS, " %p\n", s); | ||
| 218 | |||
| 219 | v9fs_dentry_release(s->s_root); /* clunk root */ | ||
| 220 | |||
| 221 | kill_anon_super(s); | ||
| 222 | |||
| 223 | v9fs_session_close(v9ses); | ||
| 224 | kfree(v9ses); | ||
| 225 | dprintk(DEBUG_VFS, "exiting kill_super\n"); | ||
| 226 | } | ||
| 227 | |||
| 228 | /** | ||
| 229 | * v9fs_show_options - Show mount options in /proc/mounts | ||
| 230 | * @m: seq_file to write to | ||
| 231 | * @mnt: mount descriptor | ||
| 232 | * | ||
| 233 | */ | ||
| 234 | |||
| 235 | static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt) | ||
| 236 | { | ||
| 237 | struct v9fs_session_info *v9ses = mnt->mnt_sb->s_fs_info; | ||
| 238 | |||
| 239 | if (v9ses->debug != 0) | ||
| 240 | seq_printf(m, ",debug=%u", v9ses->debug); | ||
| 241 | if (v9ses->port != V9FS_PORT) | ||
| 242 | seq_printf(m, ",port=%u", v9ses->port); | ||
| 243 | if (v9ses->maxdata != 9000) | ||
| 244 | seq_printf(m, ",msize=%u", v9ses->maxdata); | ||
| 245 | if (v9ses->afid != ~0) | ||
| 246 | seq_printf(m, ",afid=%u", v9ses->afid); | ||
| 247 | if (v9ses->proto == PROTO_UNIX) | ||
| 248 | seq_puts(m, ",proto=unix"); | ||
| 249 | if (v9ses->extended == 0) | ||
| 250 | seq_puts(m, ",noextend"); | ||
| 251 | if (v9ses->nodev == 1) | ||
| 252 | seq_puts(m, ",nodevmap"); | ||
| 253 | seq_printf(m, ",name=%s", v9ses->name); | ||
| 254 | seq_printf(m, ",aname=%s", v9ses->remotename); | ||
| 255 | seq_printf(m, ",uid=%u", v9ses->uid); | ||
| 256 | seq_printf(m, ",gid=%u", v9ses->gid); | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | static void | ||
| 261 | v9fs_umount_begin(struct super_block *sb) | ||
| 262 | { | ||
| 263 | struct v9fs_session_info *v9ses = sb->s_fs_info; | ||
| 264 | |||
| 265 | v9fs_session_cancel(v9ses); | ||
| 266 | } | ||
| 267 | |||
| 268 | static struct super_operations v9fs_super_ops = { | ||
| 269 | .statfs = simple_statfs, | ||
| 270 | .clear_inode = v9fs_clear_inode, | ||
| 271 | .show_options = v9fs_show_options, | ||
| 272 | .umount_begin = v9fs_umount_begin, | ||
| 273 | }; | ||
| 274 | |||
| 275 | struct file_system_type v9fs_fs_type = { | ||
| 276 | .name = "9P", | ||
| 277 | .get_sb = v9fs_get_sb, | ||
| 278 | .kill_sb = v9fs_kill_super, | ||
| 279 | .owner = THIS_MODULE, | ||
| 280 | }; | ||
diff --git a/fs/Kconfig b/fs/Kconfig index 5e817902cb3b..068ccea2f184 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
| @@ -462,6 +462,19 @@ config AUTOFS4_FS | |||
| 462 | local network, you probably do not need an automounter, and can say | 462 | local network, you probably do not need an automounter, and can say |
| 463 | N here. | 463 | N here. |
| 464 | 464 | ||
| 465 | config FUSE_FS | ||
| 466 | tristate "Filesystem in Userspace support" | ||
| 467 | help | ||
| 468 | With FUSE it is possible to implement a fully functional filesystem | ||
| 469 | in a userspace program. | ||
| 470 | |||
| 471 | There's also companion library: libfuse. This library along with | ||
| 472 | utilities is available from the FUSE homepage: | ||
| 473 | <http://fuse.sourceforge.net/> | ||
| 474 | |||
| 475 | If you want to develop a userspace FS, or if you want to use | ||
| 476 | a filesystem based on FUSE, answer Y or M. | ||
| 477 | |||
| 465 | menu "CD-ROM/DVD Filesystems" | 478 | menu "CD-ROM/DVD Filesystems" |
| 466 | 479 | ||
| 467 | config ISO9660_FS | 480 | config ISO9660_FS |
| @@ -1703,6 +1716,17 @@ config AFS_FS | |||
| 1703 | config RXRPC | 1716 | config RXRPC |
| 1704 | tristate | 1717 | tristate |
| 1705 | 1718 | ||
| 1719 | config 9P_FS | ||
| 1720 | tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" | ||
| 1721 | depends on INET && EXPERIMENTAL | ||
| 1722 | help | ||
| 1723 | If you say Y here, you will get experimental support for | ||
| 1724 | Plan 9 resource sharing via the 9P2000 protocol. | ||
| 1725 | |||
| 1726 | See <http://v9fs.sf.net> for more information. | ||
| 1727 | |||
| 1728 | If unsure, say N. | ||
| 1729 | |||
| 1706 | endmenu | 1730 | endmenu |
| 1707 | 1731 | ||
| 1708 | menu "Partition Types" | 1732 | menu "Partition Types" |
diff --git a/fs/Makefile b/fs/Makefile index 15158309dee4..1972da186272 100644 --- a/fs/Makefile +++ b/fs/Makefile | |||
| @@ -89,11 +89,13 @@ obj-$(CONFIG_QNX4FS_FS) += qnx4/ | |||
| 89 | obj-$(CONFIG_AUTOFS_FS) += autofs/ | 89 | obj-$(CONFIG_AUTOFS_FS) += autofs/ |
| 90 | obj-$(CONFIG_AUTOFS4_FS) += autofs4/ | 90 | obj-$(CONFIG_AUTOFS4_FS) += autofs4/ |
| 91 | obj-$(CONFIG_ADFS_FS) += adfs/ | 91 | obj-$(CONFIG_ADFS_FS) += adfs/ |
| 92 | obj-$(CONFIG_FUSE_FS) += fuse/ | ||
| 92 | obj-$(CONFIG_UDF_FS) += udf/ | 93 | obj-$(CONFIG_UDF_FS) += udf/ |
| 93 | obj-$(CONFIG_RELAYFS_FS) += relayfs/ | 94 | obj-$(CONFIG_RELAYFS_FS) += relayfs/ |
| 94 | obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ | 95 | obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ |
| 95 | obj-$(CONFIG_JFS_FS) += jfs/ | 96 | obj-$(CONFIG_JFS_FS) += jfs/ |
| 96 | obj-$(CONFIG_XFS_FS) += xfs/ | 97 | obj-$(CONFIG_XFS_FS) += xfs/ |
| 98 | obj-$(CONFIG_9P_FS) += 9p/ | ||
| 97 | obj-$(CONFIG_AFS_FS) += afs/ | 99 | obj-$(CONFIG_AFS_FS) += afs/ |
| 98 | obj-$(CONFIG_BEFS_FS) += befs/ | 100 | obj-$(CONFIG_BEFS_FS) += befs/ |
| 99 | obj-$(CONFIG_HOSTFS) += hostfs/ | 101 | obj-$(CONFIG_HOSTFS) += hostfs/ |
diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 7aa6f2004536..9ebe881c6786 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c | |||
| @@ -255,6 +255,7 @@ void | |||
| 255 | affs_delete_inode(struct inode *inode) | 255 | affs_delete_inode(struct inode *inode) |
| 256 | { | 256 | { |
| 257 | pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); | 257 | pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); |
| 258 | truncate_inode_pages(&inode->i_data, 0); | ||
| 258 | inode->i_size = 0; | 259 | inode->i_size = 0; |
| 259 | if (S_ISREG(inode->i_mode)) | 260 | if (S_ISREG(inode->i_mode)) |
| 260 | affs_truncate(inode); | 261 | affs_truncate(inode); |
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <linux/highmem.h> | 29 | #include <linux/highmem.h> |
| 30 | #include <linux/workqueue.h> | 30 | #include <linux/workqueue.h> |
| 31 | #include <linux/security.h> | 31 | #include <linux/security.h> |
| 32 | #include <linux/rcuref.h> | ||
| 32 | 33 | ||
| 33 | #include <asm/kmap_types.h> | 34 | #include <asm/kmap_types.h> |
| 34 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
| @@ -499,7 +500,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) | |||
| 499 | /* Must be done under the lock to serialise against cancellation. | 500 | /* Must be done under the lock to serialise against cancellation. |
| 500 | * Call this aio_fput as it duplicates fput via the fput_work. | 501 | * Call this aio_fput as it duplicates fput via the fput_work. |
| 501 | */ | 502 | */ |
| 502 | if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { | 503 | if (unlikely(rcuref_dec_and_test(&req->ki_filp->f_count))) { |
| 503 | get_ioctx(ctx); | 504 | get_ioctx(ctx); |
| 504 | spin_lock(&fput_lock); | 505 | spin_lock(&fput_lock); |
| 505 | list_add(&req->ki_list, &fput_head); | 506 | list_add(&req->ki_list, &fput_head); |
| @@ -546,6 +547,24 @@ struct kioctx *lookup_ioctx(unsigned long ctx_id) | |||
| 546 | return ioctx; | 547 | return ioctx; |
| 547 | } | 548 | } |
| 548 | 549 | ||
| 550 | static int lock_kiocb_action(void *param) | ||
| 551 | { | ||
| 552 | schedule(); | ||
| 553 | return 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | static inline void lock_kiocb(struct kiocb *iocb) | ||
| 557 | { | ||
| 558 | wait_on_bit_lock(&iocb->ki_flags, KIF_LOCKED, lock_kiocb_action, | ||
| 559 | TASK_UNINTERRUPTIBLE); | ||
| 560 | } | ||
| 561 | |||
| 562 | static inline void unlock_kiocb(struct kiocb *iocb) | ||
| 563 | { | ||
| 564 | kiocbClearLocked(iocb); | ||
| 565 | wake_up_bit(&iocb->ki_flags, KIF_LOCKED); | ||
| 566 | } | ||
| 567 | |||
| 549 | /* | 568 | /* |
| 550 | * use_mm | 569 | * use_mm |
| 551 | * Makes the calling kernel thread take on the specified | 570 | * Makes the calling kernel thread take on the specified |
| @@ -786,7 +805,9 @@ static int __aio_run_iocbs(struct kioctx *ctx) | |||
| 786 | * Hold an extra reference while retrying i/o. | 805 | * Hold an extra reference while retrying i/o. |
| 787 | */ | 806 | */ |
| 788 | iocb->ki_users++; /* grab extra reference */ | 807 | iocb->ki_users++; /* grab extra reference */ |
| 808 | lock_kiocb(iocb); | ||
| 789 | aio_run_iocb(iocb); | 809 | aio_run_iocb(iocb); |
| 810 | unlock_kiocb(iocb); | ||
| 790 | if (__aio_put_req(ctx, iocb)) /* drop extra ref */ | 811 | if (__aio_put_req(ctx, iocb)) /* drop extra ref */ |
| 791 | put_ioctx(ctx); | 812 | put_ioctx(ctx); |
| 792 | } | 813 | } |
| @@ -1527,10 +1548,9 @@ int fastcall io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, | |||
| 1527 | goto out_put_req; | 1548 | goto out_put_req; |
| 1528 | 1549 | ||
| 1529 | spin_lock_irq(&ctx->ctx_lock); | 1550 | spin_lock_irq(&ctx->ctx_lock); |
| 1530 | if (likely(list_empty(&ctx->run_list))) { | 1551 | aio_run_iocb(req); |
| 1531 | aio_run_iocb(req); | 1552 | unlock_kiocb(req); |
| 1532 | } else { | 1553 | if (!list_empty(&ctx->run_list)) { |
| 1533 | list_add_tail(&req->ki_run_list, &ctx->run_list); | ||
| 1534 | /* drain the run list */ | 1554 | /* drain the run list */ |
| 1535 | while (__aio_run_iocbs(ctx)) | 1555 | while (__aio_run_iocbs(ctx)) |
| 1536 | ; | 1556 | ; |
| @@ -1661,6 +1681,7 @@ asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb, | |||
| 1661 | if (NULL != cancel) { | 1681 | if (NULL != cancel) { |
| 1662 | struct io_event tmp; | 1682 | struct io_event tmp; |
| 1663 | pr_debug("calling cancel\n"); | 1683 | pr_debug("calling cancel\n"); |
| 1684 | lock_kiocb(kiocb); | ||
| 1664 | memset(&tmp, 0, sizeof(tmp)); | 1685 | memset(&tmp, 0, sizeof(tmp)); |
| 1665 | tmp.obj = (u64)(unsigned long)kiocb->ki_obj.user; | 1686 | tmp.obj = (u64)(unsigned long)kiocb->ki_obj.user; |
| 1666 | tmp.data = kiocb->ki_user_data; | 1687 | tmp.data = kiocb->ki_user_data; |
| @@ -1672,8 +1693,9 @@ asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb, | |||
| 1672 | if (copy_to_user(result, &tmp, sizeof(tmp))) | 1693 | if (copy_to_user(result, &tmp, sizeof(tmp))) |
| 1673 | ret = -EFAULT; | 1694 | ret = -EFAULT; |
| 1674 | } | 1695 | } |
| 1696 | unlock_kiocb(kiocb); | ||
| 1675 | } else | 1697 | } else |
| 1676 | printk(KERN_DEBUG "iocb has no cancel operation\n"); | 1698 | ret = -EINVAL; |
| 1677 | 1699 | ||
| 1678 | put_ioctx(ctx); | 1700 | put_ioctx(ctx); |
| 1679 | 1701 | ||
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 6171431272dc..990c28da5aec 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h | |||
| @@ -105,6 +105,7 @@ struct autofs_sb_info { | |||
| 105 | struct file *pipe; | 105 | struct file *pipe; |
| 106 | pid_t oz_pgrp; | 106 | pid_t oz_pgrp; |
| 107 | int catatonic; | 107 | int catatonic; |
| 108 | struct super_block *sb; | ||
| 108 | unsigned long exp_timeout; | 109 | unsigned long exp_timeout; |
| 109 | ino_t next_dir_ino; | 110 | ino_t next_dir_ino; |
| 110 | struct autofs_wait_queue *queues; /* Wait queue pointer */ | 111 | struct autofs_wait_queue *queues; /* Wait queue pointer */ |
| @@ -134,7 +135,7 @@ void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); | |||
| 134 | void autofs_hash_delete(struct autofs_dir_ent *); | 135 | void autofs_hash_delete(struct autofs_dir_ent *); |
| 135 | struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *); | 136 | struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *); |
| 136 | void autofs_hash_dputall(struct autofs_dirhash *); | 137 | void autofs_hash_dputall(struct autofs_dirhash *); |
| 137 | void autofs_hash_nuke(struct autofs_dirhash *); | 138 | void autofs_hash_nuke(struct autofs_sb_info *); |
| 138 | 139 | ||
| 139 | /* Expiration-handling functions */ | 140 | /* Expiration-handling functions */ |
| 140 | 141 | ||
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 448143fd0796..5ccfcf26310d 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c | |||
| @@ -232,13 +232,13 @@ void autofs_hash_dputall(struct autofs_dirhash *dh) | |||
| 232 | 232 | ||
| 233 | /* Delete everything. This is used on filesystem destruction, so we | 233 | /* Delete everything. This is used on filesystem destruction, so we |
| 234 | make no attempt to keep the pointers valid */ | 234 | make no attempt to keep the pointers valid */ |
| 235 | void autofs_hash_nuke(struct autofs_dirhash *dh) | 235 | void autofs_hash_nuke(struct autofs_sb_info *sbi) |
| 236 | { | 236 | { |
| 237 | int i; | 237 | int i; |
| 238 | struct autofs_dir_ent *ent, *nent; | 238 | struct autofs_dir_ent *ent, *nent; |
| 239 | 239 | ||
| 240 | for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { | 240 | for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { |
| 241 | for ( ent = dh->h[i] ; ent ; ent = nent ) { | 241 | for ( ent = sbi->dirhash.h[i] ; ent ; ent = nent ) { |
| 242 | nent = ent->next; | 242 | nent = ent->next; |
| 243 | if ( ent->dentry ) | 243 | if ( ent->dentry ) |
| 244 | dput(ent->dentry); | 244 | dput(ent->dentry); |
| @@ -246,4 +246,5 @@ void autofs_hash_nuke(struct autofs_dirhash *dh) | |||
| 246 | kfree(ent); | 246 | kfree(ent); |
| 247 | } | 247 | } |
| 248 | } | 248 | } |
| 249 | shrink_dcache_sb(sbi->sb); | ||
| 249 | } | 250 | } |
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 4888c1fabbf7..65e5ed42190e 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c | |||
| @@ -27,7 +27,7 @@ static void autofs_put_super(struct super_block *sb) | |||
| 27 | if ( !sbi->catatonic ) | 27 | if ( !sbi->catatonic ) |
| 28 | autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ | 28 | autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ |
| 29 | 29 | ||
| 30 | autofs_hash_nuke(&sbi->dirhash); | 30 | autofs_hash_nuke(sbi); |
| 31 | for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { | 31 | for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { |
| 32 | if ( test_bit(n, sbi->symlink_bitmap) ) | 32 | if ( test_bit(n, sbi->symlink_bitmap) ) |
| 33 | kfree(sbi->symlink[n].data); | 33 | kfree(sbi->symlink[n].data); |
| @@ -148,6 +148,7 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) | |||
| 148 | s->s_magic = AUTOFS_SUPER_MAGIC; | 148 | s->s_magic = AUTOFS_SUPER_MAGIC; |
| 149 | s->s_op = &autofs_sops; | 149 | s->s_op = &autofs_sops; |
| 150 | s->s_time_gran = 1; | 150 | s->s_time_gran = 1; |
| 151 | sbi->sb = s; | ||
| 151 | 152 | ||
| 152 | root_inode = iget(s, AUTOFS_ROOT_INO); | 153 | root_inode = iget(s, AUTOFS_ROOT_INO); |
| 153 | root = d_alloc_root(root_inode); | 154 | root = d_alloc_root(root_inode); |
diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h index 1020dbc88bec..1fbc53f14aba 100644 --- a/fs/bfs/bfs.h +++ b/fs/bfs/bfs.h | |||
| @@ -20,7 +20,6 @@ struct bfs_sb_info { | |||
| 20 | unsigned long si_lasti; | 20 | unsigned long si_lasti; |
| 21 | unsigned long * si_imap; | 21 | unsigned long * si_imap; |
| 22 | struct buffer_head * si_sbh; /* buffer header w/superblock */ | 22 | struct buffer_head * si_sbh; /* buffer header w/superblock */ |
| 23 | struct bfs_super_block * si_bfs_sb; /* superblock in si_sbh->b_data */ | ||
| 24 | }; | 23 | }; |
| 25 | 24 | ||
| 26 | /* | 25 | /* |
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 5a1e5ce057ff..e240c335eb23 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | * fs/bfs/dir.c | 2 | * fs/bfs/dir.c |
| 3 | * BFS directory operations. | 3 | * BFS directory operations. |
| 4 | * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com> | 4 | * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com> |
| 5 | * Made endianness-clean by Andrew Stribblehill <ads@wompom.org> 2005 | ||
| 5 | */ | 6 | */ |
| 6 | 7 | ||
| 7 | #include <linux/time.h> | 8 | #include <linux/time.h> |
| @@ -20,9 +21,9 @@ | |||
| 20 | #define dprintf(x...) | 21 | #define dprintf(x...) |
| 21 | #endif | 22 | #endif |
| 22 | 23 | ||
| 23 | static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino); | 24 | static int bfs_add_entry(struct inode * dir, const unsigned char * name, int namelen, int ino); |
| 24 | static struct buffer_head * bfs_find_entry(struct inode * dir, | 25 | static struct buffer_head * bfs_find_entry(struct inode * dir, |
| 25 | const char * name, int namelen, struct bfs_dirent ** res_dir); | 26 | const unsigned char * name, int namelen, struct bfs_dirent ** res_dir); |
| 26 | 27 | ||
| 27 | static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) | 28 | static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) |
| 28 | { | 29 | { |
| @@ -53,7 +54,7 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) | |||
| 53 | de = (struct bfs_dirent *)(bh->b_data + offset); | 54 | de = (struct bfs_dirent *)(bh->b_data + offset); |
| 54 | if (de->ino) { | 55 | if (de->ino) { |
| 55 | int size = strnlen(de->name, BFS_NAMELEN); | 56 | int size = strnlen(de->name, BFS_NAMELEN); |
| 56 | if (filldir(dirent, de->name, size, f->f_pos, de->ino, DT_UNKNOWN) < 0) { | 57 | if (filldir(dirent, de->name, size, f->f_pos, le16_to_cpu(de->ino), DT_UNKNOWN) < 0) { |
| 57 | brelse(bh); | 58 | brelse(bh); |
| 58 | unlock_kernel(); | 59 | unlock_kernel(); |
| 59 | return 0; | 60 | return 0; |
| @@ -107,7 +108,7 @@ static int bfs_create(struct inode * dir, struct dentry * dentry, int mode, | |||
| 107 | inode->i_mapping->a_ops = &bfs_aops; | 108 | inode->i_mapping->a_ops = &bfs_aops; |
| 108 | inode->i_mode = mode; | 109 | inode->i_mode = mode; |
| 109 | inode->i_ino = ino; | 110 | inode->i_ino = ino; |
| 110 | BFS_I(inode)->i_dsk_ino = ino; | 111 | BFS_I(inode)->i_dsk_ino = cpu_to_le16(ino); |
| 111 | BFS_I(inode)->i_sblock = 0; | 112 | BFS_I(inode)->i_sblock = 0; |
| 112 | BFS_I(inode)->i_eblock = 0; | 113 | BFS_I(inode)->i_eblock = 0; |
| 113 | insert_inode_hash(inode); | 114 | insert_inode_hash(inode); |
| @@ -139,7 +140,7 @@ static struct dentry * bfs_lookup(struct inode * dir, struct dentry * dentry, st | |||
| 139 | lock_kernel(); | 140 | lock_kernel(); |
| 140 | bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); | 141 | bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); |
| 141 | if (bh) { | 142 | if (bh) { |
| 142 | unsigned long ino = le32_to_cpu(de->ino); | 143 | unsigned long ino = (unsigned long)le16_to_cpu(de->ino); |
| 143 | brelse(bh); | 144 | brelse(bh); |
| 144 | inode = iget(dir->i_sb, ino); | 145 | inode = iget(dir->i_sb, ino); |
| 145 | if (!inode) { | 146 | if (!inode) { |
| @@ -183,7 +184,7 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry) | |||
| 183 | inode = dentry->d_inode; | 184 | inode = dentry->d_inode; |
| 184 | lock_kernel(); | 185 | lock_kernel(); |
| 185 | bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); | 186 | bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); |
| 186 | if (!bh || de->ino != inode->i_ino) | 187 | if (!bh || le16_to_cpu(de->ino) != inode->i_ino) |
| 187 | goto out_brelse; | 188 | goto out_brelse; |
| 188 | 189 | ||
| 189 | if (!inode->i_nlink) { | 190 | if (!inode->i_nlink) { |
| @@ -224,7 +225,7 @@ static int bfs_rename(struct inode * old_dir, struct dentry * old_dentry, | |||
| 224 | old_dentry->d_name.name, | 225 | old_dentry->d_name.name, |
| 225 | old_dentry->d_name.len, &old_de); | 226 | old_dentry->d_name.len, &old_de); |
| 226 | 227 | ||
| 227 | if (!old_bh || old_de->ino != old_inode->i_ino) | 228 | if (!old_bh || le16_to_cpu(old_de->ino) != old_inode->i_ino) |
| 228 | goto end_rename; | 229 | goto end_rename; |
| 229 | 230 | ||
| 230 | error = -EPERM; | 231 | error = -EPERM; |
| @@ -270,7 +271,7 @@ struct inode_operations bfs_dir_inops = { | |||
| 270 | .rename = bfs_rename, | 271 | .rename = bfs_rename, |
| 271 | }; | 272 | }; |
| 272 | 273 | ||
| 273 | static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino) | 274 | static int bfs_add_entry(struct inode * dir, const unsigned char * name, int namelen, int ino) |
| 274 | { | 275 | { |
| 275 | struct buffer_head * bh; | 276 | struct buffer_head * bh; |
| 276 | struct bfs_dirent * de; | 277 | struct bfs_dirent * de; |
| @@ -304,7 +305,7 @@ static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int | |||
| 304 | } | 305 | } |
| 305 | dir->i_mtime = CURRENT_TIME_SEC; | 306 | dir->i_mtime = CURRENT_TIME_SEC; |
| 306 | mark_inode_dirty(dir); | 307 | mark_inode_dirty(dir); |
| 307 | de->ino = ino; | 308 | de->ino = cpu_to_le16((u16)ino); |
| 308 | for (i=0; i<BFS_NAMELEN; i++) | 309 | for (i=0; i<BFS_NAMELEN; i++) |
| 309 | de->name[i] = (i < namelen) ? name[i] : 0; | 310 | de->name[i] = (i < namelen) ? name[i] : 0; |
| 310 | mark_buffer_dirty(bh); | 311 | mark_buffer_dirty(bh); |
| @@ -317,7 +318,7 @@ static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int | |||
| 317 | return -ENOSPC; | 318 | return -ENOSPC; |
| 318 | } | 319 | } |
| 319 | 320 | ||
| 320 | static inline int bfs_namecmp(int len, const char * name, const char * buffer) | 321 | static inline int bfs_namecmp(int len, const unsigned char * name, const char * buffer) |
| 321 | { | 322 | { |
| 322 | if (len < BFS_NAMELEN && buffer[len]) | 323 | if (len < BFS_NAMELEN && buffer[len]) |
| 323 | return 0; | 324 | return 0; |
| @@ -325,7 +326,7 @@ static inline int bfs_namecmp(int len, const char * name, const char * buffer) | |||
| 325 | } | 326 | } |
| 326 | 327 | ||
| 327 | static struct buffer_head * bfs_find_entry(struct inode * dir, | 328 | static struct buffer_head * bfs_find_entry(struct inode * dir, |
| 328 | const char * name, int namelen, struct bfs_dirent ** res_dir) | 329 | const unsigned char * name, int namelen, struct bfs_dirent ** res_dir) |
| 329 | { | 330 | { |
| 330 | unsigned long block, offset; | 331 | unsigned long block, offset; |
| 331 | struct buffer_head * bh; | 332 | struct buffer_head * bh; |
| @@ -346,7 +347,7 @@ static struct buffer_head * bfs_find_entry(struct inode * dir, | |||
| 346 | } | 347 | } |
| 347 | de = (struct bfs_dirent *)(bh->b_data + offset); | 348 | de = (struct bfs_dirent *)(bh->b_data + offset); |
| 348 | offset += BFS_DIRENT_SIZE; | 349 | offset += BFS_DIRENT_SIZE; |
| 349 | if (de->ino && bfs_namecmp(namelen, name, de->name)) { | 350 | if (le16_to_cpu(de->ino) && bfs_namecmp(namelen, name, de->name)) { |
| 350 | *res_dir = de; | 351 | *res_dir = de; |
| 351 | return bh; | 352 | return bh; |
| 352 | } | 353 | } |
diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 747fd1ea55e0..807723b65daf 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c | |||
| @@ -40,8 +40,8 @@ static int bfs_move_block(unsigned long from, unsigned long to, struct super_blo | |||
| 40 | return 0; | 40 | return 0; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | static int bfs_move_blocks(struct super_block *sb, unsigned long start, unsigned long end, | 43 | static int bfs_move_blocks(struct super_block *sb, unsigned long start, |
| 44 | unsigned long where) | 44 | unsigned long end, unsigned long where) |
| 45 | { | 45 | { |
| 46 | unsigned long i; | 46 | unsigned long i; |
| 47 | 47 | ||
| @@ -57,20 +57,21 @@ static int bfs_move_blocks(struct super_block *sb, unsigned long start, unsigned | |||
| 57 | static int bfs_get_block(struct inode * inode, sector_t block, | 57 | static int bfs_get_block(struct inode * inode, sector_t block, |
| 58 | struct buffer_head * bh_result, int create) | 58 | struct buffer_head * bh_result, int create) |
| 59 | { | 59 | { |
| 60 | long phys; | 60 | unsigned long phys; |
| 61 | int err; | 61 | int err; |
| 62 | struct super_block *sb = inode->i_sb; | 62 | struct super_block *sb = inode->i_sb; |
| 63 | struct bfs_sb_info *info = BFS_SB(sb); | 63 | struct bfs_sb_info *info = BFS_SB(sb); |
| 64 | struct bfs_inode_info *bi = BFS_I(inode); | 64 | struct bfs_inode_info *bi = BFS_I(inode); |
| 65 | struct buffer_head *sbh = info->si_sbh; | 65 | struct buffer_head *sbh = info->si_sbh; |
| 66 | 66 | ||
| 67 | if (block < 0 || block > info->si_blocks) | 67 | if (block > info->si_blocks) |
| 68 | return -EIO; | 68 | return -EIO; |
| 69 | 69 | ||
| 70 | phys = bi->i_sblock + block; | 70 | phys = bi->i_sblock + block; |
| 71 | if (!create) { | 71 | if (!create) { |
| 72 | if (phys <= bi->i_eblock) { | 72 | if (phys <= bi->i_eblock) { |
| 73 | dprintf("c=%d, b=%08lx, phys=%08lx (granted)\n", create, block, phys); | 73 | dprintf("c=%d, b=%08lx, phys=%09lx (granted)\n", |
| 74 | create, (unsigned long)block, phys); | ||
| 74 | map_bh(bh_result, sb, phys); | 75 | map_bh(bh_result, sb, phys); |
| 75 | } | 76 | } |
| 76 | return 0; | 77 | return 0; |
| @@ -80,7 +81,7 @@ static int bfs_get_block(struct inode * inode, sector_t block, | |||
| 80 | of blocks allocated for this file, we can grant it */ | 81 | of blocks allocated for this file, we can grant it */ |
| 81 | if (inode->i_size && phys <= bi->i_eblock) { | 82 | if (inode->i_size && phys <= bi->i_eblock) { |
| 82 | dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", | 83 | dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", |
| 83 | create, block, phys); | 84 | create, (unsigned long)block, phys); |
| 84 | map_bh(bh_result, sb, phys); | 85 | map_bh(bh_result, sb, phys); |
| 85 | return 0; | 86 | return 0; |
| 86 | } | 87 | } |
| @@ -88,11 +89,12 @@ static int bfs_get_block(struct inode * inode, sector_t block, | |||
| 88 | /* the rest has to be protected against itself */ | 89 | /* the rest has to be protected against itself */ |
| 89 | lock_kernel(); | 90 | lock_kernel(); |
| 90 | 91 | ||
| 91 | /* if the last data block for this file is the last allocated block, we can | 92 | /* if the last data block for this file is the last allocated |
| 92 | extend the file trivially, without moving it anywhere */ | 93 | block, we can extend the file trivially, without moving it |
| 94 | anywhere */ | ||
| 93 | if (bi->i_eblock == info->si_lf_eblk) { | 95 | if (bi->i_eblock == info->si_lf_eblk) { |
| 94 | dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", | 96 | dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", |
| 95 | create, block, phys); | 97 | create, (unsigned long)block, phys); |
| 96 | map_bh(bh_result, sb, phys); | 98 | map_bh(bh_result, sb, phys); |
| 97 | info->si_freeb -= phys - bi->i_eblock; | 99 | info->si_freeb -= phys - bi->i_eblock; |
| 98 | info->si_lf_eblk = bi->i_eblock = phys; | 100 | info->si_lf_eblk = bi->i_eblock = phys; |
| @@ -114,7 +116,8 @@ static int bfs_get_block(struct inode * inode, sector_t block, | |||
| 114 | } else | 116 | } else |
| 115 | err = 0; | 117 | err = 0; |
| 116 | 118 | ||
| 117 | dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", create, block, phys); | 119 | dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", |
| 120 | create, (unsigned long)block, phys); | ||
| 118 | bi->i_sblock = phys; | 121 | bi->i_sblock = phys; |
| 119 | phys += block; | 122 | phys += block; |
| 120 | info->si_lf_eblk = bi->i_eblock = phys; | 123 | info->si_lf_eblk = bi->i_eblock = phys; |
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 64e0fb33fc0c..c7b39aa279d7 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | * BFS superblock and inode operations. | 3 | * BFS superblock and inode operations. |
| 4 | * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com> | 4 | * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com> |
| 5 | * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds. | 5 | * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds. |
| 6 | * | ||
| 7 | * Made endianness-clean by Andrew Stribblehill <ads@wompom.org>, 2005. | ||
| 6 | */ | 8 | */ |
| 7 | 9 | ||
| 8 | #include <linux/module.h> | 10 | #include <linux/module.h> |
| @@ -54,46 +56,50 @@ static void bfs_read_inode(struct inode * inode) | |||
| 54 | off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; | 56 | off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; |
| 55 | di = (struct bfs_inode *)bh->b_data + off; | 57 | di = (struct bfs_inode *)bh->b_data + off; |
| 56 | 58 | ||
| 57 | inode->i_mode = 0x0000FFFF & di->i_mode; | 59 | inode->i_mode = 0x0000FFFF & le32_to_cpu(di->i_mode); |
| 58 | if (di->i_vtype == BFS_VDIR) { | 60 | if (le32_to_cpu(di->i_vtype) == BFS_VDIR) { |
| 59 | inode->i_mode |= S_IFDIR; | 61 | inode->i_mode |= S_IFDIR; |
| 60 | inode->i_op = &bfs_dir_inops; | 62 | inode->i_op = &bfs_dir_inops; |
| 61 | inode->i_fop = &bfs_dir_operations; | 63 | inode->i_fop = &bfs_dir_operations; |
| 62 | } else if (di->i_vtype == BFS_VREG) { | 64 | } else if (le32_to_cpu(di->i_vtype) == BFS_VREG) { |
| 63 | inode->i_mode |= S_IFREG; | 65 | inode->i_mode |= S_IFREG; |
| 64 | inode->i_op = &bfs_file_inops; | 66 | inode->i_op = &bfs_file_inops; |
| 65 | inode->i_fop = &bfs_file_operations; | 67 | inode->i_fop = &bfs_file_operations; |
| 66 | inode->i_mapping->a_ops = &bfs_aops; | 68 | inode->i_mapping->a_ops = &bfs_aops; |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | inode->i_uid = di->i_uid; | 71 | BFS_I(inode)->i_sblock = le32_to_cpu(di->i_sblock); |
| 70 | inode->i_gid = di->i_gid; | 72 | BFS_I(inode)->i_eblock = le32_to_cpu(di->i_eblock); |
| 71 | inode->i_nlink = di->i_nlink; | 73 | inode->i_uid = le32_to_cpu(di->i_uid); |
| 74 | inode->i_gid = le32_to_cpu(di->i_gid); | ||
| 75 | inode->i_nlink = le32_to_cpu(di->i_nlink); | ||
| 72 | inode->i_size = BFS_FILESIZE(di); | 76 | inode->i_size = BFS_FILESIZE(di); |
| 73 | inode->i_blocks = BFS_FILEBLOCKS(di); | 77 | inode->i_blocks = BFS_FILEBLOCKS(di); |
| 78 | if (inode->i_size || inode->i_blocks) dprintf("Registered inode with %lld size, %ld blocks\n", inode->i_size, inode->i_blocks); | ||
| 74 | inode->i_blksize = PAGE_SIZE; | 79 | inode->i_blksize = PAGE_SIZE; |
| 75 | inode->i_atime.tv_sec = di->i_atime; | 80 | inode->i_atime.tv_sec = le32_to_cpu(di->i_atime); |
| 76 | inode->i_mtime.tv_sec = di->i_mtime; | 81 | inode->i_mtime.tv_sec = le32_to_cpu(di->i_mtime); |
| 77 | inode->i_ctime.tv_sec = di->i_ctime; | 82 | inode->i_ctime.tv_sec = le32_to_cpu(di->i_ctime); |
| 78 | inode->i_atime.tv_nsec = 0; | 83 | inode->i_atime.tv_nsec = 0; |
| 79 | inode->i_mtime.tv_nsec = 0; | 84 | inode->i_mtime.tv_nsec = 0; |
| 80 | inode->i_ctime.tv_nsec = 0; | 85 | inode->i_ctime.tv_nsec = 0; |
| 81 | BFS_I(inode)->i_dsk_ino = di->i_ino; /* can be 0 so we store a copy */ | 86 | BFS_I(inode)->i_dsk_ino = le16_to_cpu(di->i_ino); /* can be 0 so we store a copy */ |
| 82 | BFS_I(inode)->i_sblock = di->i_sblock; | ||
| 83 | BFS_I(inode)->i_eblock = di->i_eblock; | ||
| 84 | 87 | ||
| 85 | brelse(bh); | 88 | brelse(bh); |
| 86 | } | 89 | } |
| 87 | 90 | ||
| 88 | static int bfs_write_inode(struct inode * inode, int unused) | 91 | static int bfs_write_inode(struct inode * inode, int unused) |
| 89 | { | 92 | { |
| 90 | unsigned long ino = inode->i_ino; | 93 | unsigned int ino = (u16)inode->i_ino; |
| 94 | unsigned long i_sblock; | ||
| 91 | struct bfs_inode * di; | 95 | struct bfs_inode * di; |
| 92 | struct buffer_head * bh; | 96 | struct buffer_head * bh; |
| 93 | int block, off; | 97 | int block, off; |
| 94 | 98 | ||
| 99 | dprintf("ino=%08x\n", ino); | ||
| 100 | |||
| 95 | if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) { | 101 | if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) { |
| 96 | printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino); | 102 | printf("Bad inode number %s:%08x\n", inode->i_sb->s_id, ino); |
| 97 | return -EIO; | 103 | return -EIO; |
| 98 | } | 104 | } |
| 99 | 105 | ||
| @@ -101,7 +107,7 @@ static int bfs_write_inode(struct inode * inode, int unused) | |||
| 101 | block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; | 107 | block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1; |
| 102 | bh = sb_bread(inode->i_sb, block); | 108 | bh = sb_bread(inode->i_sb, block); |
| 103 | if (!bh) { | 109 | if (!bh) { |
| 104 | printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino); | 110 | printf("Unable to read inode %s:%08x\n", inode->i_sb->s_id, ino); |
| 105 | unlock_kernel(); | 111 | unlock_kernel(); |
| 106 | return -EIO; | 112 | return -EIO; |
| 107 | } | 113 | } |
| @@ -109,24 +115,26 @@ static int bfs_write_inode(struct inode * inode, int unused) | |||
| 109 | off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; | 115 | off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; |
| 110 | di = (struct bfs_inode *)bh->b_data + off; | 116 | di = (struct bfs_inode *)bh->b_data + off; |
| 111 | 117 | ||
| 112 | if (inode->i_ino == BFS_ROOT_INO) | 118 | if (ino == BFS_ROOT_INO) |
| 113 | di->i_vtype = BFS_VDIR; | 119 | di->i_vtype = cpu_to_le32(BFS_VDIR); |
| 114 | else | 120 | else |
| 115 | di->i_vtype = BFS_VREG; | 121 | di->i_vtype = cpu_to_le32(BFS_VREG); |
| 116 | 122 | ||
| 117 | di->i_ino = inode->i_ino; | 123 | di->i_ino = cpu_to_le16(ino); |
| 118 | di->i_mode = inode->i_mode; | 124 | di->i_mode = cpu_to_le32(inode->i_mode); |
| 119 | di->i_uid = inode->i_uid; | 125 | di->i_uid = cpu_to_le32(inode->i_uid); |
| 120 | di->i_gid = inode->i_gid; | 126 | di->i_gid = cpu_to_le32(inode->i_gid); |
| 121 | di->i_nlink = inode->i_nlink; | 127 | di->i_nlink = cpu_to_le32(inode->i_nlink); |
| 122 | di->i_atime = inode->i_atime.tv_sec; | 128 | di->i_atime = cpu_to_le32(inode->i_atime.tv_sec); |
| 123 | di->i_mtime = inode->i_mtime.tv_sec; | 129 | di->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); |
| 124 | di->i_ctime = inode->i_ctime.tv_sec; | 130 | di->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); |
| 125 | di->i_sblock = BFS_I(inode)->i_sblock; | 131 | i_sblock = BFS_I(inode)->i_sblock; |
| 126 | di->i_eblock = BFS_I(inode)->i_eblock; | 132 | di->i_sblock = cpu_to_le32(i_sblock); |
| 127 | di->i_eoffset = di->i_sblock * BFS_BSIZE + inode->i_size - 1; | 133 | di->i_eblock = cpu_to_le32(BFS_I(inode)->i_eblock); |
| 134 | di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1); | ||
| 128 | 135 | ||
| 129 | mark_buffer_dirty(bh); | 136 | mark_buffer_dirty(bh); |
| 137 | dprintf("Written ino=%d into %d:%d\n",le16_to_cpu(di->i_ino),block,off); | ||
| 130 | brelse(bh); | 138 | brelse(bh); |
| 131 | unlock_kernel(); | 139 | unlock_kernel(); |
| 132 | return 0; | 140 | return 0; |
| @@ -140,11 +148,14 @@ static void bfs_delete_inode(struct inode * inode) | |||
| 140 | int block, off; | 148 | int block, off; |
| 141 | struct super_block * s = inode->i_sb; | 149 | struct super_block * s = inode->i_sb; |
| 142 | struct bfs_sb_info * info = BFS_SB(s); | 150 | struct bfs_sb_info * info = BFS_SB(s); |
| 151 | struct bfs_inode_info * bi = BFS_I(inode); | ||
| 143 | 152 | ||
| 144 | dprintf("ino=%08lx\n", inode->i_ino); | 153 | dprintf("ino=%08lx\n", ino); |
| 145 | 154 | ||
| 146 | if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > info->si_lasti) { | 155 | truncate_inode_pages(&inode->i_data, 0); |
| 147 | printf("invalid ino=%08lx\n", inode->i_ino); | 156 | |
| 157 | if (ino < BFS_ROOT_INO || ino > info->si_lasti) { | ||
| 158 | printf("invalid ino=%08lx\n", ino); | ||
| 148 | return; | 159 | return; |
| 149 | } | 160 | } |
| 150 | 161 | ||
| @@ -160,13 +171,13 @@ static void bfs_delete_inode(struct inode * inode) | |||
| 160 | return; | 171 | return; |
| 161 | } | 172 | } |
| 162 | off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; | 173 | off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; |
| 163 | di = (struct bfs_inode *)bh->b_data + off; | 174 | di = (struct bfs_inode *) bh->b_data + off; |
| 164 | if (di->i_ino) { | 175 | if (bi->i_dsk_ino) { |
| 165 | info->si_freeb += BFS_FILEBLOCKS(di); | 176 | info->si_freeb += 1 + bi->i_eblock - bi->i_sblock; |
| 166 | info->si_freei++; | 177 | info->si_freei++; |
| 167 | clear_bit(di->i_ino, info->si_imap); | 178 | clear_bit(ino, info->si_imap); |
| 168 | dump_imap("delete_inode", s); | 179 | dump_imap("delete_inode", s); |
| 169 | } | 180 | } |
| 170 | di->i_ino = 0; | 181 | di->i_ino = 0; |
| 171 | di->i_sblock = 0; | 182 | di->i_sblock = 0; |
| 172 | mark_buffer_dirty(bh); | 183 | mark_buffer_dirty(bh); |
| @@ -272,14 +283,14 @@ static struct super_operations bfs_sops = { | |||
| 272 | 283 | ||
| 273 | void dump_imap(const char *prefix, struct super_block * s) | 284 | void dump_imap(const char *prefix, struct super_block * s) |
| 274 | { | 285 | { |
| 275 | #if 0 | 286 | #ifdef DEBUG |
| 276 | int i; | 287 | int i; |
| 277 | char *tmpbuf = (char *)get_zeroed_page(GFP_KERNEL); | 288 | char *tmpbuf = (char *)get_zeroed_page(GFP_KERNEL); |
| 278 | 289 | ||
| 279 | if (!tmpbuf) | 290 | if (!tmpbuf) |
| 280 | return; | 291 | return; |
| 281 | for (i=BFS_SB(s)->si_lasti; i>=0; i--) { | 292 | for (i=BFS_SB(s)->si_lasti; i>=0; i--) { |
| 282 | if (i>PAGE_SIZE-100) break; | 293 | if (i > PAGE_SIZE-100) break; |
| 283 | if (test_bit(i, BFS_SB(s)->si_imap)) | 294 | if (test_bit(i, BFS_SB(s)->si_imap)) |
| 284 | strcat(tmpbuf, "1"); | 295 | strcat(tmpbuf, "1"); |
| 285 | else | 296 | else |
| @@ -295,7 +306,7 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) | |||
| 295 | struct buffer_head * bh; | 306 | struct buffer_head * bh; |
| 296 | struct bfs_super_block * bfs_sb; | 307 | struct bfs_super_block * bfs_sb; |
| 297 | struct inode * inode; | 308 | struct inode * inode; |
| 298 | int i, imap_len; | 309 | unsigned i, imap_len; |
| 299 | struct bfs_sb_info * info; | 310 | struct bfs_sb_info * info; |
| 300 | 311 | ||
| 301 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 312 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
| @@ -310,19 +321,18 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) | |||
| 310 | if(!bh) | 321 | if(!bh) |
| 311 | goto out; | 322 | goto out; |
| 312 | bfs_sb = (struct bfs_super_block *)bh->b_data; | 323 | bfs_sb = (struct bfs_super_block *)bh->b_data; |
| 313 | if (bfs_sb->s_magic != BFS_MAGIC) { | 324 | if (le32_to_cpu(bfs_sb->s_magic) != BFS_MAGIC) { |
| 314 | if (!silent) | 325 | if (!silent) |
| 315 | printf("No BFS filesystem on %s (magic=%08x)\n", | 326 | printf("No BFS filesystem on %s (magic=%08x)\n", |
| 316 | s->s_id, bfs_sb->s_magic); | 327 | s->s_id, le32_to_cpu(bfs_sb->s_magic)); |
| 317 | goto out; | 328 | goto out; |
| 318 | } | 329 | } |
| 319 | if (BFS_UNCLEAN(bfs_sb, s) && !silent) | 330 | if (BFS_UNCLEAN(bfs_sb, s) && !silent) |
| 320 | printf("%s is unclean, continuing\n", s->s_id); | 331 | printf("%s is unclean, continuing\n", s->s_id); |
| 321 | 332 | ||
| 322 | s->s_magic = BFS_MAGIC; | 333 | s->s_magic = BFS_MAGIC; |
| 323 | info->si_bfs_sb = bfs_sb; | ||
| 324 | info->si_sbh = bh; | 334 | info->si_sbh = bh; |
| 325 | info->si_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) | 335 | info->si_lasti = (le32_to_cpu(bfs_sb->s_start) - BFS_BSIZE)/sizeof(struct bfs_inode) |
| 326 | + BFS_ROOT_INO - 1; | 336 | + BFS_ROOT_INO - 1; |
| 327 | 337 | ||
| 328 | imap_len = info->si_lasti/8 + 1; | 338 | imap_len = info->si_lasti/8 + 1; |
| @@ -346,8 +356,8 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) | |||
| 346 | goto out; | 356 | goto out; |
| 347 | } | 357 | } |
| 348 | 358 | ||
| 349 | info->si_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */ | 359 | info->si_blocks = (le32_to_cpu(bfs_sb->s_end) + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */ |
| 350 | info->si_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS; | 360 | info->si_freeb = (le32_to_cpu(bfs_sb->s_end) + 1 - cpu_to_le32(bfs_sb->s_start))>>BFS_BSIZE_BITS; |
| 351 | info->si_freei = 0; | 361 | info->si_freei = 0; |
| 352 | info->si_lf_eblk = 0; | 362 | info->si_lf_eblk = 0; |
| 353 | info->si_lf_sblk = 0; | 363 | info->si_lf_sblk = 0; |
| @@ -683,7 +683,7 @@ struct bio *bio_map_user(request_queue_t *q, struct block_device *bdev, | |||
| 683 | { | 683 | { |
| 684 | struct sg_iovec iov; | 684 | struct sg_iovec iov; |
| 685 | 685 | ||
| 686 | iov.iov_base = (__user void *)uaddr; | 686 | iov.iov_base = (void __user *)uaddr; |
| 687 | iov.iov_len = len; | 687 | iov.iov_len = len; |
| 688 | 688 | ||
| 689 | return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm); | 689 | return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm); |
diff --git a/fs/buffer.c b/fs/buffer.c index 1c62203a4906..6cbfceabd95d 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #include <linux/cpu.h> | 40 | #include <linux/cpu.h> |
| 41 | #include <linux/bitops.h> | 41 | #include <linux/bitops.h> |
| 42 | #include <linux/mpage.h> | 42 | #include <linux/mpage.h> |
| 43 | #include <linux/bit_spinlock.h> | ||
| 43 | 44 | ||
| 44 | static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); | 45 | static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); |
| 45 | static void invalidate_bh_lrus(void); | 46 | static void invalidate_bh_lrus(void); |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3217ac5f6bd7..2335f14a1583 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
| @@ -3215,10 +3215,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | |||
| 3215 | } | 3215 | } |
| 3216 | 3216 | ||
| 3217 | cifs_sb->tcon = NULL; | 3217 | cifs_sb->tcon = NULL; |
| 3218 | if (ses) { | 3218 | if (ses) |
| 3219 | set_current_state(TASK_INTERRUPTIBLE); | 3219 | schedule_timeout_interruptible(msecs_to_jiffies(500)); |
| 3220 | schedule_timeout(HZ / 2); | ||
| 3221 | } | ||
| 3222 | if (ses) | 3220 | if (ses) |
| 3223 | sesInfoFree(ses); | 3221 | sesInfoFree(ses); |
| 3224 | 3222 | ||
diff --git a/fs/compat.c b/fs/compat.c index 8c665705c6a0..ac3fb9ed8eea 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
| @@ -1619,6 +1619,7 @@ compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp | |||
| 1619 | char *bits; | 1619 | char *bits; |
| 1620 | long timeout; | 1620 | long timeout; |
| 1621 | int size, max_fdset, ret = -EINVAL; | 1621 | int size, max_fdset, ret = -EINVAL; |
| 1622 | struct fdtable *fdt; | ||
| 1622 | 1623 | ||
| 1623 | timeout = MAX_SCHEDULE_TIMEOUT; | 1624 | timeout = MAX_SCHEDULE_TIMEOUT; |
| 1624 | if (tvp) { | 1625 | if (tvp) { |
| @@ -1644,7 +1645,10 @@ compat_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp | |||
| 1644 | goto out_nofds; | 1645 | goto out_nofds; |
| 1645 | 1646 | ||
| 1646 | /* max_fdset can increase, so grab it once to avoid race */ | 1647 | /* max_fdset can increase, so grab it once to avoid race */ |
| 1647 | max_fdset = current->files->max_fdset; | 1648 | rcu_read_lock(); |
| 1649 | fdt = files_fdtable(current->files); | ||
| 1650 | max_fdset = fdt->max_fdset; | ||
| 1651 | rcu_read_unlock(); | ||
| 1648 | if (n > max_fdset) | 1652 | if (n > max_fdset) |
| 1649 | n = max_fdset; | 1653 | n = max_fdset; |
| 1650 | 1654 | ||
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 155e612635f1..e28a74203f3b 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c | |||
| @@ -798,13 +798,16 @@ static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) | |||
| 798 | r = (void *) &r4; | 798 | r = (void *) &r4; |
| 799 | } | 799 | } |
| 800 | 800 | ||
| 801 | if (ret) | 801 | if (ret) { |
| 802 | return -EFAULT; | 802 | ret = -EFAULT; |
| 803 | goto out; | ||
| 804 | } | ||
| 803 | 805 | ||
| 804 | set_fs (KERNEL_DS); | 806 | set_fs (KERNEL_DS); |
| 805 | ret = sys_ioctl (fd, cmd, (unsigned long) r); | 807 | ret = sys_ioctl (fd, cmd, (unsigned long) r); |
| 806 | set_fs (old_fs); | 808 | set_fs (old_fs); |
| 807 | 809 | ||
| 810 | out: | ||
| 808 | if (mysock) | 811 | if (mysock) |
| 809 | sockfd_put(mysock); | 812 | sockfd_put(mysock); |
| 810 | 813 | ||
diff --git a/fs/cramfs/uncompress.c b/fs/cramfs/uncompress.c index 5034365b06a8..8def89f2c438 100644 --- a/fs/cramfs/uncompress.c +++ b/fs/cramfs/uncompress.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/errno.h> | 19 | #include <linux/errno.h> |
| 20 | #include <linux/vmalloc.h> | 20 | #include <linux/vmalloc.h> |
| 21 | #include <linux/zlib.h> | 21 | #include <linux/zlib.h> |
| 22 | #include <linux/cramfs_fs.h> | ||
| 22 | 23 | ||
| 23 | static z_stream stream; | 24 | static z_stream stream; |
| 24 | static int initialized; | 25 | static int initialized; |
diff --git a/fs/dcache.c b/fs/dcache.c index a15a2e1f5520..7376b61269fb 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
| @@ -337,12 +337,10 @@ struct dentry * d_find_alias(struct inode *inode) | |||
| 337 | */ | 337 | */ |
| 338 | void d_prune_aliases(struct inode *inode) | 338 | void d_prune_aliases(struct inode *inode) |
| 339 | { | 339 | { |
| 340 | struct list_head *tmp, *head = &inode->i_dentry; | 340 | struct dentry *dentry; |
| 341 | restart: | 341 | restart: |
| 342 | spin_lock(&dcache_lock); | 342 | spin_lock(&dcache_lock); |
| 343 | tmp = head; | 343 | list_for_each_entry(dentry, &inode->i_dentry, d_alias) { |
| 344 | while ((tmp = tmp->next) != head) { | ||
| 345 | struct dentry *dentry = list_entry(tmp, struct dentry, d_alias); | ||
| 346 | spin_lock(&dentry->d_lock); | 344 | spin_lock(&dentry->d_lock); |
| 347 | if (!atomic_read(&dentry->d_count)) { | 345 | if (!atomic_read(&dentry->d_count)) { |
| 348 | __dget_locked(dentry); | 346 | __dget_locked(dentry); |
| @@ -463,10 +461,7 @@ void shrink_dcache_sb(struct super_block * sb) | |||
| 463 | * superblock to the most recent end of the unused list. | 461 | * superblock to the most recent end of the unused list. |
| 464 | */ | 462 | */ |
| 465 | spin_lock(&dcache_lock); | 463 | spin_lock(&dcache_lock); |
| 466 | next = dentry_unused.next; | 464 | list_for_each_safe(tmp, next, &dentry_unused) { |
| 467 | while (next != &dentry_unused) { | ||
| 468 | tmp = next; | ||
| 469 | next = tmp->next; | ||
| 470 | dentry = list_entry(tmp, struct dentry, d_lru); | 465 | dentry = list_entry(tmp, struct dentry, d_lru); |
| 471 | if (dentry->d_sb != sb) | 466 | if (dentry->d_sb != sb) |
| 472 | continue; | 467 | continue; |
| @@ -478,10 +473,7 @@ void shrink_dcache_sb(struct super_block * sb) | |||
| 478 | * Pass two ... free the dentries for this superblock. | 473 | * Pass two ... free the dentries for this superblock. |
| 479 | */ | 474 | */ |
| 480 | repeat: | 475 | repeat: |
| 481 | next = dentry_unused.next; | 476 | list_for_each_safe(tmp, next, &dentry_unused) { |
| 482 | while (next != &dentry_unused) { | ||
| 483 | tmp = next; | ||
| 484 | next = tmp->next; | ||
| 485 | dentry = list_entry(tmp, struct dentry, d_lru); | 477 | dentry = list_entry(tmp, struct dentry, d_lru); |
| 486 | if (dentry->d_sb != sb) | 478 | if (dentry->d_sb != sb) |
| 487 | continue; | 479 | continue; |
| @@ -798,6 +798,7 @@ no_thread_group: | |||
| 798 | static inline void flush_old_files(struct files_struct * files) | 798 | static inline void flush_old_files(struct files_struct * files) |
| 799 | { | 799 | { |
| 800 | long j = -1; | 800 | long j = -1; |
| 801 | struct fdtable *fdt; | ||
| 801 | 802 | ||
| 802 | spin_lock(&files->file_lock); | 803 | spin_lock(&files->file_lock); |
| 803 | for (;;) { | 804 | for (;;) { |
| @@ -805,12 +806,13 @@ static inline void flush_old_files(struct files_struct * files) | |||
| 805 | 806 | ||
| 806 | j++; | 807 | j++; |
| 807 | i = j * __NFDBITS; | 808 | i = j * __NFDBITS; |
| 808 | if (i >= files->max_fds || i >= files->max_fdset) | 809 | fdt = files_fdtable(files); |
| 810 | if (i >= fdt->max_fds || i >= fdt->max_fdset) | ||
| 809 | break; | 811 | break; |
| 810 | set = files->close_on_exec->fds_bits[j]; | 812 | set = fdt->close_on_exec->fds_bits[j]; |
| 811 | if (!set) | 813 | if (!set) |
| 812 | continue; | 814 | continue; |
| 813 | files->close_on_exec->fds_bits[j] = 0; | 815 | fdt->close_on_exec->fds_bits[j] = 0; |
| 814 | spin_unlock(&files->file_lock); | 816 | spin_unlock(&files->file_lock); |
| 815 | for ( ; set ; i++,set >>= 1) { | 817 | for ( ; set ; i++,set >>= 1) { |
| 816 | if (set & 1) { | 818 | if (set & 1) { |
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 161f156d98c8..c8d07030c897 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c | |||
| @@ -615,6 +615,11 @@ got: | |||
| 615 | DQUOT_DROP(inode); | 615 | DQUOT_DROP(inode); |
| 616 | goto fail2; | 616 | goto fail2; |
| 617 | } | 617 | } |
| 618 | err = ext2_init_security(inode,dir); | ||
| 619 | if (err) { | ||
| 620 | DQUOT_FREE_INODE(inode); | ||
| 621 | goto fail2; | ||
| 622 | } | ||
| 618 | mark_inode_dirty(inode); | 623 | mark_inode_dirty(inode); |
| 619 | ext2_debug("allocating inode %lu\n", inode->i_ino); | 624 | ext2_debug("allocating inode %lu\n", inode->i_ino); |
| 620 | ext2_preread_inode(inode); | 625 | ext2_preread_inode(inode); |
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 53dceb0c6593..fdba4d1d3c60 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c | |||
| @@ -71,6 +71,8 @@ void ext2_put_inode(struct inode *inode) | |||
| 71 | */ | 71 | */ |
| 72 | void ext2_delete_inode (struct inode * inode) | 72 | void ext2_delete_inode (struct inode * inode) |
| 73 | { | 73 | { |
| 74 | truncate_inode_pages(&inode->i_data, 0); | ||
| 75 | |||
| 74 | if (is_bad_inode(inode)) | 76 | if (is_bad_inode(inode)) |
| 75 | goto no_delete; | 77 | goto no_delete; |
| 76 | EXT2_I(inode)->i_dtime = get_seconds(); | 78 | EXT2_I(inode)->i_dtime = get_seconds(); |
diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index 5f3bfde3b810..67cfeb66e897 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h | |||
| @@ -116,3 +116,11 @@ exit_ext2_xattr(void) | |||
| 116 | 116 | ||
| 117 | # endif /* CONFIG_EXT2_FS_XATTR */ | 117 | # endif /* CONFIG_EXT2_FS_XATTR */ |
| 118 | 118 | ||
| 119 | #ifdef CONFIG_EXT2_FS_SECURITY | ||
| 120 | extern int ext2_init_security(struct inode *inode, struct inode *dir); | ||
| 121 | #else | ||
| 122 | static inline int ext2_init_security(struct inode *inode, struct inode *dir) | ||
| 123 | { | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | #endif | ||
diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c index 6a6c59fbe599..a26612798471 100644 --- a/fs/ext2/xattr_security.c +++ b/fs/ext2/xattr_security.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <linux/fs.h> | 8 | #include <linux/fs.h> |
| 9 | #include <linux/smp_lock.h> | 9 | #include <linux/smp_lock.h> |
| 10 | #include <linux/ext2_fs.h> | 10 | #include <linux/ext2_fs.h> |
| 11 | #include <linux/security.h> | ||
| 11 | #include "xattr.h" | 12 | #include "xattr.h" |
| 12 | 13 | ||
| 13 | static size_t | 14 | static size_t |
| @@ -45,6 +46,27 @@ ext2_xattr_security_set(struct inode *inode, const char *name, | |||
| 45 | value, size, flags); | 46 | value, size, flags); |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 49 | int | ||
| 50 | ext2_init_security(struct inode *inode, struct inode *dir) | ||
| 51 | { | ||
| 52 | int err; | ||
| 53 | size_t len; | ||
| 54 | void *value; | ||
| 55 | char *name; | ||
| 56 | |||
| 57 | err = security_inode_init_security(inode, dir, &name, &value, &len); | ||
| 58 | if (err) { | ||
| 59 | if (err == -EOPNOTSUPP) | ||
| 60 | return 0; | ||
| 61 | return err; | ||
| 62 | } | ||
| 63 | err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, | ||
| 64 | name, value, len, 0); | ||
| 65 | kfree(name); | ||
| 66 | kfree(value); | ||
| 67 | return err; | ||
| 68 | } | ||
| 69 | |||
| 48 | struct xattr_handler ext2_xattr_security_handler = { | 70 | struct xattr_handler ext2_xattr_security_handler = { |
| 49 | .prefix = XATTR_SECURITY_PREFIX, | 71 | .prefix = XATTR_SECURITY_PREFIX, |
| 50 | .list = ext2_xattr_security_list, | 72 | .list = ext2_xattr_security_list, |
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 6981bd014ede..96552769d039 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c | |||
| @@ -607,6 +607,11 @@ got: | |||
| 607 | DQUOT_DROP(inode); | 607 | DQUOT_DROP(inode); |
| 608 | goto fail2; | 608 | goto fail2; |
| 609 | } | 609 | } |
| 610 | err = ext3_init_security(handle,inode, dir); | ||
| 611 | if (err) { | ||
| 612 | DQUOT_FREE_INODE(inode); | ||
| 613 | goto fail2; | ||
| 614 | } | ||
| 610 | err = ext3_mark_inode_dirty(handle, inode); | 615 | err = ext3_mark_inode_dirty(handle, inode); |
| 611 | if (err) { | 616 | if (err) { |
| 612 | ext3_std_error(sb, err); | 617 | ext3_std_error(sb, err); |
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 9989fdcf4d5a..b5177c90d6f1 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
| @@ -187,6 +187,8 @@ void ext3_delete_inode (struct inode * inode) | |||
| 187 | { | 187 | { |
| 188 | handle_t *handle; | 188 | handle_t *handle; |
| 189 | 189 | ||
| 190 | truncate_inode_pages(&inode->i_data, 0); | ||
| 191 | |||
| 190 | if (is_bad_inode(inode)) | 192 | if (is_bad_inode(inode)) |
| 191 | goto no_delete; | 193 | goto no_delete; |
| 192 | 194 | ||
diff --git a/fs/ext3/xattr.h b/fs/ext3/xattr.h index eb31a69e82dc..2ceae38f3d49 100644 --- a/fs/ext3/xattr.h +++ b/fs/ext3/xattr.h | |||
| @@ -133,3 +133,14 @@ exit_ext3_xattr(void) | |||
| 133 | #define ext3_xattr_handlers NULL | 133 | #define ext3_xattr_handlers NULL |
| 134 | 134 | ||
| 135 | # endif /* CONFIG_EXT3_FS_XATTR */ | 135 | # endif /* CONFIG_EXT3_FS_XATTR */ |
| 136 | |||
| 137 | #ifdef CONFIG_EXT3_FS_SECURITY | ||
| 138 | extern int ext3_init_security(handle_t *handle, struct inode *inode, | ||
| 139 | struct inode *dir); | ||
| 140 | #else | ||
| 141 | static inline int ext3_init_security(handle_t *handle, struct inode *inode, | ||
| 142 | struct inode *dir) | ||
| 143 | { | ||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | #endif | ||
diff --git a/fs/ext3/xattr_security.c b/fs/ext3/xattr_security.c index ddc1c41750e1..b9c40c15647b 100644 --- a/fs/ext3/xattr_security.c +++ b/fs/ext3/xattr_security.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/smp_lock.h> | 9 | #include <linux/smp_lock.h> |
| 10 | #include <linux/ext3_jbd.h> | 10 | #include <linux/ext3_jbd.h> |
| 11 | #include <linux/ext3_fs.h> | 11 | #include <linux/ext3_fs.h> |
| 12 | #include <linux/security.h> | ||
| 12 | #include "xattr.h" | 13 | #include "xattr.h" |
| 13 | 14 | ||
| 14 | static size_t | 15 | static size_t |
| @@ -47,6 +48,27 @@ ext3_xattr_security_set(struct inode *inode, const char *name, | |||
| 47 | value, size, flags); | 48 | value, size, flags); |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 51 | int | ||
| 52 | ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir) | ||
| 53 | { | ||
| 54 | int err; | ||
| 55 | size_t len; | ||
| 56 | void *value; | ||
| 57 | char *name; | ||
| 58 | |||
| 59 | err = security_inode_init_security(inode, dir, &name, &value, &len); | ||
| 60 | if (err) { | ||
| 61 | if (err == -EOPNOTSUPP) | ||
| 62 | return 0; | ||
| 63 | return err; | ||
| 64 | } | ||
| 65 | err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY, | ||
| 66 | name, value, len, 0); | ||
| 67 | kfree(name); | ||
| 68 | kfree(value); | ||
| 69 | return err; | ||
| 70 | } | ||
| 71 | |||
| 50 | struct xattr_handler ext3_xattr_security_handler = { | 72 | struct xattr_handler ext3_xattr_security_handler = { |
| 51 | .prefix = XATTR_SECURITY_PREFIX, | 73 | .prefix = XATTR_SECURITY_PREFIX, |
| 52 | .list = ext3_xattr_security_list, | 74 | .list = ext3_xattr_security_list, |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 96ae85b67eba..a7cbe68e2259 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
| @@ -335,6 +335,8 @@ EXPORT_SYMBOL(fat_build_inode); | |||
| 335 | 335 | ||
| 336 | static void fat_delete_inode(struct inode *inode) | 336 | static void fat_delete_inode(struct inode *inode) |
| 337 | { | 337 | { |
| 338 | truncate_inode_pages(&inode->i_data, 0); | ||
| 339 | |||
| 338 | if (!is_bad_inode(inode)) { | 340 | if (!is_bad_inode(inode)) { |
| 339 | inode->i_size = 0; | 341 | inode->i_size = 0; |
| 340 | fat_truncate(inode); | 342 | fat_truncate(inode); |
diff --git a/fs/fcntl.c b/fs/fcntl.c index 6fbc9d8fcc36..863b46e0d78a 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/security.h> | 16 | #include <linux/security.h> |
| 17 | #include <linux/ptrace.h> | 17 | #include <linux/ptrace.h> |
| 18 | #include <linux/signal.h> | 18 | #include <linux/signal.h> |
| 19 | #include <linux/rcupdate.h> | ||
| 19 | 20 | ||
| 20 | #include <asm/poll.h> | 21 | #include <asm/poll.h> |
| 21 | #include <asm/siginfo.h> | 22 | #include <asm/siginfo.h> |
| @@ -24,21 +25,25 @@ | |||
| 24 | void fastcall set_close_on_exec(unsigned int fd, int flag) | 25 | void fastcall set_close_on_exec(unsigned int fd, int flag) |
| 25 | { | 26 | { |
| 26 | struct files_struct *files = current->files; | 27 | struct files_struct *files = current->files; |
| 28 | struct fdtable *fdt; | ||
| 27 | spin_lock(&files->file_lock); | 29 | spin_lock(&files->file_lock); |
| 30 | fdt = files_fdtable(files); | ||
| 28 | if (flag) | 31 | if (flag) |
| 29 | FD_SET(fd, files->close_on_exec); | 32 | FD_SET(fd, fdt->close_on_exec); |
| 30 | else | 33 | else |
| 31 | FD_CLR(fd, files->close_on_exec); | 34 | FD_CLR(fd, fdt->close_on_exec); |
| 32 | spin_unlock(&files->file_lock); | 35 | spin_unlock(&files->file_lock); |
| 33 | } | 36 | } |
| 34 | 37 | ||
| 35 | static inline int get_close_on_exec(unsigned int fd) | 38 | static inline int get_close_on_exec(unsigned int fd) |
| 36 | { | 39 | { |
| 37 | struct files_struct *files = current->files; | 40 | struct files_struct *files = current->files; |
| 41 | struct fdtable *fdt; | ||
| 38 | int res; | 42 | int res; |
| 39 | spin_lock(&files->file_lock); | 43 | rcu_read_lock(); |
| 40 | res = FD_ISSET(fd, files->close_on_exec); | 44 | fdt = files_fdtable(files); |
| 41 | spin_unlock(&files->file_lock); | 45 | res = FD_ISSET(fd, fdt->close_on_exec); |
| 46 | rcu_read_unlock(); | ||
| 42 | return res; | 47 | return res; |
| 43 | } | 48 | } |
| 44 | 49 | ||
| @@ -54,24 +59,26 @@ static int locate_fd(struct files_struct *files, | |||
| 54 | unsigned int newfd; | 59 | unsigned int newfd; |
| 55 | unsigned int start; | 60 | unsigned int start; |
| 56 | int error; | 61 | int error; |
| 62 | struct fdtable *fdt; | ||
| 57 | 63 | ||
| 58 | error = -EINVAL; | 64 | error = -EINVAL; |
| 59 | if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) | 65 | if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur) |
| 60 | goto out; | 66 | goto out; |
| 61 | 67 | ||
| 62 | repeat: | 68 | repeat: |
| 69 | fdt = files_fdtable(files); | ||
| 63 | /* | 70 | /* |
| 64 | * Someone might have closed fd's in the range | 71 | * Someone might have closed fd's in the range |
| 65 | * orig_start..files->next_fd | 72 | * orig_start..fdt->next_fd |
| 66 | */ | 73 | */ |
| 67 | start = orig_start; | 74 | start = orig_start; |
| 68 | if (start < files->next_fd) | 75 | if (start < fdt->next_fd) |
| 69 | start = files->next_fd; | 76 | start = fdt->next_fd; |
| 70 | 77 | ||
| 71 | newfd = start; | 78 | newfd = start; |
| 72 | if (start < files->max_fdset) { | 79 | if (start < fdt->max_fdset) { |
| 73 | newfd = find_next_zero_bit(files->open_fds->fds_bits, | 80 | newfd = find_next_zero_bit(fdt->open_fds->fds_bits, |
| 74 | files->max_fdset, start); | 81 | fdt->max_fdset, start); |
| 75 | } | 82 | } |
| 76 | 83 | ||
| 77 | error = -EMFILE; | 84 | error = -EMFILE; |
| @@ -89,9 +96,15 @@ repeat: | |||
| 89 | if (error) | 96 | if (error) |
| 90 | goto repeat; | 97 | goto repeat; |
| 91 | 98 | ||
| 92 | if (start <= files->next_fd) | 99 | /* |
| 93 | files->next_fd = newfd + 1; | 100 | * We reacquired files_lock, so we are safe as long as |
| 94 | 101 | * we reacquire the fdtable pointer and use it while holding | |
| 102 | * the lock, no one can free it during that time. | ||
| 103 | */ | ||
| 104 | fdt = files_fdtable(files); | ||
| 105 | if (start <= fdt->next_fd) | ||
| 106 | fdt->next_fd = newfd + 1; | ||
| 107 | |||
| 95 | error = newfd; | 108 | error = newfd; |
| 96 | 109 | ||
| 97 | out: | 110 | out: |
| @@ -101,13 +114,16 @@ out: | |||
| 101 | static int dupfd(struct file *file, unsigned int start) | 114 | static int dupfd(struct file *file, unsigned int start) |
| 102 | { | 115 | { |
| 103 | struct files_struct * files = current->files; | 116 | struct files_struct * files = current->files; |
| 117 | struct fdtable *fdt; | ||
| 104 | int fd; | 118 | int fd; |
| 105 | 119 | ||
| 106 | spin_lock(&files->file_lock); | 120 | spin_lock(&files->file_lock); |
| 107 | fd = locate_fd(files, file, start); | 121 | fd = locate_fd(files, file, start); |
| 108 | if (fd >= 0) { | 122 | if (fd >= 0) { |
| 109 | FD_SET(fd, files->open_fds); | 123 | /* locate_fd() may have expanded fdtable, load the ptr */ |
| 110 | FD_CLR(fd, files->close_on_exec); | 124 | fdt = files_fdtable(files); |
| 125 | FD_SET(fd, fdt->open_fds); | ||
| 126 | FD_CLR(fd, fdt->close_on_exec); | ||
| 111 | spin_unlock(&files->file_lock); | 127 | spin_unlock(&files->file_lock); |
| 112 | fd_install(fd, file); | 128 | fd_install(fd, file); |
| 113 | } else { | 129 | } else { |
| @@ -123,6 +139,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) | |||
| 123 | int err = -EBADF; | 139 | int err = -EBADF; |
| 124 | struct file * file, *tofree; | 140 | struct file * file, *tofree; |
| 125 | struct files_struct * files = current->files; | 141 | struct files_struct * files = current->files; |
| 142 | struct fdtable *fdt; | ||
| 126 | 143 | ||
| 127 | spin_lock(&files->file_lock); | 144 | spin_lock(&files->file_lock); |
| 128 | if (!(file = fcheck(oldfd))) | 145 | if (!(file = fcheck(oldfd))) |
| @@ -148,13 +165,14 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) | |||
| 148 | 165 | ||
| 149 | /* Yes. It's a race. In user space. Nothing sane to do */ | 166 | /* Yes. It's a race. In user space. Nothing sane to do */ |
| 150 | err = -EBUSY; | 167 | err = -EBUSY; |
| 151 | tofree = files->fd[newfd]; | 168 | fdt = files_fdtable(files); |
| 152 | if (!tofree && FD_ISSET(newfd, files->open_fds)) | 169 | tofree = fdt->fd[newfd]; |
| 170 | if (!tofree && FD_ISSET(newfd, fdt->open_fds)) | ||
| 153 | goto out_fput; | 171 | goto out_fput; |
| 154 | 172 | ||
| 155 | files->fd[newfd] = file; | 173 | rcu_assign_pointer(fdt->fd[newfd], file); |
| 156 | FD_SET(newfd, files->open_fds); | 174 | FD_SET(newfd, fdt->open_fds); |
| 157 | FD_CLR(newfd, files->close_on_exec); | 175 | FD_CLR(newfd, fdt->close_on_exec); |
| 158 | spin_unlock(&files->file_lock); | 176 | spin_unlock(&files->file_lock); |
| 159 | 177 | ||
| 160 | if (tofree) | 178 | if (tofree) |
| @@ -13,6 +13,25 @@ | |||
| 13 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
| 14 | #include <linux/file.h> | 14 | #include <linux/file.h> |
| 15 | #include <linux/bitops.h> | 15 | #include <linux/bitops.h> |
| 16 | #include <linux/interrupt.h> | ||
| 17 | #include <linux/spinlock.h> | ||
| 18 | #include <linux/rcupdate.h> | ||
| 19 | #include <linux/workqueue.h> | ||
| 20 | |||
| 21 | struct fdtable_defer { | ||
| 22 | spinlock_t lock; | ||
| 23 | struct work_struct wq; | ||
| 24 | struct timer_list timer; | ||
| 25 | struct fdtable *next; | ||
| 26 | }; | ||
| 27 | |||
| 28 | /* | ||
| 29 | * We use this list to defer free fdtables that have vmalloced | ||
| 30 | * sets/arrays. By keeping a per-cpu list, we avoid having to embed | ||
| 31 | * the work_struct in fdtable itself which avoids a 64 byte (i386) increase in | ||
| 32 | * this per-task structure. | ||
| 33 | */ | ||
| 34 | static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list); | ||
| 16 | 35 | ||
| 17 | 36 | ||
| 18 | /* | 37 | /* |
| @@ -48,82 +67,143 @@ void free_fd_array(struct file **array, int num) | |||
| 48 | vfree(array); | 67 | vfree(array); |
| 49 | } | 68 | } |
| 50 | 69 | ||
| 51 | /* | 70 | static void __free_fdtable(struct fdtable *fdt) |
| 52 | * Expand the fd array in the files_struct. Called with the files | 71 | { |
| 53 | * spinlock held for write. | 72 | int fdset_size, fdarray_size; |
| 54 | */ | ||
| 55 | 73 | ||
| 56 | static int expand_fd_array(struct files_struct *files, int nr) | 74 | fdset_size = fdt->max_fdset / 8; |
| 57 | __releases(files->file_lock) | 75 | fdarray_size = fdt->max_fds * sizeof(struct file *); |
| 58 | __acquires(files->file_lock) | 76 | free_fdset(fdt->open_fds, fdset_size); |
| 77 | free_fdset(fdt->close_on_exec, fdset_size); | ||
| 78 | free_fd_array(fdt->fd, fdarray_size); | ||
| 79 | kfree(fdt); | ||
| 80 | } | ||
| 81 | |||
| 82 | static void fdtable_timer(unsigned long data) | ||
| 59 | { | 83 | { |
| 60 | struct file **new_fds; | 84 | struct fdtable_defer *fddef = (struct fdtable_defer *)data; |
| 61 | int error, nfds; | ||
| 62 | 85 | ||
| 63 | 86 | spin_lock(&fddef->lock); | |
| 64 | error = -EMFILE; | 87 | /* |
| 65 | if (files->max_fds >= NR_OPEN || nr >= NR_OPEN) | 88 | * If someone already emptied the queue return. |
| 89 | */ | ||
| 90 | if (!fddef->next) | ||
| 66 | goto out; | 91 | goto out; |
| 92 | if (!schedule_work(&fddef->wq)) | ||
| 93 | mod_timer(&fddef->timer, 5); | ||
| 94 | out: | ||
| 95 | spin_unlock(&fddef->lock); | ||
| 96 | } | ||
| 67 | 97 | ||
| 68 | nfds = files->max_fds; | 98 | static void free_fdtable_work(struct fdtable_defer *f) |
| 69 | spin_unlock(&files->file_lock); | 99 | { |
| 100 | struct fdtable *fdt; | ||
| 70 | 101 | ||
| 71 | /* | 102 | spin_lock_bh(&f->lock); |
| 72 | * Expand to the max in easy steps, and keep expanding it until | 103 | fdt = f->next; |
| 73 | * we have enough for the requested fd array size. | 104 | f->next = NULL; |
| 74 | */ | 105 | spin_unlock_bh(&f->lock); |
| 106 | while(fdt) { | ||
| 107 | struct fdtable *next = fdt->next; | ||
| 108 | __free_fdtable(fdt); | ||
| 109 | fdt = next; | ||
| 110 | } | ||
| 111 | } | ||
| 75 | 112 | ||
| 76 | do { | 113 | static void free_fdtable_rcu(struct rcu_head *rcu) |
| 77 | #if NR_OPEN_DEFAULT < 256 | 114 | { |
| 78 | if (nfds < 256) | 115 | struct fdtable *fdt = container_of(rcu, struct fdtable, rcu); |
| 79 | nfds = 256; | 116 | int fdset_size, fdarray_size; |
| 80 | else | 117 | struct fdtable_defer *fddef; |
| 81 | #endif | ||
| 82 | if (nfds < (PAGE_SIZE / sizeof(struct file *))) | ||
| 83 | nfds = PAGE_SIZE / sizeof(struct file *); | ||
| 84 | else { | ||
| 85 | nfds = nfds * 2; | ||
| 86 | if (nfds > NR_OPEN) | ||
| 87 | nfds = NR_OPEN; | ||
| 88 | } | ||
| 89 | } while (nfds <= nr); | ||
| 90 | 118 | ||
| 91 | error = -ENOMEM; | 119 | BUG_ON(!fdt); |
| 92 | new_fds = alloc_fd_array(nfds); | 120 | fdset_size = fdt->max_fdset / 8; |
| 93 | spin_lock(&files->file_lock); | 121 | fdarray_size = fdt->max_fds * sizeof(struct file *); |
| 94 | if (!new_fds) | ||
| 95 | goto out; | ||
| 96 | 122 | ||
| 97 | /* Copy the existing array and install the new pointer */ | 123 | if (fdt->free_files) { |
| 98 | 124 | /* | |
| 99 | if (nfds > files->max_fds) { | 125 | * The this fdtable was embedded in the files structure |
| 100 | struct file **old_fds; | 126 | * and the files structure itself was getting destroyed. |
| 101 | int i; | 127 | * It is now safe to free the files structure. |
| 102 | 128 | */ | |
| 103 | old_fds = xchg(&files->fd, new_fds); | 129 | kmem_cache_free(files_cachep, fdt->free_files); |
| 104 | i = xchg(&files->max_fds, nfds); | 130 | return; |
| 105 | 131 | } | |
| 106 | /* Don't copy/clear the array if we are creating a new | 132 | if (fdt->max_fdset <= __FD_SETSIZE && fdt->max_fds <= NR_OPEN_DEFAULT) { |
| 107 | fd array for fork() */ | 133 | /* |
| 108 | if (i) { | 134 | * The fdtable was embedded |
| 109 | memcpy(new_fds, old_fds, i * sizeof(struct file *)); | 135 | */ |
| 110 | /* clear the remainder of the array */ | 136 | return; |
| 111 | memset(&new_fds[i], 0, | 137 | } |
| 112 | (nfds-i) * sizeof(struct file *)); | 138 | if (fdset_size <= PAGE_SIZE && fdarray_size <= PAGE_SIZE) { |
| 113 | 139 | kfree(fdt->open_fds); | |
| 114 | spin_unlock(&files->file_lock); | 140 | kfree(fdt->close_on_exec); |
| 115 | free_fd_array(old_fds, i); | 141 | kfree(fdt->fd); |
| 116 | spin_lock(&files->file_lock); | 142 | kfree(fdt); |
| 117 | } | ||
| 118 | } else { | 143 | } else { |
| 119 | /* Somebody expanded the array while we slept ... */ | 144 | fddef = &get_cpu_var(fdtable_defer_list); |
| 120 | spin_unlock(&files->file_lock); | 145 | spin_lock(&fddef->lock); |
| 121 | free_fd_array(new_fds, nfds); | 146 | fdt->next = fddef->next; |
| 122 | spin_lock(&files->file_lock); | 147 | fddef->next = fdt; |
| 148 | /* | ||
| 149 | * vmallocs are handled from the workqueue context. | ||
| 150 | * If the per-cpu workqueue is running, then we | ||
| 151 | * defer work scheduling through a timer. | ||
| 152 | */ | ||
| 153 | if (!schedule_work(&fddef->wq)) | ||
| 154 | mod_timer(&fddef->timer, 5); | ||
| 155 | spin_unlock(&fddef->lock); | ||
| 156 | put_cpu_var(fdtable_defer_list); | ||
| 123 | } | 157 | } |
| 124 | error = 0; | 158 | } |
| 125 | out: | 159 | |
| 126 | return error; | 160 | void free_fdtable(struct fdtable *fdt) |
| 161 | { | ||
| 162 | if (fdt->free_files || fdt->max_fdset > __FD_SETSIZE || | ||
| 163 | fdt->max_fds > NR_OPEN_DEFAULT) | ||
| 164 | call_rcu(&fdt->rcu, free_fdtable_rcu); | ||
| 165 | } | ||
| 166 | |||
| 167 | /* | ||
| 168 | * Expand the fdset in the files_struct. Called with the files spinlock | ||
| 169 | * held for write. | ||
| 170 | */ | ||
| 171 | static void copy_fdtable(struct fdtable *nfdt, struct fdtable *fdt) | ||
| 172 | { | ||
| 173 | int i; | ||
| 174 | int count; | ||
| 175 | |||
| 176 | BUG_ON(nfdt->max_fdset < fdt->max_fdset); | ||
| 177 | BUG_ON(nfdt->max_fds < fdt->max_fds); | ||
| 178 | /* Copy the existing tables and install the new pointers */ | ||
| 179 | |||
| 180 | i = fdt->max_fdset / (sizeof(unsigned long) * 8); | ||
| 181 | count = (nfdt->max_fdset - fdt->max_fdset) / 8; | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Don't copy the entire array if the current fdset is | ||
| 185 | * not yet initialised. | ||
| 186 | */ | ||
| 187 | if (i) { | ||
| 188 | memcpy (nfdt->open_fds, fdt->open_fds, | ||
| 189 | fdt->max_fdset/8); | ||
| 190 | memcpy (nfdt->close_on_exec, fdt->close_on_exec, | ||
| 191 | fdt->max_fdset/8); | ||
| 192 | memset (&nfdt->open_fds->fds_bits[i], 0, count); | ||
| 193 | memset (&nfdt->close_on_exec->fds_bits[i], 0, count); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Don't copy/clear the array if we are creating a new | ||
| 197 | fd array for fork() */ | ||
| 198 | if (fdt->max_fds) { | ||
| 199 | memcpy(nfdt->fd, fdt->fd, | ||
| 200 | fdt->max_fds * sizeof(struct file *)); | ||
| 201 | /* clear the remainder of the array */ | ||
| 202 | memset(&nfdt->fd[fdt->max_fds], 0, | ||
| 203 | (nfdt->max_fds - fdt->max_fds) * | ||
| 204 | sizeof(struct file *)); | ||
| 205 | } | ||
| 206 | nfdt->next_fd = fdt->next_fd; | ||
| 127 | } | 207 | } |
| 128 | 208 | ||
| 129 | /* | 209 | /* |
| @@ -154,26 +234,21 @@ void free_fdset(fd_set *array, int num) | |||
| 154 | vfree(array); | 234 | vfree(array); |
| 155 | } | 235 | } |
| 156 | 236 | ||
| 157 | /* | 237 | static struct fdtable *alloc_fdtable(int nr) |
| 158 | * Expand the fdset in the files_struct. Called with the files spinlock | ||
| 159 | * held for write. | ||
| 160 | */ | ||
| 161 | static int expand_fdset(struct files_struct *files, int nr) | ||
| 162 | __releases(file->file_lock) | ||
| 163 | __acquires(file->file_lock) | ||
| 164 | { | 238 | { |
| 165 | fd_set *new_openset = NULL, *new_execset = NULL; | 239 | struct fdtable *fdt = NULL; |
| 166 | int error, nfds = 0; | 240 | int nfds = 0; |
| 167 | 241 | fd_set *new_openset = NULL, *new_execset = NULL; | |
| 168 | error = -EMFILE; | 242 | struct file **new_fds; |
| 169 | if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN) | ||
| 170 | goto out; | ||
| 171 | 243 | ||
| 172 | nfds = files->max_fdset; | 244 | fdt = kmalloc(sizeof(*fdt), GFP_KERNEL); |
| 173 | spin_unlock(&files->file_lock); | 245 | if (!fdt) |
| 246 | goto out; | ||
| 247 | memset(fdt, 0, sizeof(*fdt)); | ||
| 174 | 248 | ||
| 175 | /* Expand to the max in easy steps */ | 249 | nfds = __FD_SETSIZE; |
| 176 | do { | 250 | /* Expand to the max in easy steps */ |
| 251 | do { | ||
| 177 | if (nfds < (PAGE_SIZE * 8)) | 252 | if (nfds < (PAGE_SIZE * 8)) |
| 178 | nfds = PAGE_SIZE * 8; | 253 | nfds = PAGE_SIZE * 8; |
| 179 | else { | 254 | else { |
| @@ -183,49 +258,88 @@ static int expand_fdset(struct files_struct *files, int nr) | |||
| 183 | } | 258 | } |
| 184 | } while (nfds <= nr); | 259 | } while (nfds <= nr); |
| 185 | 260 | ||
| 186 | error = -ENOMEM; | 261 | new_openset = alloc_fdset(nfds); |
| 187 | new_openset = alloc_fdset(nfds); | 262 | new_execset = alloc_fdset(nfds); |
| 188 | new_execset = alloc_fdset(nfds); | 263 | if (!new_openset || !new_execset) |
| 189 | spin_lock(&files->file_lock); | 264 | goto out; |
| 190 | if (!new_openset || !new_execset) | 265 | fdt->open_fds = new_openset; |
| 266 | fdt->close_on_exec = new_execset; | ||
| 267 | fdt->max_fdset = nfds; | ||
| 268 | |||
| 269 | nfds = NR_OPEN_DEFAULT; | ||
| 270 | /* | ||
| 271 | * Expand to the max in easy steps, and keep expanding it until | ||
| 272 | * we have enough for the requested fd array size. | ||
| 273 | */ | ||
| 274 | do { | ||
| 275 | #if NR_OPEN_DEFAULT < 256 | ||
| 276 | if (nfds < 256) | ||
| 277 | nfds = 256; | ||
| 278 | else | ||
| 279 | #endif | ||
| 280 | if (nfds < (PAGE_SIZE / sizeof(struct file *))) | ||
| 281 | nfds = PAGE_SIZE / sizeof(struct file *); | ||
| 282 | else { | ||
| 283 | nfds = nfds * 2; | ||
| 284 | if (nfds > NR_OPEN) | ||
| 285 | nfds = NR_OPEN; | ||
| 286 | } | ||
| 287 | } while (nfds <= nr); | ||
| 288 | new_fds = alloc_fd_array(nfds); | ||
| 289 | if (!new_fds) | ||
| 290 | goto out; | ||
| 291 | fdt->fd = new_fds; | ||
| 292 | fdt->max_fds = nfds; | ||
| 293 | fdt->free_files = NULL; | ||
| 294 | return fdt; | ||
| 295 | out: | ||
| 296 | if (new_openset) | ||
| 297 | free_fdset(new_openset, nfds); | ||
| 298 | if (new_execset) | ||
| 299 | free_fdset(new_execset, nfds); | ||
| 300 | kfree(fdt); | ||
| 301 | return NULL; | ||
| 302 | } | ||
| 303 | |||
| 304 | /* | ||
| 305 | * Expands the file descriptor table - it will allocate a new fdtable and | ||
| 306 | * both fd array and fdset. It is expected to be called with the | ||
| 307 | * files_lock held. | ||
| 308 | */ | ||
| 309 | static int expand_fdtable(struct files_struct *files, int nr) | ||
| 310 | __releases(files->file_lock) | ||
| 311 | __acquires(files->file_lock) | ||
| 312 | { | ||
| 313 | int error = 0; | ||
| 314 | struct fdtable *fdt; | ||
| 315 | struct fdtable *nfdt = NULL; | ||
| 316 | |||
| 317 | spin_unlock(&files->file_lock); | ||
| 318 | nfdt = alloc_fdtable(nr); | ||
| 319 | if (!nfdt) { | ||
| 320 | error = -ENOMEM; | ||
| 321 | spin_lock(&files->file_lock); | ||
| 191 | goto out; | 322 | goto out; |
| 323 | } | ||
| 192 | 324 | ||
| 193 | error = 0; | 325 | spin_lock(&files->file_lock); |
| 194 | 326 | fdt = files_fdtable(files); | |
| 195 | /* Copy the existing tables and install the new pointers */ | 327 | /* |
| 196 | if (nfds > files->max_fdset) { | 328 | * Check again since another task may have expanded the |
| 197 | int i = files->max_fdset / (sizeof(unsigned long) * 8); | 329 | * fd table while we dropped the lock |
| 198 | int count = (nfds - files->max_fdset) / 8; | 330 | */ |
| 199 | 331 | if (nr >= fdt->max_fds || nr >= fdt->max_fdset) { | |
| 200 | /* | 332 | copy_fdtable(nfdt, fdt); |
| 201 | * Don't copy the entire array if the current fdset is | 333 | } else { |
| 202 | * not yet initialised. | 334 | /* Somebody expanded while we dropped file_lock */ |
| 203 | */ | ||
| 204 | if (i) { | ||
| 205 | memcpy (new_openset, files->open_fds, files->max_fdset/8); | ||
| 206 | memcpy (new_execset, files->close_on_exec, files->max_fdset/8); | ||
| 207 | memset (&new_openset->fds_bits[i], 0, count); | ||
| 208 | memset (&new_execset->fds_bits[i], 0, count); | ||
| 209 | } | ||
| 210 | |||
| 211 | nfds = xchg(&files->max_fdset, nfds); | ||
| 212 | new_openset = xchg(&files->open_fds, new_openset); | ||
| 213 | new_execset = xchg(&files->close_on_exec, new_execset); | ||
| 214 | spin_unlock(&files->file_lock); | 335 | spin_unlock(&files->file_lock); |
| 215 | free_fdset (new_openset, nfds); | 336 | __free_fdtable(nfdt); |
| 216 | free_fdset (new_execset, nfds); | ||
| 217 | spin_lock(&files->file_lock); | 337 | spin_lock(&files->file_lock); |
| 218 | return 0; | 338 | goto out; |
| 219 | } | 339 | } |
| 220 | /* Somebody expanded the array while we slept ... */ | 340 | rcu_assign_pointer(files->fdt, nfdt); |
| 221 | 341 | free_fdtable(fdt); | |
| 222 | out: | 342 | out: |
| 223 | spin_unlock(&files->file_lock); | ||
| 224 | if (new_openset) | ||
| 225 | free_fdset(new_openset, nfds); | ||
| 226 | if (new_execset) | ||
| 227 | free_fdset(new_execset, nfds); | ||
| 228 | spin_lock(&files->file_lock); | ||
| 229 | return error; | 343 | return error; |
| 230 | } | 344 | } |
| 231 | 345 | ||
| @@ -237,18 +351,39 @@ out: | |||
| 237 | int expand_files(struct files_struct *files, int nr) | 351 | int expand_files(struct files_struct *files, int nr) |
| 238 | { | 352 | { |
| 239 | int err, expand = 0; | 353 | int err, expand = 0; |
| 354 | struct fdtable *fdt; | ||
| 240 | 355 | ||
| 241 | if (nr >= files->max_fdset) { | 356 | fdt = files_fdtable(files); |
| 242 | expand = 1; | 357 | if (nr >= fdt->max_fdset || nr >= fdt->max_fds) { |
| 243 | if ((err = expand_fdset(files, nr))) | 358 | if (fdt->max_fdset >= NR_OPEN || |
| 359 | fdt->max_fds >= NR_OPEN || nr >= NR_OPEN) { | ||
| 360 | err = -EMFILE; | ||
| 244 | goto out; | 361 | goto out; |
| 245 | } | 362 | } |
| 246 | if (nr >= files->max_fds) { | ||
| 247 | expand = 1; | 363 | expand = 1; |
| 248 | if ((err = expand_fd_array(files, nr))) | 364 | if ((err = expand_fdtable(files, nr))) |
| 249 | goto out; | 365 | goto out; |
| 250 | } | 366 | } |
| 251 | err = expand; | 367 | err = expand; |
| 252 | out: | 368 | out: |
| 253 | return err; | 369 | return err; |
| 254 | } | 370 | } |
| 371 | |||
| 372 | static void __devinit fdtable_defer_list_init(int cpu) | ||
| 373 | { | ||
| 374 | struct fdtable_defer *fddef = &per_cpu(fdtable_defer_list, cpu); | ||
| 375 | spin_lock_init(&fddef->lock); | ||
| 376 | INIT_WORK(&fddef->wq, (void (*)(void *))free_fdtable_work, fddef); | ||
| 377 | init_timer(&fddef->timer); | ||
| 378 | fddef->timer.data = (unsigned long)fddef; | ||
| 379 | fddef->timer.function = fdtable_timer; | ||
| 380 | fddef->next = NULL; | ||
| 381 | } | ||
| 382 | |||
| 383 | void __init files_defer_init(void) | ||
| 384 | { | ||
| 385 | int i; | ||
| 386 | /* Really early - can't use for_each_cpu */ | ||
| 387 | for (i = 0; i < NR_CPUS; i++) | ||
| 388 | fdtable_defer_list_init(i); | ||
| 389 | } | ||
diff --git a/fs/file_table.c b/fs/file_table.c index 43e9e1737de2..86ec8ae985b4 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/fs.h> | 14 | #include <linux/fs.h> |
| 15 | #include <linux/security.h> | 15 | #include <linux/security.h> |
| 16 | #include <linux/eventpoll.h> | 16 | #include <linux/eventpoll.h> |
| 17 | #include <linux/rcupdate.h> | ||
| 17 | #include <linux/mount.h> | 18 | #include <linux/mount.h> |
| 18 | #include <linux/cdev.h> | 19 | #include <linux/cdev.h> |
| 19 | #include <linux/fsnotify.h> | 20 | #include <linux/fsnotify.h> |
| @@ -53,11 +54,17 @@ void filp_dtor(void * objp, struct kmem_cache_s *cachep, unsigned long dflags) | |||
| 53 | spin_unlock_irqrestore(&filp_count_lock, flags); | 54 | spin_unlock_irqrestore(&filp_count_lock, flags); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | static inline void file_free(struct file *f) | 57 | static inline void file_free_rcu(struct rcu_head *head) |
| 57 | { | 58 | { |
| 59 | struct file *f = container_of(head, struct file, f_rcuhead); | ||
| 58 | kmem_cache_free(filp_cachep, f); | 60 | kmem_cache_free(filp_cachep, f); |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 63 | static inline void file_free(struct file *f) | ||
| 64 | { | ||
| 65 | call_rcu(&f->f_rcuhead, file_free_rcu); | ||
| 66 | } | ||
| 67 | |||
| 61 | /* Find an unused file structure and return a pointer to it. | 68 | /* Find an unused file structure and return a pointer to it. |
| 62 | * Returns NULL, if there are no more free file structures or | 69 | * Returns NULL, if there are no more free file structures or |
| 63 | * we run out of memory. | 70 | * we run out of memory. |
| @@ -110,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp); | |||
| 110 | 117 | ||
| 111 | void fastcall fput(struct file *file) | 118 | void fastcall fput(struct file *file) |
| 112 | { | 119 | { |
| 113 | if (atomic_dec_and_test(&file->f_count)) | 120 | if (rcuref_dec_and_test(&file->f_count)) |
| 114 | __fput(file); | 121 | __fput(file); |
| 115 | } | 122 | } |
| 116 | 123 | ||
| @@ -156,11 +163,17 @@ struct file fastcall *fget(unsigned int fd) | |||
| 156 | struct file *file; | 163 | struct file *file; |
| 157 | struct files_struct *files = current->files; | 164 | struct files_struct *files = current->files; |
| 158 | 165 | ||
| 159 | spin_lock(&files->file_lock); | 166 | rcu_read_lock(); |
| 160 | file = fcheck_files(files, fd); | 167 | file = fcheck_files(files, fd); |
| 161 | if (file) | 168 | if (file) { |
| 162 | get_file(file); | 169 | if (!rcuref_inc_lf(&file->f_count)) { |
| 163 | spin_unlock(&files->file_lock); | 170 | /* File object ref couldn't be taken */ |
| 171 | rcu_read_unlock(); | ||
| 172 | return NULL; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | rcu_read_unlock(); | ||
| 176 | |||
| 164 | return file; | 177 | return file; |
| 165 | } | 178 | } |
| 166 | 179 | ||
| @@ -182,21 +195,25 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) | |||
| 182 | if (likely((atomic_read(&files->count) == 1))) { | 195 | if (likely((atomic_read(&files->count) == 1))) { |
| 183 | file = fcheck_files(files, fd); | 196 | file = fcheck_files(files, fd); |
| 184 | } else { | 197 | } else { |
| 185 | spin_lock(&files->file_lock); | 198 | rcu_read_lock(); |
| 186 | file = fcheck_files(files, fd); | 199 | file = fcheck_files(files, fd); |
| 187 | if (file) { | 200 | if (file) { |
| 188 | get_file(file); | 201 | if (rcuref_inc_lf(&file->f_count)) |
| 189 | *fput_needed = 1; | 202 | *fput_needed = 1; |
| 203 | else | ||
| 204 | /* Didn't get the reference, someone's freed */ | ||
| 205 | file = NULL; | ||
| 190 | } | 206 | } |
| 191 | spin_unlock(&files->file_lock); | 207 | rcu_read_unlock(); |
| 192 | } | 208 | } |
| 209 | |||
| 193 | return file; | 210 | return file; |
| 194 | } | 211 | } |
| 195 | 212 | ||
| 196 | 213 | ||
| 197 | void put_filp(struct file *file) | 214 | void put_filp(struct file *file) |
| 198 | { | 215 | { |
| 199 | if (atomic_dec_and_test(&file->f_count)) { | 216 | if (rcuref_dec_and_test(&file->f_count)) { |
| 200 | security_file_free(file); | 217 | security_file_free(file); |
| 201 | file_kill(file); | 218 | file_kill(file); |
| 202 | file_free(file); | 219 | file_free(file); |
| @@ -257,4 +274,5 @@ void __init files_init(unsigned long mempages) | |||
| 257 | files_stat.max_files = n; | 274 | files_stat.max_files = n; |
| 258 | if (files_stat.max_files < NR_FILE) | 275 | if (files_stat.max_files < NR_FILE) |
| 259 | files_stat.max_files = NR_FILE; | 276 | files_stat.max_files = NR_FILE; |
| 277 | files_defer_init(); | ||
| 260 | } | 278 | } |
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile new file mode 100644 index 000000000000..c3e1f760cac9 --- /dev/null +++ b/fs/fuse/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the FUSE filesystem. | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_FUSE_FS) += fuse.o | ||
| 6 | |||
| 7 | fuse-objs := dev.o dir.o file.o inode.o | ||
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c new file mode 100644 index 000000000000..d4c869c6d01b --- /dev/null +++ b/fs/fuse/dev.c | |||
| @@ -0,0 +1,877 @@ | |||
| 1 | /* | ||
| 2 | FUSE: Filesystem in Userspace | ||
| 3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | ||
| 4 | |||
| 5 | This program can be distributed under the terms of the GNU GPL. | ||
| 6 | See the file COPYING. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "fuse_i.h" | ||
| 10 | |||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/poll.h> | ||
| 14 | #include <linux/uio.h> | ||
| 15 | #include <linux/miscdevice.h> | ||
| 16 | #include <linux/pagemap.h> | ||
| 17 | #include <linux/file.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | |||
| 20 | MODULE_ALIAS_MISCDEV(FUSE_MINOR); | ||
| 21 | |||
| 22 | static kmem_cache_t *fuse_req_cachep; | ||
| 23 | |||
| 24 | static inline struct fuse_conn *fuse_get_conn(struct file *file) | ||
| 25 | { | ||
| 26 | struct fuse_conn *fc; | ||
| 27 | spin_lock(&fuse_lock); | ||
| 28 | fc = file->private_data; | ||
| 29 | if (fc && !fc->mounted) | ||
| 30 | fc = NULL; | ||
| 31 | spin_unlock(&fuse_lock); | ||
| 32 | return fc; | ||
| 33 | } | ||
| 34 | |||
| 35 | static inline void fuse_request_init(struct fuse_req *req) | ||
| 36 | { | ||
| 37 | memset(req, 0, sizeof(*req)); | ||
| 38 | INIT_LIST_HEAD(&req->list); | ||
| 39 | init_waitqueue_head(&req->waitq); | ||
| 40 | atomic_set(&req->count, 1); | ||
| 41 | } | ||
| 42 | |||
| 43 | struct fuse_req *fuse_request_alloc(void) | ||
| 44 | { | ||
| 45 | struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, SLAB_KERNEL); | ||
| 46 | if (req) | ||
| 47 | fuse_request_init(req); | ||
| 48 | return req; | ||
| 49 | } | ||
| 50 | |||
| 51 | void fuse_request_free(struct fuse_req *req) | ||
| 52 | { | ||
| 53 | kmem_cache_free(fuse_req_cachep, req); | ||
| 54 | } | ||
| 55 | |||
| 56 | static inline void block_sigs(sigset_t *oldset) | ||
| 57 | { | ||
| 58 | sigset_t mask; | ||
| 59 | |||
| 60 | siginitsetinv(&mask, sigmask(SIGKILL)); | ||
| 61 | sigprocmask(SIG_BLOCK, &mask, oldset); | ||
| 62 | } | ||
| 63 | |||
| 64 | static inline void restore_sigs(sigset_t *oldset) | ||
| 65 | { | ||
| 66 | sigprocmask(SIG_SETMASK, oldset, NULL); | ||
| 67 | } | ||
| 68 | |||
| 69 | void fuse_reset_request(struct fuse_req *req) | ||
| 70 | { | ||
| 71 | int preallocated = req->preallocated; | ||
| 72 | BUG_ON(atomic_read(&req->count) != 1); | ||
| 73 | fuse_request_init(req); | ||
| 74 | req->preallocated = preallocated; | ||
| 75 | } | ||
| 76 | |||
| 77 | static void __fuse_get_request(struct fuse_req *req) | ||
| 78 | { | ||
| 79 | atomic_inc(&req->count); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Must be called with > 1 refcount */ | ||
| 83 | static void __fuse_put_request(struct fuse_req *req) | ||
| 84 | { | ||
| 85 | BUG_ON(atomic_read(&req->count) < 2); | ||
| 86 | atomic_dec(&req->count); | ||
| 87 | } | ||
| 88 | |||
| 89 | static struct fuse_req *do_get_request(struct fuse_conn *fc) | ||
| 90 | { | ||
| 91 | struct fuse_req *req; | ||
| 92 | |||
| 93 | spin_lock(&fuse_lock); | ||
| 94 | BUG_ON(list_empty(&fc->unused_list)); | ||
| 95 | req = list_entry(fc->unused_list.next, struct fuse_req, list); | ||
| 96 | list_del_init(&req->list); | ||
| 97 | spin_unlock(&fuse_lock); | ||
| 98 | fuse_request_init(req); | ||
| 99 | req->preallocated = 1; | ||
| 100 | req->in.h.uid = current->fsuid; | ||
| 101 | req->in.h.gid = current->fsgid; | ||
| 102 | req->in.h.pid = current->pid; | ||
| 103 | return req; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* This can return NULL, but only in case it's interrupted by a SIGKILL */ | ||
| 107 | struct fuse_req *fuse_get_request(struct fuse_conn *fc) | ||
| 108 | { | ||
| 109 | int intr; | ||
| 110 | sigset_t oldset; | ||
| 111 | |||
| 112 | block_sigs(&oldset); | ||
| 113 | intr = down_interruptible(&fc->outstanding_sem); | ||
| 114 | restore_sigs(&oldset); | ||
| 115 | return intr ? NULL : do_get_request(fc); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | ||
| 119 | { | ||
| 120 | spin_lock(&fuse_lock); | ||
| 121 | if (req->preallocated) | ||
| 122 | list_add(&req->list, &fc->unused_list); | ||
| 123 | else | ||
| 124 | fuse_request_free(req); | ||
| 125 | |||
| 126 | /* If we are in debt decrease that first */ | ||
| 127 | if (fc->outstanding_debt) | ||
| 128 | fc->outstanding_debt--; | ||
| 129 | else | ||
| 130 | up(&fc->outstanding_sem); | ||
| 131 | spin_unlock(&fuse_lock); | ||
| 132 | } | ||
| 133 | |||
| 134 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | ||
| 135 | { | ||
| 136 | if (atomic_dec_and_test(&req->count)) | ||
| 137 | fuse_putback_request(fc, req); | ||
| 138 | } | ||
| 139 | |||
| 140 | void fuse_release_background(struct fuse_req *req) | ||
| 141 | { | ||
| 142 | iput(req->inode); | ||
| 143 | iput(req->inode2); | ||
| 144 | if (req->file) | ||
| 145 | fput(req->file); | ||
| 146 | spin_lock(&fuse_lock); | ||
| 147 | list_del(&req->bg_entry); | ||
| 148 | spin_unlock(&fuse_lock); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* | ||
| 152 | * This function is called when a request is finished. Either a reply | ||
| 153 | * has arrived or it was interrupted (and not yet sent) or some error | ||
| 154 | * occured during communication with userspace, or the device file was | ||
| 155 | * closed. It decreases the referece count for the request. In case | ||
| 156 | * of a background request the referece to the stored objects are | ||
| 157 | * released. The requester thread is woken up (if still waiting), and | ||
| 158 | * finally the request is either freed or put on the unused_list | ||
| 159 | * | ||
| 160 | * Called with fuse_lock, unlocks it | ||
| 161 | */ | ||
| 162 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) | ||
| 163 | { | ||
| 164 | int putback; | ||
| 165 | req->finished = 1; | ||
| 166 | putback = atomic_dec_and_test(&req->count); | ||
| 167 | spin_unlock(&fuse_lock); | ||
| 168 | if (req->background) { | ||
| 169 | down_read(&fc->sbput_sem); | ||
| 170 | if (fc->mounted) | ||
| 171 | fuse_release_background(req); | ||
| 172 | up_read(&fc->sbput_sem); | ||
| 173 | } | ||
| 174 | wake_up(&req->waitq); | ||
| 175 | if (req->in.h.opcode == FUSE_INIT) { | ||
| 176 | int i; | ||
| 177 | |||
| 178 | if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION) | ||
| 179 | fc->conn_error = 1; | ||
| 180 | |||
| 181 | /* After INIT reply is received other requests can go | ||
| 182 | out. So do (FUSE_MAX_OUTSTANDING - 1) number of | ||
| 183 | up()s on outstanding_sem. The last up() is done in | ||
| 184 | fuse_putback_request() */ | ||
| 185 | for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) | ||
| 186 | up(&fc->outstanding_sem); | ||
| 187 | } | ||
| 188 | if (putback) | ||
| 189 | fuse_putback_request(fc, req); | ||
| 190 | } | ||
| 191 | |||
| 192 | /* | ||
| 193 | * Unfortunately request interruption not just solves the deadlock | ||
| 194 | * problem, it causes problems too. These stem from the fact, that an | ||
| 195 | * interrupted request is continued to be processed in userspace, | ||
| 196 | * while all the locks and object references (inode and file) held | ||
| 197 | * during the operation are released. | ||
| 198 | * | ||
| 199 | * To release the locks is exactly why there's a need to interrupt the | ||
| 200 | * request, so there's not a lot that can be done about this, except | ||
| 201 | * introduce additional locking in userspace. | ||
| 202 | * | ||
| 203 | * More important is to keep inode and file references until userspace | ||
| 204 | * has replied, otherwise FORGET and RELEASE could be sent while the | ||
| 205 | * inode/file is still used by the filesystem. | ||
| 206 | * | ||
| 207 | * For this reason the concept of "background" request is introduced. | ||
| 208 | * An interrupted request is backgrounded if it has been already sent | ||
| 209 | * to userspace. Backgrounding involves getting an extra reference to | ||
| 210 | * inode(s) or file used in the request, and adding the request to | ||
| 211 | * fc->background list. When a reply is received for a background | ||
| 212 | * request, the object references are released, and the request is | ||
| 213 | * removed from the list. If the filesystem is unmounted while there | ||
| 214 | * are still background requests, the list is walked and references | ||
| 215 | * are released as if a reply was received. | ||
| 216 | * | ||
| 217 | * There's one more use for a background request. The RELEASE message is | ||
| 218 | * always sent as background, since it doesn't return an error or | ||
| 219 | * data. | ||
| 220 | */ | ||
| 221 | static void background_request(struct fuse_conn *fc, struct fuse_req *req) | ||
| 222 | { | ||
| 223 | req->background = 1; | ||
| 224 | list_add(&req->bg_entry, &fc->background); | ||
| 225 | if (req->inode) | ||
| 226 | req->inode = igrab(req->inode); | ||
| 227 | if (req->inode2) | ||
| 228 | req->inode2 = igrab(req->inode2); | ||
| 229 | if (req->file) | ||
| 230 | get_file(req->file); | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Called with fuse_lock held. Releases, and then reacquires it. */ | ||
| 234 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) | ||
| 235 | { | ||
| 236 | sigset_t oldset; | ||
| 237 | |||
| 238 | spin_unlock(&fuse_lock); | ||
| 239 | block_sigs(&oldset); | ||
| 240 | wait_event_interruptible(req->waitq, req->finished); | ||
| 241 | restore_sigs(&oldset); | ||
| 242 | spin_lock(&fuse_lock); | ||
| 243 | if (req->finished) | ||
| 244 | return; | ||
| 245 | |||
| 246 | req->out.h.error = -EINTR; | ||
| 247 | req->interrupted = 1; | ||
| 248 | if (req->locked) { | ||
| 249 | /* This is uninterruptible sleep, because data is | ||
| 250 | being copied to/from the buffers of req. During | ||
| 251 | locked state, there mustn't be any filesystem | ||
| 252 | operation (e.g. page fault), since that could lead | ||
| 253 | to deadlock */ | ||
| 254 | spin_unlock(&fuse_lock); | ||
| 255 | wait_event(req->waitq, !req->locked); | ||
| 256 | spin_lock(&fuse_lock); | ||
| 257 | } | ||
| 258 | if (!req->sent && !list_empty(&req->list)) { | ||
| 259 | list_del(&req->list); | ||
| 260 | __fuse_put_request(req); | ||
| 261 | } else if (!req->finished && req->sent) | ||
| 262 | background_request(fc, req); | ||
| 263 | } | ||
| 264 | |||
| 265 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) | ||
| 266 | { | ||
| 267 | unsigned nbytes = 0; | ||
| 268 | unsigned i; | ||
| 269 | |||
| 270 | for (i = 0; i < numargs; i++) | ||
| 271 | nbytes += args[i].size; | ||
| 272 | |||
| 273 | return nbytes; | ||
| 274 | } | ||
| 275 | |||
| 276 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | ||
| 277 | { | ||
| 278 | fc->reqctr++; | ||
| 279 | /* zero is special */ | ||
| 280 | if (fc->reqctr == 0) | ||
| 281 | fc->reqctr = 1; | ||
| 282 | req->in.h.unique = fc->reqctr; | ||
| 283 | req->in.h.len = sizeof(struct fuse_in_header) + | ||
| 284 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); | ||
| 285 | if (!req->preallocated) { | ||
| 286 | /* If request is not preallocated (either FORGET or | ||
| 287 | RELEASE), then still decrease outstanding_sem, so | ||
| 288 | user can't open infinite number of files while not | ||
| 289 | processing the RELEASE requests. However for | ||
| 290 | efficiency do it without blocking, so if down() | ||
| 291 | would block, just increase the debt instead */ | ||
| 292 | if (down_trylock(&fc->outstanding_sem)) | ||
| 293 | fc->outstanding_debt++; | ||
| 294 | } | ||
| 295 | list_add_tail(&req->list, &fc->pending); | ||
| 296 | wake_up(&fc->waitq); | ||
| 297 | } | ||
| 298 | |||
| 299 | /* | ||
| 300 | * This can only be interrupted by a SIGKILL | ||
| 301 | */ | ||
| 302 | void request_send(struct fuse_conn *fc, struct fuse_req *req) | ||
| 303 | { | ||
| 304 | req->isreply = 1; | ||
| 305 | spin_lock(&fuse_lock); | ||
| 306 | if (!fc->connected) | ||
| 307 | req->out.h.error = -ENOTCONN; | ||
| 308 | else if (fc->conn_error) | ||
| 309 | req->out.h.error = -ECONNREFUSED; | ||
| 310 | else { | ||
| 311 | queue_request(fc, req); | ||
| 312 | /* acquire extra reference, since request is still needed | ||
| 313 | after request_end() */ | ||
| 314 | __fuse_get_request(req); | ||
| 315 | |||
| 316 | request_wait_answer(fc, req); | ||
| 317 | } | ||
| 318 | spin_unlock(&fuse_lock); | ||
| 319 | } | ||
| 320 | |||
| 321 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) | ||
| 322 | { | ||
| 323 | spin_lock(&fuse_lock); | ||
| 324 | if (fc->connected) { | ||
| 325 | queue_request(fc, req); | ||
| 326 | spin_unlock(&fuse_lock); | ||
| 327 | } else { | ||
| 328 | req->out.h.error = -ENOTCONN; | ||
| 329 | request_end(fc, req); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) | ||
| 334 | { | ||
| 335 | req->isreply = 0; | ||
| 336 | request_send_nowait(fc, req); | ||
| 337 | } | ||
| 338 | |||
| 339 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req) | ||
| 340 | { | ||
| 341 | req->isreply = 1; | ||
| 342 | spin_lock(&fuse_lock); | ||
| 343 | background_request(fc, req); | ||
| 344 | spin_unlock(&fuse_lock); | ||
| 345 | request_send_nowait(fc, req); | ||
| 346 | } | ||
| 347 | |||
| 348 | void fuse_send_init(struct fuse_conn *fc) | ||
| 349 | { | ||
| 350 | /* This is called from fuse_read_super() so there's guaranteed | ||
| 351 | to be a request available */ | ||
| 352 | struct fuse_req *req = do_get_request(fc); | ||
| 353 | struct fuse_init_in_out *arg = &req->misc.init_in_out; | ||
| 354 | arg->major = FUSE_KERNEL_VERSION; | ||
| 355 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | ||
| 356 | req->in.h.opcode = FUSE_INIT; | ||
| 357 | req->in.numargs = 1; | ||
| 358 | req->in.args[0].size = sizeof(*arg); | ||
| 359 | req->in.args[0].value = arg; | ||
| 360 | req->out.numargs = 1; | ||
| 361 | req->out.args[0].size = sizeof(*arg); | ||
| 362 | req->out.args[0].value = arg; | ||
| 363 | request_send_background(fc, req); | ||
| 364 | } | ||
| 365 | |||
| 366 | /* | ||
| 367 | * Lock the request. Up to the next unlock_request() there mustn't be | ||
| 368 | * anything that could cause a page-fault. If the request was already | ||
| 369 | * interrupted bail out. | ||
| 370 | */ | ||
| 371 | static inline int lock_request(struct fuse_req *req) | ||
| 372 | { | ||
| 373 | int err = 0; | ||
| 374 | if (req) { | ||
| 375 | spin_lock(&fuse_lock); | ||
| 376 | if (req->interrupted) | ||
| 377 | err = -ENOENT; | ||
| 378 | else | ||
| 379 | req->locked = 1; | ||
| 380 | spin_unlock(&fuse_lock); | ||
| 381 | } | ||
| 382 | return err; | ||
| 383 | } | ||
| 384 | |||
| 385 | /* | ||
| 386 | * Unlock request. If it was interrupted during being locked, the | ||
| 387 | * requester thread is currently waiting for it to be unlocked, so | ||
| 388 | * wake it up. | ||
| 389 | */ | ||
| 390 | static inline void unlock_request(struct fuse_req *req) | ||
| 391 | { | ||
| 392 | if (req) { | ||
| 393 | spin_lock(&fuse_lock); | ||
| 394 | req->locked = 0; | ||
| 395 | if (req->interrupted) | ||
| 396 | wake_up(&req->waitq); | ||
| 397 | spin_unlock(&fuse_lock); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | struct fuse_copy_state { | ||
| 402 | int write; | ||
| 403 | struct fuse_req *req; | ||
| 404 | const struct iovec *iov; | ||
| 405 | unsigned long nr_segs; | ||
| 406 | unsigned long seglen; | ||
| 407 | unsigned long addr; | ||
| 408 | struct page *pg; | ||
| 409 | void *mapaddr; | ||
| 410 | void *buf; | ||
| 411 | unsigned len; | ||
| 412 | }; | ||
| 413 | |||
| 414 | static void fuse_copy_init(struct fuse_copy_state *cs, int write, | ||
| 415 | struct fuse_req *req, const struct iovec *iov, | ||
| 416 | unsigned long nr_segs) | ||
| 417 | { | ||
| 418 | memset(cs, 0, sizeof(*cs)); | ||
| 419 | cs->write = write; | ||
| 420 | cs->req = req; | ||
| 421 | cs->iov = iov; | ||
| 422 | cs->nr_segs = nr_segs; | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Unmap and put previous page of userspace buffer */ | ||
| 426 | static inline void fuse_copy_finish(struct fuse_copy_state *cs) | ||
| 427 | { | ||
| 428 | if (cs->mapaddr) { | ||
| 429 | kunmap_atomic(cs->mapaddr, KM_USER0); | ||
| 430 | if (cs->write) { | ||
| 431 | flush_dcache_page(cs->pg); | ||
| 432 | set_page_dirty_lock(cs->pg); | ||
| 433 | } | ||
| 434 | put_page(cs->pg); | ||
| 435 | cs->mapaddr = NULL; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | /* | ||
| 440 | * Get another pagefull of userspace buffer, and map it to kernel | ||
| 441 | * address space, and lock request | ||
| 442 | */ | ||
| 443 | static int fuse_copy_fill(struct fuse_copy_state *cs) | ||
| 444 | { | ||
| 445 | unsigned long offset; | ||
| 446 | int err; | ||
| 447 | |||
| 448 | unlock_request(cs->req); | ||
| 449 | fuse_copy_finish(cs); | ||
| 450 | if (!cs->seglen) { | ||
| 451 | BUG_ON(!cs->nr_segs); | ||
| 452 | cs->seglen = cs->iov[0].iov_len; | ||
| 453 | cs->addr = (unsigned long) cs->iov[0].iov_base; | ||
| 454 | cs->iov ++; | ||
| 455 | cs->nr_segs --; | ||
| 456 | } | ||
| 457 | down_read(¤t->mm->mmap_sem); | ||
| 458 | err = get_user_pages(current, current->mm, cs->addr, 1, cs->write, 0, | ||
| 459 | &cs->pg, NULL); | ||
| 460 | up_read(¤t->mm->mmap_sem); | ||
| 461 | if (err < 0) | ||
| 462 | return err; | ||
| 463 | BUG_ON(err != 1); | ||
| 464 | offset = cs->addr % PAGE_SIZE; | ||
| 465 | cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); | ||
| 466 | cs->buf = cs->mapaddr + offset; | ||
| 467 | cs->len = min(PAGE_SIZE - offset, cs->seglen); | ||
| 468 | cs->seglen -= cs->len; | ||
| 469 | cs->addr += cs->len; | ||
| 470 | |||
| 471 | return lock_request(cs->req); | ||
| 472 | } | ||
| 473 | |||
| 474 | /* Do as much copy to/from userspace buffer as we can */ | ||
| 475 | static inline int fuse_copy_do(struct fuse_copy_state *cs, void **val, | ||
| 476 | unsigned *size) | ||
| 477 | { | ||
| 478 | unsigned ncpy = min(*size, cs->len); | ||
| 479 | if (val) { | ||
| 480 | if (cs->write) | ||
| 481 | memcpy(cs->buf, *val, ncpy); | ||
| 482 | else | ||
| 483 | memcpy(*val, cs->buf, ncpy); | ||
| 484 | *val += ncpy; | ||
| 485 | } | ||
| 486 | *size -= ncpy; | ||
| 487 | cs->len -= ncpy; | ||
| 488 | cs->buf += ncpy; | ||
| 489 | return ncpy; | ||
| 490 | } | ||
| 491 | |||
| 492 | /* | ||
| 493 | * Copy a page in the request to/from the userspace buffer. Must be | ||
| 494 | * done atomically | ||
| 495 | */ | ||
| 496 | static inline int fuse_copy_page(struct fuse_copy_state *cs, struct page *page, | ||
| 497 | unsigned offset, unsigned count, int zeroing) | ||
| 498 | { | ||
| 499 | if (page && zeroing && count < PAGE_SIZE) { | ||
| 500 | void *mapaddr = kmap_atomic(page, KM_USER1); | ||
| 501 | memset(mapaddr, 0, PAGE_SIZE); | ||
| 502 | kunmap_atomic(mapaddr, KM_USER1); | ||
| 503 | } | ||
| 504 | while (count) { | ||
| 505 | int err; | ||
| 506 | if (!cs->len && (err = fuse_copy_fill(cs))) | ||
| 507 | return err; | ||
| 508 | if (page) { | ||
| 509 | void *mapaddr = kmap_atomic(page, KM_USER1); | ||
| 510 | void *buf = mapaddr + offset; | ||
| 511 | offset += fuse_copy_do(cs, &buf, &count); | ||
| 512 | kunmap_atomic(mapaddr, KM_USER1); | ||
| 513 | } else | ||
| 514 | offset += fuse_copy_do(cs, NULL, &count); | ||
| 515 | } | ||
| 516 | if (page && !cs->write) | ||
| 517 | flush_dcache_page(page); | ||
| 518 | return 0; | ||
| 519 | } | ||
| 520 | |||
| 521 | /* Copy pages in the request to/from userspace buffer */ | ||
| 522 | static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, | ||
| 523 | int zeroing) | ||
| 524 | { | ||
| 525 | unsigned i; | ||
| 526 | struct fuse_req *req = cs->req; | ||
| 527 | unsigned offset = req->page_offset; | ||
| 528 | unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); | ||
| 529 | |||
| 530 | for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { | ||
| 531 | struct page *page = req->pages[i]; | ||
| 532 | int err = fuse_copy_page(cs, page, offset, count, zeroing); | ||
| 533 | if (err) | ||
| 534 | return err; | ||
| 535 | |||
| 536 | nbytes -= count; | ||
| 537 | count = min(nbytes, (unsigned) PAGE_SIZE); | ||
| 538 | offset = 0; | ||
| 539 | } | ||
| 540 | return 0; | ||
| 541 | } | ||
| 542 | |||
| 543 | /* Copy a single argument in the request to/from userspace buffer */ | ||
| 544 | static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size) | ||
| 545 | { | ||
| 546 | while (size) { | ||
| 547 | int err; | ||
| 548 | if (!cs->len && (err = fuse_copy_fill(cs))) | ||
| 549 | return err; | ||
| 550 | fuse_copy_do(cs, &val, &size); | ||
| 551 | } | ||
| 552 | return 0; | ||
| 553 | } | ||
| 554 | |||
| 555 | /* Copy request arguments to/from userspace buffer */ | ||
| 556 | static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, | ||
| 557 | unsigned argpages, struct fuse_arg *args, | ||
| 558 | int zeroing) | ||
| 559 | { | ||
| 560 | int err = 0; | ||
| 561 | unsigned i; | ||
| 562 | |||
| 563 | for (i = 0; !err && i < numargs; i++) { | ||
| 564 | struct fuse_arg *arg = &args[i]; | ||
| 565 | if (i == numargs - 1 && argpages) | ||
| 566 | err = fuse_copy_pages(cs, arg->size, zeroing); | ||
| 567 | else | ||
| 568 | err = fuse_copy_one(cs, arg->value, arg->size); | ||
| 569 | } | ||
| 570 | return err; | ||
| 571 | } | ||
| 572 | |||
| 573 | /* Wait until a request is available on the pending list */ | ||
| 574 | static void request_wait(struct fuse_conn *fc) | ||
| 575 | { | ||
| 576 | DECLARE_WAITQUEUE(wait, current); | ||
| 577 | |||
| 578 | add_wait_queue_exclusive(&fc->waitq, &wait); | ||
| 579 | while (fc->mounted && list_empty(&fc->pending)) { | ||
| 580 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 581 | if (signal_pending(current)) | ||
| 582 | break; | ||
| 583 | |||
| 584 | spin_unlock(&fuse_lock); | ||
| 585 | schedule(); | ||
| 586 | spin_lock(&fuse_lock); | ||
| 587 | } | ||
| 588 | set_current_state(TASK_RUNNING); | ||
| 589 | remove_wait_queue(&fc->waitq, &wait); | ||
| 590 | } | ||
| 591 | |||
| 592 | /* | ||
| 593 | * Read a single request into the userspace filesystem's buffer. This | ||
| 594 | * function waits until a request is available, then removes it from | ||
| 595 | * the pending list and copies request data to userspace buffer. If | ||
| 596 | * no reply is needed (FORGET) or request has been interrupted or | ||
| 597 | * there was an error during the copying then it's finished by calling | ||
| 598 | * request_end(). Otherwise add it to the processing list, and set | ||
| 599 | * the 'sent' flag. | ||
| 600 | */ | ||
| 601 | static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | ||
| 602 | unsigned long nr_segs, loff_t *off) | ||
| 603 | { | ||
| 604 | int err; | ||
| 605 | struct fuse_conn *fc; | ||
| 606 | struct fuse_req *req; | ||
| 607 | struct fuse_in *in; | ||
| 608 | struct fuse_copy_state cs; | ||
| 609 | unsigned reqsize; | ||
| 610 | |||
| 611 | spin_lock(&fuse_lock); | ||
| 612 | fc = file->private_data; | ||
| 613 | err = -EPERM; | ||
| 614 | if (!fc) | ||
| 615 | goto err_unlock; | ||
| 616 | request_wait(fc); | ||
| 617 | err = -ENODEV; | ||
| 618 | if (!fc->mounted) | ||
| 619 | goto err_unlock; | ||
| 620 | err = -ERESTARTSYS; | ||
| 621 | if (list_empty(&fc->pending)) | ||
| 622 | goto err_unlock; | ||
| 623 | |||
| 624 | req = list_entry(fc->pending.next, struct fuse_req, list); | ||
| 625 | list_del_init(&req->list); | ||
| 626 | spin_unlock(&fuse_lock); | ||
| 627 | |||
| 628 | in = &req->in; | ||
| 629 | reqsize = req->in.h.len; | ||
| 630 | fuse_copy_init(&cs, 1, req, iov, nr_segs); | ||
| 631 | err = -EINVAL; | ||
| 632 | if (iov_length(iov, nr_segs) >= reqsize) { | ||
| 633 | err = fuse_copy_one(&cs, &in->h, sizeof(in->h)); | ||
| 634 | if (!err) | ||
| 635 | err = fuse_copy_args(&cs, in->numargs, in->argpages, | ||
| 636 | (struct fuse_arg *) in->args, 0); | ||
| 637 | } | ||
| 638 | fuse_copy_finish(&cs); | ||
| 639 | |||
| 640 | spin_lock(&fuse_lock); | ||
| 641 | req->locked = 0; | ||
| 642 | if (!err && req->interrupted) | ||
| 643 | err = -ENOENT; | ||
| 644 | if (err) { | ||
| 645 | if (!req->interrupted) | ||
| 646 | req->out.h.error = -EIO; | ||
| 647 | request_end(fc, req); | ||
| 648 | return err; | ||
| 649 | } | ||
| 650 | if (!req->isreply) | ||
| 651 | request_end(fc, req); | ||
| 652 | else { | ||
| 653 | req->sent = 1; | ||
| 654 | list_add_tail(&req->list, &fc->processing); | ||
| 655 | spin_unlock(&fuse_lock); | ||
| 656 | } | ||
| 657 | return reqsize; | ||
| 658 | |||
| 659 | err_unlock: | ||
| 660 | spin_unlock(&fuse_lock); | ||
| 661 | return err; | ||
| 662 | } | ||
| 663 | |||
| 664 | static ssize_t fuse_dev_read(struct file *file, char __user *buf, | ||
| 665 | size_t nbytes, loff_t *off) | ||
| 666 | { | ||
| 667 | struct iovec iov; | ||
| 668 | iov.iov_len = nbytes; | ||
| 669 | iov.iov_base = buf; | ||
| 670 | return fuse_dev_readv(file, &iov, 1, off); | ||
| 671 | } | ||
| 672 | |||
| 673 | /* Look up request on processing list by unique ID */ | ||
| 674 | static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) | ||
| 675 | { | ||
| 676 | struct list_head *entry; | ||
| 677 | |||
| 678 | list_for_each(entry, &fc->processing) { | ||
| 679 | struct fuse_req *req; | ||
| 680 | req = list_entry(entry, struct fuse_req, list); | ||
| 681 | if (req->in.h.unique == unique) | ||
| 682 | return req; | ||
| 683 | } | ||
| 684 | return NULL; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out, | ||
| 688 | unsigned nbytes) | ||
| 689 | { | ||
| 690 | unsigned reqsize = sizeof(struct fuse_out_header); | ||
| 691 | |||
| 692 | if (out->h.error) | ||
| 693 | return nbytes != reqsize ? -EINVAL : 0; | ||
| 694 | |||
| 695 | reqsize += len_args(out->numargs, out->args); | ||
| 696 | |||
| 697 | if (reqsize < nbytes || (reqsize > nbytes && !out->argvar)) | ||
| 698 | return -EINVAL; | ||
| 699 | else if (reqsize > nbytes) { | ||
| 700 | struct fuse_arg *lastarg = &out->args[out->numargs-1]; | ||
| 701 | unsigned diffsize = reqsize - nbytes; | ||
| 702 | if (diffsize > lastarg->size) | ||
| 703 | return -EINVAL; | ||
| 704 | lastarg->size -= diffsize; | ||
| 705 | } | ||
| 706 | return fuse_copy_args(cs, out->numargs, out->argpages, out->args, | ||
| 707 | out->page_zeroing); | ||
| 708 | } | ||
| 709 | |||
| 710 | /* | ||
| 711 | * Write a single reply to a request. First the header is copied from | ||
| 712 | * the write buffer. The request is then searched on the processing | ||
| 713 | * list by the unique ID found in the header. If found, then remove | ||
| 714 | * it from the list and copy the rest of the buffer to the request. | ||
| 715 | * The request is finished by calling request_end() | ||
| 716 | */ | ||
| 717 | static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, | ||
| 718 | unsigned long nr_segs, loff_t *off) | ||
| 719 | { | ||
| 720 | int err; | ||
| 721 | unsigned nbytes = iov_length(iov, nr_segs); | ||
| 722 | struct fuse_req *req; | ||
| 723 | struct fuse_out_header oh; | ||
| 724 | struct fuse_copy_state cs; | ||
| 725 | struct fuse_conn *fc = fuse_get_conn(file); | ||
| 726 | if (!fc) | ||
| 727 | return -ENODEV; | ||
| 728 | |||
| 729 | fuse_copy_init(&cs, 0, NULL, iov, nr_segs); | ||
| 730 | if (nbytes < sizeof(struct fuse_out_header)) | ||
| 731 | return -EINVAL; | ||
| 732 | |||
| 733 | err = fuse_copy_one(&cs, &oh, sizeof(oh)); | ||
| 734 | if (err) | ||
| 735 | goto err_finish; | ||
| 736 | err = -EINVAL; | ||
| 737 | if (!oh.unique || oh.error <= -1000 || oh.error > 0 || | ||
| 738 | oh.len != nbytes) | ||
| 739 | goto err_finish; | ||
| 740 | |||
| 741 | spin_lock(&fuse_lock); | ||
| 742 | req = request_find(fc, oh.unique); | ||
| 743 | err = -EINVAL; | ||
| 744 | if (!req) | ||
| 745 | goto err_unlock; | ||
| 746 | |||
| 747 | list_del_init(&req->list); | ||
| 748 | if (req->interrupted) { | ||
| 749 | request_end(fc, req); | ||
| 750 | fuse_copy_finish(&cs); | ||
| 751 | return -ENOENT; | ||
| 752 | } | ||
| 753 | req->out.h = oh; | ||
| 754 | req->locked = 1; | ||
| 755 | cs.req = req; | ||
| 756 | spin_unlock(&fuse_lock); | ||
| 757 | |||
| 758 | err = copy_out_args(&cs, &req->out, nbytes); | ||
| 759 | fuse_copy_finish(&cs); | ||
| 760 | |||
| 761 | spin_lock(&fuse_lock); | ||
| 762 | req->locked = 0; | ||
| 763 | if (!err) { | ||
| 764 | if (req->interrupted) | ||
| 765 | err = -ENOENT; | ||
| 766 | } else if (!req->interrupted) | ||
| 767 | req->out.h.error = -EIO; | ||
| 768 | request_end(fc, req); | ||
| 769 | |||
| 770 | return err ? err : nbytes; | ||
| 771 | |||
| 772 | err_unlock: | ||
| 773 | spin_unlock(&fuse_lock); | ||
| 774 | err_finish: | ||
| 775 | fuse_copy_finish(&cs); | ||
| 776 | return err; | ||
| 777 | } | ||
| 778 | |||
| 779 | static ssize_t fuse_dev_write(struct file *file, const char __user *buf, | ||
| 780 | size_t nbytes, loff_t *off) | ||
| 781 | { | ||
| 782 | struct iovec iov; | ||
| 783 | iov.iov_len = nbytes; | ||
| 784 | iov.iov_base = (char __user *) buf; | ||
| 785 | return fuse_dev_writev(file, &iov, 1, off); | ||
| 786 | } | ||
| 787 | |||
| 788 | static unsigned fuse_dev_poll(struct file *file, poll_table *wait) | ||
| 789 | { | ||
| 790 | struct fuse_conn *fc = fuse_get_conn(file); | ||
| 791 | unsigned mask = POLLOUT | POLLWRNORM; | ||
| 792 | |||
| 793 | if (!fc) | ||
| 794 | return -ENODEV; | ||
| 795 | |||
| 796 | poll_wait(file, &fc->waitq, wait); | ||
| 797 | |||
| 798 | spin_lock(&fuse_lock); | ||
| 799 | if (!list_empty(&fc->pending)) | ||
| 800 | mask |= POLLIN | POLLRDNORM; | ||
| 801 | spin_unlock(&fuse_lock); | ||
| 802 | |||
| 803 | return mask; | ||
| 804 | } | ||
| 805 | |||
| 806 | /* Abort all requests on the given list (pending or processing) */ | ||
| 807 | static void end_requests(struct fuse_conn *fc, struct list_head *head) | ||
| 808 | { | ||
| 809 | while (!list_empty(head)) { | ||
| 810 | struct fuse_req *req; | ||
| 811 | req = list_entry(head->next, struct fuse_req, list); | ||
| 812 | list_del_init(&req->list); | ||
| 813 | req->out.h.error = -ECONNABORTED; | ||
| 814 | request_end(fc, req); | ||
| 815 | spin_lock(&fuse_lock); | ||
| 816 | } | ||
| 817 | } | ||
| 818 | |||
| 819 | static int fuse_dev_release(struct inode *inode, struct file *file) | ||
| 820 | { | ||
| 821 | struct fuse_conn *fc; | ||
| 822 | |||
| 823 | spin_lock(&fuse_lock); | ||
| 824 | fc = file->private_data; | ||
| 825 | if (fc) { | ||
| 826 | fc->connected = 0; | ||
| 827 | end_requests(fc, &fc->pending); | ||
| 828 | end_requests(fc, &fc->processing); | ||
| 829 | fuse_release_conn(fc); | ||
| 830 | } | ||
| 831 | spin_unlock(&fuse_lock); | ||
| 832 | return 0; | ||
| 833 | } | ||
| 834 | |||
| 835 | struct file_operations fuse_dev_operations = { | ||
| 836 | .owner = THIS_MODULE, | ||
| 837 | .llseek = no_llseek, | ||
| 838 | .read = fuse_dev_read, | ||
| 839 | .readv = fuse_dev_readv, | ||
| 840 | .write = fuse_dev_write, | ||
| 841 | .writev = fuse_dev_writev, | ||
| 842 | .poll = fuse_dev_poll, | ||
| 843 | .release = fuse_dev_release, | ||
| 844 | }; | ||
| 845 | |||
| 846 | static struct miscdevice fuse_miscdevice = { | ||
| 847 | .minor = FUSE_MINOR, | ||
| 848 | .name = "fuse", | ||
| 849 | .fops = &fuse_dev_operations, | ||
| 850 | }; | ||
| 851 | |||
| 852 | int __init fuse_dev_init(void) | ||
| 853 | { | ||
| 854 | int err = -ENOMEM; | ||
| 855 | fuse_req_cachep = kmem_cache_create("fuse_request", | ||
| 856 | sizeof(struct fuse_req), | ||
| 857 | 0, 0, NULL, NULL); | ||
| 858 | if (!fuse_req_cachep) | ||
| 859 | goto out; | ||
| 860 | |||
| 861 | err = misc_register(&fuse_miscdevice); | ||
| 862 | if (err) | ||
| 863 | goto out_cache_clean; | ||
| 864 | |||
| 865 | return 0; | ||
| 866 | |||
| 867 | out_cache_clean: | ||
| 868 | kmem_cache_destroy(fuse_req_cachep); | ||
| 869 | out: | ||
| 870 | return err; | ||
| 871 | } | ||
| 872 | |||
| 873 | void fuse_dev_cleanup(void) | ||
| 874 | { | ||
| 875 | misc_deregister(&fuse_miscdevice); | ||
| 876 | kmem_cache_destroy(fuse_req_cachep); | ||
| 877 | } | ||
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c new file mode 100644 index 000000000000..e79e49b3eec7 --- /dev/null +++ b/fs/fuse/dir.c | |||
| @@ -0,0 +1,982 @@ | |||
| 1 | /* | ||
| 2 | FUSE: Filesystem in Userspace | ||
| 3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | ||
| 4 | |||
| 5 | This program can be distributed under the terms of the GNU GPL. | ||
| 6 | See the file COPYING. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "fuse_i.h" | ||
| 10 | |||
| 11 | #include <linux/pagemap.h> | ||
| 12 | #include <linux/file.h> | ||
| 13 | #include <linux/gfp.h> | ||
| 14 | #include <linux/sched.h> | ||
| 15 | #include <linux/namei.h> | ||
| 16 | |||
| 17 | static inline unsigned long time_to_jiffies(unsigned long sec, | ||
| 18 | unsigned long nsec) | ||
| 19 | { | ||
| 20 | struct timespec ts = {sec, nsec}; | ||
| 21 | return jiffies + timespec_to_jiffies(&ts); | ||
| 22 | } | ||
| 23 | |||
| 24 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | ||
| 25 | struct dentry *entry, | ||
| 26 | struct fuse_entry_out *outarg) | ||
| 27 | { | ||
| 28 | req->in.h.opcode = FUSE_LOOKUP; | ||
| 29 | req->in.h.nodeid = get_node_id(dir); | ||
| 30 | req->inode = dir; | ||
| 31 | req->in.numargs = 1; | ||
| 32 | req->in.args[0].size = entry->d_name.len + 1; | ||
| 33 | req->in.args[0].value = entry->d_name.name; | ||
| 34 | req->out.numargs = 1; | ||
| 35 | req->out.args[0].size = sizeof(struct fuse_entry_out); | ||
| 36 | req->out.args[0].value = outarg; | ||
| 37 | } | ||
| 38 | |||
| 39 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | ||
| 40 | { | ||
| 41 | if (!entry->d_inode || is_bad_inode(entry->d_inode)) | ||
| 42 | return 0; | ||
| 43 | else if (time_after(jiffies, entry->d_time)) { | ||
| 44 | int err; | ||
| 45 | struct fuse_entry_out outarg; | ||
| 46 | struct inode *inode = entry->d_inode; | ||
| 47 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 48 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 49 | struct fuse_req *req = fuse_get_request(fc); | ||
| 50 | if (!req) | ||
| 51 | return 0; | ||
| 52 | |||
| 53 | fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); | ||
| 54 | request_send(fc, req); | ||
| 55 | err = req->out.h.error; | ||
| 56 | if (!err) { | ||
| 57 | if (outarg.nodeid != get_node_id(inode)) { | ||
| 58 | fuse_send_forget(fc, req, outarg.nodeid, 1); | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | fi->nlookup ++; | ||
| 62 | } | ||
| 63 | fuse_put_request(fc, req); | ||
| 64 | if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) | ||
| 65 | return 0; | ||
| 66 | |||
| 67 | fuse_change_attributes(inode, &outarg.attr); | ||
| 68 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
| 69 | outarg.entry_valid_nsec); | ||
| 70 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
| 71 | outarg.attr_valid_nsec); | ||
| 72 | } | ||
| 73 | return 1; | ||
| 74 | } | ||
| 75 | |||
| 76 | static struct dentry_operations fuse_dentry_operations = { | ||
| 77 | .d_revalidate = fuse_dentry_revalidate, | ||
| 78 | }; | ||
| 79 | |||
| 80 | static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | ||
| 81 | struct inode **inodep) | ||
| 82 | { | ||
| 83 | int err; | ||
| 84 | struct fuse_entry_out outarg; | ||
| 85 | struct inode *inode = NULL; | ||
| 86 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 87 | struct fuse_req *req; | ||
| 88 | |||
| 89 | if (entry->d_name.len > FUSE_NAME_MAX) | ||
| 90 | return -ENAMETOOLONG; | ||
| 91 | |||
| 92 | req = fuse_get_request(fc); | ||
| 93 | if (!req) | ||
| 94 | return -EINTR; | ||
| 95 | |||
| 96 | fuse_lookup_init(req, dir, entry, &outarg); | ||
| 97 | request_send(fc, req); | ||
| 98 | err = req->out.h.error; | ||
| 99 | if (!err) { | ||
| 100 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | ||
| 101 | &outarg.attr); | ||
| 102 | if (!inode) { | ||
| 103 | fuse_send_forget(fc, req, outarg.nodeid, 1); | ||
| 104 | return -ENOMEM; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | fuse_put_request(fc, req); | ||
| 108 | if (err && err != -ENOENT) | ||
| 109 | return err; | ||
| 110 | |||
| 111 | if (inode) { | ||
| 112 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 113 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
| 114 | outarg.entry_valid_nsec); | ||
| 115 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
| 116 | outarg.attr_valid_nsec); | ||
| 117 | } | ||
| 118 | |||
| 119 | entry->d_op = &fuse_dentry_operations; | ||
| 120 | *inodep = inode; | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | void fuse_invalidate_attr(struct inode *inode) | ||
| 125 | { | ||
| 126 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
| 127 | } | ||
| 128 | |||
| 129 | static void fuse_invalidate_entry(struct dentry *entry) | ||
| 130 | { | ||
| 131 | d_invalidate(entry); | ||
| 132 | entry->d_time = jiffies - 1; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | ||
| 136 | struct inode *dir, struct dentry *entry, | ||
| 137 | int mode) | ||
| 138 | { | ||
| 139 | struct fuse_entry_out outarg; | ||
| 140 | struct inode *inode; | ||
| 141 | struct fuse_inode *fi; | ||
| 142 | int err; | ||
| 143 | |||
| 144 | req->in.h.nodeid = get_node_id(dir); | ||
| 145 | req->inode = dir; | ||
| 146 | req->out.numargs = 1; | ||
| 147 | req->out.args[0].size = sizeof(outarg); | ||
| 148 | req->out.args[0].value = &outarg; | ||
| 149 | request_send(fc, req); | ||
| 150 | err = req->out.h.error; | ||
| 151 | if (err) { | ||
| 152 | fuse_put_request(fc, req); | ||
| 153 | return err; | ||
| 154 | } | ||
| 155 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | ||
| 156 | &outarg.attr); | ||
| 157 | if (!inode) { | ||
| 158 | fuse_send_forget(fc, req, outarg.nodeid, 1); | ||
| 159 | return -ENOMEM; | ||
| 160 | } | ||
| 161 | fuse_put_request(fc, req); | ||
| 162 | |||
| 163 | /* Don't allow userspace to do really stupid things... */ | ||
| 164 | if ((inode->i_mode ^ mode) & S_IFMT) { | ||
| 165 | iput(inode); | ||
| 166 | return -EIO; | ||
| 167 | } | ||
| 168 | |||
| 169 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
| 170 | outarg.entry_valid_nsec); | ||
| 171 | |||
| 172 | fi = get_fuse_inode(inode); | ||
| 173 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
| 174 | outarg.attr_valid_nsec); | ||
| 175 | |||
| 176 | d_instantiate(entry, inode); | ||
| 177 | fuse_invalidate_attr(dir); | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, | ||
| 182 | dev_t rdev) | ||
| 183 | { | ||
| 184 | struct fuse_mknod_in inarg; | ||
| 185 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 186 | struct fuse_req *req = fuse_get_request(fc); | ||
| 187 | if (!req) | ||
| 188 | return -EINTR; | ||
| 189 | |||
| 190 | memset(&inarg, 0, sizeof(inarg)); | ||
| 191 | inarg.mode = mode; | ||
| 192 | inarg.rdev = new_encode_dev(rdev); | ||
| 193 | req->in.h.opcode = FUSE_MKNOD; | ||
| 194 | req->in.numargs = 2; | ||
| 195 | req->in.args[0].size = sizeof(inarg); | ||
| 196 | req->in.args[0].value = &inarg; | ||
| 197 | req->in.args[1].size = entry->d_name.len + 1; | ||
| 198 | req->in.args[1].value = entry->d_name.name; | ||
| 199 | return create_new_entry(fc, req, dir, entry, mode); | ||
| 200 | } | ||
| 201 | |||
| 202 | static int fuse_create(struct inode *dir, struct dentry *entry, int mode, | ||
| 203 | struct nameidata *nd) | ||
| 204 | { | ||
| 205 | return fuse_mknod(dir, entry, mode, 0); | ||
| 206 | } | ||
| 207 | |||
| 208 | static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) | ||
| 209 | { | ||
| 210 | struct fuse_mkdir_in inarg; | ||
| 211 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 212 | struct fuse_req *req = fuse_get_request(fc); | ||
| 213 | if (!req) | ||
| 214 | return -EINTR; | ||
| 215 | |||
| 216 | memset(&inarg, 0, sizeof(inarg)); | ||
| 217 | inarg.mode = mode; | ||
| 218 | req->in.h.opcode = FUSE_MKDIR; | ||
| 219 | req->in.numargs = 2; | ||
| 220 | req->in.args[0].size = sizeof(inarg); | ||
| 221 | req->in.args[0].value = &inarg; | ||
| 222 | req->in.args[1].size = entry->d_name.len + 1; | ||
| 223 | req->in.args[1].value = entry->d_name.name; | ||
| 224 | return create_new_entry(fc, req, dir, entry, S_IFDIR); | ||
| 225 | } | ||
| 226 | |||
| 227 | static int fuse_symlink(struct inode *dir, struct dentry *entry, | ||
| 228 | const char *link) | ||
| 229 | { | ||
| 230 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 231 | unsigned len = strlen(link) + 1; | ||
| 232 | struct fuse_req *req; | ||
| 233 | |||
| 234 | if (len > FUSE_SYMLINK_MAX) | ||
| 235 | return -ENAMETOOLONG; | ||
| 236 | |||
| 237 | req = fuse_get_request(fc); | ||
| 238 | if (!req) | ||
| 239 | return -EINTR; | ||
| 240 | |||
| 241 | req->in.h.opcode = FUSE_SYMLINK; | ||
| 242 | req->in.numargs = 2; | ||
| 243 | req->in.args[0].size = entry->d_name.len + 1; | ||
| 244 | req->in.args[0].value = entry->d_name.name; | ||
| 245 | req->in.args[1].size = len; | ||
| 246 | req->in.args[1].value = link; | ||
| 247 | return create_new_entry(fc, req, dir, entry, S_IFLNK); | ||
| 248 | } | ||
| 249 | |||
| 250 | static int fuse_unlink(struct inode *dir, struct dentry *entry) | ||
| 251 | { | ||
| 252 | int err; | ||
| 253 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 254 | struct fuse_req *req = fuse_get_request(fc); | ||
| 255 | if (!req) | ||
| 256 | return -EINTR; | ||
| 257 | |||
| 258 | req->in.h.opcode = FUSE_UNLINK; | ||
| 259 | req->in.h.nodeid = get_node_id(dir); | ||
| 260 | req->inode = dir; | ||
| 261 | req->in.numargs = 1; | ||
| 262 | req->in.args[0].size = entry->d_name.len + 1; | ||
| 263 | req->in.args[0].value = entry->d_name.name; | ||
| 264 | request_send(fc, req); | ||
| 265 | err = req->out.h.error; | ||
| 266 | fuse_put_request(fc, req); | ||
| 267 | if (!err) { | ||
| 268 | struct inode *inode = entry->d_inode; | ||
| 269 | |||
| 270 | /* Set nlink to zero so the inode can be cleared, if | ||
| 271 | the inode does have more links this will be | ||
| 272 | discovered at the next lookup/getattr */ | ||
| 273 | inode->i_nlink = 0; | ||
| 274 | fuse_invalidate_attr(inode); | ||
| 275 | fuse_invalidate_attr(dir); | ||
| 276 | } else if (err == -EINTR) | ||
| 277 | fuse_invalidate_entry(entry); | ||
| 278 | return err; | ||
| 279 | } | ||
| 280 | |||
| 281 | static int fuse_rmdir(struct inode *dir, struct dentry *entry) | ||
| 282 | { | ||
| 283 | int err; | ||
| 284 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
| 285 | struct fuse_req *req = fuse_get_request(fc); | ||
| 286 | if (!req) | ||
| 287 | return -EINTR; | ||
| 288 | |||
| 289 | req->in.h.opcode = FUSE_RMDIR; | ||
| 290 | req->in.h.nodeid = get_node_id(dir); | ||
| 291 | req->inode = dir; | ||
| 292 | req->in.numargs = 1; | ||
| 293 | req->in.args[0].size = entry->d_name.len + 1; | ||
| 294 | req->in.args[0].value = entry->d_name.name; | ||
| 295 | request_send(fc, req); | ||
| 296 | err = req->out.h.error; | ||
| 297 | fuse_put_request(fc, req); | ||
| 298 | if (!err) { | ||
| 299 | entry->d_inode->i_nlink = 0; | ||
| 300 | fuse_invalidate_attr(dir); | ||
| 301 | } else if (err == -EINTR) | ||
| 302 | fuse_invalidate_entry(entry); | ||
| 303 | return err; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int fuse_rename(struct inode *olddir, struct dentry *oldent, | ||
| 307 | struct inode *newdir, struct dentry *newent) | ||
| 308 | { | ||
| 309 | int err; | ||
| 310 | struct fuse_rename_in inarg; | ||
| 311 | struct fuse_conn *fc = get_fuse_conn(olddir); | ||
| 312 | struct fuse_req *req = fuse_get_request(fc); | ||
| 313 | if (!req) | ||
| 314 | return -EINTR; | ||
| 315 | |||
| 316 | memset(&inarg, 0, sizeof(inarg)); | ||
| 317 | inarg.newdir = get_node_id(newdir); | ||
| 318 | req->in.h.opcode = FUSE_RENAME; | ||
| 319 | req->in.h.nodeid = get_node_id(olddir); | ||
| 320 | req->inode = olddir; | ||
| 321 | req->inode2 = newdir; | ||
| 322 | req->in.numargs = 3; | ||
| 323 | req->in.args[0].size = sizeof(inarg); | ||
| 324 | req->in.args[0].value = &inarg; | ||
| 325 | req->in.args[1].size = oldent->d_name.len + 1; | ||
| 326 | req->in.args[1].value = oldent->d_name.name; | ||
| 327 | req->in.args[2].size = newent->d_name.len + 1; | ||
| 328 | req->in.args[2].value = newent->d_name.name; | ||
| 329 | request_send(fc, req); | ||
| 330 | err = req->out.h.error; | ||
| 331 | fuse_put_request(fc, req); | ||
| 332 | if (!err) { | ||
| 333 | fuse_invalidate_attr(olddir); | ||
| 334 | if (olddir != newdir) | ||
| 335 | fuse_invalidate_attr(newdir); | ||
| 336 | } else if (err == -EINTR) { | ||
| 337 | /* If request was interrupted, DEITY only knows if the | ||
| 338 | rename actually took place. If the invalidation | ||
| 339 | fails (e.g. some process has CWD under the renamed | ||
| 340 | directory), then there can be inconsistency between | ||
| 341 | the dcache and the real filesystem. Tough luck. */ | ||
| 342 | fuse_invalidate_entry(oldent); | ||
| 343 | if (newent->d_inode) | ||
| 344 | fuse_invalidate_entry(newent); | ||
| 345 | } | ||
| 346 | |||
| 347 | return err; | ||
| 348 | } | ||
| 349 | |||
| 350 | static int fuse_link(struct dentry *entry, struct inode *newdir, | ||
| 351 | struct dentry *newent) | ||
| 352 | { | ||
| 353 | int err; | ||
| 354 | struct fuse_link_in inarg; | ||
| 355 | struct inode *inode = entry->d_inode; | ||
| 356 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 357 | struct fuse_req *req = fuse_get_request(fc); | ||
| 358 | if (!req) | ||
| 359 | return -EINTR; | ||
| 360 | |||
| 361 | memset(&inarg, 0, sizeof(inarg)); | ||
| 362 | inarg.oldnodeid = get_node_id(inode); | ||
| 363 | req->in.h.opcode = FUSE_LINK; | ||
| 364 | req->inode2 = inode; | ||
| 365 | req->in.numargs = 2; | ||
| 366 | req->in.args[0].size = sizeof(inarg); | ||
| 367 | req->in.args[0].value = &inarg; | ||
| 368 | req->in.args[1].size = newent->d_name.len + 1; | ||
| 369 | req->in.args[1].value = newent->d_name.name; | ||
| 370 | err = create_new_entry(fc, req, newdir, newent, inode->i_mode); | ||
| 371 | /* Contrary to "normal" filesystems it can happen that link | ||
| 372 | makes two "logical" inodes point to the same "physical" | ||
| 373 | inode. We invalidate the attributes of the old one, so it | ||
| 374 | will reflect changes in the backing inode (link count, | ||
| 375 | etc.) | ||
| 376 | */ | ||
| 377 | if (!err || err == -EINTR) | ||
| 378 | fuse_invalidate_attr(inode); | ||
| 379 | return err; | ||
| 380 | } | ||
| 381 | |||
| 382 | int fuse_do_getattr(struct inode *inode) | ||
| 383 | { | ||
| 384 | int err; | ||
| 385 | struct fuse_attr_out arg; | ||
| 386 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 387 | struct fuse_req *req = fuse_get_request(fc); | ||
| 388 | if (!req) | ||
| 389 | return -EINTR; | ||
| 390 | |||
| 391 | req->in.h.opcode = FUSE_GETATTR; | ||
| 392 | req->in.h.nodeid = get_node_id(inode); | ||
| 393 | req->inode = inode; | ||
| 394 | req->out.numargs = 1; | ||
| 395 | req->out.args[0].size = sizeof(arg); | ||
| 396 | req->out.args[0].value = &arg; | ||
| 397 | request_send(fc, req); | ||
| 398 | err = req->out.h.error; | ||
| 399 | fuse_put_request(fc, req); | ||
| 400 | if (!err) { | ||
| 401 | if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) { | ||
| 402 | make_bad_inode(inode); | ||
| 403 | err = -EIO; | ||
| 404 | } else { | ||
| 405 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 406 | fuse_change_attributes(inode, &arg.attr); | ||
| 407 | fi->i_time = time_to_jiffies(arg.attr_valid, | ||
| 408 | arg.attr_valid_nsec); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | return err; | ||
| 412 | } | ||
| 413 | |||
| 414 | /* | ||
| 415 | * Calling into a user-controlled filesystem gives the filesystem | ||
| 416 | * daemon ptrace-like capabilities over the requester process. This | ||
| 417 | * means, that the filesystem daemon is able to record the exact | ||
| 418 | * filesystem operations performed, and can also control the behavior | ||
| 419 | * of the requester process in otherwise impossible ways. For example | ||
| 420 | * it can delay the operation for arbitrary length of time allowing | ||
| 421 | * DoS against the requester. | ||
| 422 | * | ||
| 423 | * For this reason only those processes can call into the filesystem, | ||
| 424 | * for which the owner of the mount has ptrace privilege. This | ||
| 425 | * excludes processes started by other users, suid or sgid processes. | ||
| 426 | */ | ||
| 427 | static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | ||
| 428 | { | ||
| 429 | if (fc->flags & FUSE_ALLOW_OTHER) | ||
| 430 | return 1; | ||
| 431 | |||
| 432 | if (task->euid == fc->user_id && | ||
| 433 | task->suid == fc->user_id && | ||
| 434 | task->uid == fc->user_id && | ||
| 435 | task->egid == fc->group_id && | ||
| 436 | task->sgid == fc->group_id && | ||
| 437 | task->gid == fc->group_id) | ||
| 438 | return 1; | ||
| 439 | |||
| 440 | return 0; | ||
| 441 | } | ||
| 442 | |||
| 443 | static int fuse_revalidate(struct dentry *entry) | ||
| 444 | { | ||
| 445 | struct inode *inode = entry->d_inode; | ||
| 446 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 447 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 448 | |||
| 449 | if (!fuse_allow_task(fc, current)) | ||
| 450 | return -EACCES; | ||
| 451 | if (get_node_id(inode) != FUSE_ROOT_ID && | ||
| 452 | time_before_eq(jiffies, fi->i_time)) | ||
| 453 | return 0; | ||
| 454 | |||
| 455 | return fuse_do_getattr(inode); | ||
| 456 | } | ||
| 457 | |||
| 458 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
| 459 | { | ||
| 460 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 461 | |||
| 462 | if (!fuse_allow_task(fc, current)) | ||
| 463 | return -EACCES; | ||
| 464 | else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { | ||
| 465 | int err = generic_permission(inode, mask, NULL); | ||
| 466 | |||
| 467 | /* If permission is denied, try to refresh file | ||
| 468 | attributes. This is also needed, because the root | ||
| 469 | node will at first have no permissions */ | ||
| 470 | if (err == -EACCES) { | ||
| 471 | err = fuse_do_getattr(inode); | ||
| 472 | if (!err) | ||
| 473 | err = generic_permission(inode, mask, NULL); | ||
| 474 | } | ||
| 475 | |||
| 476 | /* FIXME: Need some mechanism to revoke permissions: | ||
| 477 | currently if the filesystem suddenly changes the | ||
| 478 | file mode, we will not be informed about it, and | ||
| 479 | continue to allow access to the file/directory. | ||
| 480 | |||
| 481 | This is actually not so grave, since the user can | ||
| 482 | simply keep access to the file/directory anyway by | ||
| 483 | keeping it open... */ | ||
| 484 | |||
| 485 | return err; | ||
| 486 | } else { | ||
| 487 | int mode = inode->i_mode; | ||
| 488 | if ((mask & MAY_WRITE) && IS_RDONLY(inode) && | ||
| 489 | (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) | ||
| 490 | return -EROFS; | ||
| 491 | if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO)) | ||
| 492 | return -EACCES; | ||
| 493 | return 0; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | static int parse_dirfile(char *buf, size_t nbytes, struct file *file, | ||
| 498 | void *dstbuf, filldir_t filldir) | ||
| 499 | { | ||
| 500 | while (nbytes >= FUSE_NAME_OFFSET) { | ||
| 501 | struct fuse_dirent *dirent = (struct fuse_dirent *) buf; | ||
| 502 | size_t reclen = FUSE_DIRENT_SIZE(dirent); | ||
| 503 | int over; | ||
| 504 | if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) | ||
| 505 | return -EIO; | ||
| 506 | if (reclen > nbytes) | ||
| 507 | break; | ||
| 508 | |||
| 509 | over = filldir(dstbuf, dirent->name, dirent->namelen, | ||
| 510 | file->f_pos, dirent->ino, dirent->type); | ||
| 511 | if (over) | ||
| 512 | break; | ||
| 513 | |||
| 514 | buf += reclen; | ||
| 515 | nbytes -= reclen; | ||
| 516 | file->f_pos = dirent->off; | ||
| 517 | } | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | static inline size_t fuse_send_readdir(struct fuse_req *req, struct file *file, | ||
| 523 | struct inode *inode, loff_t pos, | ||
| 524 | size_t count) | ||
| 525 | { | ||
| 526 | return fuse_send_read_common(req, file, inode, pos, count, 1); | ||
| 527 | } | ||
| 528 | |||
| 529 | static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | ||
| 530 | { | ||
| 531 | int err; | ||
| 532 | size_t nbytes; | ||
| 533 | struct page *page; | ||
| 534 | struct inode *inode = file->f_dentry->d_inode; | ||
| 535 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 536 | struct fuse_req *req = fuse_get_request(fc); | ||
| 537 | if (!req) | ||
| 538 | return -EINTR; | ||
| 539 | |||
| 540 | page = alloc_page(GFP_KERNEL); | ||
| 541 | if (!page) { | ||
| 542 | fuse_put_request(fc, req); | ||
| 543 | return -ENOMEM; | ||
| 544 | } | ||
| 545 | req->num_pages = 1; | ||
| 546 | req->pages[0] = page; | ||
| 547 | nbytes = fuse_send_readdir(req, file, inode, file->f_pos, PAGE_SIZE); | ||
| 548 | err = req->out.h.error; | ||
| 549 | fuse_put_request(fc, req); | ||
| 550 | if (!err) | ||
| 551 | err = parse_dirfile(page_address(page), nbytes, file, dstbuf, | ||
| 552 | filldir); | ||
| 553 | |||
| 554 | __free_page(page); | ||
| 555 | fuse_invalidate_attr(inode); /* atime changed */ | ||
| 556 | return err; | ||
| 557 | } | ||
| 558 | |||
| 559 | static char *read_link(struct dentry *dentry) | ||
| 560 | { | ||
| 561 | struct inode *inode = dentry->d_inode; | ||
| 562 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 563 | struct fuse_req *req = fuse_get_request(fc); | ||
| 564 | char *link; | ||
| 565 | |||
| 566 | if (!req) | ||
| 567 | return ERR_PTR(-EINTR); | ||
| 568 | |||
| 569 | link = (char *) __get_free_page(GFP_KERNEL); | ||
| 570 | if (!link) { | ||
| 571 | link = ERR_PTR(-ENOMEM); | ||
| 572 | goto out; | ||
| 573 | } | ||
| 574 | req->in.h.opcode = FUSE_READLINK; | ||
| 575 | req->in.h.nodeid = get_node_id(inode); | ||
| 576 | req->inode = inode; | ||
| 577 | req->out.argvar = 1; | ||
| 578 | req->out.numargs = 1; | ||
| 579 | req->out.args[0].size = PAGE_SIZE - 1; | ||
| 580 | req->out.args[0].value = link; | ||
| 581 | request_send(fc, req); | ||
| 582 | if (req->out.h.error) { | ||
| 583 | free_page((unsigned long) link); | ||
| 584 | link = ERR_PTR(req->out.h.error); | ||
| 585 | } else | ||
| 586 | link[req->out.args[0].size] = '\0'; | ||
| 587 | out: | ||
| 588 | fuse_put_request(fc, req); | ||
| 589 | fuse_invalidate_attr(inode); /* atime changed */ | ||
| 590 | return link; | ||
| 591 | } | ||
| 592 | |||
| 593 | static void free_link(char *link) | ||
| 594 | { | ||
| 595 | if (!IS_ERR(link)) | ||
| 596 | free_page((unsigned long) link); | ||
| 597 | } | ||
| 598 | |||
| 599 | static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
| 600 | { | ||
| 601 | nd_set_link(nd, read_link(dentry)); | ||
| 602 | return NULL; | ||
| 603 | } | ||
| 604 | |||
| 605 | static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c) | ||
| 606 | { | ||
| 607 | free_link(nd_get_link(nd)); | ||
| 608 | } | ||
| 609 | |||
| 610 | static int fuse_dir_open(struct inode *inode, struct file *file) | ||
| 611 | { | ||
| 612 | return fuse_open_common(inode, file, 1); | ||
| 613 | } | ||
| 614 | |||
| 615 | static int fuse_dir_release(struct inode *inode, struct file *file) | ||
| 616 | { | ||
| 617 | return fuse_release_common(inode, file, 1); | ||
| 618 | } | ||
| 619 | |||
| 620 | static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync) | ||
| 621 | { | ||
| 622 | /* nfsd can call this with no file */ | ||
| 623 | return file ? fuse_fsync_common(file, de, datasync, 1) : 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | static unsigned iattr_to_fattr(struct iattr *iattr, struct fuse_attr *fattr) | ||
| 627 | { | ||
| 628 | unsigned ivalid = iattr->ia_valid; | ||
| 629 | unsigned fvalid = 0; | ||
| 630 | |||
| 631 | memset(fattr, 0, sizeof(*fattr)); | ||
| 632 | |||
| 633 | if (ivalid & ATTR_MODE) | ||
| 634 | fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode; | ||
| 635 | if (ivalid & ATTR_UID) | ||
| 636 | fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid; | ||
| 637 | if (ivalid & ATTR_GID) | ||
| 638 | fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid; | ||
| 639 | if (ivalid & ATTR_SIZE) | ||
| 640 | fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size; | ||
| 641 | /* You can only _set_ these together (they may change by themselves) */ | ||
| 642 | if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { | ||
| 643 | fvalid |= FATTR_ATIME | FATTR_MTIME; | ||
| 644 | fattr->atime = iattr->ia_atime.tv_sec; | ||
| 645 | fattr->mtime = iattr->ia_mtime.tv_sec; | ||
| 646 | } | ||
| 647 | |||
| 648 | return fvalid; | ||
| 649 | } | ||
| 650 | |||
| 651 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | ||
| 652 | { | ||
| 653 | struct inode *inode = entry->d_inode; | ||
| 654 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 655 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 656 | struct fuse_req *req; | ||
| 657 | struct fuse_setattr_in inarg; | ||
| 658 | struct fuse_attr_out outarg; | ||
| 659 | int err; | ||
| 660 | int is_truncate = 0; | ||
| 661 | |||
| 662 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { | ||
| 663 | err = inode_change_ok(inode, attr); | ||
| 664 | if (err) | ||
| 665 | return err; | ||
| 666 | } | ||
| 667 | |||
| 668 | if (attr->ia_valid & ATTR_SIZE) { | ||
| 669 | unsigned long limit; | ||
| 670 | is_truncate = 1; | ||
| 671 | limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; | ||
| 672 | if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) { | ||
| 673 | send_sig(SIGXFSZ, current, 0); | ||
| 674 | return -EFBIG; | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | req = fuse_get_request(fc); | ||
| 679 | if (!req) | ||
| 680 | return -EINTR; | ||
| 681 | |||
| 682 | memset(&inarg, 0, sizeof(inarg)); | ||
| 683 | inarg.valid = iattr_to_fattr(attr, &inarg.attr); | ||
| 684 | req->in.h.opcode = FUSE_SETATTR; | ||
| 685 | req->in.h.nodeid = get_node_id(inode); | ||
| 686 | req->inode = inode; | ||
| 687 | req->in.numargs = 1; | ||
| 688 | req->in.args[0].size = sizeof(inarg); | ||
| 689 | req->in.args[0].value = &inarg; | ||
| 690 | req->out.numargs = 1; | ||
| 691 | req->out.args[0].size = sizeof(outarg); | ||
| 692 | req->out.args[0].value = &outarg; | ||
| 693 | request_send(fc, req); | ||
| 694 | err = req->out.h.error; | ||
| 695 | fuse_put_request(fc, req); | ||
| 696 | if (!err) { | ||
| 697 | if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { | ||
| 698 | make_bad_inode(inode); | ||
| 699 | err = -EIO; | ||
| 700 | } else { | ||
| 701 | if (is_truncate) { | ||
| 702 | loff_t origsize = i_size_read(inode); | ||
| 703 | i_size_write(inode, outarg.attr.size); | ||
| 704 | if (origsize > outarg.attr.size) | ||
| 705 | vmtruncate(inode, outarg.attr.size); | ||
| 706 | } | ||
| 707 | fuse_change_attributes(inode, &outarg.attr); | ||
| 708 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
| 709 | outarg.attr_valid_nsec); | ||
| 710 | } | ||
| 711 | } else if (err == -EINTR) | ||
| 712 | fuse_invalidate_attr(inode); | ||
| 713 | |||
| 714 | return err; | ||
| 715 | } | ||
| 716 | |||
| 717 | static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | ||
| 718 | struct kstat *stat) | ||
| 719 | { | ||
| 720 | struct inode *inode = entry->d_inode; | ||
| 721 | int err = fuse_revalidate(entry); | ||
| 722 | if (!err) | ||
| 723 | generic_fillattr(inode, stat); | ||
| 724 | |||
| 725 | return err; | ||
| 726 | } | ||
| 727 | |||
| 728 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
| 729 | struct nameidata *nd) | ||
| 730 | { | ||
| 731 | struct inode *inode; | ||
| 732 | int err = fuse_lookup_iget(dir, entry, &inode); | ||
| 733 | if (err) | ||
| 734 | return ERR_PTR(err); | ||
| 735 | if (inode && S_ISDIR(inode->i_mode)) { | ||
| 736 | /* Don't allow creating an alias to a directory */ | ||
| 737 | struct dentry *alias = d_find_alias(inode); | ||
| 738 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { | ||
| 739 | dput(alias); | ||
| 740 | iput(inode); | ||
| 741 | return ERR_PTR(-EIO); | ||
| 742 | } | ||
| 743 | } | ||
| 744 | return d_splice_alias(inode, entry); | ||
| 745 | } | ||
| 746 | |||
| 747 | static int fuse_setxattr(struct dentry *entry, const char *name, | ||
| 748 | const void *value, size_t size, int flags) | ||
| 749 | { | ||
| 750 | struct inode *inode = entry->d_inode; | ||
| 751 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 752 | struct fuse_req *req; | ||
| 753 | struct fuse_setxattr_in inarg; | ||
| 754 | int err; | ||
| 755 | |||
| 756 | if (size > FUSE_XATTR_SIZE_MAX) | ||
| 757 | return -E2BIG; | ||
| 758 | |||
| 759 | if (fc->no_setxattr) | ||
| 760 | return -EOPNOTSUPP; | ||
| 761 | |||
| 762 | req = fuse_get_request(fc); | ||
| 763 | if (!req) | ||
| 764 | return -EINTR; | ||
| 765 | |||
| 766 | memset(&inarg, 0, sizeof(inarg)); | ||
| 767 | inarg.size = size; | ||
| 768 | inarg.flags = flags; | ||
| 769 | req->in.h.opcode = FUSE_SETXATTR; | ||
| 770 | req->in.h.nodeid = get_node_id(inode); | ||
| 771 | req->inode = inode; | ||
| 772 | req->in.numargs = 3; | ||
| 773 | req->in.args[0].size = sizeof(inarg); | ||
| 774 | req->in.args[0].value = &inarg; | ||
| 775 | req->in.args[1].size = strlen(name) + 1; | ||
| 776 | req->in.args[1].value = name; | ||
| 777 | req->in.args[2].size = size; | ||
| 778 | req->in.args[2].value = value; | ||
| 779 | request_send(fc, req); | ||
| 780 | err = req->out.h.error; | ||
| 781 | fuse_put_request(fc, req); | ||
| 782 | if (err == -ENOSYS) { | ||
| 783 | fc->no_setxattr = 1; | ||
| 784 | err = -EOPNOTSUPP; | ||
| 785 | } | ||
| 786 | return err; | ||
| 787 | } | ||
| 788 | |||
| 789 | static ssize_t fuse_getxattr(struct dentry *entry, const char *name, | ||
| 790 | void *value, size_t size) | ||
| 791 | { | ||
| 792 | struct inode *inode = entry->d_inode; | ||
| 793 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 794 | struct fuse_req *req; | ||
| 795 | struct fuse_getxattr_in inarg; | ||
| 796 | struct fuse_getxattr_out outarg; | ||
| 797 | ssize_t ret; | ||
| 798 | |||
| 799 | if (fc->no_getxattr) | ||
| 800 | return -EOPNOTSUPP; | ||
| 801 | |||
| 802 | req = fuse_get_request(fc); | ||
| 803 | if (!req) | ||
| 804 | return -EINTR; | ||
| 805 | |||
| 806 | memset(&inarg, 0, sizeof(inarg)); | ||
| 807 | inarg.size = size; | ||
| 808 | req->in.h.opcode = FUSE_GETXATTR; | ||
| 809 | req->in.h.nodeid = get_node_id(inode); | ||
| 810 | req->inode = inode; | ||
| 811 | req->in.numargs = 2; | ||
| 812 | req->in.args[0].size = sizeof(inarg); | ||
| 813 | req->in.args[0].value = &inarg; | ||
| 814 | req->in.args[1].size = strlen(name) + 1; | ||
| 815 | req->in.args[1].value = name; | ||
| 816 | /* This is really two different operations rolled into one */ | ||
| 817 | req->out.numargs = 1; | ||
| 818 | if (size) { | ||
| 819 | req->out.argvar = 1; | ||
| 820 | req->out.args[0].size = size; | ||
| 821 | req->out.args[0].value = value; | ||
| 822 | } else { | ||
| 823 | req->out.args[0].size = sizeof(outarg); | ||
| 824 | req->out.args[0].value = &outarg; | ||
| 825 | } | ||
| 826 | request_send(fc, req); | ||
| 827 | ret = req->out.h.error; | ||
| 828 | if (!ret) | ||
| 829 | ret = size ? req->out.args[0].size : outarg.size; | ||
| 830 | else { | ||
| 831 | if (ret == -ENOSYS) { | ||
| 832 | fc->no_getxattr = 1; | ||
| 833 | ret = -EOPNOTSUPP; | ||
| 834 | } | ||
| 835 | } | ||
| 836 | fuse_put_request(fc, req); | ||
| 837 | return ret; | ||
| 838 | } | ||
| 839 | |||
| 840 | static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) | ||
| 841 | { | ||
| 842 | struct inode *inode = entry->d_inode; | ||
| 843 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 844 | struct fuse_req *req; | ||
| 845 | struct fuse_getxattr_in inarg; | ||
| 846 | struct fuse_getxattr_out outarg; | ||
| 847 | ssize_t ret; | ||
| 848 | |||
| 849 | if (fc->no_listxattr) | ||
| 850 | return -EOPNOTSUPP; | ||
| 851 | |||
| 852 | req = fuse_get_request(fc); | ||
| 853 | if (!req) | ||
| 854 | return -EINTR; | ||
| 855 | |||
| 856 | memset(&inarg, 0, sizeof(inarg)); | ||
| 857 | inarg.size = size; | ||
| 858 | req->in.h.opcode = FUSE_LISTXATTR; | ||
| 859 | req->in.h.nodeid = get_node_id(inode); | ||
| 860 | req->inode = inode; | ||
| 861 | req->in.numargs = 1; | ||
| 862 | req->in.args[0].size = sizeof(inarg); | ||
| 863 | req->in.args[0].value = &inarg; | ||
| 864 | /* This is really two different operations rolled into one */ | ||
| 865 | req->out.numargs = 1; | ||
| 866 | if (size) { | ||
| 867 | req->out.argvar = 1; | ||
| 868 | req->out.args[0].size = size; | ||
| 869 | req->out.args[0].value = list; | ||
| 870 | } else { | ||
| 871 | req->out.args[0].size = sizeof(outarg); | ||
| 872 | req->out.args[0].value = &outarg; | ||
| 873 | } | ||
| 874 | request_send(fc, req); | ||
| 875 | ret = req->out.h.error; | ||
| 876 | if (!ret) | ||
| 877 | ret = size ? req->out.args[0].size : outarg.size; | ||
| 878 | else { | ||
| 879 | if (ret == -ENOSYS) { | ||
| 880 | fc->no_listxattr = 1; | ||
| 881 | ret = -EOPNOTSUPP; | ||
| 882 | } | ||
| 883 | } | ||
| 884 | fuse_put_request(fc, req); | ||
| 885 | return ret; | ||
| 886 | } | ||
| 887 | |||
| 888 | static int fuse_removexattr(struct dentry *entry, const char *name) | ||
| 889 | { | ||
| 890 | struct inode *inode = entry->d_inode; | ||
| 891 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 892 | struct fuse_req *req; | ||
| 893 | int err; | ||
| 894 | |||
| 895 | if (fc->no_removexattr) | ||
| 896 | return -EOPNOTSUPP; | ||
| 897 | |||
| 898 | req = fuse_get_request(fc); | ||
| 899 | if (!req) | ||
| 900 | return -EINTR; | ||
| 901 | |||
| 902 | req->in.h.opcode = FUSE_REMOVEXATTR; | ||
| 903 | req->in.h.nodeid = get_node_id(inode); | ||
| 904 | req->inode = inode; | ||
| 905 | req->in.numargs = 1; | ||
| 906 | req->in.args[0].size = strlen(name) + 1; | ||
| 907 | req->in.args[0].value = name; | ||
| 908 | request_send(fc, req); | ||
| 909 | err = req->out.h.error; | ||
| 910 | fuse_put_request(fc, req); | ||
| 911 | if (err == -ENOSYS) { | ||
| 912 | fc->no_removexattr = 1; | ||
| 913 | err = -EOPNOTSUPP; | ||
| 914 | } | ||
| 915 | return err; | ||
| 916 | } | ||
| 917 | |||
| 918 | static struct inode_operations fuse_dir_inode_operations = { | ||
| 919 | .lookup = fuse_lookup, | ||
| 920 | .mkdir = fuse_mkdir, | ||
| 921 | .symlink = fuse_symlink, | ||
| 922 | .unlink = fuse_unlink, | ||
| 923 | .rmdir = fuse_rmdir, | ||
| 924 | .rename = fuse_rename, | ||
| 925 | .link = fuse_link, | ||
| 926 | .setattr = fuse_setattr, | ||
| 927 | .create = fuse_create, | ||
| 928 | .mknod = fuse_mknod, | ||
| 929 | .permission = fuse_permission, | ||
| 930 | .getattr = fuse_getattr, | ||
| 931 | .setxattr = fuse_setxattr, | ||
| 932 | .getxattr = fuse_getxattr, | ||
| 933 | .listxattr = fuse_listxattr, | ||
| 934 | .removexattr = fuse_removexattr, | ||
| 935 | }; | ||
| 936 | |||
| 937 | static struct file_operations fuse_dir_operations = { | ||
| 938 | .llseek = generic_file_llseek, | ||
| 939 | .read = generic_read_dir, | ||
| 940 | .readdir = fuse_readdir, | ||
| 941 | .open = fuse_dir_open, | ||
| 942 | .release = fuse_dir_release, | ||
| 943 | .fsync = fuse_dir_fsync, | ||
| 944 | }; | ||
| 945 | |||
| 946 | static struct inode_operations fuse_common_inode_operations = { | ||
| 947 | .setattr = fuse_setattr, | ||
| 948 | .permission = fuse_permission, | ||
| 949 | .getattr = fuse_getattr, | ||
| 950 | .setxattr = fuse_setxattr, | ||
| 951 | .getxattr = fuse_getxattr, | ||
| 952 | .listxattr = fuse_listxattr, | ||
| 953 | .removexattr = fuse_removexattr, | ||
| 954 | }; | ||
| 955 | |||
| 956 | static struct inode_operations fuse_symlink_inode_operations = { | ||
| 957 | .setattr = fuse_setattr, | ||
| 958 | .follow_link = fuse_follow_link, | ||
| 959 | .put_link = fuse_put_link, | ||
| 960 | .readlink = generic_readlink, | ||
| 961 | .getattr = fuse_getattr, | ||
| 962 | .setxattr = fuse_setxattr, | ||
| 963 | .getxattr = fuse_getxattr, | ||
| 964 | .listxattr = fuse_listxattr, | ||
| 965 | .removexattr = fuse_removexattr, | ||
| 966 | }; | ||
| 967 | |||
| 968 | void fuse_init_common(struct inode *inode) | ||
| 969 | { | ||
| 970 | inode->i_op = &fuse_common_inode_operations; | ||
| 971 | } | ||
| 972 | |||
| 973 | void fuse_init_dir(struct inode *inode) | ||
| 974 | { | ||
| 975 | inode->i_op = &fuse_dir_inode_operations; | ||
| 976 | inode->i_fop = &fuse_dir_operations; | ||
| 977 | } | ||
| 978 | |||
| 979 | void fuse_init_symlink(struct inode *inode) | ||
| 980 | { | ||
| 981 | inode->i_op = &fuse_symlink_inode_operations; | ||
| 982 | } | ||
diff --git a/fs/fuse/file.c b/fs/fuse/file.c new file mode 100644 index 000000000000..6454022b0536 --- /dev/null +++ b/fs/fuse/file.c | |||
| @@ -0,0 +1,555 @@ | |||
| 1 | /* | ||
| 2 | FUSE: Filesystem in Userspace | ||
| 3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | ||
| 4 | |||
| 5 | This program can be distributed under the terms of the GNU GPL. | ||
| 6 | See the file COPYING. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "fuse_i.h" | ||
| 10 | |||
| 11 | #include <linux/pagemap.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | |||
| 15 | static struct file_operations fuse_direct_io_file_operations; | ||
| 16 | |||
| 17 | int fuse_open_common(struct inode *inode, struct file *file, int isdir) | ||
| 18 | { | ||
| 19 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 20 | struct fuse_req *req; | ||
| 21 | struct fuse_open_in inarg; | ||
| 22 | struct fuse_open_out outarg; | ||
| 23 | struct fuse_file *ff; | ||
| 24 | int err; | ||
| 25 | |||
| 26 | err = generic_file_open(inode, file); | ||
| 27 | if (err) | ||
| 28 | return err; | ||
| 29 | |||
| 30 | /* If opening the root node, no lookup has been performed on | ||
| 31 | it, so the attributes must be refreshed */ | ||
| 32 | if (get_node_id(inode) == FUSE_ROOT_ID) { | ||
| 33 | int err = fuse_do_getattr(inode); | ||
| 34 | if (err) | ||
| 35 | return err; | ||
| 36 | } | ||
| 37 | |||
| 38 | req = fuse_get_request(fc); | ||
| 39 | if (!req) | ||
| 40 | return -EINTR; | ||
| 41 | |||
| 42 | err = -ENOMEM; | ||
| 43 | ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); | ||
| 44 | if (!ff) | ||
| 45 | goto out_put_request; | ||
| 46 | |||
| 47 | ff->release_req = fuse_request_alloc(); | ||
| 48 | if (!ff->release_req) { | ||
| 49 | kfree(ff); | ||
| 50 | goto out_put_request; | ||
| 51 | } | ||
| 52 | |||
| 53 | memset(&inarg, 0, sizeof(inarg)); | ||
| 54 | inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | ||
| 55 | req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; | ||
| 56 | req->in.h.nodeid = get_node_id(inode); | ||
| 57 | req->inode = inode; | ||
| 58 | req->in.numargs = 1; | ||
| 59 | req->in.args[0].size = sizeof(inarg); | ||
| 60 | req->in.args[0].value = &inarg; | ||
| 61 | req->out.numargs = 1; | ||
| 62 | req->out.args[0].size = sizeof(outarg); | ||
| 63 | req->out.args[0].value = &outarg; | ||
| 64 | request_send(fc, req); | ||
| 65 | err = req->out.h.error; | ||
| 66 | if (err) { | ||
| 67 | fuse_request_free(ff->release_req); | ||
| 68 | kfree(ff); | ||
| 69 | } else { | ||
| 70 | if (!isdir && (outarg.open_flags & FOPEN_DIRECT_IO)) | ||
| 71 | file->f_op = &fuse_direct_io_file_operations; | ||
| 72 | if (!(outarg.open_flags & FOPEN_KEEP_CACHE)) | ||
| 73 | invalidate_inode_pages(inode->i_mapping); | ||
| 74 | ff->fh = outarg.fh; | ||
| 75 | file->private_data = ff; | ||
| 76 | } | ||
| 77 | |||
| 78 | out_put_request: | ||
| 79 | fuse_put_request(fc, req); | ||
| 80 | return err; | ||
| 81 | } | ||
| 82 | |||
| 83 | int fuse_release_common(struct inode *inode, struct file *file, int isdir) | ||
| 84 | { | ||
| 85 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 86 | struct fuse_file *ff = file->private_data; | ||
| 87 | struct fuse_req *req = ff->release_req; | ||
| 88 | struct fuse_release_in *inarg = &req->misc.release_in; | ||
| 89 | |||
| 90 | inarg->fh = ff->fh; | ||
| 91 | inarg->flags = file->f_flags & ~O_EXCL; | ||
| 92 | req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; | ||
| 93 | req->in.h.nodeid = get_node_id(inode); | ||
| 94 | req->inode = inode; | ||
| 95 | req->in.numargs = 1; | ||
| 96 | req->in.args[0].size = sizeof(struct fuse_release_in); | ||
| 97 | req->in.args[0].value = inarg; | ||
| 98 | request_send_background(fc, req); | ||
| 99 | kfree(ff); | ||
| 100 | |||
| 101 | /* Return value is ignored by VFS */ | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | static int fuse_open(struct inode *inode, struct file *file) | ||
| 106 | { | ||
| 107 | return fuse_open_common(inode, file, 0); | ||
| 108 | } | ||
| 109 | |||
| 110 | static int fuse_release(struct inode *inode, struct file *file) | ||
| 111 | { | ||
| 112 | return fuse_release_common(inode, file, 0); | ||
| 113 | } | ||
| 114 | |||
| 115 | static int fuse_flush(struct file *file) | ||
| 116 | { | ||
| 117 | struct inode *inode = file->f_dentry->d_inode; | ||
| 118 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 119 | struct fuse_file *ff = file->private_data; | ||
| 120 | struct fuse_req *req; | ||
| 121 | struct fuse_flush_in inarg; | ||
| 122 | int err; | ||
| 123 | |||
| 124 | if (fc->no_flush) | ||
| 125 | return 0; | ||
| 126 | |||
| 127 | req = fuse_get_request(fc); | ||
| 128 | if (!req) | ||
| 129 | return -EINTR; | ||
| 130 | |||
| 131 | memset(&inarg, 0, sizeof(inarg)); | ||
| 132 | inarg.fh = ff->fh; | ||
| 133 | req->in.h.opcode = FUSE_FLUSH; | ||
| 134 | req->in.h.nodeid = get_node_id(inode); | ||
| 135 | req->inode = inode; | ||
| 136 | req->file = file; | ||
| 137 | req->in.numargs = 1; | ||
| 138 | req->in.args[0].size = sizeof(inarg); | ||
| 139 | req->in.args[0].value = &inarg; | ||
| 140 | request_send(fc, req); | ||
| 141 | err = req->out.h.error; | ||
| 142 | fuse_put_request(fc, req); | ||
| 143 | if (err == -ENOSYS) { | ||
| 144 | fc->no_flush = 1; | ||
| 145 | err = 0; | ||
| 146 | } | ||
| 147 | return err; | ||
| 148 | } | ||
| 149 | |||
| 150 | int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, | ||
| 151 | int isdir) | ||
| 152 | { | ||
| 153 | struct inode *inode = de->d_inode; | ||
| 154 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 155 | struct fuse_file *ff = file->private_data; | ||
| 156 | struct fuse_req *req; | ||
| 157 | struct fuse_fsync_in inarg; | ||
| 158 | int err; | ||
| 159 | |||
| 160 | if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) | ||
| 161 | return 0; | ||
| 162 | |||
| 163 | req = fuse_get_request(fc); | ||
| 164 | if (!req) | ||
| 165 | return -EINTR; | ||
| 166 | |||
| 167 | memset(&inarg, 0, sizeof(inarg)); | ||
| 168 | inarg.fh = ff->fh; | ||
| 169 | inarg.fsync_flags = datasync ? 1 : 0; | ||
| 170 | req->in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC; | ||
| 171 | req->in.h.nodeid = get_node_id(inode); | ||
| 172 | req->inode = inode; | ||
| 173 | req->file = file; | ||
| 174 | req->in.numargs = 1; | ||
| 175 | req->in.args[0].size = sizeof(inarg); | ||
| 176 | req->in.args[0].value = &inarg; | ||
| 177 | request_send(fc, req); | ||
| 178 | err = req->out.h.error; | ||
| 179 | fuse_put_request(fc, req); | ||
| 180 | if (err == -ENOSYS) { | ||
| 181 | if (isdir) | ||
| 182 | fc->no_fsyncdir = 1; | ||
| 183 | else | ||
| 184 | fc->no_fsync = 1; | ||
| 185 | err = 0; | ||
| 186 | } | ||
| 187 | return err; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int fuse_fsync(struct file *file, struct dentry *de, int datasync) | ||
| 191 | { | ||
| 192 | return fuse_fsync_common(file, de, datasync, 0); | ||
| 193 | } | ||
| 194 | |||
| 195 | size_t fuse_send_read_common(struct fuse_req *req, struct file *file, | ||
| 196 | struct inode *inode, loff_t pos, size_t count, | ||
| 197 | int isdir) | ||
| 198 | { | ||
| 199 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 200 | struct fuse_file *ff = file->private_data; | ||
| 201 | struct fuse_read_in inarg; | ||
| 202 | |||
| 203 | memset(&inarg, 0, sizeof(struct fuse_read_in)); | ||
| 204 | inarg.fh = ff->fh; | ||
| 205 | inarg.offset = pos; | ||
| 206 | inarg.size = count; | ||
| 207 | req->in.h.opcode = isdir ? FUSE_READDIR : FUSE_READ; | ||
| 208 | req->in.h.nodeid = get_node_id(inode); | ||
| 209 | req->inode = inode; | ||
| 210 | req->file = file; | ||
| 211 | req->in.numargs = 1; | ||
| 212 | req->in.args[0].size = sizeof(struct fuse_read_in); | ||
| 213 | req->in.args[0].value = &inarg; | ||
| 214 | req->out.argpages = 1; | ||
| 215 | req->out.argvar = 1; | ||
| 216 | req->out.numargs = 1; | ||
| 217 | req->out.args[0].size = count; | ||
| 218 | request_send(fc, req); | ||
| 219 | return req->out.args[0].size; | ||
| 220 | } | ||
| 221 | |||
| 222 | static inline size_t fuse_send_read(struct fuse_req *req, struct file *file, | ||
| 223 | struct inode *inode, loff_t pos, | ||
| 224 | size_t count) | ||
| 225 | { | ||
| 226 | return fuse_send_read_common(req, file, inode, pos, count, 0); | ||
| 227 | } | ||
| 228 | |||
| 229 | static int fuse_readpage(struct file *file, struct page *page) | ||
| 230 | { | ||
| 231 | struct inode *inode = page->mapping->host; | ||
| 232 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 233 | loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT; | ||
| 234 | struct fuse_req *req = fuse_get_request(fc); | ||
| 235 | int err = -EINTR; | ||
| 236 | if (!req) | ||
| 237 | goto out; | ||
| 238 | |||
| 239 | req->out.page_zeroing = 1; | ||
| 240 | req->num_pages = 1; | ||
| 241 | req->pages[0] = page; | ||
| 242 | fuse_send_read(req, file, inode, pos, PAGE_CACHE_SIZE); | ||
| 243 | err = req->out.h.error; | ||
| 244 | fuse_put_request(fc, req); | ||
| 245 | if (!err) | ||
| 246 | SetPageUptodate(page); | ||
| 247 | fuse_invalidate_attr(inode); /* atime changed */ | ||
| 248 | out: | ||
| 249 | unlock_page(page); | ||
| 250 | return err; | ||
| 251 | } | ||
| 252 | |||
| 253 | static int fuse_send_readpages(struct fuse_req *req, struct file *file, | ||
| 254 | struct inode *inode) | ||
| 255 | { | ||
| 256 | loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT; | ||
| 257 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; | ||
| 258 | unsigned i; | ||
| 259 | req->out.page_zeroing = 1; | ||
| 260 | fuse_send_read(req, file, inode, pos, count); | ||
| 261 | for (i = 0; i < req->num_pages; i++) { | ||
| 262 | struct page *page = req->pages[i]; | ||
| 263 | if (!req->out.h.error) | ||
| 264 | SetPageUptodate(page); | ||
| 265 | unlock_page(page); | ||
| 266 | } | ||
| 267 | return req->out.h.error; | ||
| 268 | } | ||
| 269 | |||
| 270 | struct fuse_readpages_data { | ||
| 271 | struct fuse_req *req; | ||
| 272 | struct file *file; | ||
| 273 | struct inode *inode; | ||
| 274 | }; | ||
| 275 | |||
| 276 | static int fuse_readpages_fill(void *_data, struct page *page) | ||
| 277 | { | ||
| 278 | struct fuse_readpages_data *data = _data; | ||
| 279 | struct fuse_req *req = data->req; | ||
| 280 | struct inode *inode = data->inode; | ||
| 281 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 282 | |||
| 283 | if (req->num_pages && | ||
| 284 | (req->num_pages == FUSE_MAX_PAGES_PER_REQ || | ||
| 285 | (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || | ||
| 286 | req->pages[req->num_pages - 1]->index + 1 != page->index)) { | ||
| 287 | int err = fuse_send_readpages(req, data->file, inode); | ||
| 288 | if (err) { | ||
| 289 | unlock_page(page); | ||
| 290 | return err; | ||
| 291 | } | ||
| 292 | fuse_reset_request(req); | ||
| 293 | } | ||
| 294 | req->pages[req->num_pages] = page; | ||
| 295 | req->num_pages ++; | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | static int fuse_readpages(struct file *file, struct address_space *mapping, | ||
| 300 | struct list_head *pages, unsigned nr_pages) | ||
| 301 | { | ||
| 302 | struct inode *inode = mapping->host; | ||
| 303 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 304 | struct fuse_readpages_data data; | ||
| 305 | int err; | ||
| 306 | data.file = file; | ||
| 307 | data.inode = inode; | ||
| 308 | data.req = fuse_get_request(fc); | ||
| 309 | if (!data.req) | ||
| 310 | return -EINTR; | ||
| 311 | |||
| 312 | err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data); | ||
| 313 | if (!err && data.req->num_pages) | ||
| 314 | err = fuse_send_readpages(data.req, file, inode); | ||
| 315 | fuse_put_request(fc, data.req); | ||
| 316 | fuse_invalidate_attr(inode); /* atime changed */ | ||
| 317 | return err; | ||
| 318 | } | ||
| 319 | |||
| 320 | static size_t fuse_send_write(struct fuse_req *req, struct file *file, | ||
| 321 | struct inode *inode, loff_t pos, size_t count) | ||
| 322 | { | ||
| 323 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 324 | struct fuse_file *ff = file->private_data; | ||
| 325 | struct fuse_write_in inarg; | ||
| 326 | struct fuse_write_out outarg; | ||
| 327 | |||
| 328 | memset(&inarg, 0, sizeof(struct fuse_write_in)); | ||
| 329 | inarg.fh = ff->fh; | ||
| 330 | inarg.offset = pos; | ||
| 331 | inarg.size = count; | ||
| 332 | req->in.h.opcode = FUSE_WRITE; | ||
| 333 | req->in.h.nodeid = get_node_id(inode); | ||
| 334 | req->inode = inode; | ||
| 335 | req->file = file; | ||
| 336 | req->in.argpages = 1; | ||
| 337 | req->in.numargs = 2; | ||
| 338 | req->in.args[0].size = sizeof(struct fuse_write_in); | ||
| 339 | req->in.args[0].value = &inarg; | ||
| 340 | req->in.args[1].size = count; | ||
| 341 | req->out.numargs = 1; | ||
| 342 | req->out.args[0].size = sizeof(struct fuse_write_out); | ||
| 343 | req->out.args[0].value = &outarg; | ||
| 344 | request_send(fc, req); | ||
| 345 | return outarg.size; | ||
| 346 | } | ||
| 347 | |||
| 348 | static int fuse_prepare_write(struct file *file, struct page *page, | ||
| 349 | unsigned offset, unsigned to) | ||
| 350 | { | ||
| 351 | /* No op */ | ||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | static int fuse_commit_write(struct file *file, struct page *page, | ||
| 356 | unsigned offset, unsigned to) | ||
| 357 | { | ||
| 358 | int err; | ||
| 359 | size_t nres; | ||
| 360 | unsigned count = to - offset; | ||
| 361 | struct inode *inode = page->mapping->host; | ||
| 362 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 363 | loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; | ||
| 364 | struct fuse_req *req = fuse_get_request(fc); | ||
| 365 | if (!req) | ||
| 366 | return -EINTR; | ||
| 367 | |||
| 368 | req->num_pages = 1; | ||
| 369 | req->pages[0] = page; | ||
| 370 | req->page_offset = offset; | ||
| 371 | nres = fuse_send_write(req, file, inode, pos, count); | ||
| 372 | err = req->out.h.error; | ||
| 373 | fuse_put_request(fc, req); | ||
| 374 | if (!err && nres != count) | ||
| 375 | err = -EIO; | ||
| 376 | if (!err) { | ||
| 377 | pos += count; | ||
| 378 | if (pos > i_size_read(inode)) | ||
| 379 | i_size_write(inode, pos); | ||
| 380 | |||
| 381 | if (offset == 0 && to == PAGE_CACHE_SIZE) { | ||
| 382 | clear_page_dirty(page); | ||
| 383 | SetPageUptodate(page); | ||
| 384 | } | ||
| 385 | } | ||
| 386 | fuse_invalidate_attr(inode); | ||
| 387 | return err; | ||
| 388 | } | ||
| 389 | |||
| 390 | static void fuse_release_user_pages(struct fuse_req *req, int write) | ||
| 391 | { | ||
| 392 | unsigned i; | ||
| 393 | |||
| 394 | for (i = 0; i < req->num_pages; i++) { | ||
| 395 | struct page *page = req->pages[i]; | ||
| 396 | if (write) | ||
| 397 | set_page_dirty_lock(page); | ||
| 398 | put_page(page); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, | ||
| 403 | unsigned nbytes, int write) | ||
| 404 | { | ||
| 405 | unsigned long user_addr = (unsigned long) buf; | ||
| 406 | unsigned offset = user_addr & ~PAGE_MASK; | ||
| 407 | int npages; | ||
| 408 | |||
| 409 | /* This doesn't work with nfsd */ | ||
| 410 | if (!current->mm) | ||
| 411 | return -EPERM; | ||
| 412 | |||
| 413 | nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); | ||
| 414 | npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 415 | npages = min(npages, FUSE_MAX_PAGES_PER_REQ); | ||
| 416 | down_read(¤t->mm->mmap_sem); | ||
| 417 | npages = get_user_pages(current, current->mm, user_addr, npages, write, | ||
| 418 | 0, req->pages, NULL); | ||
| 419 | up_read(¤t->mm->mmap_sem); | ||
| 420 | if (npages < 0) | ||
| 421 | return npages; | ||
| 422 | |||
| 423 | req->num_pages = npages; | ||
| 424 | req->page_offset = offset; | ||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | |||
| 428 | static ssize_t fuse_direct_io(struct file *file, const char __user *buf, | ||
| 429 | size_t count, loff_t *ppos, int write) | ||
| 430 | { | ||
| 431 | struct inode *inode = file->f_dentry->d_inode; | ||
| 432 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 433 | size_t nmax = write ? fc->max_write : fc->max_read; | ||
| 434 | loff_t pos = *ppos; | ||
| 435 | ssize_t res = 0; | ||
| 436 | struct fuse_req *req = fuse_get_request(fc); | ||
| 437 | if (!req) | ||
| 438 | return -EINTR; | ||
| 439 | |||
| 440 | while (count) { | ||
| 441 | size_t tmp; | ||
| 442 | size_t nres; | ||
| 443 | size_t nbytes = min(count, nmax); | ||
| 444 | int err = fuse_get_user_pages(req, buf, nbytes, !write); | ||
| 445 | if (err) { | ||
| 446 | res = err; | ||
| 447 | break; | ||
| 448 | } | ||
| 449 | tmp = (req->num_pages << PAGE_SHIFT) - req->page_offset; | ||
| 450 | nbytes = min(nbytes, tmp); | ||
| 451 | if (write) | ||
| 452 | nres = fuse_send_write(req, file, inode, pos, nbytes); | ||
| 453 | else | ||
| 454 | nres = fuse_send_read(req, file, inode, pos, nbytes); | ||
| 455 | fuse_release_user_pages(req, !write); | ||
| 456 | if (req->out.h.error) { | ||
| 457 | if (!res) | ||
| 458 | res = req->out.h.error; | ||
| 459 | break; | ||
| 460 | } else if (nres > nbytes) { | ||
| 461 | res = -EIO; | ||
| 462 | break; | ||
| 463 | } | ||
| 464 | count -= nres; | ||
| 465 | res += nres; | ||
| 466 | pos += nres; | ||
| 467 | buf += nres; | ||
| 468 | if (nres != nbytes) | ||
| 469 | break; | ||
| 470 | if (count) | ||
| 471 | fuse_reset_request(req); | ||
| 472 | } | ||
| 473 | fuse_put_request(fc, req); | ||
| 474 | if (res > 0) { | ||
| 475 | if (write && pos > i_size_read(inode)) | ||
| 476 | i_size_write(inode, pos); | ||
| 477 | *ppos = pos; | ||
| 478 | } | ||
| 479 | fuse_invalidate_attr(inode); | ||
| 480 | |||
| 481 | return res; | ||
| 482 | } | ||
| 483 | |||
| 484 | static ssize_t fuse_direct_read(struct file *file, char __user *buf, | ||
| 485 | size_t count, loff_t *ppos) | ||
| 486 | { | ||
| 487 | return fuse_direct_io(file, buf, count, ppos, 0); | ||
| 488 | } | ||
| 489 | |||
| 490 | static ssize_t fuse_direct_write(struct file *file, const char __user *buf, | ||
| 491 | size_t count, loff_t *ppos) | ||
| 492 | { | ||
| 493 | struct inode *inode = file->f_dentry->d_inode; | ||
| 494 | ssize_t res; | ||
| 495 | /* Don't allow parallel writes to the same file */ | ||
| 496 | down(&inode->i_sem); | ||
| 497 | res = fuse_direct_io(file, buf, count, ppos, 1); | ||
| 498 | up(&inode->i_sem); | ||
| 499 | return res; | ||
| 500 | } | ||
| 501 | |||
| 502 | static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 503 | { | ||
| 504 | if ((vma->vm_flags & VM_SHARED)) { | ||
| 505 | if ((vma->vm_flags & VM_WRITE)) | ||
| 506 | return -ENODEV; | ||
| 507 | else | ||
| 508 | vma->vm_flags &= ~VM_MAYWRITE; | ||
| 509 | } | ||
| 510 | return generic_file_mmap(file, vma); | ||
| 511 | } | ||
| 512 | |||
| 513 | static int fuse_set_page_dirty(struct page *page) | ||
| 514 | { | ||
| 515 | printk("fuse_set_page_dirty: should not happen\n"); | ||
| 516 | dump_stack(); | ||
| 517 | return 0; | ||
| 518 | } | ||
| 519 | |||
| 520 | static struct file_operations fuse_file_operations = { | ||
| 521 | .llseek = generic_file_llseek, | ||
| 522 | .read = generic_file_read, | ||
| 523 | .write = generic_file_write, | ||
| 524 | .mmap = fuse_file_mmap, | ||
| 525 | .open = fuse_open, | ||
| 526 | .flush = fuse_flush, | ||
| 527 | .release = fuse_release, | ||
| 528 | .fsync = fuse_fsync, | ||
| 529 | .sendfile = generic_file_sendfile, | ||
| 530 | }; | ||
| 531 | |||
| 532 | static struct file_operations fuse_direct_io_file_operations = { | ||
| 533 | .llseek = generic_file_llseek, | ||
| 534 | .read = fuse_direct_read, | ||
| 535 | .write = fuse_direct_write, | ||
| 536 | .open = fuse_open, | ||
| 537 | .flush = fuse_flush, | ||
| 538 | .release = fuse_release, | ||
| 539 | .fsync = fuse_fsync, | ||
| 540 | /* no mmap and sendfile */ | ||
| 541 | }; | ||
| 542 | |||
| 543 | static struct address_space_operations fuse_file_aops = { | ||
| 544 | .readpage = fuse_readpage, | ||
| 545 | .prepare_write = fuse_prepare_write, | ||
| 546 | .commit_write = fuse_commit_write, | ||
| 547 | .readpages = fuse_readpages, | ||
| 548 | .set_page_dirty = fuse_set_page_dirty, | ||
| 549 | }; | ||
| 550 | |||
| 551 | void fuse_init_file_inode(struct inode *inode) | ||
| 552 | { | ||
| 553 | inode->i_fop = &fuse_file_operations; | ||
| 554 | inode->i_data.a_ops = &fuse_file_aops; | ||
| 555 | } | ||
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h new file mode 100644 index 000000000000..24d761518d86 --- /dev/null +++ b/fs/fuse/fuse_i.h | |||
| @@ -0,0 +1,451 @@ | |||
| 1 | /* | ||
| 2 | FUSE: Filesystem in Userspace | ||
| 3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | ||
| 4 | |||
| 5 | This program can be distributed under the terms of the GNU GPL. | ||
| 6 | See the file COPYING. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/fuse.h> | ||
| 10 | #include <linux/fs.h> | ||
| 11 | #include <linux/wait.h> | ||
| 12 | #include <linux/list.h> | ||
| 13 | #include <linux/spinlock.h> | ||
| 14 | #include <linux/mm.h> | ||
| 15 | #include <linux/backing-dev.h> | ||
| 16 | #include <asm/semaphore.h> | ||
| 17 | |||
| 18 | /** Max number of pages that can be used in a single read request */ | ||
| 19 | #define FUSE_MAX_PAGES_PER_REQ 32 | ||
| 20 | |||
| 21 | /** If more requests are outstanding, then the operation will block */ | ||
| 22 | #define FUSE_MAX_OUTSTANDING 10 | ||
| 23 | |||
| 24 | /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem | ||
| 25 | module will check permissions based on the file mode. Otherwise no | ||
| 26 | permission checking is done in the kernel */ | ||
| 27 | #define FUSE_DEFAULT_PERMISSIONS (1 << 0) | ||
| 28 | |||
| 29 | /** If the FUSE_ALLOW_OTHER flag is given, then not only the user | ||
| 30 | doing the mount will be allowed to access the filesystem */ | ||
| 31 | #define FUSE_ALLOW_OTHER (1 << 1) | ||
| 32 | |||
| 33 | |||
| 34 | /** FUSE inode */ | ||
| 35 | struct fuse_inode { | ||
| 36 | /** Inode data */ | ||
| 37 | struct inode inode; | ||
| 38 | |||
| 39 | /** Unique ID, which identifies the inode between userspace | ||
| 40 | * and kernel */ | ||
| 41 | u64 nodeid; | ||
| 42 | |||
| 43 | /** Number of lookups on this inode */ | ||
| 44 | u64 nlookup; | ||
| 45 | |||
| 46 | /** The request used for sending the FORGET message */ | ||
| 47 | struct fuse_req *forget_req; | ||
| 48 | |||
| 49 | /** Time in jiffies until the file attributes are valid */ | ||
| 50 | unsigned long i_time; | ||
| 51 | }; | ||
| 52 | |||
| 53 | /** FUSE specific file data */ | ||
| 54 | struct fuse_file { | ||
| 55 | /** Request reserved for flush and release */ | ||
| 56 | struct fuse_req *release_req; | ||
| 57 | |||
| 58 | /** File handle used by userspace */ | ||
| 59 | u64 fh; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** One input argument of a request */ | ||
| 63 | struct fuse_in_arg { | ||
| 64 | unsigned size; | ||
| 65 | const void *value; | ||
| 66 | }; | ||
| 67 | |||
| 68 | /** The request input */ | ||
| 69 | struct fuse_in { | ||
| 70 | /** The request header */ | ||
| 71 | struct fuse_in_header h; | ||
| 72 | |||
| 73 | /** True if the data for the last argument is in req->pages */ | ||
| 74 | unsigned argpages:1; | ||
| 75 | |||
| 76 | /** Number of arguments */ | ||
| 77 | unsigned numargs; | ||
| 78 | |||
| 79 | /** Array of arguments */ | ||
| 80 | struct fuse_in_arg args[3]; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /** One output argument of a request */ | ||
| 84 | struct fuse_arg { | ||
| 85 | unsigned size; | ||
| 86 | void *value; | ||
| 87 | }; | ||
| 88 | |||
| 89 | /** The request output */ | ||
| 90 | struct fuse_out { | ||
| 91 | /** Header returned from userspace */ | ||
| 92 | struct fuse_out_header h; | ||
| 93 | |||
| 94 | /** Last argument is variable length (can be shorter than | ||
| 95 | arg->size) */ | ||
| 96 | unsigned argvar:1; | ||
| 97 | |||
| 98 | /** Last argument is a list of pages to copy data to */ | ||
| 99 | unsigned argpages:1; | ||
| 100 | |||
| 101 | /** Zero partially or not copied pages */ | ||
| 102 | unsigned page_zeroing:1; | ||
| 103 | |||
| 104 | /** Number or arguments */ | ||
| 105 | unsigned numargs; | ||
| 106 | |||
| 107 | /** Array of arguments */ | ||
| 108 | struct fuse_arg args[3]; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct fuse_req; | ||
| 112 | struct fuse_conn; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * A request to the client | ||
| 116 | */ | ||
| 117 | struct fuse_req { | ||
| 118 | /** This can be on either unused_list, pending or processing | ||
| 119 | lists in fuse_conn */ | ||
| 120 | struct list_head list; | ||
| 121 | |||
| 122 | /** Entry on the background list */ | ||
| 123 | struct list_head bg_entry; | ||
| 124 | |||
| 125 | /** refcount */ | ||
| 126 | atomic_t count; | ||
| 127 | |||
| 128 | /** True if the request has reply */ | ||
| 129 | unsigned isreply:1; | ||
| 130 | |||
| 131 | /** The request is preallocated */ | ||
| 132 | unsigned preallocated:1; | ||
| 133 | |||
| 134 | /** The request was interrupted */ | ||
| 135 | unsigned interrupted:1; | ||
| 136 | |||
| 137 | /** Request is sent in the background */ | ||
| 138 | unsigned background:1; | ||
| 139 | |||
| 140 | /** Data is being copied to/from the request */ | ||
| 141 | unsigned locked:1; | ||
| 142 | |||
| 143 | /** Request has been sent to userspace */ | ||
| 144 | unsigned sent:1; | ||
| 145 | |||
| 146 | /** The request is finished */ | ||
| 147 | unsigned finished:1; | ||
| 148 | |||
| 149 | /** The request input */ | ||
| 150 | struct fuse_in in; | ||
| 151 | |||
| 152 | /** The request output */ | ||
| 153 | struct fuse_out out; | ||
| 154 | |||
| 155 | /** Used to wake up the task waiting for completion of request*/ | ||
| 156 | wait_queue_head_t waitq; | ||
| 157 | |||
| 158 | /** Data for asynchronous requests */ | ||
| 159 | union { | ||
| 160 | struct fuse_forget_in forget_in; | ||
| 161 | struct fuse_release_in release_in; | ||
| 162 | struct fuse_init_in_out init_in_out; | ||
| 163 | } misc; | ||
| 164 | |||
| 165 | /** page vector */ | ||
| 166 | struct page *pages[FUSE_MAX_PAGES_PER_REQ]; | ||
| 167 | |||
| 168 | /** number of pages in vector */ | ||
| 169 | unsigned num_pages; | ||
| 170 | |||
| 171 | /** offset of data on first page */ | ||
| 172 | unsigned page_offset; | ||
| 173 | |||
| 174 | /** Inode used in the request */ | ||
| 175 | struct inode *inode; | ||
| 176 | |||
| 177 | /** Second inode used in the request (or NULL) */ | ||
| 178 | struct inode *inode2; | ||
| 179 | |||
| 180 | /** File used in the request (or NULL) */ | ||
| 181 | struct file *file; | ||
| 182 | }; | ||
| 183 | |||
| 184 | /** | ||
| 185 | * A Fuse connection. | ||
| 186 | * | ||
| 187 | * This structure is created, when the filesystem is mounted, and is | ||
| 188 | * destroyed, when the client device is closed and the filesystem is | ||
| 189 | * unmounted. | ||
| 190 | */ | ||
| 191 | struct fuse_conn { | ||
| 192 | /** Reference count */ | ||
| 193 | int count; | ||
| 194 | |||
| 195 | /** The user id for this mount */ | ||
| 196 | uid_t user_id; | ||
| 197 | |||
| 198 | /** The group id for this mount */ | ||
| 199 | gid_t group_id; | ||
| 200 | |||
| 201 | /** The fuse mount flags for this mount */ | ||
| 202 | unsigned flags; | ||
| 203 | |||
| 204 | /** Maximum read size */ | ||
| 205 | unsigned max_read; | ||
| 206 | |||
| 207 | /** Maximum write size */ | ||
| 208 | unsigned max_write; | ||
| 209 | |||
| 210 | /** Readers of the connection are waiting on this */ | ||
| 211 | wait_queue_head_t waitq; | ||
| 212 | |||
| 213 | /** The list of pending requests */ | ||
| 214 | struct list_head pending; | ||
| 215 | |||
| 216 | /** The list of requests being processed */ | ||
| 217 | struct list_head processing; | ||
| 218 | |||
| 219 | /** Requests put in the background (RELEASE or any other | ||
| 220 | interrupted request) */ | ||
| 221 | struct list_head background; | ||
| 222 | |||
| 223 | /** Controls the maximum number of outstanding requests */ | ||
| 224 | struct semaphore outstanding_sem; | ||
| 225 | |||
| 226 | /** This counts the number of outstanding requests if | ||
| 227 | outstanding_sem would go negative */ | ||
| 228 | unsigned outstanding_debt; | ||
| 229 | |||
| 230 | /** RW semaphore for exclusion with fuse_put_super() */ | ||
| 231 | struct rw_semaphore sbput_sem; | ||
| 232 | |||
| 233 | /** The list of unused requests */ | ||
| 234 | struct list_head unused_list; | ||
| 235 | |||
| 236 | /** The next unique request id */ | ||
| 237 | u64 reqctr; | ||
| 238 | |||
| 239 | /** Mount is active */ | ||
| 240 | unsigned mounted : 1; | ||
| 241 | |||
| 242 | /** Connection established */ | ||
| 243 | unsigned connected : 1; | ||
| 244 | |||
| 245 | /** Connection failed (version mismatch) */ | ||
| 246 | unsigned conn_error : 1; | ||
| 247 | |||
| 248 | /** Is fsync not implemented by fs? */ | ||
| 249 | unsigned no_fsync : 1; | ||
| 250 | |||
| 251 | /** Is fsyncdir not implemented by fs? */ | ||
| 252 | unsigned no_fsyncdir : 1; | ||
| 253 | |||
| 254 | /** Is flush not implemented by fs? */ | ||
| 255 | unsigned no_flush : 1; | ||
| 256 | |||
| 257 | /** Is setxattr not implemented by fs? */ | ||
| 258 | unsigned no_setxattr : 1; | ||
| 259 | |||
| 260 | /** Is getxattr not implemented by fs? */ | ||
| 261 | unsigned no_getxattr : 1; | ||
| 262 | |||
| 263 | /** Is listxattr not implemented by fs? */ | ||
| 264 | unsigned no_listxattr : 1; | ||
| 265 | |||
| 266 | /** Is removexattr not implemented by fs? */ | ||
| 267 | unsigned no_removexattr : 1; | ||
| 268 | |||
| 269 | /** Backing dev info */ | ||
| 270 | struct backing_dev_info bdi; | ||
| 271 | }; | ||
| 272 | |||
| 273 | static inline struct fuse_conn **get_fuse_conn_super_p(struct super_block *sb) | ||
| 274 | { | ||
| 275 | return (struct fuse_conn **) &sb->s_fs_info; | ||
| 276 | } | ||
| 277 | |||
| 278 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) | ||
| 279 | { | ||
| 280 | return *get_fuse_conn_super_p(sb); | ||
| 281 | } | ||
| 282 | |||
| 283 | static inline struct fuse_conn *get_fuse_conn(struct inode *inode) | ||
| 284 | { | ||
| 285 | return get_fuse_conn_super(inode->i_sb); | ||
| 286 | } | ||
| 287 | |||
| 288 | static inline struct fuse_inode *get_fuse_inode(struct inode *inode) | ||
| 289 | { | ||
| 290 | return container_of(inode, struct fuse_inode, inode); | ||
| 291 | } | ||
| 292 | |||
| 293 | static inline u64 get_node_id(struct inode *inode) | ||
| 294 | { | ||
| 295 | return get_fuse_inode(inode)->nodeid; | ||
| 296 | } | ||
| 297 | |||
| 298 | /** Device operations */ | ||
| 299 | extern struct file_operations fuse_dev_operations; | ||
| 300 | |||
| 301 | /** | ||
| 302 | * This is the single global spinlock which protects FUSE's structures | ||
| 303 | * | ||
| 304 | * The following data is protected by this lock: | ||
| 305 | * | ||
| 306 | * - the private_data field of the device file | ||
| 307 | * - the s_fs_info field of the super block | ||
| 308 | * - unused_list, pending, processing lists in fuse_conn | ||
| 309 | * - background list in fuse_conn | ||
| 310 | * - the unique request ID counter reqctr in fuse_conn | ||
| 311 | * - the sb (super_block) field in fuse_conn | ||
| 312 | * - the file (device file) field in fuse_conn | ||
| 313 | */ | ||
| 314 | extern spinlock_t fuse_lock; | ||
| 315 | |||
| 316 | /** | ||
| 317 | * Get a filled in inode | ||
| 318 | */ | ||
| 319 | struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, | ||
| 320 | int generation, struct fuse_attr *attr); | ||
| 321 | |||
| 322 | /** | ||
| 323 | * Send FORGET command | ||
| 324 | */ | ||
| 325 | void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, | ||
| 326 | unsigned long nodeid, u64 nlookup); | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Send READ or READDIR request | ||
| 330 | */ | ||
| 331 | size_t fuse_send_read_common(struct fuse_req *req, struct file *file, | ||
| 332 | struct inode *inode, loff_t pos, size_t count, | ||
| 333 | int isdir); | ||
| 334 | |||
| 335 | /** | ||
| 336 | * Send OPEN or OPENDIR request | ||
| 337 | */ | ||
| 338 | int fuse_open_common(struct inode *inode, struct file *file, int isdir); | ||
| 339 | |||
| 340 | /** | ||
| 341 | * Send RELEASE or RELEASEDIR request | ||
| 342 | */ | ||
| 343 | int fuse_release_common(struct inode *inode, struct file *file, int isdir); | ||
| 344 | |||
| 345 | /** | ||
| 346 | * Send FSYNC or FSYNCDIR request | ||
| 347 | */ | ||
| 348 | int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, | ||
| 349 | int isdir); | ||
| 350 | |||
| 351 | /** | ||
| 352 | * Initialise file operations on a regular file | ||
| 353 | */ | ||
| 354 | void fuse_init_file_inode(struct inode *inode); | ||
| 355 | |||
| 356 | /** | ||
| 357 | * Initialise inode operations on regular files and special files | ||
| 358 | */ | ||
| 359 | void fuse_init_common(struct inode *inode); | ||
| 360 | |||
| 361 | /** | ||
| 362 | * Initialise inode and file operations on a directory | ||
| 363 | */ | ||
| 364 | void fuse_init_dir(struct inode *inode); | ||
| 365 | |||
| 366 | /** | ||
| 367 | * Initialise inode operations on a symlink | ||
| 368 | */ | ||
| 369 | void fuse_init_symlink(struct inode *inode); | ||
| 370 | |||
| 371 | /** | ||
| 372 | * Change attributes of an inode | ||
| 373 | */ | ||
| 374 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr); | ||
| 375 | |||
| 376 | /** | ||
| 377 | * Check if the connection can be released, and if yes, then free the | ||
| 378 | * connection structure | ||
| 379 | */ | ||
| 380 | void fuse_release_conn(struct fuse_conn *fc); | ||
| 381 | |||
| 382 | /** | ||
| 383 | * Initialize the client device | ||
| 384 | */ | ||
| 385 | int fuse_dev_init(void); | ||
| 386 | |||
| 387 | /** | ||
| 388 | * Cleanup the client device | ||
| 389 | */ | ||
| 390 | void fuse_dev_cleanup(void); | ||
| 391 | |||
| 392 | /** | ||
| 393 | * Allocate a request | ||
| 394 | */ | ||
| 395 | struct fuse_req *fuse_request_alloc(void); | ||
| 396 | |||
| 397 | /** | ||
| 398 | * Free a request | ||
| 399 | */ | ||
| 400 | void fuse_request_free(struct fuse_req *req); | ||
| 401 | |||
| 402 | /** | ||
| 403 | * Reinitialize a request, the preallocated flag is left unmodified | ||
| 404 | */ | ||
| 405 | void fuse_reset_request(struct fuse_req *req); | ||
| 406 | |||
| 407 | /** | ||
| 408 | * Reserve a preallocated request | ||
| 409 | */ | ||
| 410 | struct fuse_req *fuse_get_request(struct fuse_conn *fc); | ||
| 411 | |||
| 412 | /** | ||
| 413 | * Decrement reference count of a request. If count goes to zero put | ||
| 414 | * on unused list (preallocated) or free reqest (not preallocated). | ||
| 415 | */ | ||
| 416 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req); | ||
| 417 | |||
| 418 | /** | ||
| 419 | * Send a request (synchronous) | ||
| 420 | */ | ||
| 421 | void request_send(struct fuse_conn *fc, struct fuse_req *req); | ||
| 422 | |||
| 423 | /** | ||
| 424 | * Send a request with no reply | ||
| 425 | */ | ||
| 426 | void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); | ||
| 427 | |||
| 428 | /** | ||
| 429 | * Send a request in the background | ||
| 430 | */ | ||
| 431 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req); | ||
| 432 | |||
| 433 | /** | ||
| 434 | * Release inodes and file assiciated with background request | ||
| 435 | */ | ||
| 436 | void fuse_release_background(struct fuse_req *req); | ||
| 437 | |||
| 438 | /** | ||
| 439 | * Get the attributes of a file | ||
| 440 | */ | ||
| 441 | int fuse_do_getattr(struct inode *inode); | ||
| 442 | |||
| 443 | /** | ||
| 444 | * Invalidate inode attributes | ||
| 445 | */ | ||
| 446 | void fuse_invalidate_attr(struct inode *inode); | ||
| 447 | |||
| 448 | /** | ||
| 449 | * Send the INIT message | ||
| 450 | */ | ||
| 451 | void fuse_send_init(struct fuse_conn *fc); | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c new file mode 100644 index 000000000000..e69a546844d0 --- /dev/null +++ b/fs/fuse/inode.c | |||
| @@ -0,0 +1,591 @@ | |||
| 1 | /* | ||
| 2 | FUSE: Filesystem in Userspace | ||
| 3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | ||
| 4 | |||
| 5 | This program can be distributed under the terms of the GNU GPL. | ||
| 6 | See the file COPYING. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "fuse_i.h" | ||
| 10 | |||
| 11 | #include <linux/pagemap.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/file.h> | ||
| 14 | #include <linux/mount.h> | ||
| 15 | #include <linux/seq_file.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/parser.h> | ||
| 19 | #include <linux/statfs.h> | ||
| 20 | |||
| 21 | MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); | ||
| 22 | MODULE_DESCRIPTION("Filesystem in Userspace"); | ||
| 23 | MODULE_LICENSE("GPL"); | ||
| 24 | |||
| 25 | spinlock_t fuse_lock; | ||
| 26 | static kmem_cache_t *fuse_inode_cachep; | ||
| 27 | |||
| 28 | #define FUSE_SUPER_MAGIC 0x65735546 | ||
| 29 | |||
| 30 | struct fuse_mount_data { | ||
| 31 | int fd; | ||
| 32 | unsigned rootmode; | ||
| 33 | unsigned user_id; | ||
| 34 | unsigned group_id; | ||
| 35 | unsigned fd_present : 1; | ||
| 36 | unsigned rootmode_present : 1; | ||
| 37 | unsigned user_id_present : 1; | ||
| 38 | unsigned group_id_present : 1; | ||
| 39 | unsigned flags; | ||
| 40 | unsigned max_read; | ||
| 41 | }; | ||
| 42 | |||
| 43 | static struct inode *fuse_alloc_inode(struct super_block *sb) | ||
| 44 | { | ||
| 45 | struct inode *inode; | ||
| 46 | struct fuse_inode *fi; | ||
| 47 | |||
| 48 | inode = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL); | ||
| 49 | if (!inode) | ||
| 50 | return NULL; | ||
| 51 | |||
| 52 | fi = get_fuse_inode(inode); | ||
| 53 | fi->i_time = jiffies - 1; | ||
| 54 | fi->nodeid = 0; | ||
| 55 | fi->nlookup = 0; | ||
| 56 | fi->forget_req = fuse_request_alloc(); | ||
| 57 | if (!fi->forget_req) { | ||
| 58 | kmem_cache_free(fuse_inode_cachep, inode); | ||
| 59 | return NULL; | ||
| 60 | } | ||
| 61 | |||
| 62 | return inode; | ||
| 63 | } | ||
| 64 | |||
| 65 | static void fuse_destroy_inode(struct inode *inode) | ||
| 66 | { | ||
| 67 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 68 | if (fi->forget_req) | ||
| 69 | fuse_request_free(fi->forget_req); | ||
| 70 | kmem_cache_free(fuse_inode_cachep, inode); | ||
| 71 | } | ||
| 72 | |||
| 73 | static void fuse_read_inode(struct inode *inode) | ||
| 74 | { | ||
| 75 | /* No op */ | ||
| 76 | } | ||
| 77 | |||
| 78 | void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, | ||
| 79 | unsigned long nodeid, u64 nlookup) | ||
| 80 | { | ||
| 81 | struct fuse_forget_in *inarg = &req->misc.forget_in; | ||
| 82 | inarg->nlookup = nlookup; | ||
| 83 | req->in.h.opcode = FUSE_FORGET; | ||
| 84 | req->in.h.nodeid = nodeid; | ||
| 85 | req->in.numargs = 1; | ||
| 86 | req->in.args[0].size = sizeof(struct fuse_forget_in); | ||
| 87 | req->in.args[0].value = inarg; | ||
| 88 | request_send_noreply(fc, req); | ||
| 89 | } | ||
| 90 | |||
| 91 | static void fuse_clear_inode(struct inode *inode) | ||
| 92 | { | ||
| 93 | if (inode->i_sb->s_flags & MS_ACTIVE) { | ||
| 94 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 95 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
| 96 | fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); | ||
| 97 | fi->forget_req = NULL; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) | ||
| 102 | { | ||
| 103 | if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) | ||
| 104 | invalidate_inode_pages(inode->i_mapping); | ||
| 105 | |||
| 106 | inode->i_ino = attr->ino; | ||
| 107 | inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); | ||
| 108 | inode->i_nlink = attr->nlink; | ||
| 109 | inode->i_uid = attr->uid; | ||
| 110 | inode->i_gid = attr->gid; | ||
| 111 | i_size_write(inode, attr->size); | ||
| 112 | inode->i_blksize = PAGE_CACHE_SIZE; | ||
| 113 | inode->i_blocks = attr->blocks; | ||
| 114 | inode->i_atime.tv_sec = attr->atime; | ||
| 115 | inode->i_atime.tv_nsec = attr->atimensec; | ||
| 116 | inode->i_mtime.tv_sec = attr->mtime; | ||
| 117 | inode->i_mtime.tv_nsec = attr->mtimensec; | ||
| 118 | inode->i_ctime.tv_sec = attr->ctime; | ||
| 119 | inode->i_ctime.tv_nsec = attr->ctimensec; | ||
| 120 | } | ||
| 121 | |||
| 122 | static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) | ||
| 123 | { | ||
| 124 | inode->i_mode = attr->mode & S_IFMT; | ||
| 125 | i_size_write(inode, attr->size); | ||
| 126 | if (S_ISREG(inode->i_mode)) { | ||
| 127 | fuse_init_common(inode); | ||
| 128 | fuse_init_file_inode(inode); | ||
| 129 | } else if (S_ISDIR(inode->i_mode)) | ||
| 130 | fuse_init_dir(inode); | ||
| 131 | else if (S_ISLNK(inode->i_mode)) | ||
| 132 | fuse_init_symlink(inode); | ||
| 133 | else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || | ||
| 134 | S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { | ||
| 135 | fuse_init_common(inode); | ||
| 136 | init_special_inode(inode, inode->i_mode, | ||
| 137 | new_decode_dev(attr->rdev)); | ||
| 138 | } else { | ||
| 139 | /* Don't let user create weird files */ | ||
| 140 | inode->i_mode = S_IFREG; | ||
| 141 | fuse_init_common(inode); | ||
| 142 | fuse_init_file_inode(inode); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | static int fuse_inode_eq(struct inode *inode, void *_nodeidp) | ||
| 147 | { | ||
| 148 | unsigned long nodeid = *(unsigned long *) _nodeidp; | ||
| 149 | if (get_node_id(inode) == nodeid) | ||
| 150 | return 1; | ||
| 151 | else | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int fuse_inode_set(struct inode *inode, void *_nodeidp) | ||
| 156 | { | ||
| 157 | unsigned long nodeid = *(unsigned long *) _nodeidp; | ||
| 158 | get_fuse_inode(inode)->nodeid = nodeid; | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, | ||
| 163 | int generation, struct fuse_attr *attr) | ||
| 164 | { | ||
| 165 | struct inode *inode; | ||
| 166 | struct fuse_inode *fi; | ||
| 167 | struct fuse_conn *fc = get_fuse_conn_super(sb); | ||
| 168 | int retried = 0; | ||
| 169 | |||
| 170 | retry: | ||
| 171 | inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); | ||
| 172 | if (!inode) | ||
| 173 | return NULL; | ||
| 174 | |||
| 175 | if ((inode->i_state & I_NEW)) { | ||
| 176 | inode->i_flags |= S_NOATIME|S_NOCMTIME; | ||
| 177 | inode->i_generation = generation; | ||
| 178 | inode->i_data.backing_dev_info = &fc->bdi; | ||
| 179 | fuse_init_inode(inode, attr); | ||
| 180 | unlock_new_inode(inode); | ||
| 181 | } else if ((inode->i_mode ^ attr->mode) & S_IFMT) { | ||
| 182 | BUG_ON(retried); | ||
| 183 | /* Inode has changed type, any I/O on the old should fail */ | ||
| 184 | make_bad_inode(inode); | ||
| 185 | iput(inode); | ||
| 186 | retried = 1; | ||
| 187 | goto retry; | ||
| 188 | } | ||
| 189 | |||
| 190 | fi = get_fuse_inode(inode); | ||
| 191 | fi->nlookup ++; | ||
| 192 | fuse_change_attributes(inode, attr); | ||
| 193 | return inode; | ||
| 194 | } | ||
| 195 | |||
| 196 | static void fuse_put_super(struct super_block *sb) | ||
| 197 | { | ||
| 198 | struct fuse_conn *fc = get_fuse_conn_super(sb); | ||
| 199 | |||
| 200 | down_write(&fc->sbput_sem); | ||
| 201 | while (!list_empty(&fc->background)) | ||
| 202 | fuse_release_background(list_entry(fc->background.next, | ||
| 203 | struct fuse_req, bg_entry)); | ||
| 204 | |||
| 205 | spin_lock(&fuse_lock); | ||
| 206 | fc->mounted = 0; | ||
| 207 | fc->user_id = 0; | ||
| 208 | fc->group_id = 0; | ||
| 209 | fc->flags = 0; | ||
| 210 | /* Flush all readers on this fs */ | ||
| 211 | wake_up_all(&fc->waitq); | ||
| 212 | up_write(&fc->sbput_sem); | ||
| 213 | fuse_release_conn(fc); | ||
| 214 | spin_unlock(&fuse_lock); | ||
| 215 | } | ||
| 216 | |||
| 217 | static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) | ||
| 218 | { | ||
| 219 | stbuf->f_type = FUSE_SUPER_MAGIC; | ||
| 220 | stbuf->f_bsize = attr->bsize; | ||
| 221 | stbuf->f_blocks = attr->blocks; | ||
| 222 | stbuf->f_bfree = attr->bfree; | ||
| 223 | stbuf->f_bavail = attr->bavail; | ||
| 224 | stbuf->f_files = attr->files; | ||
| 225 | stbuf->f_ffree = attr->ffree; | ||
| 226 | stbuf->f_namelen = attr->namelen; | ||
| 227 | /* fsid is left zero */ | ||
| 228 | } | ||
| 229 | |||
| 230 | static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) | ||
| 231 | { | ||
| 232 | struct fuse_conn *fc = get_fuse_conn_super(sb); | ||
| 233 | struct fuse_req *req; | ||
| 234 | struct fuse_statfs_out outarg; | ||
| 235 | int err; | ||
| 236 | |||
| 237 | req = fuse_get_request(fc); | ||
| 238 | if (!req) | ||
| 239 | return -EINTR; | ||
| 240 | |||
| 241 | req->in.numargs = 0; | ||
| 242 | req->in.h.opcode = FUSE_STATFS; | ||
| 243 | req->out.numargs = 1; | ||
| 244 | req->out.args[0].size = sizeof(outarg); | ||
| 245 | req->out.args[0].value = &outarg; | ||
| 246 | request_send(fc, req); | ||
| 247 | err = req->out.h.error; | ||
| 248 | if (!err) | ||
| 249 | convert_fuse_statfs(buf, &outarg.st); | ||
| 250 | fuse_put_request(fc, req); | ||
| 251 | return err; | ||
| 252 | } | ||
| 253 | |||
| 254 | enum { | ||
| 255 | OPT_FD, | ||
| 256 | OPT_ROOTMODE, | ||
| 257 | OPT_USER_ID, | ||
| 258 | OPT_GROUP_ID, | ||
| 259 | OPT_DEFAULT_PERMISSIONS, | ||
| 260 | OPT_ALLOW_OTHER, | ||
| 261 | OPT_MAX_READ, | ||
| 262 | OPT_ERR | ||
| 263 | }; | ||
| 264 | |||
| 265 | static match_table_t tokens = { | ||
| 266 | {OPT_FD, "fd=%u"}, | ||
| 267 | {OPT_ROOTMODE, "rootmode=%o"}, | ||
| 268 | {OPT_USER_ID, "user_id=%u"}, | ||
| 269 | {OPT_GROUP_ID, "group_id=%u"}, | ||
| 270 | {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, | ||
| 271 | {OPT_ALLOW_OTHER, "allow_other"}, | ||
| 272 | {OPT_MAX_READ, "max_read=%u"}, | ||
| 273 | {OPT_ERR, NULL} | ||
| 274 | }; | ||
| 275 | |||
| 276 | static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) | ||
| 277 | { | ||
| 278 | char *p; | ||
| 279 | memset(d, 0, sizeof(struct fuse_mount_data)); | ||
| 280 | d->max_read = ~0; | ||
| 281 | |||
| 282 | while ((p = strsep(&opt, ",")) != NULL) { | ||
| 283 | int token; | ||
| 284 | int value; | ||
| 285 | substring_t args[MAX_OPT_ARGS]; | ||
| 286 | if (!*p) | ||
| 287 | continue; | ||
| 288 | |||
| 289 | token = match_token(p, tokens, args); | ||
| 290 | switch (token) { | ||
| 291 | case OPT_FD: | ||
| 292 | if (match_int(&args[0], &value)) | ||
| 293 | return 0; | ||
| 294 | d->fd = value; | ||
| 295 | d->fd_present = 1; | ||
| 296 | break; | ||
| 297 | |||
| 298 | case OPT_ROOTMODE: | ||
| 299 | if (match_octal(&args[0], &value)) | ||
| 300 | return 0; | ||
| 301 | d->rootmode = value; | ||
| 302 | d->rootmode_present = 1; | ||
| 303 | break; | ||
| 304 | |||
| 305 | case OPT_USER_ID: | ||
| 306 | if (match_int(&args[0], &value)) | ||
| 307 | return 0; | ||
| 308 | d->user_id = value; | ||
| 309 | d->user_id_present = 1; | ||
| 310 | break; | ||
| 311 | |||
| 312 | case OPT_GROUP_ID: | ||
| 313 | if (match_int(&args[0], &value)) | ||
| 314 | return 0; | ||
| 315 | d->group_id = value; | ||
| 316 | d->group_id_present = 1; | ||
| 317 | break; | ||
| 318 | |||
| 319 | case OPT_DEFAULT_PERMISSIONS: | ||
| 320 | d->flags |= FUSE_DEFAULT_PERMISSIONS; | ||
| 321 | break; | ||
| 322 | |||
| 323 | case OPT_ALLOW_OTHER: | ||
| 324 | d->flags |= FUSE_ALLOW_OTHER; | ||
| 325 | break; | ||
| 326 | |||
| 327 | case OPT_MAX_READ: | ||
| 328 | if (match_int(&args[0], &value)) | ||
| 329 | return 0; | ||
| 330 | d->max_read = value; | ||
| 331 | break; | ||
| 332 | |||
| 333 | default: | ||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | if (!d->fd_present || !d->rootmode_present || | ||
| 339 | !d->user_id_present || !d->group_id_present) | ||
| 340 | return 0; | ||
| 341 | |||
| 342 | return 1; | ||
| 343 | } | ||
| 344 | |||
| 345 | static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) | ||
| 346 | { | ||
| 347 | struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); | ||
| 348 | |||
| 349 | seq_printf(m, ",user_id=%u", fc->user_id); | ||
| 350 | seq_printf(m, ",group_id=%u", fc->group_id); | ||
| 351 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) | ||
| 352 | seq_puts(m, ",default_permissions"); | ||
| 353 | if (fc->flags & FUSE_ALLOW_OTHER) | ||
| 354 | seq_puts(m, ",allow_other"); | ||
| 355 | if (fc->max_read != ~0) | ||
| 356 | seq_printf(m, ",max_read=%u", fc->max_read); | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | static void free_conn(struct fuse_conn *fc) | ||
| 361 | { | ||
| 362 | while (!list_empty(&fc->unused_list)) { | ||
| 363 | struct fuse_req *req; | ||
| 364 | req = list_entry(fc->unused_list.next, struct fuse_req, list); | ||
| 365 | list_del(&req->list); | ||
| 366 | fuse_request_free(req); | ||
| 367 | } | ||
| 368 | kfree(fc); | ||
| 369 | } | ||
| 370 | |||
| 371 | /* Must be called with the fuse lock held */ | ||
| 372 | void fuse_release_conn(struct fuse_conn *fc) | ||
| 373 | { | ||
| 374 | fc->count--; | ||
| 375 | if (!fc->count) | ||
| 376 | free_conn(fc); | ||
| 377 | } | ||
| 378 | |||
| 379 | static struct fuse_conn *new_conn(void) | ||
| 380 | { | ||
| 381 | struct fuse_conn *fc; | ||
| 382 | |||
| 383 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | ||
| 384 | if (fc != NULL) { | ||
| 385 | int i; | ||
| 386 | memset(fc, 0, sizeof(*fc)); | ||
| 387 | init_waitqueue_head(&fc->waitq); | ||
| 388 | INIT_LIST_HEAD(&fc->pending); | ||
| 389 | INIT_LIST_HEAD(&fc->processing); | ||
| 390 | INIT_LIST_HEAD(&fc->unused_list); | ||
| 391 | INIT_LIST_HEAD(&fc->background); | ||
| 392 | sema_init(&fc->outstanding_sem, 0); | ||
| 393 | init_rwsem(&fc->sbput_sem); | ||
| 394 | for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { | ||
| 395 | struct fuse_req *req = fuse_request_alloc(); | ||
| 396 | if (!req) { | ||
| 397 | free_conn(fc); | ||
| 398 | return NULL; | ||
| 399 | } | ||
| 400 | list_add(&req->list, &fc->unused_list); | ||
| 401 | } | ||
| 402 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; | ||
| 403 | fc->bdi.unplug_io_fn = default_unplug_io_fn; | ||
| 404 | fc->reqctr = 0; | ||
| 405 | } | ||
| 406 | return fc; | ||
| 407 | } | ||
| 408 | |||
| 409 | static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) | ||
| 410 | { | ||
| 411 | struct fuse_conn *fc; | ||
| 412 | |||
| 413 | if (file->f_op != &fuse_dev_operations) | ||
| 414 | return ERR_PTR(-EINVAL); | ||
| 415 | fc = new_conn(); | ||
| 416 | if (fc == NULL) | ||
| 417 | return ERR_PTR(-ENOMEM); | ||
| 418 | spin_lock(&fuse_lock); | ||
| 419 | if (file->private_data) { | ||
| 420 | free_conn(fc); | ||
| 421 | fc = ERR_PTR(-EINVAL); | ||
| 422 | } else { | ||
| 423 | file->private_data = fc; | ||
| 424 | *get_fuse_conn_super_p(sb) = fc; | ||
| 425 | fc->mounted = 1; | ||
| 426 | fc->connected = 1; | ||
| 427 | fc->count = 2; | ||
| 428 | } | ||
| 429 | spin_unlock(&fuse_lock); | ||
| 430 | return fc; | ||
| 431 | } | ||
| 432 | |||
| 433 | static struct inode *get_root_inode(struct super_block *sb, unsigned mode) | ||
| 434 | { | ||
| 435 | struct fuse_attr attr; | ||
| 436 | memset(&attr, 0, sizeof(attr)); | ||
| 437 | |||
| 438 | attr.mode = mode; | ||
| 439 | attr.ino = FUSE_ROOT_ID; | ||
| 440 | return fuse_iget(sb, 1, 0, &attr); | ||
| 441 | } | ||
| 442 | |||
| 443 | static struct super_operations fuse_super_operations = { | ||
| 444 | .alloc_inode = fuse_alloc_inode, | ||
| 445 | .destroy_inode = fuse_destroy_inode, | ||
| 446 | .read_inode = fuse_read_inode, | ||
| 447 | .clear_inode = fuse_clear_inode, | ||
| 448 | .put_super = fuse_put_super, | ||
| 449 | .statfs = fuse_statfs, | ||
| 450 | .show_options = fuse_show_options, | ||
| 451 | }; | ||
| 452 | |||
| 453 | static int fuse_fill_super(struct super_block *sb, void *data, int silent) | ||
| 454 | { | ||
| 455 | struct fuse_conn *fc; | ||
| 456 | struct inode *root; | ||
| 457 | struct fuse_mount_data d; | ||
| 458 | struct file *file; | ||
| 459 | int err; | ||
| 460 | |||
| 461 | if (!parse_fuse_opt((char *) data, &d)) | ||
| 462 | return -EINVAL; | ||
| 463 | |||
| 464 | sb->s_blocksize = PAGE_CACHE_SIZE; | ||
| 465 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | ||
| 466 | sb->s_magic = FUSE_SUPER_MAGIC; | ||
| 467 | sb->s_op = &fuse_super_operations; | ||
| 468 | sb->s_maxbytes = MAX_LFS_FILESIZE; | ||
| 469 | |||
| 470 | file = fget(d.fd); | ||
| 471 | if (!file) | ||
| 472 | return -EINVAL; | ||
| 473 | |||
| 474 | fc = get_conn(file, sb); | ||
| 475 | fput(file); | ||
| 476 | if (IS_ERR(fc)) | ||
| 477 | return PTR_ERR(fc); | ||
| 478 | |||
| 479 | fc->flags = d.flags; | ||
| 480 | fc->user_id = d.user_id; | ||
| 481 | fc->group_id = d.group_id; | ||
| 482 | fc->max_read = d.max_read; | ||
| 483 | if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) | ||
| 484 | fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; | ||
| 485 | fc->max_write = FUSE_MAX_IN / 2; | ||
| 486 | |||
| 487 | err = -ENOMEM; | ||
| 488 | root = get_root_inode(sb, d.rootmode); | ||
| 489 | if (root == NULL) | ||
| 490 | goto err; | ||
| 491 | |||
| 492 | sb->s_root = d_alloc_root(root); | ||
| 493 | if (!sb->s_root) { | ||
| 494 | iput(root); | ||
| 495 | goto err; | ||
| 496 | } | ||
| 497 | fuse_send_init(fc); | ||
| 498 | return 0; | ||
| 499 | |||
| 500 | err: | ||
| 501 | spin_lock(&fuse_lock); | ||
| 502 | fuse_release_conn(fc); | ||
| 503 | spin_unlock(&fuse_lock); | ||
| 504 | return err; | ||
| 505 | } | ||
| 506 | |||
| 507 | static struct super_block *fuse_get_sb(struct file_system_type *fs_type, | ||
| 508 | int flags, const char *dev_name, | ||
| 509 | void *raw_data) | ||
| 510 | { | ||
| 511 | return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super); | ||
| 512 | } | ||
| 513 | |||
| 514 | static struct file_system_type fuse_fs_type = { | ||
| 515 | .owner = THIS_MODULE, | ||
| 516 | .name = "fuse", | ||
| 517 | .get_sb = fuse_get_sb, | ||
| 518 | .kill_sb = kill_anon_super, | ||
| 519 | }; | ||
| 520 | |||
| 521 | static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep, | ||
| 522 | unsigned long flags) | ||
| 523 | { | ||
| 524 | struct inode * inode = foo; | ||
| 525 | |||
| 526 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | ||
| 527 | SLAB_CTOR_CONSTRUCTOR) | ||
| 528 | inode_init_once(inode); | ||
| 529 | } | ||
| 530 | |||
| 531 | static int __init fuse_fs_init(void) | ||
| 532 | { | ||
| 533 | int err; | ||
| 534 | |||
| 535 | err = register_filesystem(&fuse_fs_type); | ||
| 536 | if (err) | ||
| 537 | printk("fuse: failed to register filesystem\n"); | ||
| 538 | else { | ||
| 539 | fuse_inode_cachep = kmem_cache_create("fuse_inode", | ||
| 540 | sizeof(struct fuse_inode), | ||
| 541 | 0, SLAB_HWCACHE_ALIGN, | ||
| 542 | fuse_inode_init_once, NULL); | ||
| 543 | if (!fuse_inode_cachep) { | ||
| 544 | unregister_filesystem(&fuse_fs_type); | ||
| 545 | err = -ENOMEM; | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | return err; | ||
| 550 | } | ||
| 551 | |||
| 552 | static void fuse_fs_cleanup(void) | ||
| 553 | { | ||
| 554 | unregister_filesystem(&fuse_fs_type); | ||
| 555 | kmem_cache_destroy(fuse_inode_cachep); | ||
| 556 | } | ||
| 557 | |||
| 558 | static int __init fuse_init(void) | ||
| 559 | { | ||
| 560 | int res; | ||
| 561 | |||
| 562 | printk("fuse init (API version %i.%i)\n", | ||
| 563 | FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); | ||
| 564 | |||
| 565 | spin_lock_init(&fuse_lock); | ||
| 566 | res = fuse_fs_init(); | ||
| 567 | if (res) | ||
| 568 | goto err; | ||
| 569 | |||
| 570 | res = fuse_dev_init(); | ||
| 571 | if (res) | ||
| 572 | goto err_fs_cleanup; | ||
| 573 | |||
| 574 | return 0; | ||
| 575 | |||
| 576 | err_fs_cleanup: | ||
| 577 | fuse_fs_cleanup(); | ||
| 578 | err: | ||
| 579 | return res; | ||
| 580 | } | ||
| 581 | |||
| 582 | static void __exit fuse_exit(void) | ||
| 583 | { | ||
| 584 | printk(KERN_DEBUG "fuse exit\n"); | ||
| 585 | |||
| 586 | fuse_fs_cleanup(); | ||
| 587 | fuse_dev_cleanup(); | ||
| 588 | } | ||
| 589 | |||
| 590 | module_init(fuse_init); | ||
| 591 | module_exit(fuse_exit); | ||
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index b2d18200a003..59c5062cd63f 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c | |||
| @@ -284,6 +284,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) | |||
| 284 | 284 | ||
| 285 | static void hostfs_delete_inode(struct inode *inode) | 285 | static void hostfs_delete_inode(struct inode *inode) |
| 286 | { | 286 | { |
| 287 | truncate_inode_pages(&inode->i_data, 0); | ||
| 287 | if(HOSTFS_I(inode)->fd != -1) { | 288 | if(HOSTFS_I(inode)->fd != -1) { |
| 288 | close_file(&HOSTFS_I(inode)->fd); | 289 | close_file(&HOSTFS_I(inode)->fd); |
| 289 | HOSTFS_I(inode)->fd = -1; | 290 | HOSTFS_I(inode)->fd = -1; |
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 38b1741fa539..e3d17e9ea6c1 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c | |||
| @@ -284,6 +284,7 @@ void hpfs_write_if_changed(struct inode *inode) | |||
| 284 | 284 | ||
| 285 | void hpfs_delete_inode(struct inode *inode) | 285 | void hpfs_delete_inode(struct inode *inode) |
| 286 | { | 286 | { |
| 287 | truncate_inode_pages(&inode->i_data, 0); | ||
| 287 | lock_kernel(); | 288 | lock_kernel(); |
| 288 | hpfs_remove_fnode(inode->i_sb, inode->i_ino); | 289 | hpfs_remove_fnode(inode->i_sb, inode->i_ino); |
| 289 | unlock_kernel(); | 290 | unlock_kernel(); |
diff --git a/fs/inode.c b/fs/inode.c index 71df1b1e8f75..f80a79ff156b 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
| @@ -1034,19 +1034,21 @@ void generic_delete_inode(struct inode *inode) | |||
| 1034 | inodes_stat.nr_inodes--; | 1034 | inodes_stat.nr_inodes--; |
| 1035 | spin_unlock(&inode_lock); | 1035 | spin_unlock(&inode_lock); |
| 1036 | 1036 | ||
| 1037 | if (inode->i_data.nrpages) | ||
| 1038 | truncate_inode_pages(&inode->i_data, 0); | ||
| 1039 | |||
| 1040 | security_inode_delete(inode); | 1037 | security_inode_delete(inode); |
| 1041 | 1038 | ||
| 1042 | if (op->delete_inode) { | 1039 | if (op->delete_inode) { |
| 1043 | void (*delete)(struct inode *) = op->delete_inode; | 1040 | void (*delete)(struct inode *) = op->delete_inode; |
| 1044 | if (!is_bad_inode(inode)) | 1041 | if (!is_bad_inode(inode)) |
| 1045 | DQUOT_INIT(inode); | 1042 | DQUOT_INIT(inode); |
| 1046 | /* s_op->delete_inode internally recalls clear_inode() */ | 1043 | /* Filesystems implementing their own |
| 1044 | * s_op->delete_inode are required to call | ||
| 1045 | * truncate_inode_pages and clear_inode() | ||
| 1046 | * internally */ | ||
| 1047 | delete(inode); | 1047 | delete(inode); |
| 1048 | } else | 1048 | } else { |
| 1049 | truncate_inode_pages(&inode->i_data, 0); | ||
| 1049 | clear_inode(inode); | 1050 | clear_inode(inode); |
| 1051 | } | ||
| 1050 | spin_lock(&inode_lock); | 1052 | spin_lock(&inode_lock); |
| 1051 | hlist_del_init(&inode->i_hash); | 1053 | hlist_del_init(&inode->i_hash); |
| 1052 | spin_unlock(&inode_lock); | 1054 | spin_unlock(&inode_lock); |
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index c6ec66fd8766..49bbc2be3d72 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c | |||
| @@ -1340,8 +1340,7 @@ int journal_stop(handle_t *handle) | |||
| 1340 | if (handle->h_sync) { | 1340 | if (handle->h_sync) { |
| 1341 | do { | 1341 | do { |
| 1342 | old_handle_count = transaction->t_handle_count; | 1342 | old_handle_count = transaction->t_handle_count; |
| 1343 | set_current_state(TASK_UNINTERRUPTIBLE); | 1343 | schedule_timeout_uninterruptible(1); |
| 1344 | schedule_timeout(1); | ||
| 1345 | } while (old_handle_count != transaction->t_handle_count); | 1344 | } while (old_handle_count != transaction->t_handle_count); |
| 1346 | } | 1345 | } |
| 1347 | 1346 | ||
diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 777b90057b89..3dcc6d2162cb 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c | |||
| @@ -1744,6 +1744,7 @@ jffs_delete_inode(struct inode *inode) | |||
| 1744 | D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n", | 1744 | D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n", |
| 1745 | inode->i_ino)); | 1745 | inode->i_ino)); |
| 1746 | 1746 | ||
| 1747 | truncate_inode_pages(&inode->i_data, 0); | ||
| 1747 | lock_kernel(); | 1748 | lock_kernel(); |
| 1748 | inode->i_size = 0; | 1749 | inode->i_size = 0; |
| 1749 | inode->i_blocks = 0; | 1750 | inode->i_blocks = 0; |
diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index 456d7e6e29c2..27f199e94cfc 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c | |||
| @@ -1701,12 +1701,10 @@ jffs_find_file(struct jffs_control *c, __u32 ino) | |||
| 1701 | { | 1701 | { |
| 1702 | struct jffs_file *f; | 1702 | struct jffs_file *f; |
| 1703 | int i = ino % c->hash_len; | 1703 | int i = ino % c->hash_len; |
| 1704 | struct list_head *tmp; | ||
| 1705 | 1704 | ||
| 1706 | D3(printk("jffs_find_file(): ino: %u\n", ino)); | 1705 | D3(printk("jffs_find_file(): ino: %u\n", ino)); |
| 1707 | 1706 | ||
| 1708 | for (tmp = c->hash[i].next; tmp != &c->hash[i]; tmp = tmp->next) { | 1707 | list_for_each_entry(f, &c->hash[i], hash) { |
| 1709 | f = list_entry(tmp, struct jffs_file, hash); | ||
| 1710 | if (ino != f->ino) | 1708 | if (ino != f->ino) |
| 1711 | continue; | 1709 | continue; |
| 1712 | D3(printk("jffs_find_file(): Found file with ino " | 1710 | D3(printk("jffs_find_file(): Found file with ino " |
| @@ -2102,13 +2100,12 @@ jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *)) | |||
| 2102 | int result = 0; | 2100 | int result = 0; |
| 2103 | 2101 | ||
| 2104 | for (pos = 0; pos < c->hash_len; pos++) { | 2102 | for (pos = 0; pos < c->hash_len; pos++) { |
| 2105 | struct list_head *p, *next; | 2103 | struct jffs_file *f, *next; |
| 2106 | for (p = c->hash[pos].next; p != &c->hash[pos]; p = next) { | 2104 | |
| 2107 | /* We need a reference to the next file in the | 2105 | /* We must do _safe, because 'func' might remove the |
| 2108 | list because `func' might remove the current | 2106 | current file 'f' from the list. */ |
| 2109 | file `f'. */ | 2107 | list_for_each_entry_safe(f, next, &c->hash[pos], hash) { |
| 2110 | next = p->next; | 2108 | r = func(f); |
| 2111 | r = func(list_entry(p, struct jffs_file, hash)); | ||
| 2112 | if (r < 0) | 2109 | if (r < 0) |
| 2113 | return r; | 2110 | return r; |
| 2114 | result += r; | 2111 | result += r; |
| @@ -2613,9 +2610,8 @@ jffs_print_hash_table(struct jffs_control *c) | |||
| 2613 | 2610 | ||
| 2614 | printk("JFFS: Dumping the file system's hash table...\n"); | 2611 | printk("JFFS: Dumping the file system's hash table...\n"); |
| 2615 | for (i = 0; i < c->hash_len; i++) { | 2612 | for (i = 0; i < c->hash_len; i++) { |
| 2616 | struct list_head *p; | 2613 | struct jffs_file *f; |
| 2617 | for (p = c->hash[i].next; p != &c->hash[i]; p = p->next) { | 2614 | list_for_each_entry(f, &c->hash[i], hash) { |
| 2618 | struct jffs_file *f=list_entry(p,struct jffs_file,hash); | ||
| 2619 | printk("*** c->hash[%u]: \"%s\" " | 2615 | printk("*** c->hash[%u]: \"%s\" " |
| 2620 | "(ino: %u, pino: %u)\n", | 2616 | "(ino: %u, pino: %u)\n", |
| 2621 | i, (f->name ? f->name : ""), | 2617 | i, (f->name ? f->name : ""), |
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 767c7ecb429e..cff352f4ec18 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c | |||
| @@ -132,6 +132,8 @@ void jfs_delete_inode(struct inode *inode) | |||
| 132 | (JFS_IP(inode)->fileset != cpu_to_le32(FILESYSTEM_I))) | 132 | (JFS_IP(inode)->fileset != cpu_to_le32(FILESYSTEM_I))) |
| 133 | return; | 133 | return; |
| 134 | 134 | ||
| 135 | truncate_inode_pages(&inode->i_data, 0); | ||
| 136 | |||
| 135 | if (test_cflag(COMMIT_Freewmap, inode)) | 137 | if (test_cflag(COMMIT_Freewmap, inode)) |
| 136 | jfs_free_zero_link(inode); | 138 | jfs_free_zero_link(inode); |
| 137 | 139 | ||
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 14b3ce87fa29..87332f30141b 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c | |||
| @@ -299,8 +299,7 @@ nlmclnt_alloc_call(void) | |||
| 299 | return call; | 299 | return call; |
| 300 | } | 300 | } |
| 301 | printk("nlmclnt_alloc_call: failed, waiting for memory\n"); | 301 | printk("nlmclnt_alloc_call: failed, waiting for memory\n"); |
| 302 | current->state = TASK_INTERRUPTIBLE; | 302 | schedule_timeout_interruptible(5*HZ); |
| 303 | schedule_timeout(5*HZ); | ||
| 304 | } | 303 | } |
| 305 | return NULL; | 304 | return NULL; |
| 306 | } | 305 | } |
diff --git a/fs/locks.c b/fs/locks.c index 11956b6179ff..c2c09b4798d6 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
| @@ -2198,21 +2198,23 @@ void steal_locks(fl_owner_t from) | |||
| 2198 | { | 2198 | { |
| 2199 | struct files_struct *files = current->files; | 2199 | struct files_struct *files = current->files; |
| 2200 | int i, j; | 2200 | int i, j; |
| 2201 | struct fdtable *fdt; | ||
| 2201 | 2202 | ||
| 2202 | if (from == files) | 2203 | if (from == files) |
| 2203 | return; | 2204 | return; |
| 2204 | 2205 | ||
| 2205 | lock_kernel(); | 2206 | lock_kernel(); |
| 2206 | j = 0; | 2207 | j = 0; |
| 2208 | fdt = files_fdtable(files); | ||
| 2207 | for (;;) { | 2209 | for (;;) { |
| 2208 | unsigned long set; | 2210 | unsigned long set; |
| 2209 | i = j * __NFDBITS; | 2211 | i = j * __NFDBITS; |
| 2210 | if (i >= files->max_fdset || i >= files->max_fds) | 2212 | if (i >= fdt->max_fdset || i >= fdt->max_fds) |
| 2211 | break; | 2213 | break; |
| 2212 | set = files->open_fds->fds_bits[j++]; | 2214 | set = fdt->open_fds->fds_bits[j++]; |
| 2213 | while (set) { | 2215 | while (set) { |
| 2214 | if (set & 1) { | 2216 | if (set & 1) { |
| 2215 | struct file *file = files->fd[i]; | 2217 | struct file *file = fdt->fd[i]; |
| 2216 | if (file) | 2218 | if (file) |
| 2217 | __steal_locks(file, from); | 2219 | __steal_locks(file, from); |
| 2218 | } | 2220 | } |
diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 3f18c21198d7..790cc0d0e970 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c | |||
| @@ -24,6 +24,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data); | |||
| 24 | 24 | ||
| 25 | static void minix_delete_inode(struct inode *inode) | 25 | static void minix_delete_inode(struct inode *inode) |
| 26 | { | 26 | { |
| 27 | truncate_inode_pages(&inode->i_data, 0); | ||
| 27 | inode->i_size = 0; | 28 | inode->i_size = 0; |
| 28 | minix_truncate(inode); | 29 | minix_truncate(inode); |
| 29 | minix_free_inode(inode); | 30 | minix_free_inode(inode); |
diff --git a/fs/namei.c b/fs/namei.c index 145e852c4bd0..21d85f1ac839 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -1316,10 +1316,8 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
| 1316 | return error; | 1316 | return error; |
| 1317 | DQUOT_INIT(dir); | 1317 | DQUOT_INIT(dir); |
| 1318 | error = dir->i_op->create(dir, dentry, mode, nd); | 1318 | error = dir->i_op->create(dir, dentry, mode, nd); |
| 1319 | if (!error) { | 1319 | if (!error) |
| 1320 | fsnotify_create(dir, dentry->d_name.name); | 1320 | fsnotify_create(dir, dentry->d_name.name); |
| 1321 | security_inode_post_create(dir, dentry, mode); | ||
| 1322 | } | ||
| 1323 | return error; | 1321 | return error; |
| 1324 | } | 1322 | } |
| 1325 | 1323 | ||
| @@ -1635,10 +1633,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | |||
| 1635 | 1633 | ||
| 1636 | DQUOT_INIT(dir); | 1634 | DQUOT_INIT(dir); |
| 1637 | error = dir->i_op->mknod(dir, dentry, mode, dev); | 1635 | error = dir->i_op->mknod(dir, dentry, mode, dev); |
| 1638 | if (!error) { | 1636 | if (!error) |
| 1639 | fsnotify_create(dir, dentry->d_name.name); | 1637 | fsnotify_create(dir, dentry->d_name.name); |
| 1640 | security_inode_post_mknod(dir, dentry, mode, dev); | ||
| 1641 | } | ||
| 1642 | return error; | 1638 | return error; |
| 1643 | } | 1639 | } |
| 1644 | 1640 | ||
| @@ -1708,10 +1704,8 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
| 1708 | 1704 | ||
| 1709 | DQUOT_INIT(dir); | 1705 | DQUOT_INIT(dir); |
| 1710 | error = dir->i_op->mkdir(dir, dentry, mode); | 1706 | error = dir->i_op->mkdir(dir, dentry, mode); |
| 1711 | if (!error) { | 1707 | if (!error) |
| 1712 | fsnotify_mkdir(dir, dentry->d_name.name); | 1708 | fsnotify_mkdir(dir, dentry->d_name.name); |
| 1713 | security_inode_post_mkdir(dir,dentry, mode); | ||
| 1714 | } | ||
| 1715 | return error; | 1709 | return error; |
| 1716 | } | 1710 | } |
| 1717 | 1711 | ||
| @@ -1947,10 +1941,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i | |||
| 1947 | 1941 | ||
| 1948 | DQUOT_INIT(dir); | 1942 | DQUOT_INIT(dir); |
| 1949 | error = dir->i_op->symlink(dir, dentry, oldname); | 1943 | error = dir->i_op->symlink(dir, dentry, oldname); |
| 1950 | if (!error) { | 1944 | if (!error) |
| 1951 | fsnotify_create(dir, dentry->d_name.name); | 1945 | fsnotify_create(dir, dentry->d_name.name); |
| 1952 | security_inode_post_symlink(dir, dentry, oldname); | ||
| 1953 | } | ||
| 1954 | return error; | 1946 | return error; |
| 1955 | } | 1947 | } |
| 1956 | 1948 | ||
| @@ -2020,10 +2012,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de | |||
| 2020 | DQUOT_INIT(dir); | 2012 | DQUOT_INIT(dir); |
| 2021 | error = dir->i_op->link(old_dentry, dir, new_dentry); | 2013 | error = dir->i_op->link(old_dentry, dir, new_dentry); |
| 2022 | up(&old_dentry->d_inode->i_sem); | 2014 | up(&old_dentry->d_inode->i_sem); |
| 2023 | if (!error) { | 2015 | if (!error) |
| 2024 | fsnotify_create(dir, new_dentry->d_name.name); | 2016 | fsnotify_create(dir, new_dentry->d_name.name); |
| 2025 | security_inode_post_link(old_dentry, dir, new_dentry); | ||
| 2026 | } | ||
| 2027 | return error; | 2017 | return error; |
| 2028 | } | 2018 | } |
| 2029 | 2019 | ||
| @@ -2142,11 +2132,8 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, | |||
| 2142 | d_rehash(new_dentry); | 2132 | d_rehash(new_dentry); |
| 2143 | dput(new_dentry); | 2133 | dput(new_dentry); |
| 2144 | } | 2134 | } |
| 2145 | if (!error) { | 2135 | if (!error) |
| 2146 | d_move(old_dentry,new_dentry); | 2136 | d_move(old_dentry,new_dentry); |
| 2147 | security_inode_post_rename(old_dir, old_dentry, | ||
| 2148 | new_dir, new_dentry); | ||
| 2149 | } | ||
| 2150 | return error; | 2137 | return error; |
| 2151 | } | 2138 | } |
| 2152 | 2139 | ||
| @@ -2172,7 +2159,6 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, | |||
| 2172 | /* The following d_move() should become unconditional */ | 2159 | /* The following d_move() should become unconditional */ |
| 2173 | if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) | 2160 | if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) |
| 2174 | d_move(old_dentry, new_dentry); | 2161 | d_move(old_dentry, new_dentry); |
| 2175 | security_inode_post_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
| 2176 | } | 2162 | } |
| 2177 | if (target) | 2163 | if (target) |
| 2178 | up(&target->i_sem); | 2164 | up(&target->i_sem); |
diff --git a/fs/namespace.c b/fs/namespace.c index 34156260c9b6..2fa9fdf7d6f5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -537,7 +537,6 @@ lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) | |||
| 537 | static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) | 537 | static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) |
| 538 | { | 538 | { |
| 539 | struct vfsmount *res, *p, *q, *r, *s; | 539 | struct vfsmount *res, *p, *q, *r, *s; |
| 540 | struct list_head *h; | ||
| 541 | struct nameidata nd; | 540 | struct nameidata nd; |
| 542 | 541 | ||
| 543 | res = q = clone_mnt(mnt, dentry); | 542 | res = q = clone_mnt(mnt, dentry); |
| @@ -546,8 +545,7 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) | |||
| 546 | q->mnt_mountpoint = mnt->mnt_mountpoint; | 545 | q->mnt_mountpoint = mnt->mnt_mountpoint; |
| 547 | 546 | ||
| 548 | p = mnt; | 547 | p = mnt; |
| 549 | for (h = mnt->mnt_mounts.next; h != &mnt->mnt_mounts; h = h->next) { | 548 | list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) { |
| 550 | r = list_entry(h, struct vfsmount, mnt_child); | ||
| 551 | if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry)) | 549 | if (!lives_below_in_same_fs(r->mnt_mountpoint, dentry)) |
| 552 | continue; | 550 | continue; |
| 553 | 551 | ||
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 44795d2f4b30..8c8839203cd5 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c | |||
| @@ -286,6 +286,8 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) | |||
| 286 | static void | 286 | static void |
| 287 | ncp_delete_inode(struct inode *inode) | 287 | ncp_delete_inode(struct inode *inode) |
| 288 | { | 288 | { |
| 289 | truncate_inode_pages(&inode->i_data, 0); | ||
| 290 | |||
| 289 | if (S_ISDIR(inode->i_mode)) { | 291 | if (S_ISDIR(inode->i_mode)) { |
| 290 | DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); | 292 | DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); |
| 291 | } | 293 | } |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 541b418327c8..6922469d6fc5 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
| @@ -146,6 +146,8 @@ nfs_delete_inode(struct inode * inode) | |||
| 146 | { | 146 | { |
| 147 | dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); | 147 | dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); |
| 148 | 148 | ||
| 149 | truncate_inode_pages(&inode->i_data, 0); | ||
| 150 | |||
| 149 | nfs_wb_all(inode); | 151 | nfs_wb_all(inode); |
| 150 | /* | 152 | /* |
| 151 | * The following should never happen... | 153 | * The following should never happen... |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 2681485cf2d0..edc95514046d 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
| @@ -34,8 +34,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | |||
| 34 | res = rpc_call_sync(clnt, msg, flags); | 34 | res = rpc_call_sync(clnt, msg, flags); |
| 35 | if (res != -EJUKEBOX) | 35 | if (res != -EJUKEBOX) |
| 36 | break; | 36 | break; |
| 37 | set_current_state(TASK_INTERRUPTIBLE); | 37 | schedule_timeout_interruptible(NFS_JUKEBOX_RETRY_TIME); |
| 38 | schedule_timeout(NFS_JUKEBOX_RETRY_TIME); | ||
| 39 | res = -ERESTARTSYS; | 38 | res = -ERESTARTSYS; |
| 40 | } while (!signalled()); | 39 | } while (!signalled()); |
| 41 | rpc_clnt_sigunmask(clnt, &oldset); | 40 | rpc_clnt_sigunmask(clnt, &oldset); |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0c5a308e4963..9701ca8c9428 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -2418,14 +2418,11 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) | |||
| 2418 | *timeout = NFS4_POLL_RETRY_MAX; | 2418 | *timeout = NFS4_POLL_RETRY_MAX; |
| 2419 | rpc_clnt_sigmask(clnt, &oldset); | 2419 | rpc_clnt_sigmask(clnt, &oldset); |
| 2420 | if (clnt->cl_intr) { | 2420 | if (clnt->cl_intr) { |
| 2421 | set_current_state(TASK_INTERRUPTIBLE); | 2421 | schedule_timeout_interruptible(*timeout); |
| 2422 | schedule_timeout(*timeout); | ||
| 2423 | if (signalled()) | 2422 | if (signalled()) |
| 2424 | res = -ERESTARTSYS; | 2423 | res = -ERESTARTSYS; |
| 2425 | } else { | 2424 | } else |
| 2426 | set_current_state(TASK_UNINTERRUPTIBLE); | 2425 | schedule_timeout_uninterruptible(*timeout); |
| 2427 | schedule_timeout(*timeout); | ||
| 2428 | } | ||
| 2429 | rpc_clnt_sigunmask(clnt, &oldset); | 2426 | rpc_clnt_sigunmask(clnt, &oldset); |
| 2430 | *timeout <<= 1; | 2427 | *timeout <<= 1; |
| 2431 | return res; | 2428 | return res; |
| @@ -2578,8 +2575,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4 | |||
| 2578 | static unsigned long | 2575 | static unsigned long |
| 2579 | nfs4_set_lock_task_retry(unsigned long timeout) | 2576 | nfs4_set_lock_task_retry(unsigned long timeout) |
| 2580 | { | 2577 | { |
| 2581 | current->state = TASK_INTERRUPTIBLE; | 2578 | schedule_timeout_interruptible(timeout); |
| 2582 | schedule_timeout(timeout); | ||
| 2583 | timeout <<= 1; | 2579 | timeout <<= 1; |
| 2584 | if (timeout > NFS4_LOCK_MAXTIMEOUT) | 2580 | if (timeout > NFS4_LOCK_MAXTIMEOUT) |
| 2585 | return NFS4_LOCK_MAXTIMEOUT; | 2581 | return NFS4_LOCK_MAXTIMEOUT; |
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 9eecc9939dfe..e4fd6134244d 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog | |||
| @@ -22,6 +22,76 @@ ToDo/Notes: | |||
| 22 | - Enable the code for setting the NT4 compatibility flag when we start | 22 | - Enable the code for setting the NT4 compatibility flag when we start |
| 23 | making NTFS 1.2 specific modifications. | 23 | making NTFS 1.2 specific modifications. |
| 24 | 24 | ||
| 25 | 2.1.24 - Lots of bug fixes and support more clean journal states. | ||
| 26 | |||
| 27 | - Support journals ($LogFile) which have been modified by chkdsk. This | ||
| 28 | means users can boot into Windows after we marked the volume dirty. | ||
| 29 | The Windows boot will run chkdsk and then reboot. The user can then | ||
| 30 | immediately boot into Linux rather than having to do a full Windows | ||
| 31 | boot first before rebooting into Linux and we will recognize such a | ||
| 32 | journal and empty it as it is clean by definition. | ||
| 33 | - Support journals ($LogFile) with only one restart page as well as | ||
| 34 | journals with two different restart pages. We sanity check both and | ||
| 35 | either use the only sane one or the more recent one of the two in the | ||
| 36 | case that both are valid. | ||
| 37 | - Modify fs/ntfs/malloc.h::ntfs_malloc_nofs() to do the kmalloc() based | ||
| 38 | allocations with __GFP_HIGHMEM, analogous to how the vmalloc() based | ||
| 39 | allocations are done. | ||
| 40 | - Add fs/ntfs/malloc.h::ntfs_malloc_nofs_nofail() which is analogous to | ||
| 41 | ntfs_malloc_nofs() but it performs allocations with __GFP_NOFAIL and | ||
| 42 | hence cannot fail. | ||
| 43 | - Use ntfs_malloc_nofs_nofail() in the two critical regions in | ||
| 44 | fs/ntfs/runlist.c::ntfs_runlists_merge(). This means we no longer | ||
| 45 | need to panic() if the allocation fails as it now cannot fail. | ||
| 46 | - Fix two nasty runlist merging bugs that had gone unnoticed so far. | ||
| 47 | Thanks to Stefano Picerno for the bug report. | ||
| 48 | - Remove two bogus BUG_ON()s from fs/ntfs/mft.c. | ||
| 49 | - Fix handling of valid but empty mapping pairs array in | ||
| 50 | fs/ntfs/runlist.c::ntfs_mapping_pairs_decompress(). | ||
| 51 | - Report unrepresentable inodes during ntfs_readdir() as KERN_WARNING | ||
| 52 | messages and include the inode number. Thanks to Yura Pakhuchiy for | ||
| 53 | pointing this out. | ||
| 54 | - Change ntfs_rl_truncate_nolock() to throw away the runlist if the new | ||
| 55 | length is zero. | ||
| 56 | - Add runlist.[hc]::ntfs_rl_punch_nolock() which punches a caller | ||
| 57 | specified hole into a runlist. | ||
| 58 | - Fix a bug in fs/ntfs/index.c::ntfs_index_lookup(). When the returned | ||
| 59 | index entry is in the index root, we forgot to set the @ir pointer in | ||
| 60 | the index context. Thanks to Yura Pakhuchiy for finding this bug. | ||
| 61 | - Remove bogus setting of PageError in ntfs_read_compressed_block(). | ||
| 62 | - Add fs/ntfs/attrib.[hc]::ntfs_resident_attr_value_resize(). | ||
| 63 | - Fix a bug in ntfs_map_runlist_nolock() where we forgot to protect | ||
| 64 | access to the allocated size in the ntfs inode with the size lock. | ||
| 65 | - Fix ntfs_attr_vcn_to_lcn_nolock() and ntfs_attr_find_vcn_nolock() to | ||
| 66 | return LCN_ENOENT when there is no runlist and the allocated size is | ||
| 67 | zero. | ||
| 68 | - Fix load_attribute_list() to handle the case of a NULL runlist. | ||
| 69 | - Fix handling of sparse attributes in ntfs_attr_make_non_resident(). | ||
| 70 | - Add BUG() checks to ntfs_attr_make_non_resident() and ntfs_attr_set() | ||
| 71 | to ensure that these functions are never called for compressed or | ||
| 72 | encrypted attributes. | ||
| 73 | - Fix cluster (de)allocators to work when the runlist is NULL and more | ||
| 74 | importantly to take a locked runlist rather than them locking it | ||
| 75 | which leads to lock reversal. | ||
| 76 | - Truncate {a,c,m}time to the ntfs supported time granularity when | ||
| 77 | updating the times in the inode in ntfs_setattr(). | ||
| 78 | - Fixup handling of sparse, compressed, and encrypted attributes in | ||
| 79 | fs/ntfs/inode.c::ntfs_read_locked_{,attr_,index_}inode(), | ||
| 80 | fs/ntfs/aops.c::ntfs_{read,write}page(). | ||
| 81 | - Make ntfs_write_block() not instantiate sparse blocks if they contain | ||
| 82 | only zeroes. | ||
| 83 | - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page | ||
| 84 | lock protection over the buffer submission for i/o which allows the | ||
| 85 | removal of the get_bh()/put_bh() pairs for each buffer. | ||
| 86 | - Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case | ||
| 87 | where a concurrent truncate has truncated the runlist under our feet. | ||
| 88 | - Fix page_has_buffers()/page_buffers() handling in fs/ntfs/aops.c. | ||
| 89 | - In fs/ntfs/aops.c::ntfs_end_buffer_async_read(), use a bit spin lock | ||
| 90 | in the first buffer head instead of a driver global spin lock to | ||
| 91 | improve scalability. | ||
| 92 | - Minor fix to error handling and error message display in | ||
| 93 | fs/ntfs/aops.c::ntfs_prepare_nonresident_write(). | ||
| 94 | |||
| 25 | 2.1.23 - Implement extension of resident files and make writing safe as well as | 95 | 2.1.23 - Implement extension of resident files and make writing safe as well as |
| 26 | many bug fixes, cleanups, and enhancements... | 96 | many bug fixes, cleanups, and enhancements... |
| 27 | 97 | ||
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index f083f27d8b69..894b2b876d35 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile | |||
| @@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ | |||
| 6 | index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ | 6 | index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ |
| 7 | unistr.o upcase.o | 7 | unistr.o upcase.o |
| 8 | 8 | ||
| 9 | EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.23\" | 9 | EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24\" |
| 10 | 10 | ||
| 11 | ifeq ($(CONFIG_NTFS_DEBUG),y) | 11 | ifeq ($(CONFIG_NTFS_DEBUG),y) |
| 12 | EXTRA_CFLAGS += -DDEBUG | 12 | EXTRA_CFLAGS += -DDEBUG |
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 78adad7a988d..b6cc8cf24626 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/swap.h> | 27 | #include <linux/swap.h> |
| 28 | #include <linux/buffer_head.h> | 28 | #include <linux/buffer_head.h> |
| 29 | #include <linux/writeback.h> | 29 | #include <linux/writeback.h> |
| 30 | #include <linux/bit_spinlock.h> | ||
| 30 | 31 | ||
| 31 | #include "aops.h" | 32 | #include "aops.h" |
| 32 | #include "attrib.h" | 33 | #include "attrib.h" |
| @@ -55,9 +56,8 @@ | |||
| 55 | */ | 56 | */ |
| 56 | static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) | 57 | static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) |
| 57 | { | 58 | { |
| 58 | static DEFINE_SPINLOCK(page_uptodate_lock); | ||
| 59 | unsigned long flags; | 59 | unsigned long flags; |
| 60 | struct buffer_head *tmp; | 60 | struct buffer_head *first, *tmp; |
| 61 | struct page *page; | 61 | struct page *page; |
| 62 | ntfs_inode *ni; | 62 | ntfs_inode *ni; |
| 63 | int page_uptodate = 1; | 63 | int page_uptodate = 1; |
| @@ -89,11 +89,13 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) | |||
| 89 | } | 89 | } |
| 90 | } else { | 90 | } else { |
| 91 | clear_buffer_uptodate(bh); | 91 | clear_buffer_uptodate(bh); |
| 92 | SetPageError(page); | ||
| 92 | ntfs_error(ni->vol->sb, "Buffer I/O error, logical block %llu.", | 93 | ntfs_error(ni->vol->sb, "Buffer I/O error, logical block %llu.", |
| 93 | (unsigned long long)bh->b_blocknr); | 94 | (unsigned long long)bh->b_blocknr); |
| 94 | SetPageError(page); | ||
| 95 | } | 95 | } |
| 96 | spin_lock_irqsave(&page_uptodate_lock, flags); | 96 | first = page_buffers(page); |
| 97 | local_irq_save(flags); | ||
| 98 | bit_spin_lock(BH_Uptodate_Lock, &first->b_state); | ||
| 97 | clear_buffer_async_read(bh); | 99 | clear_buffer_async_read(bh); |
| 98 | unlock_buffer(bh); | 100 | unlock_buffer(bh); |
| 99 | tmp = bh; | 101 | tmp = bh; |
| @@ -108,7 +110,8 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) | |||
| 108 | } | 110 | } |
| 109 | tmp = tmp->b_this_page; | 111 | tmp = tmp->b_this_page; |
| 110 | } while (tmp != bh); | 112 | } while (tmp != bh); |
| 111 | spin_unlock_irqrestore(&page_uptodate_lock, flags); | 113 | bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); |
| 114 | local_irq_restore(flags); | ||
| 112 | /* | 115 | /* |
| 113 | * If none of the buffers had errors then we can set the page uptodate, | 116 | * If none of the buffers had errors then we can set the page uptodate, |
| 114 | * but we first have to perform the post read mst fixups, if the | 117 | * but we first have to perform the post read mst fixups, if the |
| @@ -141,7 +144,8 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) | |||
| 141 | unlock_page(page); | 144 | unlock_page(page); |
| 142 | return; | 145 | return; |
| 143 | still_busy: | 146 | still_busy: |
| 144 | spin_unlock_irqrestore(&page_uptodate_lock, flags); | 147 | bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); |
| 148 | local_irq_restore(flags); | ||
| 145 | return; | 149 | return; |
| 146 | } | 150 | } |
| 147 | 151 | ||
| @@ -185,13 +189,15 @@ static int ntfs_read_block(struct page *page) | |||
| 185 | blocksize_bits = VFS_I(ni)->i_blkbits; | 189 | blocksize_bits = VFS_I(ni)->i_blkbits; |
| 186 | blocksize = 1 << blocksize_bits; | 190 | blocksize = 1 << blocksize_bits; |
| 187 | 191 | ||
| 188 | if (!page_has_buffers(page)) | 192 | if (!page_has_buffers(page)) { |
| 189 | create_empty_buffers(page, blocksize, 0); | 193 | create_empty_buffers(page, blocksize, 0); |
| 190 | bh = head = page_buffers(page); | 194 | if (unlikely(!page_has_buffers(page))) { |
| 191 | if (unlikely(!bh)) { | 195 | unlock_page(page); |
| 192 | unlock_page(page); | 196 | return -ENOMEM; |
| 193 | return -ENOMEM; | 197 | } |
| 194 | } | 198 | } |
| 199 | bh = head = page_buffers(page); | ||
| 200 | BUG_ON(!bh); | ||
| 195 | 201 | ||
| 196 | iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); | 202 | iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); |
| 197 | read_lock_irqsave(&ni->size_lock, flags); | 203 | read_lock_irqsave(&ni->size_lock, flags); |
| @@ -204,6 +210,7 @@ static int ntfs_read_block(struct page *page) | |||
| 204 | nr = i = 0; | 210 | nr = i = 0; |
| 205 | do { | 211 | do { |
| 206 | u8 *kaddr; | 212 | u8 *kaddr; |
| 213 | int err; | ||
| 207 | 214 | ||
| 208 | if (unlikely(buffer_uptodate(bh))) | 215 | if (unlikely(buffer_uptodate(bh))) |
| 209 | continue; | 216 | continue; |
| @@ -211,6 +218,7 @@ static int ntfs_read_block(struct page *page) | |||
| 211 | arr[nr++] = bh; | 218 | arr[nr++] = bh; |
| 212 | continue; | 219 | continue; |
| 213 | } | 220 | } |
| 221 | err = 0; | ||
| 214 | bh->b_bdev = vol->sb->s_bdev; | 222 | bh->b_bdev = vol->sb->s_bdev; |
| 215 | /* Is the block within the allowed limits? */ | 223 | /* Is the block within the allowed limits? */ |
| 216 | if (iblock < lblock) { | 224 | if (iblock < lblock) { |
| @@ -252,7 +260,6 @@ lock_retry_remap: | |||
| 252 | goto handle_hole; | 260 | goto handle_hole; |
| 253 | /* If first try and runlist unmapped, map and retry. */ | 261 | /* If first try and runlist unmapped, map and retry. */ |
| 254 | if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { | 262 | if (!is_retry && lcn == LCN_RL_NOT_MAPPED) { |
| 255 | int err; | ||
| 256 | is_retry = TRUE; | 263 | is_retry = TRUE; |
| 257 | /* | 264 | /* |
| 258 | * Attempt to map runlist, dropping lock for | 265 | * Attempt to map runlist, dropping lock for |
| @@ -263,20 +270,30 @@ lock_retry_remap: | |||
| 263 | if (likely(!err)) | 270 | if (likely(!err)) |
| 264 | goto lock_retry_remap; | 271 | goto lock_retry_remap; |
| 265 | rl = NULL; | 272 | rl = NULL; |
| 266 | lcn = err; | ||
| 267 | } else if (!rl) | 273 | } else if (!rl) |
| 268 | up_read(&ni->runlist.lock); | 274 | up_read(&ni->runlist.lock); |
| 275 | /* | ||
| 276 | * If buffer is outside the runlist, treat it as a | ||
| 277 | * hole. This can happen due to concurrent truncate | ||
| 278 | * for example. | ||
| 279 | */ | ||
| 280 | if (err == -ENOENT || lcn == LCN_ENOENT) { | ||
| 281 | err = 0; | ||
| 282 | goto handle_hole; | ||
| 283 | } | ||
| 269 | /* Hard error, zero out region. */ | 284 | /* Hard error, zero out region. */ |
| 285 | if (!err) | ||
| 286 | err = -EIO; | ||
| 270 | bh->b_blocknr = -1; | 287 | bh->b_blocknr = -1; |
| 271 | SetPageError(page); | 288 | SetPageError(page); |
| 272 | ntfs_error(vol->sb, "Failed to read from inode 0x%lx, " | 289 | ntfs_error(vol->sb, "Failed to read from inode 0x%lx, " |
| 273 | "attribute type 0x%x, vcn 0x%llx, " | 290 | "attribute type 0x%x, vcn 0x%llx, " |
| 274 | "offset 0x%x because its location on " | 291 | "offset 0x%x because its location on " |
| 275 | "disk could not be determined%s " | 292 | "disk could not be determined%s " |
| 276 | "(error code %lli).", ni->mft_no, | 293 | "(error code %i).", ni->mft_no, |
| 277 | ni->type, (unsigned long long)vcn, | 294 | ni->type, (unsigned long long)vcn, |
| 278 | vcn_ofs, is_retry ? " even after " | 295 | vcn_ofs, is_retry ? " even after " |
| 279 | "retrying" : "", (long long)lcn); | 296 | "retrying" : "", err); |
| 280 | } | 297 | } |
| 281 | /* | 298 | /* |
| 282 | * Either iblock was outside lblock limits or | 299 | * Either iblock was outside lblock limits or |
| @@ -289,9 +306,10 @@ handle_hole: | |||
| 289 | handle_zblock: | 306 | handle_zblock: |
| 290 | kaddr = kmap_atomic(page, KM_USER0); | 307 | kaddr = kmap_atomic(page, KM_USER0); |
| 291 | memset(kaddr + i * blocksize, 0, blocksize); | 308 | memset(kaddr + i * blocksize, 0, blocksize); |
| 292 | flush_dcache_page(page); | ||
| 293 | kunmap_atomic(kaddr, KM_USER0); | 309 | kunmap_atomic(kaddr, KM_USER0); |
| 294 | set_buffer_uptodate(bh); | 310 | flush_dcache_page(page); |
| 311 | if (likely(!err)) | ||
| 312 | set_buffer_uptodate(bh); | ||
| 295 | } while (i++, iblock++, (bh = bh->b_this_page) != head); | 313 | } while (i++, iblock++, (bh = bh->b_this_page) != head); |
| 296 | 314 | ||
| 297 | /* Release the lock if we took it. */ | 315 | /* Release the lock if we took it. */ |
| @@ -367,31 +385,38 @@ retry_readpage: | |||
| 367 | return 0; | 385 | return 0; |
| 368 | } | 386 | } |
| 369 | ni = NTFS_I(page->mapping->host); | 387 | ni = NTFS_I(page->mapping->host); |
| 370 | 388 | /* | |
| 389 | * Only $DATA attributes can be encrypted and only unnamed $DATA | ||
| 390 | * attributes can be compressed. Index root can have the flags set but | ||
| 391 | * this means to create compressed/encrypted files, not that the | ||
| 392 | * attribute is compressed/encrypted. | ||
| 393 | */ | ||
| 394 | if (ni->type != AT_INDEX_ROOT) { | ||
| 395 | /* If attribute is encrypted, deny access, just like NT4. */ | ||
| 396 | if (NInoEncrypted(ni)) { | ||
| 397 | BUG_ON(ni->type != AT_DATA); | ||
| 398 | err = -EACCES; | ||
| 399 | goto err_out; | ||
| 400 | } | ||
| 401 | /* Compressed data streams are handled in compress.c. */ | ||
| 402 | if (NInoNonResident(ni) && NInoCompressed(ni)) { | ||
| 403 | BUG_ON(ni->type != AT_DATA); | ||
| 404 | BUG_ON(ni->name_len); | ||
| 405 | return ntfs_read_compressed_block(page); | ||
| 406 | } | ||
| 407 | } | ||
| 371 | /* NInoNonResident() == NInoIndexAllocPresent() */ | 408 | /* NInoNonResident() == NInoIndexAllocPresent() */ |
| 372 | if (NInoNonResident(ni)) { | 409 | if (NInoNonResident(ni)) { |
| 373 | /* | 410 | /* Normal, non-resident data stream. */ |
| 374 | * Only unnamed $DATA attributes can be compressed or | ||
| 375 | * encrypted. | ||
| 376 | */ | ||
| 377 | if (ni->type == AT_DATA && !ni->name_len) { | ||
| 378 | /* If file is encrypted, deny access, just like NT4. */ | ||
| 379 | if (NInoEncrypted(ni)) { | ||
| 380 | err = -EACCES; | ||
| 381 | goto err_out; | ||
| 382 | } | ||
| 383 | /* Compressed data streams are handled in compress.c. */ | ||
| 384 | if (NInoCompressed(ni)) | ||
| 385 | return ntfs_read_compressed_block(page); | ||
| 386 | } | ||
| 387 | /* Normal data stream. */ | ||
| 388 | return ntfs_read_block(page); | 411 | return ntfs_read_block(page); |
| 389 | } | 412 | } |
| 390 | /* | 413 | /* |
| 391 | * Attribute is resident, implying it is not compressed or encrypted. | 414 | * Attribute is resident, implying it is not compressed or encrypted. |
| 392 | * This also means the attribute is smaller than an mft record and | 415 | * This also means the attribute is smaller than an mft record and |
| 393 | * hence smaller than a page, so can simply zero out any pages with | 416 | * hence smaller than a page, so can simply zero out any pages with |
| 394 | * index above 0. | 417 | * index above 0. Note the attribute can actually be marked compressed |
| 418 | * but if it is resident the actual data is not compressed so we are | ||
| 419 | * ok to ignore the compressed flag here. | ||
| 395 | */ | 420 | */ |
| 396 | if (unlikely(page->index > 0)) { | 421 | if (unlikely(page->index > 0)) { |
| 397 | kaddr = kmap_atomic(page, KM_USER0); | 422 | kaddr = kmap_atomic(page, KM_USER0); |
| @@ -511,19 +536,21 @@ static int ntfs_write_block(struct page *page, struct writeback_control *wbc) | |||
| 511 | BUG_ON(!PageUptodate(page)); | 536 | BUG_ON(!PageUptodate(page)); |
| 512 | create_empty_buffers(page, blocksize, | 537 | create_empty_buffers(page, blocksize, |
| 513 | (1 << BH_Uptodate) | (1 << BH_Dirty)); | 538 | (1 << BH_Uptodate) | (1 << BH_Dirty)); |
| 539 | if (unlikely(!page_has_buffers(page))) { | ||
| 540 | ntfs_warning(vol->sb, "Error allocating page " | ||
| 541 | "buffers. Redirtying page so we try " | ||
| 542 | "again later."); | ||
| 543 | /* | ||
| 544 | * Put the page back on mapping->dirty_pages, but leave | ||
| 545 | * its buffers' dirty state as-is. | ||
| 546 | */ | ||
| 547 | redirty_page_for_writepage(wbc, page); | ||
| 548 | unlock_page(page); | ||
| 549 | return 0; | ||
| 550 | } | ||
| 514 | } | 551 | } |
| 515 | bh = head = page_buffers(page); | 552 | bh = head = page_buffers(page); |
| 516 | if (unlikely(!bh)) { | 553 | BUG_ON(!bh); |
| 517 | ntfs_warning(vol->sb, "Error allocating page buffers. " | ||
| 518 | "Redirtying page so we try again later."); | ||
| 519 | /* | ||
| 520 | * Put the page back on mapping->dirty_pages, but leave its | ||
| 521 | * buffer's dirty state as-is. | ||
| 522 | */ | ||
| 523 | redirty_page_for_writepage(wbc, page); | ||
| 524 | unlock_page(page); | ||
| 525 | return 0; | ||
| 526 | } | ||
| 527 | 554 | ||
| 528 | /* NOTE: Different naming scheme to ntfs_read_block()! */ | 555 | /* NOTE: Different naming scheme to ntfs_read_block()! */ |
| 529 | 556 | ||
| @@ -670,6 +697,27 @@ lock_retry_remap: | |||
| 670 | } | 697 | } |
| 671 | /* It is a hole, need to instantiate it. */ | 698 | /* It is a hole, need to instantiate it. */ |
| 672 | if (lcn == LCN_HOLE) { | 699 | if (lcn == LCN_HOLE) { |
| 700 | u8 *kaddr; | ||
| 701 | unsigned long *bpos, *bend; | ||
| 702 | |||
| 703 | /* Check if the buffer is zero. */ | ||
| 704 | kaddr = kmap_atomic(page, KM_USER0); | ||
| 705 | bpos = (unsigned long *)(kaddr + bh_offset(bh)); | ||
| 706 | bend = (unsigned long *)((u8*)bpos + blocksize); | ||
| 707 | do { | ||
| 708 | if (unlikely(*bpos)) | ||
| 709 | break; | ||
| 710 | } while (likely(++bpos < bend)); | ||
| 711 | kunmap_atomic(kaddr, KM_USER0); | ||
| 712 | if (bpos == bend) { | ||
| 713 | /* | ||
| 714 | * Buffer is zero and sparse, no need to write | ||
| 715 | * it. | ||
| 716 | */ | ||
| 717 | bh->b_blocknr = -1; | ||
| 718 | clear_buffer_dirty(bh); | ||
| 719 | continue; | ||
| 720 | } | ||
| 673 | // TODO: Instantiate the hole. | 721 | // TODO: Instantiate the hole. |
| 674 | // clear_buffer_new(bh); | 722 | // clear_buffer_new(bh); |
| 675 | // unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); | 723 | // unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); |
| @@ -690,20 +738,37 @@ lock_retry_remap: | |||
| 690 | if (likely(!err)) | 738 | if (likely(!err)) |
| 691 | goto lock_retry_remap; | 739 | goto lock_retry_remap; |
| 692 | rl = NULL; | 740 | rl = NULL; |
| 693 | lcn = err; | ||
| 694 | } else if (!rl) | 741 | } else if (!rl) |
| 695 | up_read(&ni->runlist.lock); | 742 | up_read(&ni->runlist.lock); |
| 743 | /* | ||
| 744 | * If buffer is outside the runlist, truncate has cut it out | ||
| 745 | * of the runlist. Just clean and clear the buffer and set it | ||
| 746 | * uptodate so it can get discarded by the VM. | ||
| 747 | */ | ||
| 748 | if (err == -ENOENT || lcn == LCN_ENOENT) { | ||
| 749 | u8 *kaddr; | ||
| 750 | |||
| 751 | bh->b_blocknr = -1; | ||
| 752 | clear_buffer_dirty(bh); | ||
| 753 | kaddr = kmap_atomic(page, KM_USER0); | ||
| 754 | memset(kaddr + bh_offset(bh), 0, blocksize); | ||
| 755 | kunmap_atomic(kaddr, KM_USER0); | ||
| 756 | flush_dcache_page(page); | ||
| 757 | set_buffer_uptodate(bh); | ||
| 758 | err = 0; | ||
| 759 | continue; | ||
| 760 | } | ||
| 696 | /* Failed to map the buffer, even after retrying. */ | 761 | /* Failed to map the buffer, even after retrying. */ |
| 762 | if (!err) | ||
| 763 | err = -EIO; | ||
| 697 | bh->b_blocknr = -1; | 764 | bh->b_blocknr = -1; |
| 698 | ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " | 765 | ntfs_error(vol->sb, "Failed to write to inode 0x%lx, " |
| 699 | "attribute type 0x%x, vcn 0x%llx, offset 0x%x " | 766 | "attribute type 0x%x, vcn 0x%llx, offset 0x%x " |
| 700 | "because its location on disk could not be " | 767 | "because its location on disk could not be " |
| 701 | "determined%s (error code %lli).", ni->mft_no, | 768 | "determined%s (error code %i).", ni->mft_no, |
| 702 | ni->type, (unsigned long long)vcn, | 769 | ni->type, (unsigned long long)vcn, |
| 703 | vcn_ofs, is_retry ? " even after " | 770 | vcn_ofs, is_retry ? " even after " |
| 704 | "retrying" : "", (long long)lcn); | 771 | "retrying" : "", err); |
| 705 | if (!err) | ||
| 706 | err = -EIO; | ||
| 707 | break; | 772 | break; |
| 708 | } while (block++, (bh = bh->b_this_page) != head); | 773 | } while (block++, (bh = bh->b_this_page) != head); |
| 709 | 774 | ||
| @@ -714,7 +779,7 @@ lock_retry_remap: | |||
| 714 | /* For the error case, need to reset bh to the beginning. */ | 779 | /* For the error case, need to reset bh to the beginning. */ |
| 715 | bh = head; | 780 | bh = head; |
| 716 | 781 | ||
| 717 | /* Just an optimization, so ->readpage() isn't called later. */ | 782 | /* Just an optimization, so ->readpage() is not called later. */ |
| 718 | if (unlikely(!PageUptodate(page))) { | 783 | if (unlikely(!PageUptodate(page))) { |
| 719 | int uptodate = 1; | 784 | int uptodate = 1; |
| 720 | do { | 785 | do { |
| @@ -730,7 +795,6 @@ lock_retry_remap: | |||
| 730 | 795 | ||
| 731 | /* Setup all mapped, dirty buffers for async write i/o. */ | 796 | /* Setup all mapped, dirty buffers for async write i/o. */ |
| 732 | do { | 797 | do { |
| 733 | get_bh(bh); | ||
| 734 | if (buffer_mapped(bh) && buffer_dirty(bh)) { | 798 | if (buffer_mapped(bh) && buffer_dirty(bh)) { |
| 735 | lock_buffer(bh); | 799 | lock_buffer(bh); |
| 736 | if (test_clear_buffer_dirty(bh)) { | 800 | if (test_clear_buffer_dirty(bh)) { |
| @@ -768,14 +832,8 @@ lock_retry_remap: | |||
| 768 | 832 | ||
| 769 | BUG_ON(PageWriteback(page)); | 833 | BUG_ON(PageWriteback(page)); |
| 770 | set_page_writeback(page); /* Keeps try_to_free_buffers() away. */ | 834 | set_page_writeback(page); /* Keeps try_to_free_buffers() away. */ |
| 771 | unlock_page(page); | ||
| 772 | 835 | ||
| 773 | /* | 836 | /* Submit the prepared buffers for i/o. */ |
| 774 | * Submit the prepared buffers for i/o. Note the page is unlocked, | ||
| 775 | * and the async write i/o completion handler can end_page_writeback() | ||
| 776 | * at any time after the *first* submit_bh(). So the buffers can then | ||
| 777 | * disappear... | ||
| 778 | */ | ||
| 779 | need_end_writeback = TRUE; | 837 | need_end_writeback = TRUE; |
| 780 | do { | 838 | do { |
| 781 | struct buffer_head *next = bh->b_this_page; | 839 | struct buffer_head *next = bh->b_this_page; |
| @@ -783,9 +841,9 @@ lock_retry_remap: | |||
| 783 | submit_bh(WRITE, bh); | 841 | submit_bh(WRITE, bh); |
| 784 | need_end_writeback = FALSE; | 842 | need_end_writeback = FALSE; |
| 785 | } | 843 | } |
| 786 | put_bh(bh); | ||
| 787 | bh = next; | 844 | bh = next; |
| 788 | } while (bh != head); | 845 | } while (bh != head); |
| 846 | unlock_page(page); | ||
| 789 | 847 | ||
| 790 | /* If no i/o was started, need to end_page_writeback(). */ | 848 | /* If no i/o was started, need to end_page_writeback(). */ |
| 791 | if (unlikely(need_end_writeback)) | 849 | if (unlikely(need_end_writeback)) |
| @@ -860,7 +918,6 @@ static int ntfs_write_mst_block(struct page *page, | |||
| 860 | sync = (wbc->sync_mode == WB_SYNC_ALL); | 918 | sync = (wbc->sync_mode == WB_SYNC_ALL); |
| 861 | 919 | ||
| 862 | /* Make sure we have mapped buffers. */ | 920 | /* Make sure we have mapped buffers. */ |
| 863 | BUG_ON(!page_has_buffers(page)); | ||
| 864 | bh = head = page_buffers(page); | 921 | bh = head = page_buffers(page); |
| 865 | BUG_ON(!bh); | 922 | BUG_ON(!bh); |
| 866 | 923 | ||
| @@ -1280,38 +1337,42 @@ retry_writepage: | |||
| 1280 | ntfs_debug("Write outside i_size - truncated?"); | 1337 | ntfs_debug("Write outside i_size - truncated?"); |
| 1281 | return 0; | 1338 | return 0; |
| 1282 | } | 1339 | } |
| 1340 | /* | ||
| 1341 | * Only $DATA attributes can be encrypted and only unnamed $DATA | ||
| 1342 | * attributes can be compressed. Index root can have the flags set but | ||
| 1343 | * this means to create compressed/encrypted files, not that the | ||
| 1344 | * attribute is compressed/encrypted. | ||
| 1345 | */ | ||
| 1346 | if (ni->type != AT_INDEX_ROOT) { | ||
| 1347 | /* If file is encrypted, deny access, just like NT4. */ | ||
| 1348 | if (NInoEncrypted(ni)) { | ||
| 1349 | unlock_page(page); | ||
| 1350 | BUG_ON(ni->type != AT_DATA); | ||
| 1351 | ntfs_debug("Denying write access to encrypted " | ||
| 1352 | "file."); | ||
| 1353 | return -EACCES; | ||
| 1354 | } | ||
| 1355 | /* Compressed data streams are handled in compress.c. */ | ||
| 1356 | if (NInoNonResident(ni) && NInoCompressed(ni)) { | ||
| 1357 | BUG_ON(ni->type != AT_DATA); | ||
| 1358 | BUG_ON(ni->name_len); | ||
| 1359 | // TODO: Implement and replace this with | ||
| 1360 | // return ntfs_write_compressed_block(page); | ||
| 1361 | unlock_page(page); | ||
| 1362 | ntfs_error(vi->i_sb, "Writing to compressed files is " | ||
| 1363 | "not supported yet. Sorry."); | ||
| 1364 | return -EOPNOTSUPP; | ||
| 1365 | } | ||
| 1366 | // TODO: Implement and remove this check. | ||
| 1367 | if (NInoNonResident(ni) && NInoSparse(ni)) { | ||
| 1368 | unlock_page(page); | ||
| 1369 | ntfs_error(vi->i_sb, "Writing to sparse files is not " | ||
| 1370 | "supported yet. Sorry."); | ||
| 1371 | return -EOPNOTSUPP; | ||
| 1372 | } | ||
| 1373 | } | ||
| 1283 | /* NInoNonResident() == NInoIndexAllocPresent() */ | 1374 | /* NInoNonResident() == NInoIndexAllocPresent() */ |
| 1284 | if (NInoNonResident(ni)) { | 1375 | if (NInoNonResident(ni)) { |
| 1285 | /* | ||
| 1286 | * Only unnamed $DATA attributes can be compressed, encrypted, | ||
| 1287 | * and/or sparse. | ||
| 1288 | */ | ||
| 1289 | if (ni->type == AT_DATA && !ni->name_len) { | ||
| 1290 | /* If file is encrypted, deny access, just like NT4. */ | ||
| 1291 | if (NInoEncrypted(ni)) { | ||
| 1292 | unlock_page(page); | ||
| 1293 | ntfs_debug("Denying write access to encrypted " | ||
| 1294 | "file."); | ||
| 1295 | return -EACCES; | ||
| 1296 | } | ||
| 1297 | /* Compressed data streams are handled in compress.c. */ | ||
| 1298 | if (NInoCompressed(ni)) { | ||
| 1299 | // TODO: Implement and replace this check with | ||
| 1300 | // return ntfs_write_compressed_block(page); | ||
| 1301 | unlock_page(page); | ||
| 1302 | ntfs_error(vi->i_sb, "Writing to compressed " | ||
| 1303 | "files is not supported yet. " | ||
| 1304 | "Sorry."); | ||
| 1305 | return -EOPNOTSUPP; | ||
| 1306 | } | ||
| 1307 | // TODO: Implement and remove this check. | ||
| 1308 | if (NInoSparse(ni)) { | ||
| 1309 | unlock_page(page); | ||
| 1310 | ntfs_error(vi->i_sb, "Writing to sparse files " | ||
| 1311 | "is not supported yet. Sorry."); | ||
| 1312 | return -EOPNOTSUPP; | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | /* We have to zero every time due to mmap-at-end-of-file. */ | 1376 | /* We have to zero every time due to mmap-at-end-of-file. */ |
| 1316 | if (page->index >= (i_size >> PAGE_CACHE_SHIFT)) { | 1377 | if (page->index >= (i_size >> PAGE_CACHE_SHIFT)) { |
| 1317 | /* The page straddles i_size. */ | 1378 | /* The page straddles i_size. */ |
| @@ -1324,14 +1385,16 @@ retry_writepage: | |||
| 1324 | /* Handle mst protected attributes. */ | 1385 | /* Handle mst protected attributes. */ |
| 1325 | if (NInoMstProtected(ni)) | 1386 | if (NInoMstProtected(ni)) |
| 1326 | return ntfs_write_mst_block(page, wbc); | 1387 | return ntfs_write_mst_block(page, wbc); |
| 1327 | /* Normal data stream. */ | 1388 | /* Normal, non-resident data stream. */ |
| 1328 | return ntfs_write_block(page, wbc); | 1389 | return ntfs_write_block(page, wbc); |
| 1329 | } | 1390 | } |
| 1330 | /* | 1391 | /* |
| 1331 | * Attribute is resident, implying it is not compressed, encrypted, | 1392 | * Attribute is resident, implying it is not compressed, encrypted, or |
| 1332 | * sparse, or mst protected. This also means the attribute is smaller | 1393 | * mst protected. This also means the attribute is smaller than an mft |
| 1333 | * than an mft record and hence smaller than a page, so can simply | 1394 | * record and hence smaller than a page, so can simply return error on |
| 1334 | * return error on any pages with index above 0. | 1395 | * any pages with index above 0. Note the attribute can actually be |
| 1396 | * marked compressed but if it is resident the actual data is not | ||
| 1397 | * compressed so we are ok to ignore the compressed flag here. | ||
| 1335 | */ | 1398 | */ |
| 1336 | BUG_ON(page_has_buffers(page)); | 1399 | BUG_ON(page_has_buffers(page)); |
| 1337 | BUG_ON(!PageUptodate(page)); | 1400 | BUG_ON(!PageUptodate(page)); |
| @@ -1380,30 +1443,14 @@ retry_writepage: | |||
| 1380 | BUG_ON(PageWriteback(page)); | 1443 | BUG_ON(PageWriteback(page)); |
| 1381 | set_page_writeback(page); | 1444 | set_page_writeback(page); |
| 1382 | unlock_page(page); | 1445 | unlock_page(page); |
| 1383 | |||
| 1384 | /* | 1446 | /* |
| 1385 | * Here, we don't need to zero the out of bounds area everytime because | 1447 | * Here, we do not need to zero the out of bounds area everytime |
| 1386 | * the below memcpy() already takes care of the mmap-at-end-of-file | 1448 | * because the below memcpy() already takes care of the |
| 1387 | * requirements. If the file is converted to a non-resident one, then | 1449 | * mmap-at-end-of-file requirements. If the file is converted to a |
| 1388 | * the code path use is switched to the non-resident one where the | 1450 | * non-resident one, then the code path use is switched to the |
| 1389 | * zeroing happens on each ntfs_writepage() invocation. | 1451 | * non-resident one where the zeroing happens on each ntfs_writepage() |
| 1390 | * | 1452 | * invocation. |
| 1391 | * The above also applies nicely when i_size is decreased. | ||
| 1392 | * | ||
| 1393 | * When i_size is increased, the memory between the old and new i_size | ||
| 1394 | * _must_ be zeroed (or overwritten with new data). Otherwise we will | ||
| 1395 | * expose data to userspace/disk which should never have been exposed. | ||
| 1396 | * | ||
| 1397 | * FIXME: Ensure that i_size increases do the zeroing/overwriting and | ||
| 1398 | * if we cannot guarantee that, then enable the zeroing below. If the | ||
| 1399 | * zeroing below is enabled, we MUST move the unlock_page() from above | ||
| 1400 | * to after the kunmap_atomic(), i.e. just before the | ||
| 1401 | * end_page_writeback(). | ||
| 1402 | * UPDATE: ntfs_prepare/commit_write() do the zeroing on i_size | ||
| 1403 | * increases for resident attributes so those are ok. | ||
| 1404 | * TODO: ntfs_truncate(), others? | ||
| 1405 | */ | 1453 | */ |
| 1406 | |||
| 1407 | attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); | 1454 | attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); |
| 1408 | i_size = i_size_read(vi); | 1455 | i_size = i_size_read(vi); |
| 1409 | if (unlikely(attr_len > i_size)) { | 1456 | if (unlikely(attr_len > i_size)) { |
| @@ -1681,27 +1728,25 @@ lock_retry_remap: | |||
| 1681 | if (likely(!err)) | 1728 | if (likely(!err)) |
| 1682 | goto lock_retry_remap; | 1729 | goto lock_retry_remap; |
| 1683 | rl = NULL; | 1730 | rl = NULL; |
| 1684 | lcn = err; | ||
| 1685 | } else if (!rl) | 1731 | } else if (!rl) |
| 1686 | up_read(&ni->runlist.lock); | 1732 | up_read(&ni->runlist.lock); |
| 1687 | /* | 1733 | /* |
| 1688 | * Failed to map the buffer, even after | 1734 | * Failed to map the buffer, even after |
| 1689 | * retrying. | 1735 | * retrying. |
| 1690 | */ | 1736 | */ |
| 1737 | if (!err) | ||
| 1738 | err = -EIO; | ||
| 1691 | bh->b_blocknr = -1; | 1739 | bh->b_blocknr = -1; |
| 1692 | ntfs_error(vol->sb, "Failed to write to inode " | 1740 | ntfs_error(vol->sb, "Failed to write to inode " |
| 1693 | "0x%lx, attribute type 0x%x, " | 1741 | "0x%lx, attribute type 0x%x, " |
| 1694 | "vcn 0x%llx, offset 0x%x " | 1742 | "vcn 0x%llx, offset 0x%x " |
| 1695 | "because its location on disk " | 1743 | "because its location on disk " |
| 1696 | "could not be determined%s " | 1744 | "could not be determined%s " |
| 1697 | "(error code %lli).", | 1745 | "(error code %i).", |
| 1698 | ni->mft_no, ni->type, | 1746 | ni->mft_no, ni->type, |
| 1699 | (unsigned long long)vcn, | 1747 | (unsigned long long)vcn, |
| 1700 | vcn_ofs, is_retry ? " even " | 1748 | vcn_ofs, is_retry ? " even " |
| 1701 | "after retrying" : "", | 1749 | "after retrying" : "", err); |
| 1702 | (long long)lcn); | ||
| 1703 | if (!err) | ||
| 1704 | err = -EIO; | ||
| 1705 | goto err_out; | 1750 | goto err_out; |
| 1706 | } | 1751 | } |
| 1707 | /* We now have a successful remap, i.e. lcn >= 0. */ | 1752 | /* We now have a successful remap, i.e. lcn >= 0. */ |
| @@ -2357,6 +2402,7 @@ void mark_ntfs_record_dirty(struct page *page, const unsigned int ofs) { | |||
| 2357 | buffers_to_free = bh; | 2402 | buffers_to_free = bh; |
| 2358 | } | 2403 | } |
| 2359 | bh = head = page_buffers(page); | 2404 | bh = head = page_buffers(page); |
| 2405 | BUG_ON(!bh); | ||
| 2360 | do { | 2406 | do { |
| 2361 | bh_ofs = bh_offset(bh); | 2407 | bh_ofs = bh_offset(bh); |
| 2362 | if (bh_ofs + bh_size <= ofs) | 2408 | if (bh_ofs + bh_size <= ofs) |
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index cd0f9e740b14..3f9a4ff42ee5 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c | |||
| @@ -43,6 +43,9 @@ | |||
| 43 | * which is not an error as such. This is -ENOENT. It means that @vcn is out | 43 | * which is not an error as such. This is -ENOENT. It means that @vcn is out |
| 44 | * of bounds of the runlist. | 44 | * of bounds of the runlist. |
| 45 | * | 45 | * |
| 46 | * Note the runlist can be NULL after this function returns if @vcn is zero and | ||
| 47 | * the attribute has zero allocated size, i.e. there simply is no runlist. | ||
| 48 | * | ||
| 46 | * Locking: - The runlist must be locked for writing. | 49 | * Locking: - The runlist must be locked for writing. |
| 47 | * - This function modifies the runlist. | 50 | * - This function modifies the runlist. |
| 48 | */ | 51 | */ |
| @@ -54,6 +57,7 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn) | |||
| 54 | ATTR_RECORD *a; | 57 | ATTR_RECORD *a; |
| 55 | ntfs_attr_search_ctx *ctx; | 58 | ntfs_attr_search_ctx *ctx; |
| 56 | runlist_element *rl; | 59 | runlist_element *rl; |
| 60 | unsigned long flags; | ||
| 57 | int err = 0; | 61 | int err = 0; |
| 58 | 62 | ||
| 59 | ntfs_debug("Mapping runlist part containing vcn 0x%llx.", | 63 | ntfs_debug("Mapping runlist part containing vcn 0x%llx.", |
| @@ -85,8 +89,11 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn) | |||
| 85 | * ntfs_mapping_pairs_decompress() fails. | 89 | * ntfs_mapping_pairs_decompress() fails. |
| 86 | */ | 90 | */ |
| 87 | end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1; | 91 | end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1; |
| 88 | if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) | 92 | if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) { |
| 93 | read_lock_irqsave(&ni->size_lock, flags); | ||
| 89 | end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits; | 94 | end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits; |
| 95 | read_unlock_irqrestore(&ni->size_lock, flags); | ||
| 96 | } | ||
| 90 | if (unlikely(vcn >= end_vcn)) { | 97 | if (unlikely(vcn >= end_vcn)) { |
| 91 | err = -ENOENT; | 98 | err = -ENOENT; |
| 92 | goto err_out; | 99 | goto err_out; |
| @@ -165,6 +172,7 @@ LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn, | |||
| 165 | const BOOL write_locked) | 172 | const BOOL write_locked) |
| 166 | { | 173 | { |
| 167 | LCN lcn; | 174 | LCN lcn; |
| 175 | unsigned long flags; | ||
| 168 | BOOL is_retry = FALSE; | 176 | BOOL is_retry = FALSE; |
| 169 | 177 | ||
| 170 | ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.", | 178 | ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.", |
| @@ -173,6 +181,14 @@ LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn, | |||
| 173 | BUG_ON(!ni); | 181 | BUG_ON(!ni); |
| 174 | BUG_ON(!NInoNonResident(ni)); | 182 | BUG_ON(!NInoNonResident(ni)); |
| 175 | BUG_ON(vcn < 0); | 183 | BUG_ON(vcn < 0); |
| 184 | if (!ni->runlist.rl) { | ||
| 185 | read_lock_irqsave(&ni->size_lock, flags); | ||
| 186 | if (!ni->allocated_size) { | ||
| 187 | read_unlock_irqrestore(&ni->size_lock, flags); | ||
| 188 | return LCN_ENOENT; | ||
| 189 | } | ||
| 190 | read_unlock_irqrestore(&ni->size_lock, flags); | ||
| 191 | } | ||
| 176 | retry_remap: | 192 | retry_remap: |
| 177 | /* Convert vcn to lcn. If that fails map the runlist and retry once. */ | 193 | /* Convert vcn to lcn. If that fails map the runlist and retry once. */ |
| 178 | lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn); | 194 | lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn); |
| @@ -255,6 +271,7 @@ retry_remap: | |||
| 255 | runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, | 271 | runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, |
| 256 | const BOOL write_locked) | 272 | const BOOL write_locked) |
| 257 | { | 273 | { |
| 274 | unsigned long flags; | ||
| 258 | runlist_element *rl; | 275 | runlist_element *rl; |
| 259 | int err = 0; | 276 | int err = 0; |
| 260 | BOOL is_retry = FALSE; | 277 | BOOL is_retry = FALSE; |
| @@ -265,6 +282,14 @@ runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, | |||
| 265 | BUG_ON(!ni); | 282 | BUG_ON(!ni); |
| 266 | BUG_ON(!NInoNonResident(ni)); | 283 | BUG_ON(!NInoNonResident(ni)); |
| 267 | BUG_ON(vcn < 0); | 284 | BUG_ON(vcn < 0); |
| 285 | if (!ni->runlist.rl) { | ||
| 286 | read_lock_irqsave(&ni->size_lock, flags); | ||
| 287 | if (!ni->allocated_size) { | ||
| 288 | read_unlock_irqrestore(&ni->size_lock, flags); | ||
| 289 | return ERR_PTR(-ENOENT); | ||
| 290 | } | ||
| 291 | read_unlock_irqrestore(&ni->size_lock, flags); | ||
| 292 | } | ||
| 268 | retry_remap: | 293 | retry_remap: |
| 269 | rl = ni->runlist.rl; | 294 | rl = ni->runlist.rl; |
| 270 | if (likely(rl && vcn >= rl[0].vcn)) { | 295 | if (likely(rl && vcn >= rl[0].vcn)) { |
| @@ -528,6 +553,11 @@ int load_attribute_list(ntfs_volume *vol, runlist *runlist, u8 *al_start, | |||
| 528 | block_size_bits = sb->s_blocksize_bits; | 553 | block_size_bits = sb->s_blocksize_bits; |
| 529 | down_read(&runlist->lock); | 554 | down_read(&runlist->lock); |
| 530 | rl = runlist->rl; | 555 | rl = runlist->rl; |
| 556 | if (!rl) { | ||
| 557 | ntfs_error(sb, "Cannot read attribute list since runlist is " | ||
| 558 | "missing."); | ||
| 559 | goto err_out; | ||
| 560 | } | ||
| 531 | /* Read all clusters specified by the runlist one run at a time. */ | 561 | /* Read all clusters specified by the runlist one run at a time. */ |
| 532 | while (rl->length) { | 562 | while (rl->length) { |
| 533 | lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); | 563 | lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); |
| @@ -1247,6 +1277,46 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) | |||
| 1247 | } | 1277 | } |
| 1248 | 1278 | ||
| 1249 | /** | 1279 | /** |
| 1280 | * ntfs_resident_attr_value_resize - resize the value of a resident attribute | ||
| 1281 | * @m: mft record containing attribute record | ||
| 1282 | * @a: attribute record whose value to resize | ||
| 1283 | * @new_size: new size in bytes to which to resize the attribute value of @a | ||
| 1284 | * | ||
| 1285 | * Resize the value of the attribute @a in the mft record @m to @new_size bytes. | ||
| 1286 | * If the value is made bigger, the newly allocated space is cleared. | ||
| 1287 | * | ||
| 1288 | * Return 0 on success and -errno on error. The following error codes are | ||
| 1289 | * defined: | ||
| 1290 | * -ENOSPC - Not enough space in the mft record @m to perform the resize. | ||
| 1291 | * | ||
| 1292 | * Note: On error, no modifications have been performed whatsoever. | ||
| 1293 | * | ||
| 1294 | * Warning: If you make a record smaller without having copied all the data you | ||
| 1295 | * are interested in the data may be overwritten. | ||
| 1296 | */ | ||
| 1297 | int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, | ||
| 1298 | const u32 new_size) | ||
| 1299 | { | ||
| 1300 | u32 old_size; | ||
| 1301 | |||
| 1302 | /* Resize the resident part of the attribute record. */ | ||
| 1303 | if (ntfs_attr_record_resize(m, a, | ||
| 1304 | le16_to_cpu(a->data.resident.value_offset) + new_size)) | ||
| 1305 | return -ENOSPC; | ||
| 1306 | /* | ||
| 1307 | * The resize succeeded! If we made the attribute value bigger, clear | ||
| 1308 | * the area between the old size and @new_size. | ||
| 1309 | */ | ||
| 1310 | old_size = le32_to_cpu(a->data.resident.value_length); | ||
| 1311 | if (new_size > old_size) | ||
| 1312 | memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) + | ||
| 1313 | old_size, 0, new_size - old_size); | ||
| 1314 | /* Finally update the length of the attribute value. */ | ||
| 1315 | a->data.resident.value_length = cpu_to_le32(new_size); | ||
| 1316 | return 0; | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | /** | ||
| 1250 | * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute | 1320 | * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute |
| 1251 | * @ni: ntfs inode describing the attribute to convert | 1321 | * @ni: ntfs inode describing the attribute to convert |
| 1252 | * | 1322 | * |
| @@ -1302,6 +1372,12 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni) | |||
| 1302 | return err; | 1372 | return err; |
| 1303 | } | 1373 | } |
| 1304 | /* | 1374 | /* |
| 1375 | * FIXME: Compressed and encrypted attributes are not supported when | ||
| 1376 | * writing and we should never have gotten here for them. | ||
| 1377 | */ | ||
| 1378 | BUG_ON(NInoCompressed(ni)); | ||
| 1379 | BUG_ON(NInoEncrypted(ni)); | ||
| 1380 | /* | ||
| 1305 | * The size needs to be aligned to a cluster boundary for allocation | 1381 | * The size needs to be aligned to a cluster boundary for allocation |
| 1306 | * purposes. | 1382 | * purposes. |
| 1307 | */ | 1383 | */ |
| @@ -1377,10 +1453,15 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni) | |||
| 1377 | BUG_ON(a->non_resident); | 1453 | BUG_ON(a->non_resident); |
| 1378 | /* | 1454 | /* |
| 1379 | * Calculate new offsets for the name and the mapping pairs array. | 1455 | * Calculate new offsets for the name and the mapping pairs array. |
| 1380 | * We assume the attribute is not compressed or sparse. | ||
| 1381 | */ | 1456 | */ |
| 1382 | name_ofs = (offsetof(ATTR_REC, | 1457 | if (NInoSparse(ni) || NInoCompressed(ni)) |
| 1383 | data.non_resident.compressed_size) + 7) & ~7; | 1458 | name_ofs = (offsetof(ATTR_REC, |
| 1459 | data.non_resident.compressed_size) + | ||
| 1460 | sizeof(a->data.non_resident.compressed_size) + | ||
| 1461 | 7) & ~7; | ||
| 1462 | else | ||
| 1463 | name_ofs = (offsetof(ATTR_REC, | ||
| 1464 | data.non_resident.compressed_size) + 7) & ~7; | ||
| 1384 | mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; | 1465 | mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; |
| 1385 | /* | 1466 | /* |
| 1386 | * Determine the size of the resident part of the now non-resident | 1467 | * Determine the size of the resident part of the now non-resident |
| @@ -1419,24 +1500,23 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni) | |||
| 1419 | memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), | 1500 | memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), |
| 1420 | a->name_length * sizeof(ntfschar)); | 1501 | a->name_length * sizeof(ntfschar)); |
| 1421 | a->name_offset = cpu_to_le16(name_ofs); | 1502 | a->name_offset = cpu_to_le16(name_ofs); |
| 1422 | /* | ||
| 1423 | * FIXME: For now just clear all of these as we do not support them | ||
| 1424 | * when writing. | ||
| 1425 | */ | ||
| 1426 | a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE | | ||
| 1427 | ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK)); | ||
| 1428 | /* Setup the fields specific to non-resident attributes. */ | 1503 | /* Setup the fields specific to non-resident attributes. */ |
| 1429 | a->data.non_resident.lowest_vcn = 0; | 1504 | a->data.non_resident.lowest_vcn = 0; |
| 1430 | a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >> | 1505 | a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >> |
| 1431 | vol->cluster_size_bits); | 1506 | vol->cluster_size_bits); |
| 1432 | a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); | 1507 | a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); |
| 1433 | a->data.non_resident.compression_unit = 0; | ||
| 1434 | memset(&a->data.non_resident.reserved, 0, | 1508 | memset(&a->data.non_resident.reserved, 0, |
| 1435 | sizeof(a->data.non_resident.reserved)); | 1509 | sizeof(a->data.non_resident.reserved)); |
| 1436 | a->data.non_resident.allocated_size = cpu_to_sle64(new_size); | 1510 | a->data.non_resident.allocated_size = cpu_to_sle64(new_size); |
| 1437 | a->data.non_resident.data_size = | 1511 | a->data.non_resident.data_size = |
| 1438 | a->data.non_resident.initialized_size = | 1512 | a->data.non_resident.initialized_size = |
| 1439 | cpu_to_sle64(attr_size); | 1513 | cpu_to_sle64(attr_size); |
| 1514 | if (NInoSparse(ni) || NInoCompressed(ni)) { | ||
| 1515 | a->data.non_resident.compression_unit = 4; | ||
| 1516 | a->data.non_resident.compressed_size = | ||
| 1517 | a->data.non_resident.allocated_size; | ||
| 1518 | } else | ||
| 1519 | a->data.non_resident.compression_unit = 0; | ||
| 1440 | /* Generate the mapping pairs array into the attribute record. */ | 1520 | /* Generate the mapping pairs array into the attribute record. */ |
| 1441 | err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, | 1521 | err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, |
| 1442 | arec_size - mp_ofs, rl, 0, -1, NULL); | 1522 | arec_size - mp_ofs, rl, 0, -1, NULL); |
| @@ -1446,16 +1526,19 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni) | |||
| 1446 | goto undo_err_out; | 1526 | goto undo_err_out; |
| 1447 | } | 1527 | } |
| 1448 | /* Setup the in-memory attribute structure to be non-resident. */ | 1528 | /* Setup the in-memory attribute structure to be non-resident. */ |
| 1449 | /* | ||
| 1450 | * FIXME: For now just clear all of these as we do not support them | ||
| 1451 | * when writing. | ||
| 1452 | */ | ||
| 1453 | NInoClearSparse(ni); | ||
| 1454 | NInoClearEncrypted(ni); | ||
| 1455 | NInoClearCompressed(ni); | ||
| 1456 | ni->runlist.rl = rl; | 1529 | ni->runlist.rl = rl; |
| 1457 | write_lock_irqsave(&ni->size_lock, flags); | 1530 | write_lock_irqsave(&ni->size_lock, flags); |
| 1458 | ni->allocated_size = new_size; | 1531 | ni->allocated_size = new_size; |
| 1532 | if (NInoSparse(ni) || NInoCompressed(ni)) { | ||
| 1533 | ni->itype.compressed.size = ni->allocated_size; | ||
| 1534 | ni->itype.compressed.block_size = 1U << | ||
| 1535 | (a->data.non_resident.compression_unit + | ||
| 1536 | vol->cluster_size_bits); | ||
| 1537 | ni->itype.compressed.block_size_bits = | ||
| 1538 | ffs(ni->itype.compressed.block_size) - 1; | ||
| 1539 | ni->itype.compressed.block_clusters = 1U << | ||
| 1540 | a->data.non_resident.compression_unit; | ||
| 1541 | } | ||
| 1459 | write_unlock_irqrestore(&ni->size_lock, flags); | 1542 | write_unlock_irqrestore(&ni->size_lock, flags); |
| 1460 | /* | 1543 | /* |
| 1461 | * This needs to be last since the address space operations ->readpage | 1544 | * This needs to be last since the address space operations ->readpage |
| @@ -1603,6 +1686,12 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) | |||
| 1603 | BUG_ON(cnt < 0); | 1686 | BUG_ON(cnt < 0); |
| 1604 | if (!cnt) | 1687 | if (!cnt) |
| 1605 | goto done; | 1688 | goto done; |
| 1689 | /* | ||
| 1690 | * FIXME: Compressed and encrypted attributes are not supported when | ||
| 1691 | * writing and we should never have gotten here for them. | ||
| 1692 | */ | ||
| 1693 | BUG_ON(NInoCompressed(ni)); | ||
| 1694 | BUG_ON(NInoEncrypted(ni)); | ||
| 1606 | mapping = VFS_I(ni)->i_mapping; | 1695 | mapping = VFS_I(ni)->i_mapping; |
| 1607 | /* Work out the starting index and page offset. */ | 1696 | /* Work out the starting index and page offset. */ |
| 1608 | idx = ofs >> PAGE_CACHE_SHIFT; | 1697 | idx = ofs >> PAGE_CACHE_SHIFT; |
diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h index 0e4ac6d3c0e7..0618ed6fd7b3 100644 --- a/fs/ntfs/attrib.h +++ b/fs/ntfs/attrib.h | |||
| @@ -99,6 +99,8 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, | |||
| 99 | const ATTR_TYPE type); | 99 | const ATTR_TYPE type); |
| 100 | 100 | ||
| 101 | extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); | 101 | extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); |
| 102 | extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, | ||
| 103 | const u32 new_size); | ||
| 102 | 104 | ||
| 103 | extern int ntfs_attr_make_non_resident(ntfs_inode *ni); | 105 | extern int ntfs_attr_make_non_resident(ntfs_inode *ni); |
| 104 | 106 | ||
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index 6d265cfd49aa..25d24106f893 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c | |||
| @@ -539,7 +539,6 @@ int ntfs_read_compressed_block(struct page *page) | |||
| 539 | if (unlikely(!pages || !bhs)) { | 539 | if (unlikely(!pages || !bhs)) { |
| 540 | kfree(bhs); | 540 | kfree(bhs); |
| 541 | kfree(pages); | 541 | kfree(pages); |
| 542 | SetPageError(page); | ||
| 543 | unlock_page(page); | 542 | unlock_page(page); |
| 544 | ntfs_error(vol->sb, "Failed to allocate internal buffers."); | 543 | ntfs_error(vol->sb, "Failed to allocate internal buffers."); |
| 545 | return -ENOMEM; | 544 | return -ENOMEM; |
| @@ -871,9 +870,6 @@ lock_retry_remap: | |||
| 871 | for (; prev_cur_page < cur_page; prev_cur_page++) { | 870 | for (; prev_cur_page < cur_page; prev_cur_page++) { |
| 872 | page = pages[prev_cur_page]; | 871 | page = pages[prev_cur_page]; |
| 873 | if (page) { | 872 | if (page) { |
| 874 | if (prev_cur_page == xpage && | ||
| 875 | !xpage_done) | ||
| 876 | SetPageError(page); | ||
| 877 | flush_dcache_page(page); | 873 | flush_dcache_page(page); |
| 878 | kunmap(page); | 874 | kunmap(page); |
| 879 | unlock_page(page); | 875 | unlock_page(page); |
| @@ -904,8 +900,6 @@ lock_retry_remap: | |||
| 904 | "Terminating them with extreme " | 900 | "Terminating them with extreme " |
| 905 | "prejudice. Inode 0x%lx, page index " | 901 | "prejudice. Inode 0x%lx, page index " |
| 906 | "0x%lx.", ni->mft_no, page->index); | 902 | "0x%lx.", ni->mft_no, page->index); |
| 907 | if (cur_page == xpage && !xpage_done) | ||
| 908 | SetPageError(page); | ||
| 909 | flush_dcache_page(page); | 903 | flush_dcache_page(page); |
| 910 | kunmap(page); | 904 | kunmap(page); |
| 911 | unlock_page(page); | 905 | unlock_page(page); |
| @@ -953,8 +947,6 @@ err_out: | |||
| 953 | for (i = cur_page; i < max_page; i++) { | 947 | for (i = cur_page; i < max_page; i++) { |
| 954 | page = pages[i]; | 948 | page = pages[i]; |
| 955 | if (page) { | 949 | if (page) { |
| 956 | if (i == xpage && !xpage_done) | ||
| 957 | SetPageError(page); | ||
| 958 | flush_dcache_page(page); | 950 | flush_dcache_page(page); |
| 959 | kunmap(page); | 951 | kunmap(page); |
| 960 | unlock_page(page); | 952 | unlock_page(page); |
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 46779471c542..795c3d1930f5 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c | |||
| @@ -1051,7 +1051,8 @@ static inline int ntfs_filldir(ntfs_volume *vol, loff_t fpos, | |||
| 1051 | ie->key.file_name.file_name_length, &name, | 1051 | ie->key.file_name.file_name_length, &name, |
| 1052 | NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1); | 1052 | NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1); |
| 1053 | if (name_len <= 0) { | 1053 | if (name_len <= 0) { |
| 1054 | ntfs_debug("Skipping unrepresentable file."); | 1054 | ntfs_warning(vol->sb, "Skipping unrepresentable inode 0x%llx.", |
| 1055 | (long long)MREF_LE(ie->data.dir.indexed_file)); | ||
| 1055 | return 0; | 1056 | return 0; |
| 1056 | } | 1057 | } |
| 1057 | if (ie->key.file_name.file_attributes & | 1058 | if (ie->key.file_name.file_attributes & |
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index e0f530ce6b99..be9fd1dd423d 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * file.c - NTFS kernel file operations. Part of the Linux-NTFS project. | 2 | * file.c - NTFS kernel file operations. Part of the Linux-NTFS project. |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2001-2004 Anton Altaparmakov | 4 | * Copyright (c) 2001-2005 Anton Altaparmakov |
| 5 | * | 5 | * |
| 6 | * This program/include file is free software; you can redistribute it and/or | 6 | * This program/include file is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License as published | 7 | * modify it under the terms of the GNU General Public License as published |
| @@ -94,6 +94,11 @@ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, | |||
| 94 | if (!datasync || !NInoNonResident(NTFS_I(vi))) | 94 | if (!datasync || !NInoNonResident(NTFS_I(vi))) |
| 95 | ret = ntfs_write_inode(vi, 1); | 95 | ret = ntfs_write_inode(vi, 1); |
| 96 | write_inode_now(vi, !datasync); | 96 | write_inode_now(vi, !datasync); |
| 97 | /* | ||
| 98 | * NOTE: If we were to use mapping->private_list (see ext2 and | ||
| 99 | * fs/buffer.c) for dirty blocks then we could optimize the below to be | ||
| 100 | * sync_mapping_buffers(vi->i_mapping). | ||
| 101 | */ | ||
| 97 | err = sync_blockdev(vi->i_sb->s_bdev); | 102 | err = sync_blockdev(vi->i_sb->s_bdev); |
| 98 | if (unlikely(err && !ret)) | 103 | if (unlikely(err && !ret)) |
| 99 | ret = err; | 104 | ret = err; |
diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 11fd5307d780..8f2d5727546f 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c | |||
| @@ -205,6 +205,7 @@ int ntfs_index_lookup(const void *key, const int key_len, | |||
| 205 | &ie->key, key_len)) { | 205 | &ie->key, key_len)) { |
| 206 | ir_done: | 206 | ir_done: |
| 207 | ictx->is_in_root = TRUE; | 207 | ictx->is_in_root = TRUE; |
| 208 | ictx->ir = ir; | ||
| 208 | ictx->actx = actx; | 209 | ictx->actx = actx; |
| 209 | ictx->base_ni = base_ni; | 210 | ictx->base_ni = base_ni; |
| 210 | ictx->ia = NULL; | 211 | ictx->ia = NULL; |
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 886214a77f90..dc4bbe3acf5c 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c | |||
| @@ -1013,41 +1013,50 @@ skip_large_dir_stuff: | |||
| 1013 | } | 1013 | } |
| 1014 | a = ctx->attr; | 1014 | a = ctx->attr; |
| 1015 | /* Setup the state. */ | 1015 | /* Setup the state. */ |
| 1016 | if (a->non_resident) { | 1016 | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) { |
| 1017 | NInoSetNonResident(ni); | 1017 | if (a->flags & ATTR_COMPRESSION_MASK) { |
| 1018 | if (a->flags & (ATTR_COMPRESSION_MASK | | 1018 | NInoSetCompressed(ni); |
| 1019 | ATTR_IS_SPARSE)) { | 1019 | if (vol->cluster_size > 4096) { |
| 1020 | if (a->flags & ATTR_COMPRESSION_MASK) { | 1020 | ntfs_error(vi->i_sb, "Found " |
| 1021 | NInoSetCompressed(ni); | ||
| 1022 | if (vol->cluster_size > 4096) { | ||
| 1023 | ntfs_error(vi->i_sb, "Found " | ||
| 1024 | "compressed data but " | 1021 | "compressed data but " |
| 1025 | "compression is " | 1022 | "compression is " |
| 1026 | "disabled due to " | 1023 | "disabled due to " |
| 1027 | "cluster size (%i) > " | 1024 | "cluster size (%i) > " |
| 1028 | "4kiB.", | 1025 | "4kiB.", |
| 1029 | vol->cluster_size); | 1026 | vol->cluster_size); |
| 1030 | goto unm_err_out; | 1027 | goto unm_err_out; |
| 1031 | } | 1028 | } |
| 1032 | if ((a->flags & ATTR_COMPRESSION_MASK) | 1029 | if ((a->flags & ATTR_COMPRESSION_MASK) |
| 1033 | != ATTR_IS_COMPRESSED) { | 1030 | != ATTR_IS_COMPRESSED) { |
| 1034 | ntfs_error(vi->i_sb, "Found " | 1031 | ntfs_error(vi->i_sb, "Found unknown " |
| 1035 | "unknown compression " | 1032 | "compression method " |
| 1036 | "method or corrupt " | 1033 | "or corrupt file."); |
| 1037 | "file."); | 1034 | goto unm_err_out; |
| 1038 | goto unm_err_out; | ||
| 1039 | } | ||
| 1040 | } | 1035 | } |
| 1041 | if (a->flags & ATTR_IS_SPARSE) | 1036 | } |
| 1042 | NInoSetSparse(ni); | 1037 | if (a->flags & ATTR_IS_SPARSE) |
| 1038 | NInoSetSparse(ni); | ||
| 1039 | } | ||
| 1040 | if (a->flags & ATTR_IS_ENCRYPTED) { | ||
| 1041 | if (NInoCompressed(ni)) { | ||
| 1042 | ntfs_error(vi->i_sb, "Found encrypted and " | ||
| 1043 | "compressed data."); | ||
| 1044 | goto unm_err_out; | ||
| 1045 | } | ||
| 1046 | NInoSetEncrypted(ni); | ||
| 1047 | } | ||
| 1048 | if (a->non_resident) { | ||
| 1049 | NInoSetNonResident(ni); | ||
| 1050 | if (NInoCompressed(ni) || NInoSparse(ni)) { | ||
| 1043 | if (a->data.non_resident.compression_unit != | 1051 | if (a->data.non_resident.compression_unit != |
| 1044 | 4) { | 1052 | 4) { |
| 1045 | ntfs_error(vi->i_sb, "Found " | 1053 | ntfs_error(vi->i_sb, "Found " |
| 1046 | "nonstandard compression unit " | 1054 | "nonstandard " |
| 1047 | "(%u instead of 4). Cannot " | 1055 | "compression unit (%u " |
| 1048 | "handle this.", | 1056 | "instead of 4). " |
| 1049 | a->data.non_resident. | 1057 | "Cannot handle this.", |
| 1050 | compression_unit); | 1058 | a->data.non_resident. |
| 1059 | compression_unit); | ||
| 1051 | err = -EOPNOTSUPP; | 1060 | err = -EOPNOTSUPP; |
| 1052 | goto unm_err_out; | 1061 | goto unm_err_out; |
| 1053 | } | 1062 | } |
| @@ -1065,14 +1074,6 @@ skip_large_dir_stuff: | |||
| 1065 | a->data.non_resident. | 1074 | a->data.non_resident. |
| 1066 | compressed_size); | 1075 | compressed_size); |
| 1067 | } | 1076 | } |
| 1068 | if (a->flags & ATTR_IS_ENCRYPTED) { | ||
| 1069 | if (a->flags & ATTR_COMPRESSION_MASK) { | ||
| 1070 | ntfs_error(vi->i_sb, "Found encrypted " | ||
| 1071 | "and compressed data."); | ||
| 1072 | goto unm_err_out; | ||
| 1073 | } | ||
| 1074 | NInoSetEncrypted(ni); | ||
| 1075 | } | ||
| 1076 | if (a->data.non_resident.lowest_vcn) { | 1077 | if (a->data.non_resident.lowest_vcn) { |
| 1077 | ntfs_error(vi->i_sb, "First extent of $DATA " | 1078 | ntfs_error(vi->i_sb, "First extent of $DATA " |
| 1078 | "attribute has non zero " | 1079 | "attribute has non zero " |
| @@ -1212,6 +1213,75 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) | |||
| 1212 | if (unlikely(err)) | 1213 | if (unlikely(err)) |
| 1213 | goto unm_err_out; | 1214 | goto unm_err_out; |
| 1214 | a = ctx->attr; | 1215 | a = ctx->attr; |
| 1216 | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) { | ||
| 1217 | if (a->flags & ATTR_COMPRESSION_MASK) { | ||
| 1218 | NInoSetCompressed(ni); | ||
| 1219 | if ((ni->type != AT_DATA) || (ni->type == AT_DATA && | ||
| 1220 | ni->name_len)) { | ||
| 1221 | ntfs_error(vi->i_sb, "Found compressed " | ||
| 1222 | "non-data or named data " | ||
| 1223 | "attribute. Please report " | ||
| 1224 | "you saw this message to " | ||
| 1225 | "linux-ntfs-dev@lists." | ||
| 1226 | "sourceforge.net"); | ||
| 1227 | goto unm_err_out; | ||
| 1228 | } | ||
| 1229 | if (vol->cluster_size > 4096) { | ||
| 1230 | ntfs_error(vi->i_sb, "Found compressed " | ||
| 1231 | "attribute but compression is " | ||
| 1232 | "disabled due to cluster size " | ||
| 1233 | "(%i) > 4kiB.", | ||
| 1234 | vol->cluster_size); | ||
| 1235 | goto unm_err_out; | ||
| 1236 | } | ||
| 1237 | if ((a->flags & ATTR_COMPRESSION_MASK) != | ||
| 1238 | ATTR_IS_COMPRESSED) { | ||
| 1239 | ntfs_error(vi->i_sb, "Found unknown " | ||
| 1240 | "compression method."); | ||
| 1241 | goto unm_err_out; | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | /* | ||
| 1245 | * The encryption flag set in an index root just means to | ||
| 1246 | * compress all files. | ||
| 1247 | */ | ||
| 1248 | if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) { | ||
| 1249 | ntfs_error(vi->i_sb, "Found mst protected attribute " | ||
| 1250 | "but the attribute is %s. Please " | ||
| 1251 | "report you saw this message to " | ||
| 1252 | "linux-ntfs-dev@lists.sourceforge.net", | ||
| 1253 | NInoCompressed(ni) ? "compressed" : | ||
| 1254 | "sparse"); | ||
| 1255 | goto unm_err_out; | ||
| 1256 | } | ||
| 1257 | if (a->flags & ATTR_IS_SPARSE) | ||
| 1258 | NInoSetSparse(ni); | ||
| 1259 | } | ||
| 1260 | if (a->flags & ATTR_IS_ENCRYPTED) { | ||
| 1261 | if (NInoCompressed(ni)) { | ||
| 1262 | ntfs_error(vi->i_sb, "Found encrypted and compressed " | ||
| 1263 | "data."); | ||
| 1264 | goto unm_err_out; | ||
| 1265 | } | ||
| 1266 | /* | ||
| 1267 | * The encryption flag set in an index root just means to | ||
| 1268 | * encrypt all files. | ||
| 1269 | */ | ||
| 1270 | if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) { | ||
| 1271 | ntfs_error(vi->i_sb, "Found mst protected attribute " | ||
| 1272 | "but the attribute is encrypted. " | ||
| 1273 | "Please report you saw this message " | ||
| 1274 | "to linux-ntfs-dev@lists.sourceforge." | ||
| 1275 | "net"); | ||
| 1276 | goto unm_err_out; | ||
| 1277 | } | ||
| 1278 | if (ni->type != AT_DATA) { | ||
| 1279 | ntfs_error(vi->i_sb, "Found encrypted non-data " | ||
| 1280 | "attribute."); | ||
| 1281 | goto unm_err_out; | ||
| 1282 | } | ||
| 1283 | NInoSetEncrypted(ni); | ||
| 1284 | } | ||
| 1215 | if (!a->non_resident) { | 1285 | if (!a->non_resident) { |
| 1216 | /* Ensure the attribute name is placed before the value. */ | 1286 | /* Ensure the attribute name is placed before the value. */ |
| 1217 | if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= | 1287 | if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >= |
| @@ -1220,11 +1290,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) | |||
| 1220 | "the attribute value."); | 1290 | "the attribute value."); |
| 1221 | goto unm_err_out; | 1291 | goto unm_err_out; |
| 1222 | } | 1292 | } |
| 1223 | if (NInoMstProtected(ni) || a->flags) { | 1293 | if (NInoMstProtected(ni)) { |
| 1224 | ntfs_error(vi->i_sb, "Found mst protected attribute " | 1294 | ntfs_error(vi->i_sb, "Found mst protected attribute " |
| 1225 | "or attribute with non-zero flags but " | 1295 | "but the attribute is resident. " |
| 1226 | "the attribute is resident. Please " | 1296 | "Please report you saw this message to " |
| 1227 | "report you saw this message to " | ||
| 1228 | "linux-ntfs-dev@lists.sourceforge.net"); | 1297 | "linux-ntfs-dev@lists.sourceforge.net"); |
| 1229 | goto unm_err_out; | 1298 | goto unm_err_out; |
| 1230 | } | 1299 | } |
| @@ -1250,50 +1319,8 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) | |||
| 1250 | "the mapping pairs array."); | 1319 | "the mapping pairs array."); |
| 1251 | goto unm_err_out; | 1320 | goto unm_err_out; |
| 1252 | } | 1321 | } |
| 1253 | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) { | 1322 | if ((NInoCompressed(ni) || NInoSparse(ni)) && |
| 1254 | if (a->flags & ATTR_COMPRESSION_MASK) { | 1323 | ni->type != AT_INDEX_ROOT) { |
| 1255 | NInoSetCompressed(ni); | ||
| 1256 | if ((ni->type != AT_DATA) || (ni->type == | ||
| 1257 | AT_DATA && ni->name_len)) { | ||
| 1258 | ntfs_error(vi->i_sb, "Found compressed " | ||
| 1259 | "non-data or named " | ||
| 1260 | "data attribute. " | ||
| 1261 | "Please report you " | ||
| 1262 | "saw this message to " | ||
| 1263 | "linux-ntfs-dev@lists." | ||
| 1264 | "sourceforge.net"); | ||
| 1265 | goto unm_err_out; | ||
| 1266 | } | ||
| 1267 | if (vol->cluster_size > 4096) { | ||
| 1268 | ntfs_error(vi->i_sb, "Found compressed " | ||
| 1269 | "attribute but " | ||
| 1270 | "compression is " | ||
| 1271 | "disabled due to " | ||
| 1272 | "cluster size (%i) > " | ||
| 1273 | "4kiB.", | ||
| 1274 | vol->cluster_size); | ||
| 1275 | goto unm_err_out; | ||
| 1276 | } | ||
| 1277 | if ((a->flags & ATTR_COMPRESSION_MASK) != | ||
| 1278 | ATTR_IS_COMPRESSED) { | ||
| 1279 | ntfs_error(vi->i_sb, "Found unknown " | ||
| 1280 | "compression method."); | ||
| 1281 | goto unm_err_out; | ||
| 1282 | } | ||
| 1283 | } | ||
| 1284 | if (NInoMstProtected(ni)) { | ||
| 1285 | ntfs_error(vi->i_sb, "Found mst protected " | ||
| 1286 | "attribute but the attribute " | ||
| 1287 | "is %s. Please report you " | ||
| 1288 | "saw this message to " | ||
| 1289 | "linux-ntfs-dev@lists." | ||
| 1290 | "sourceforge.net", | ||
| 1291 | NInoCompressed(ni) ? | ||
| 1292 | "compressed" : "sparse"); | ||
| 1293 | goto unm_err_out; | ||
| 1294 | } | ||
| 1295 | if (a->flags & ATTR_IS_SPARSE) | ||
| 1296 | NInoSetSparse(ni); | ||
| 1297 | if (a->data.non_resident.compression_unit != 4) { | 1324 | if (a->data.non_resident.compression_unit != 4) { |
| 1298 | ntfs_error(vi->i_sb, "Found nonstandard " | 1325 | ntfs_error(vi->i_sb, "Found nonstandard " |
| 1299 | "compression unit (%u instead " | 1326 | "compression unit (%u instead " |
| @@ -1313,23 +1340,6 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) | |||
| 1313 | ni->itype.compressed.size = sle64_to_cpu( | 1340 | ni->itype.compressed.size = sle64_to_cpu( |
| 1314 | a->data.non_resident.compressed_size); | 1341 | a->data.non_resident.compressed_size); |
| 1315 | } | 1342 | } |
| 1316 | if (a->flags & ATTR_IS_ENCRYPTED) { | ||
| 1317 | if (a->flags & ATTR_COMPRESSION_MASK) { | ||
| 1318 | ntfs_error(vi->i_sb, "Found encrypted and " | ||
| 1319 | "compressed data."); | ||
| 1320 | goto unm_err_out; | ||
| 1321 | } | ||
| 1322 | if (NInoMstProtected(ni)) { | ||
| 1323 | ntfs_error(vi->i_sb, "Found mst protected " | ||
| 1324 | "attribute but the attribute " | ||
| 1325 | "is encrypted. Please report " | ||
| 1326 | "you saw this message to " | ||
| 1327 | "linux-ntfs-dev@lists." | ||
| 1328 | "sourceforge.net"); | ||
| 1329 | goto unm_err_out; | ||
| 1330 | } | ||
| 1331 | NInoSetEncrypted(ni); | ||
| 1332 | } | ||
| 1333 | if (a->data.non_resident.lowest_vcn) { | 1343 | if (a->data.non_resident.lowest_vcn) { |
| 1334 | ntfs_error(vi->i_sb, "First extent of attribute has " | 1344 | ntfs_error(vi->i_sb, "First extent of attribute has " |
| 1335 | "non-zero lowest_vcn."); | 1345 | "non-zero lowest_vcn."); |
| @@ -1348,12 +1358,12 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) | |||
| 1348 | vi->i_mapping->a_ops = &ntfs_mst_aops; | 1358 | vi->i_mapping->a_ops = &ntfs_mst_aops; |
| 1349 | else | 1359 | else |
| 1350 | vi->i_mapping->a_ops = &ntfs_aops; | 1360 | vi->i_mapping->a_ops = &ntfs_aops; |
| 1351 | if (NInoCompressed(ni) || NInoSparse(ni)) | 1361 | if ((NInoCompressed(ni) || NInoSparse(ni)) && ni->type != AT_INDEX_ROOT) |
| 1352 | vi->i_blocks = ni->itype.compressed.size >> 9; | 1362 | vi->i_blocks = ni->itype.compressed.size >> 9; |
| 1353 | else | 1363 | else |
| 1354 | vi->i_blocks = ni->allocated_size >> 9; | 1364 | vi->i_blocks = ni->allocated_size >> 9; |
| 1355 | /* | 1365 | /* |
| 1356 | * Make sure the base inode doesn't go away and attach it to the | 1366 | * Make sure the base inode does not go away and attach it to the |
| 1357 | * attribute inode. | 1367 | * attribute inode. |
| 1358 | */ | 1368 | */ |
| 1359 | igrab(base_vi); | 1369 | igrab(base_vi); |
| @@ -1480,7 +1490,10 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi) | |||
| 1480 | "after the attribute value."); | 1490 | "after the attribute value."); |
| 1481 | goto unm_err_out; | 1491 | goto unm_err_out; |
| 1482 | } | 1492 | } |
| 1483 | /* Compressed/encrypted/sparse index root is not allowed. */ | 1493 | /* |
| 1494 | * Compressed/encrypted/sparse index root is not allowed, except for | ||
| 1495 | * directories of course but those are not dealt with here. | ||
| 1496 | */ | ||
| 1484 | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED | | 1497 | if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED | |
| 1485 | ATTR_IS_SPARSE)) { | 1498 | ATTR_IS_SPARSE)) { |
| 1486 | ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index " | 1499 | ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index " |
| @@ -2430,16 +2443,18 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr) | |||
| 2430 | * We skipped the truncate but must still update | 2443 | * We skipped the truncate but must still update |
| 2431 | * timestamps. | 2444 | * timestamps. |
| 2432 | */ | 2445 | */ |
| 2433 | ia_valid |= ATTR_MTIME|ATTR_CTIME; | 2446 | ia_valid |= ATTR_MTIME | ATTR_CTIME; |
| 2434 | } | 2447 | } |
| 2435 | } | 2448 | } |
| 2436 | |||
| 2437 | if (ia_valid & ATTR_ATIME) | 2449 | if (ia_valid & ATTR_ATIME) |
| 2438 | vi->i_atime = attr->ia_atime; | 2450 | vi->i_atime = timespec_trunc(attr->ia_atime, |
| 2451 | vi->i_sb->s_time_gran); | ||
| 2439 | if (ia_valid & ATTR_MTIME) | 2452 | if (ia_valid & ATTR_MTIME) |
| 2440 | vi->i_mtime = attr->ia_mtime; | 2453 | vi->i_mtime = timespec_trunc(attr->ia_mtime, |
| 2454 | vi->i_sb->s_time_gran); | ||
| 2441 | if (ia_valid & ATTR_CTIME) | 2455 | if (ia_valid & ATTR_CTIME) |
| 2442 | vi->i_ctime = attr->ia_ctime; | 2456 | vi->i_ctime = timespec_trunc(attr->ia_ctime, |
| 2457 | vi->i_sb->s_time_gran); | ||
| 2443 | mark_inode_dirty(vi); | 2458 | mark_inode_dirty(vi); |
| 2444 | out: | 2459 | out: |
| 2445 | return err; | 2460 | return err; |
diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c index a4bc07616e5d..7b5934290685 100644 --- a/fs/ntfs/lcnalloc.c +++ b/fs/ntfs/lcnalloc.c | |||
| @@ -54,6 +54,8 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, | |||
| 54 | int ret = 0; | 54 | int ret = 0; |
| 55 | 55 | ||
| 56 | ntfs_debug("Entering."); | 56 | ntfs_debug("Entering."); |
| 57 | if (!rl) | ||
| 58 | return 0; | ||
| 57 | for (; rl->length; rl++) { | 59 | for (; rl->length; rl++) { |
| 58 | int err; | 60 | int err; |
| 59 | 61 | ||
| @@ -163,17 +165,9 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 163 | BUG_ON(zone < FIRST_ZONE); | 165 | BUG_ON(zone < FIRST_ZONE); |
| 164 | BUG_ON(zone > LAST_ZONE); | 166 | BUG_ON(zone > LAST_ZONE); |
| 165 | 167 | ||
| 166 | /* Return empty runlist if @count == 0 */ | 168 | /* Return NULL if @count is zero. */ |
| 167 | // FIXME: Do we want to just return NULL instead? (AIA) | 169 | if (!count) |
| 168 | if (!count) { | 170 | return NULL; |
| 169 | rl = ntfs_malloc_nofs(PAGE_SIZE); | ||
| 170 | if (!rl) | ||
| 171 | return ERR_PTR(-ENOMEM); | ||
| 172 | rl[0].vcn = start_vcn; | ||
| 173 | rl[0].lcn = LCN_RL_NOT_MAPPED; | ||
| 174 | rl[0].length = 0; | ||
| 175 | return rl; | ||
| 176 | } | ||
| 177 | /* Take the lcnbmp lock for writing. */ | 171 | /* Take the lcnbmp lock for writing. */ |
| 178 | down_write(&vol->lcnbmp_lock); | 172 | down_write(&vol->lcnbmp_lock); |
| 179 | /* | 173 | /* |
| @@ -788,7 +782,8 @@ out: | |||
| 788 | * @vi: vfs inode whose runlist describes the clusters to free | 782 | * @vi: vfs inode whose runlist describes the clusters to free |
| 789 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters | 783 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters |
| 790 | * @count: number of clusters to free or -1 for all clusters | 784 | * @count: number of clusters to free or -1 for all clusters |
| 791 | * @is_rollback: if TRUE this is a rollback operation | 785 | * @write_locked: true if the runlist is locked for writing |
| 786 | * @is_rollback: true if this is a rollback operation | ||
| 792 | * | 787 | * |
| 793 | * Free @count clusters starting at the cluster @start_vcn in the runlist | 788 | * Free @count clusters starting at the cluster @start_vcn in the runlist |
| 794 | * described by the vfs inode @vi. | 789 | * described by the vfs inode @vi. |
| @@ -806,17 +801,17 @@ out: | |||
| 806 | * Return the number of deallocated clusters (not counting sparse ones) on | 801 | * Return the number of deallocated clusters (not counting sparse ones) on |
| 807 | * success and -errno on error. | 802 | * success and -errno on error. |
| 808 | * | 803 | * |
| 809 | * Locking: - The runlist described by @vi must be unlocked on entry and is | 804 | * Locking: - The runlist described by @vi must be locked on entry and is |
| 810 | * unlocked on return. | 805 | * locked on return. Note if the runlist is locked for reading the |
| 811 | * - This function takes the runlist lock of @vi for reading and | 806 | * lock may be dropped and reacquired. Note the runlist may be |
| 812 | * sometimes for writing and sometimes modifies the runlist. | 807 | * modified when needed runlist fragments need to be mapped. |
| 813 | * - The volume lcn bitmap must be unlocked on entry and is unlocked | 808 | * - The volume lcn bitmap must be unlocked on entry and is unlocked |
| 814 | * on return. | 809 | * on return. |
| 815 | * - This function takes the volume lcn bitmap lock for writing and | 810 | * - This function takes the volume lcn bitmap lock for writing and |
| 816 | * modifies the bitmap contents. | 811 | * modifies the bitmap contents. |
| 817 | */ | 812 | */ |
| 818 | s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | 813 | s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, |
| 819 | const BOOL is_rollback) | 814 | const BOOL write_locked, const BOOL is_rollback) |
| 820 | { | 815 | { |
| 821 | s64 delta, to_free, total_freed, real_freed; | 816 | s64 delta, to_free, total_freed, real_freed; |
| 822 | ntfs_inode *ni; | 817 | ntfs_inode *ni; |
| @@ -848,8 +843,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 848 | 843 | ||
| 849 | total_freed = real_freed = 0; | 844 | total_freed = real_freed = 0; |
| 850 | 845 | ||
| 851 | down_read(&ni->runlist.lock); | 846 | rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, write_locked); |
| 852 | rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, FALSE); | ||
| 853 | if (IS_ERR(rl)) { | 847 | if (IS_ERR(rl)) { |
| 854 | if (!is_rollback) | 848 | if (!is_rollback) |
| 855 | ntfs_error(vol->sb, "Failed to find first runlist " | 849 | ntfs_error(vol->sb, "Failed to find first runlist " |
| @@ -903,7 +897,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 903 | 897 | ||
| 904 | /* Attempt to map runlist. */ | 898 | /* Attempt to map runlist. */ |
| 905 | vcn = rl->vcn; | 899 | vcn = rl->vcn; |
| 906 | rl = ntfs_attr_find_vcn_nolock(ni, vcn, FALSE); | 900 | rl = ntfs_attr_find_vcn_nolock(ni, vcn, write_locked); |
| 907 | if (IS_ERR(rl)) { | 901 | if (IS_ERR(rl)) { |
| 908 | err = PTR_ERR(rl); | 902 | err = PTR_ERR(rl); |
| 909 | if (!is_rollback) | 903 | if (!is_rollback) |
| @@ -950,7 +944,6 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 950 | /* Update the total done clusters. */ | 944 | /* Update the total done clusters. */ |
| 951 | total_freed += to_free; | 945 | total_freed += to_free; |
| 952 | } | 946 | } |
| 953 | up_read(&ni->runlist.lock); | ||
| 954 | if (likely(!is_rollback)) | 947 | if (likely(!is_rollback)) |
| 955 | up_write(&vol->lcnbmp_lock); | 948 | up_write(&vol->lcnbmp_lock); |
| 956 | 949 | ||
| @@ -960,7 +953,6 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 960 | ntfs_debug("Done."); | 953 | ntfs_debug("Done."); |
| 961 | return real_freed; | 954 | return real_freed; |
| 962 | err_out: | 955 | err_out: |
| 963 | up_read(&ni->runlist.lock); | ||
| 964 | if (is_rollback) | 956 | if (is_rollback) |
| 965 | return err; | 957 | return err; |
| 966 | /* If no real clusters were freed, no need to rollback. */ | 958 | /* If no real clusters were freed, no need to rollback. */ |
| @@ -973,7 +965,8 @@ err_out: | |||
| 973 | * If rollback fails, set the volume errors flag, emit an error | 965 | * If rollback fails, set the volume errors flag, emit an error |
| 974 | * message, and return the error code. | 966 | * message, and return the error code. |
| 975 | */ | 967 | */ |
| 976 | delta = __ntfs_cluster_free(vi, start_vcn, total_freed, TRUE); | 968 | delta = __ntfs_cluster_free(vi, start_vcn, total_freed, write_locked, |
| 969 | TRUE); | ||
| 977 | if (delta < 0) { | 970 | if (delta < 0) { |
| 978 | ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving " | 971 | ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving " |
| 979 | "inconsistent metadata! Unmount and run " | 972 | "inconsistent metadata! Unmount and run " |
diff --git a/fs/ntfs/lcnalloc.h b/fs/ntfs/lcnalloc.h index 4cac1c024af6..e4d7fb98d685 100644 --- a/fs/ntfs/lcnalloc.h +++ b/fs/ntfs/lcnalloc.h | |||
| @@ -43,13 +43,14 @@ extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, | |||
| 43 | const NTFS_CLUSTER_ALLOCATION_ZONES zone); | 43 | const NTFS_CLUSTER_ALLOCATION_ZONES zone); |
| 44 | 44 | ||
| 45 | extern s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, | 45 | extern s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, |
| 46 | s64 count, const BOOL is_rollback); | 46 | s64 count, const BOOL write_locked, const BOOL is_rollback); |
| 47 | 47 | ||
| 48 | /** | 48 | /** |
| 49 | * ntfs_cluster_free - free clusters on an ntfs volume | 49 | * ntfs_cluster_free - free clusters on an ntfs volume |
| 50 | * @vi: vfs inode whose runlist describes the clusters to free | 50 | * @vi: vfs inode whose runlist describes the clusters to free |
| 51 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters | 51 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters |
| 52 | * @count: number of clusters to free or -1 for all clusters | 52 | * @count: number of clusters to free or -1 for all clusters |
| 53 | * @write_locked: true if the runlist is locked for writing | ||
| 53 | * | 54 | * |
| 54 | * Free @count clusters starting at the cluster @start_vcn in the runlist | 55 | * Free @count clusters starting at the cluster @start_vcn in the runlist |
| 55 | * described by the vfs inode @vi. | 56 | * described by the vfs inode @vi. |
| @@ -64,19 +65,19 @@ extern s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, | |||
| 64 | * Return the number of deallocated clusters (not counting sparse ones) on | 65 | * Return the number of deallocated clusters (not counting sparse ones) on |
| 65 | * success and -errno on error. | 66 | * success and -errno on error. |
| 66 | * | 67 | * |
| 67 | * Locking: - The runlist described by @vi must be unlocked on entry and is | 68 | * Locking: - The runlist described by @vi must be locked on entry and is |
| 68 | * unlocked on return. | 69 | * locked on return. Note if the runlist is locked for reading the |
| 69 | * - This function takes the runlist lock of @vi for reading and | 70 | * lock may be dropped and reacquired. Note the runlist may be |
| 70 | * sometimes for writing and sometimes modifies the runlist. | 71 | * modified when needed runlist fragments need to be mapped. |
| 71 | * - The volume lcn bitmap must be unlocked on entry and is unlocked | 72 | * - The volume lcn bitmap must be unlocked on entry and is unlocked |
| 72 | * on return. | 73 | * on return. |
| 73 | * - This function takes the volume lcn bitmap lock for writing and | 74 | * - This function takes the volume lcn bitmap lock for writing and |
| 74 | * modifies the bitmap contents. | 75 | * modifies the bitmap contents. |
| 75 | */ | 76 | */ |
| 76 | static inline s64 ntfs_cluster_free(struct inode *vi, const VCN start_vcn, | 77 | static inline s64 ntfs_cluster_free(struct inode *vi, const VCN start_vcn, |
| 77 | s64 count) | 78 | s64 count, const BOOL write_locked) |
| 78 | { | 79 | { |
| 79 | return __ntfs_cluster_free(vi, start_vcn, count, FALSE); | 80 | return __ntfs_cluster_free(vi, start_vcn, count, write_locked, FALSE); |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, | 83 | extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, |
| @@ -93,8 +94,10 @@ extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, | |||
| 93 | * | 94 | * |
| 94 | * Return 0 on success and -errno on error. | 95 | * Return 0 on success and -errno on error. |
| 95 | * | 96 | * |
| 96 | * Locking: This function takes the volume lcn bitmap lock for writing and | 97 | * Locking: - This function takes the volume lcn bitmap lock for writing and |
| 97 | * modifies the bitmap contents. | 98 | * modifies the bitmap contents. |
| 99 | * - The caller must have locked the runlist @rl for reading or | ||
| 100 | * writing. | ||
| 98 | */ | 101 | */ |
| 99 | static inline int ntfs_cluster_free_from_rl(ntfs_volume *vol, | 102 | static inline int ntfs_cluster_free_from_rl(ntfs_volume *vol, |
| 100 | const runlist_element *rl) | 103 | const runlist_element *rl) |
diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index 8edb8e20fb08..0173e95500d9 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c | |||
| @@ -121,7 +121,7 @@ static BOOL ntfs_check_restart_page_header(struct inode *vi, | |||
| 121 | */ | 121 | */ |
| 122 | if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { | 122 | if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { |
| 123 | ntfs_error(vi->i_sb, "$LogFile restart page is not modified " | 123 | ntfs_error(vi->i_sb, "$LogFile restart page is not modified " |
| 124 | "chkdsk but a chkdsk LSN is specified."); | 124 | "by chkdsk but a chkdsk LSN is specified."); |
| 125 | return FALSE; | 125 | return FALSE; |
| 126 | } | 126 | } |
| 127 | ntfs_debug("Done."); | 127 | ntfs_debug("Done."); |
| @@ -312,10 +312,12 @@ err_out: | |||
| 312 | * @vi: $LogFile inode to which the restart page belongs | 312 | * @vi: $LogFile inode to which the restart page belongs |
| 313 | * @rp: restart page to check | 313 | * @rp: restart page to check |
| 314 | * @pos: position in @vi at which the restart page resides | 314 | * @pos: position in @vi at which the restart page resides |
| 315 | * @wrp: copy of the multi sector transfer deprotected restart page | 315 | * @wrp: [OUT] copy of the multi sector transfer deprotected restart page |
| 316 | * @lsn: [OUT] set to the current logfile lsn on success | ||
| 316 | * | 317 | * |
| 317 | * Check the restart page @rp for consistency and return TRUE if it is | 318 | * Check the restart page @rp for consistency and return 0 if it is consistent |
| 318 | * consistent and FALSE otherwise. | 319 | * and -errno otherwise. The restart page may have been modified by chkdsk in |
| 320 | * which case its magic is CHKD instead of RSTR. | ||
| 319 | * | 321 | * |
| 320 | * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not | 322 | * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not |
| 321 | * require the full restart page. | 323 | * require the full restart page. |
| @@ -323,25 +325,33 @@ err_out: | |||
| 323 | * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a | 325 | * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a |
| 324 | * copy of the complete multi sector transfer deprotected page. On failure, | 326 | * copy of the complete multi sector transfer deprotected page. On failure, |
| 325 | * *@wrp is undefined. | 327 | * *@wrp is undefined. |
| 328 | * | ||
| 329 | * Simillarly, if @lsn is not NULL, on succes *@lsn will be set to the current | ||
| 330 | * logfile lsn according to this restart page. On failure, *@lsn is undefined. | ||
| 331 | * | ||
| 332 | * The following error codes are defined: | ||
| 333 | * -EINVAL - The restart page is inconsistent. | ||
| 334 | * -ENOMEM - Not enough memory to load the restart page. | ||
| 335 | * -EIO - Failed to reading from $LogFile. | ||
| 326 | */ | 336 | */ |
| 327 | static BOOL ntfs_check_and_load_restart_page(struct inode *vi, | 337 | static int ntfs_check_and_load_restart_page(struct inode *vi, |
| 328 | RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp) | 338 | RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, |
| 339 | LSN *lsn) | ||
| 329 | { | 340 | { |
| 330 | RESTART_AREA *ra; | 341 | RESTART_AREA *ra; |
| 331 | RESTART_PAGE_HEADER *trp; | 342 | RESTART_PAGE_HEADER *trp; |
| 332 | int size; | 343 | int size, err; |
| 333 | BOOL ret; | ||
| 334 | 344 | ||
| 335 | ntfs_debug("Entering."); | 345 | ntfs_debug("Entering."); |
| 336 | /* Check the restart page header for consistency. */ | 346 | /* Check the restart page header for consistency. */ |
| 337 | if (!ntfs_check_restart_page_header(vi, rp, pos)) { | 347 | if (!ntfs_check_restart_page_header(vi, rp, pos)) { |
| 338 | /* Error output already done inside the function. */ | 348 | /* Error output already done inside the function. */ |
| 339 | return FALSE; | 349 | return -EINVAL; |
| 340 | } | 350 | } |
| 341 | /* Check the restart area for consistency. */ | 351 | /* Check the restart area for consistency. */ |
| 342 | if (!ntfs_check_restart_area(vi, rp)) { | 352 | if (!ntfs_check_restart_area(vi, rp)) { |
| 343 | /* Error output already done inside the function. */ | 353 | /* Error output already done inside the function. */ |
| 344 | return FALSE; | 354 | return -EINVAL; |
| 345 | } | 355 | } |
| 346 | ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); | 356 | ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); |
| 347 | /* | 357 | /* |
| @@ -352,7 +362,7 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi, | |||
| 352 | if (!trp) { | 362 | if (!trp) { |
| 353 | ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile " | 363 | ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile " |
| 354 | "restart page buffer."); | 364 | "restart page buffer."); |
| 355 | return FALSE; | 365 | return -ENOMEM; |
| 356 | } | 366 | } |
| 357 | /* | 367 | /* |
| 358 | * Read the whole of the restart page into the buffer. If it fits | 368 | * Read the whole of the restart page into the buffer. If it fits |
| @@ -379,6 +389,9 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi, | |||
| 379 | if (IS_ERR(page)) { | 389 | if (IS_ERR(page)) { |
| 380 | ntfs_error(vi->i_sb, "Error mapping $LogFile " | 390 | ntfs_error(vi->i_sb, "Error mapping $LogFile " |
| 381 | "page (index %lu).", idx); | 391 | "page (index %lu).", idx); |
| 392 | err = PTR_ERR(page); | ||
| 393 | if (err != -EIO && err != -ENOMEM) | ||
| 394 | err = -EIO; | ||
| 382 | goto err_out; | 395 | goto err_out; |
| 383 | } | 396 | } |
| 384 | size = min_t(int, to_read, PAGE_CACHE_SIZE); | 397 | size = min_t(int, to_read, PAGE_CACHE_SIZE); |
| @@ -392,29 +405,57 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi, | |||
| 392 | /* Perform the multi sector transfer deprotection on the buffer. */ | 405 | /* Perform the multi sector transfer deprotection on the buffer. */ |
| 393 | if (post_read_mst_fixup((NTFS_RECORD*)trp, | 406 | if (post_read_mst_fixup((NTFS_RECORD*)trp, |
| 394 | le32_to_cpu(rp->system_page_size))) { | 407 | le32_to_cpu(rp->system_page_size))) { |
| 395 | ntfs_error(vi->i_sb, "Multi sector transfer error detected in " | 408 | /* |
| 396 | "$LogFile restart page."); | 409 | * A multi sector tranfer error was detected. We only need to |
| 397 | goto err_out; | 410 | * abort if the restart page contents exceed the multi sector |
| 411 | * transfer fixup of the first sector. | ||
| 412 | */ | ||
| 413 | if (le16_to_cpu(rp->restart_area_offset) + | ||
| 414 | le16_to_cpu(ra->restart_area_length) > | ||
| 415 | NTFS_BLOCK_SIZE - sizeof(u16)) { | ||
| 416 | ntfs_error(vi->i_sb, "Multi sector transfer error " | ||
| 417 | "detected in $LogFile restart page."); | ||
| 418 | err = -EINVAL; | ||
| 419 | goto err_out; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | /* | ||
| 423 | * If the restart page is modified by chkdsk or there are no active | ||
| 424 | * logfile clients, the logfile is consistent. Otherwise, need to | ||
| 425 | * check the log client records for consistency, too. | ||
| 426 | */ | ||
| 427 | err = 0; | ||
| 428 | if (ntfs_is_rstr_record(rp->magic) && | ||
| 429 | ra->client_in_use_list != LOGFILE_NO_CLIENT) { | ||
| 430 | if (!ntfs_check_log_client_array(vi, trp)) { | ||
| 431 | err = -EINVAL; | ||
| 432 | goto err_out; | ||
| 433 | } | ||
| 434 | } | ||
| 435 | if (lsn) { | ||
| 436 | if (ntfs_is_rstr_record(rp->magic)) | ||
| 437 | *lsn = sle64_to_cpu(ra->current_lsn); | ||
| 438 | else /* if (ntfs_is_chkd_record(rp->magic)) */ | ||
| 439 | *lsn = sle64_to_cpu(rp->chkdsk_lsn); | ||
| 398 | } | 440 | } |
| 399 | /* Check the log client records for consistency. */ | ||
| 400 | ret = ntfs_check_log_client_array(vi, trp); | ||
| 401 | if (ret && wrp) | ||
| 402 | *wrp = trp; | ||
| 403 | else | ||
| 404 | ntfs_free(trp); | ||
| 405 | ntfs_debug("Done."); | 441 | ntfs_debug("Done."); |
| 406 | return ret; | 442 | if (wrp) |
| 443 | *wrp = trp; | ||
| 444 | else { | ||
| 407 | err_out: | 445 | err_out: |
| 408 | ntfs_free(trp); | 446 | ntfs_free(trp); |
| 409 | return FALSE; | 447 | } |
| 448 | return err; | ||
| 410 | } | 449 | } |
| 411 | 450 | ||
| 412 | /** | 451 | /** |
| 413 | * ntfs_check_logfile - check the journal for consistency | 452 | * ntfs_check_logfile - check the journal for consistency |
| 414 | * @log_vi: struct inode of loaded journal $LogFile to check | 453 | * @log_vi: struct inode of loaded journal $LogFile to check |
| 454 | * @rp: [OUT] on success this is a copy of the current restart page | ||
| 415 | * | 455 | * |
| 416 | * Check the $LogFile journal for consistency and return TRUE if it is | 456 | * Check the $LogFile journal for consistency and return TRUE if it is |
| 417 | * consistent and FALSE if not. | 457 | * consistent and FALSE if not. On success, the current restart page is |
| 458 | * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. | ||
| 418 | * | 459 | * |
| 419 | * At present we only check the two restart pages and ignore the log record | 460 | * At present we only check the two restart pages and ignore the log record |
| 420 | * pages. | 461 | * pages. |
| @@ -424,19 +465,18 @@ err_out: | |||
| 424 | * if the $LogFile was created on a system with a different page size to ours | 465 | * if the $LogFile was created on a system with a different page size to ours |
| 425 | * yet and mst deprotection would fail if our page size is smaller. | 466 | * yet and mst deprotection would fail if our page size is smaller. |
| 426 | */ | 467 | */ |
| 427 | BOOL ntfs_check_logfile(struct inode *log_vi) | 468 | BOOL ntfs_check_logfile(struct inode *log_vi, RESTART_PAGE_HEADER **rp) |
| 428 | { | 469 | { |
| 429 | s64 size, pos, rstr1_pos, rstr2_pos; | 470 | s64 size, pos; |
| 471 | LSN rstr1_lsn, rstr2_lsn; | ||
| 430 | ntfs_volume *vol = NTFS_SB(log_vi->i_sb); | 472 | ntfs_volume *vol = NTFS_SB(log_vi->i_sb); |
| 431 | struct address_space *mapping = log_vi->i_mapping; | 473 | struct address_space *mapping = log_vi->i_mapping; |
| 432 | struct page *page = NULL; | 474 | struct page *page = NULL; |
| 433 | u8 *kaddr = NULL; | 475 | u8 *kaddr = NULL; |
| 434 | RESTART_PAGE_HEADER *rstr1_ph = NULL; | 476 | RESTART_PAGE_HEADER *rstr1_ph = NULL; |
| 435 | RESTART_PAGE_HEADER *rstr2_ph = NULL; | 477 | RESTART_PAGE_HEADER *rstr2_ph = NULL; |
| 436 | int log_page_size, log_page_mask, ofs; | 478 | int log_page_size, log_page_mask, err; |
| 437 | BOOL logfile_is_empty = TRUE; | 479 | BOOL logfile_is_empty = TRUE; |
| 438 | BOOL rstr1_found = FALSE; | ||
| 439 | BOOL rstr2_found = FALSE; | ||
| 440 | u8 log_page_bits; | 480 | u8 log_page_bits; |
| 441 | 481 | ||
| 442 | ntfs_debug("Entering."); | 482 | ntfs_debug("Entering."); |
| @@ -491,7 +531,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi) | |||
| 491 | if (IS_ERR(page)) { | 531 | if (IS_ERR(page)) { |
| 492 | ntfs_error(vol->sb, "Error mapping $LogFile " | 532 | ntfs_error(vol->sb, "Error mapping $LogFile " |
| 493 | "page (index %lu).", idx); | 533 | "page (index %lu).", idx); |
| 494 | return FALSE; | 534 | goto err_out; |
| 495 | } | 535 | } |
| 496 | } | 536 | } |
| 497 | kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK); | 537 | kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK); |
| @@ -510,99 +550,95 @@ BOOL ntfs_check_logfile(struct inode *log_vi) | |||
| 510 | */ | 550 | */ |
| 511 | if (ntfs_is_rcrd_recordp((le32*)kaddr)) | 551 | if (ntfs_is_rcrd_recordp((le32*)kaddr)) |
| 512 | break; | 552 | break; |
| 513 | /* | 553 | /* If not a (modified by chkdsk) restart page, continue. */ |
| 514 | * A modified by chkdsk restart page means we cannot handle | 554 | if (!ntfs_is_rstr_recordp((le32*)kaddr) && |
| 515 | * this log file. | 555 | !ntfs_is_chkd_recordp((le32*)kaddr)) { |
| 516 | */ | ||
| 517 | if (ntfs_is_chkd_recordp((le32*)kaddr)) { | ||
| 518 | ntfs_error(vol->sb, "$LogFile has been modified by " | ||
| 519 | "chkdsk. Mount this volume in " | ||
| 520 | "Windows."); | ||
| 521 | goto err_out; | ||
| 522 | } | ||
| 523 | /* If not a restart page, continue. */ | ||
| 524 | if (!ntfs_is_rstr_recordp((le32*)kaddr)) { | ||
| 525 | /* Skip to the minimum page size for the next one. */ | ||
| 526 | if (!pos) | 556 | if (!pos) |
| 527 | pos = NTFS_BLOCK_SIZE >> 1; | 557 | pos = NTFS_BLOCK_SIZE >> 1; |
| 528 | continue; | 558 | continue; |
| 529 | } | 559 | } |
| 530 | /* We now know we have a restart page. */ | ||
| 531 | if (!pos) { | ||
| 532 | rstr1_found = TRUE; | ||
| 533 | rstr1_pos = pos; | ||
| 534 | } else { | ||
| 535 | if (rstr2_found) { | ||
| 536 | ntfs_error(vol->sb, "Found more than two " | ||
| 537 | "restart pages in $LogFile."); | ||
| 538 | goto err_out; | ||
| 539 | } | ||
| 540 | rstr2_found = TRUE; | ||
| 541 | rstr2_pos = pos; | ||
| 542 | } | ||
| 543 | /* | 560 | /* |
| 544 | * Check the restart page for consistency and get a copy of the | 561 | * Check the (modified by chkdsk) restart page for consistency |
| 545 | * complete multi sector transfer deprotected restart page. | 562 | * and get a copy of the complete multi sector transfer |
| 563 | * deprotected restart page. | ||
| 546 | */ | 564 | */ |
| 547 | if (!ntfs_check_and_load_restart_page(log_vi, | 565 | err = ntfs_check_and_load_restart_page(log_vi, |
| 548 | (RESTART_PAGE_HEADER*)kaddr, pos, | 566 | (RESTART_PAGE_HEADER*)kaddr, pos, |
| 549 | !pos ? &rstr1_ph : &rstr2_ph)) { | 567 | !rstr1_ph ? &rstr1_ph : &rstr2_ph, |
| 550 | /* Error output already done inside the function. */ | 568 | !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); |
| 551 | goto err_out; | 569 | if (!err) { |
| 570 | /* | ||
| 571 | * If we have now found the first (modified by chkdsk) | ||
| 572 | * restart page, continue looking for the second one. | ||
| 573 | */ | ||
| 574 | if (!pos) { | ||
| 575 | pos = NTFS_BLOCK_SIZE >> 1; | ||
| 576 | continue; | ||
| 577 | } | ||
| 578 | /* | ||
| 579 | * We have now found the second (modified by chkdsk) | ||
| 580 | * restart page, so we can stop looking. | ||
| 581 | */ | ||
| 582 | break; | ||
| 552 | } | 583 | } |
| 553 | /* | 584 | /* |
| 554 | * We have a valid restart page. The next one must be after | 585 | * Error output already done inside the function. Note, we do |
| 555 | * a whole system page size as specified by the valid restart | 586 | * not abort if the restart page was invalid as we might still |
| 556 | * page. | 587 | * find a valid one further in the file. |
| 557 | */ | 588 | */ |
| 589 | if (err != -EINVAL) { | ||
| 590 | ntfs_unmap_page(page); | ||
| 591 | goto err_out; | ||
| 592 | } | ||
| 593 | /* Continue looking. */ | ||
| 558 | if (!pos) | 594 | if (!pos) |
| 559 | pos = le32_to_cpu(rstr1_ph->system_page_size) >> 1; | 595 | pos = NTFS_BLOCK_SIZE >> 1; |
| 560 | } | 596 | } |
| 561 | if (page) { | 597 | if (page) |
| 562 | ntfs_unmap_page(page); | 598 | ntfs_unmap_page(page); |
| 563 | page = NULL; | ||
| 564 | } | ||
| 565 | if (logfile_is_empty) { | 599 | if (logfile_is_empty) { |
| 566 | NVolSetLogFileEmpty(vol); | 600 | NVolSetLogFileEmpty(vol); |
| 567 | is_empty: | 601 | is_empty: |
| 568 | ntfs_debug("Done. ($LogFile is empty.)"); | 602 | ntfs_debug("Done. ($LogFile is empty.)"); |
| 569 | return TRUE; | 603 | return TRUE; |
| 570 | } | 604 | } |
| 571 | if (!rstr1_found || !rstr2_found) { | 605 | if (!rstr1_ph) { |
| 572 | ntfs_error(vol->sb, "Did not find two restart pages in " | 606 | BUG_ON(rstr2_ph); |
| 573 | "$LogFile."); | 607 | ntfs_error(vol->sb, "Did not find any restart pages in " |
| 574 | goto err_out; | 608 | "$LogFile and it was not empty."); |
| 609 | return FALSE; | ||
| 610 | } | ||
| 611 | /* If both restart pages were found, use the more recent one. */ | ||
| 612 | if (rstr2_ph) { | ||
| 613 | /* | ||
| 614 | * If the second restart area is more recent, switch to it. | ||
| 615 | * Otherwise just throw it away. | ||
| 616 | */ | ||
| 617 | if (rstr2_lsn > rstr1_lsn) { | ||
| 618 | ntfs_free(rstr1_ph); | ||
| 619 | rstr1_ph = rstr2_ph; | ||
| 620 | /* rstr1_lsn = rstr2_lsn; */ | ||
| 621 | } else | ||
| 622 | ntfs_free(rstr2_ph); | ||
| 623 | rstr2_ph = NULL; | ||
| 575 | } | 624 | } |
| 576 | /* | ||
| 577 | * The two restart areas must be identical except for the update | ||
| 578 | * sequence number. | ||
| 579 | */ | ||
| 580 | ofs = le16_to_cpu(rstr1_ph->usa_ofs); | ||
| 581 | if (memcmp(rstr1_ph, rstr2_ph, ofs) || (ofs += sizeof(u16), | ||
| 582 | memcmp((u8*)rstr1_ph + ofs, (u8*)rstr2_ph + ofs, | ||
| 583 | le32_to_cpu(rstr1_ph->system_page_size) - ofs))) { | ||
| 584 | ntfs_error(vol->sb, "The two restart pages in $LogFile do not " | ||
| 585 | "match."); | ||
| 586 | goto err_out; | ||
| 587 | } | ||
| 588 | ntfs_free(rstr1_ph); | ||
| 589 | ntfs_free(rstr2_ph); | ||
| 590 | /* All consistency checks passed. */ | 625 | /* All consistency checks passed. */ |
| 626 | if (rp) | ||
| 627 | *rp = rstr1_ph; | ||
| 628 | else | ||
| 629 | ntfs_free(rstr1_ph); | ||
| 591 | ntfs_debug("Done."); | 630 | ntfs_debug("Done."); |
| 592 | return TRUE; | 631 | return TRUE; |
| 593 | err_out: | 632 | err_out: |
| 594 | if (page) | ||
| 595 | ntfs_unmap_page(page); | ||
| 596 | if (rstr1_ph) | 633 | if (rstr1_ph) |
| 597 | ntfs_free(rstr1_ph); | 634 | ntfs_free(rstr1_ph); |
| 598 | if (rstr2_ph) | ||
| 599 | ntfs_free(rstr2_ph); | ||
| 600 | return FALSE; | 635 | return FALSE; |
| 601 | } | 636 | } |
| 602 | 637 | ||
| 603 | /** | 638 | /** |
| 604 | * ntfs_is_logfile_clean - check in the journal if the volume is clean | 639 | * ntfs_is_logfile_clean - check in the journal if the volume is clean |
| 605 | * @log_vi: struct inode of loaded journal $LogFile to check | 640 | * @log_vi: struct inode of loaded journal $LogFile to check |
| 641 | * @rp: copy of the current restart page | ||
| 606 | * | 642 | * |
| 607 | * Analyze the $LogFile journal and return TRUE if it indicates the volume was | 643 | * Analyze the $LogFile journal and return TRUE if it indicates the volume was |
| 608 | * shutdown cleanly and FALSE if not. | 644 | * shutdown cleanly and FALSE if not. |
| @@ -619,11 +655,9 @@ err_out: | |||
| 619 | * is empty this function requires that NVolLogFileEmpty() is true otherwise an | 655 | * is empty this function requires that NVolLogFileEmpty() is true otherwise an |
| 620 | * empty volume will be reported as dirty. | 656 | * empty volume will be reported as dirty. |
| 621 | */ | 657 | */ |
| 622 | BOOL ntfs_is_logfile_clean(struct inode *log_vi) | 658 | BOOL ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp) |
| 623 | { | 659 | { |
| 624 | ntfs_volume *vol = NTFS_SB(log_vi->i_sb); | 660 | ntfs_volume *vol = NTFS_SB(log_vi->i_sb); |
| 625 | struct page *page; | ||
| 626 | RESTART_PAGE_HEADER *rp; | ||
| 627 | RESTART_AREA *ra; | 661 | RESTART_AREA *ra; |
| 628 | 662 | ||
| 629 | ntfs_debug("Entering."); | 663 | ntfs_debug("Entering."); |
| @@ -632,24 +666,15 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi) | |||
| 632 | ntfs_debug("Done. ($LogFile is empty.)"); | 666 | ntfs_debug("Done. ($LogFile is empty.)"); |
| 633 | return TRUE; | 667 | return TRUE; |
| 634 | } | 668 | } |
| 635 | /* | 669 | BUG_ON(!rp); |
| 636 | * Read the first restart page. It will be possibly incomplete and | 670 | if (!ntfs_is_rstr_record(rp->magic) && |
| 637 | * will not be multi sector transfer deprotected but we only need the | 671 | !ntfs_is_chkd_record(rp->magic)) { |
| 638 | * first NTFS_BLOCK_SIZE bytes so it does not matter. | 672 | ntfs_error(vol->sb, "Restart page buffer is invalid. This is " |
| 639 | */ | 673 | "probably a bug in that the $LogFile should " |
| 640 | page = ntfs_map_page(log_vi->i_mapping, 0); | 674 | "have been consistency checked before calling " |
| 641 | if (IS_ERR(page)) { | 675 | "this function."); |
| 642 | ntfs_error(vol->sb, "Error mapping $LogFile page (index 0)."); | ||
| 643 | return FALSE; | 676 | return FALSE; |
| 644 | } | 677 | } |
| 645 | rp = (RESTART_PAGE_HEADER*)page_address(page); | ||
| 646 | if (!ntfs_is_rstr_record(rp->magic)) { | ||
| 647 | ntfs_error(vol->sb, "No restart page found at offset zero in " | ||
| 648 | "$LogFile. This is probably a bug in that " | ||
| 649 | "the $LogFile should have been consistency " | ||
| 650 | "checked before calling this function."); | ||
| 651 | goto err_out; | ||
| 652 | } | ||
| 653 | ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); | 678 | ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); |
| 654 | /* | 679 | /* |
| 655 | * If the $LogFile has active clients, i.e. it is open, and we do not | 680 | * If the $LogFile has active clients, i.e. it is open, and we do not |
| @@ -659,15 +684,11 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi) | |||
| 659 | if (ra->client_in_use_list != LOGFILE_NO_CLIENT && | 684 | if (ra->client_in_use_list != LOGFILE_NO_CLIENT && |
| 660 | !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { | 685 | !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { |
| 661 | ntfs_debug("Done. $LogFile indicates a dirty shutdown."); | 686 | ntfs_debug("Done. $LogFile indicates a dirty shutdown."); |
| 662 | goto err_out; | 687 | return FALSE; |
| 663 | } | 688 | } |
| 664 | ntfs_unmap_page(page); | ||
| 665 | /* $LogFile indicates a clean shutdown. */ | 689 | /* $LogFile indicates a clean shutdown. */ |
| 666 | ntfs_debug("Done. $LogFile indicates a clean shutdown."); | 690 | ntfs_debug("Done. $LogFile indicates a clean shutdown."); |
| 667 | return TRUE; | 691 | return TRUE; |
| 668 | err_out: | ||
| 669 | ntfs_unmap_page(page); | ||
| 670 | return FALSE; | ||
| 671 | } | 692 | } |
| 672 | 693 | ||
| 673 | /** | 694 | /** |
diff --git a/fs/ntfs/logfile.h b/fs/ntfs/logfile.h index 4ee4378de061..42388f95ea6d 100644 --- a/fs/ntfs/logfile.h +++ b/fs/ntfs/logfile.h | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of | 2 | * logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of |
| 3 | * the Linux-NTFS project. | 3 | * the Linux-NTFS project. |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2000-2004 Anton Altaparmakov | 5 | * Copyright (c) 2000-2005 Anton Altaparmakov |
| 6 | * | 6 | * |
| 7 | * This program/include file is free software; you can redistribute it and/or | 7 | * This program/include file is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License as published | 8 | * modify it under the terms of the GNU General Public License as published |
| @@ -296,9 +296,11 @@ typedef struct { | |||
| 296 | /* sizeof() = 160 (0xa0) bytes */ | 296 | /* sizeof() = 160 (0xa0) bytes */ |
| 297 | } __attribute__ ((__packed__)) LOG_CLIENT_RECORD; | 297 | } __attribute__ ((__packed__)) LOG_CLIENT_RECORD; |
| 298 | 298 | ||
| 299 | extern BOOL ntfs_check_logfile(struct inode *log_vi); | 299 | extern BOOL ntfs_check_logfile(struct inode *log_vi, |
| 300 | RESTART_PAGE_HEADER **rp); | ||
| 300 | 301 | ||
| 301 | extern BOOL ntfs_is_logfile_clean(struct inode *log_vi); | 302 | extern BOOL ntfs_is_logfile_clean(struct inode *log_vi, |
| 303 | const RESTART_PAGE_HEADER *rp); | ||
| 302 | 304 | ||
| 303 | extern BOOL ntfs_empty_logfile(struct inode *log_vi); | 305 | extern BOOL ntfs_empty_logfile(struct inode *log_vi); |
| 304 | 306 | ||
diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h index fac5944df6d8..9994e019a3cf 100644 --- a/fs/ntfs/malloc.h +++ b/fs/ntfs/malloc.h | |||
| @@ -27,27 +27,63 @@ | |||
| 27 | #include <linux/highmem.h> | 27 | #include <linux/highmem.h> |
| 28 | 28 | ||
| 29 | /** | 29 | /** |
| 30 | * ntfs_malloc_nofs - allocate memory in multiples of pages | 30 | * __ntfs_malloc - allocate memory in multiples of pages |
| 31 | * @size number of bytes to allocate | 31 | * @size: number of bytes to allocate |
| 32 | * @gfp_mask: extra flags for the allocator | ||
| 33 | * | ||
| 34 | * Internal function. You probably want ntfs_malloc_nofs()... | ||
| 32 | * | 35 | * |
| 33 | * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and | 36 | * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and |
| 34 | * returns a pointer to the allocated memory. | 37 | * returns a pointer to the allocated memory. |
| 35 | * | 38 | * |
| 36 | * If there was insufficient memory to complete the request, return NULL. | 39 | * If there was insufficient memory to complete the request, return NULL. |
| 40 | * Depending on @gfp_mask the allocation may be guaranteed to succeed. | ||
| 37 | */ | 41 | */ |
| 38 | static inline void *ntfs_malloc_nofs(unsigned long size) | 42 | static inline void *__ntfs_malloc(unsigned long size, |
| 43 | unsigned int __nocast gfp_mask) | ||
| 39 | { | 44 | { |
| 40 | if (likely(size <= PAGE_SIZE)) { | 45 | if (likely(size <= PAGE_SIZE)) { |
| 41 | BUG_ON(!size); | 46 | BUG_ON(!size); |
| 42 | /* kmalloc() has per-CPU caches so is faster for now. */ | 47 | /* kmalloc() has per-CPU caches so is faster for now. */ |
| 43 | return kmalloc(PAGE_SIZE, GFP_NOFS); | 48 | return kmalloc(PAGE_SIZE, gfp_mask); |
| 44 | /* return (void *)__get_free_page(GFP_NOFS | __GFP_HIGHMEM); */ | 49 | /* return (void *)__get_free_page(gfp_mask); */ |
| 45 | } | 50 | } |
| 46 | if (likely(size >> PAGE_SHIFT < num_physpages)) | 51 | if (likely(size >> PAGE_SHIFT < num_physpages)) |
| 47 | return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL); | 52 | return __vmalloc(size, gfp_mask, PAGE_KERNEL); |
| 48 | return NULL; | 53 | return NULL; |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 56 | /** | ||
| 57 | * ntfs_malloc_nofs - allocate memory in multiples of pages | ||
| 58 | * @size: number of bytes to allocate | ||
| 59 | * | ||
| 60 | * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and | ||
| 61 | * returns a pointer to the allocated memory. | ||
| 62 | * | ||
| 63 | * If there was insufficient memory to complete the request, return NULL. | ||
| 64 | */ | ||
| 65 | static inline void *ntfs_malloc_nofs(unsigned long size) | ||
| 66 | { | ||
| 67 | return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM); | ||
| 68 | } | ||
| 69 | |||
| 70 | /** | ||
| 71 | * ntfs_malloc_nofs_nofail - allocate memory in multiples of pages | ||
| 72 | * @size: number of bytes to allocate | ||
| 73 | * | ||
| 74 | * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and | ||
| 75 | * returns a pointer to the allocated memory. | ||
| 76 | * | ||
| 77 | * This function guarantees that the allocation will succeed. It will sleep | ||
| 78 | * for as long as it takes to complete the allocation. | ||
| 79 | * | ||
| 80 | * If there was insufficient memory to complete the request, return NULL. | ||
| 81 | */ | ||
| 82 | static inline void *ntfs_malloc_nofs_nofail(unsigned long size) | ||
| 83 | { | ||
| 84 | return __ntfs_malloc(size, GFP_NOFS | __GFP_HIGHMEM | __GFP_NOFAIL); | ||
| 85 | } | ||
| 86 | |||
| 51 | static inline void ntfs_free(void *addr) | 87 | static inline void ntfs_free(void *addr) |
| 52 | { | 88 | { |
| 53 | if (likely(((unsigned long)addr < VMALLOC_START) || | 89 | if (likely(((unsigned long)addr < VMALLOC_START) || |
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 317f7c679fd3..2c32b84385a8 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c | |||
| @@ -511,7 +511,6 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, | |||
| 511 | } while (bh); | 511 | } while (bh); |
| 512 | tail->b_this_page = head; | 512 | tail->b_this_page = head; |
| 513 | attach_page_buffers(page, head); | 513 | attach_page_buffers(page, head); |
| 514 | BUG_ON(!page_has_buffers(page)); | ||
| 515 | } | 514 | } |
| 516 | bh = head = page_buffers(page); | 515 | bh = head = page_buffers(page); |
| 517 | BUG_ON(!bh); | 516 | BUG_ON(!bh); |
| @@ -692,7 +691,6 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) | |||
| 692 | */ | 691 | */ |
| 693 | if (!NInoTestClearDirty(ni)) | 692 | if (!NInoTestClearDirty(ni)) |
| 694 | goto done; | 693 | goto done; |
| 695 | BUG_ON(!page_has_buffers(page)); | ||
| 696 | bh = head = page_buffers(page); | 694 | bh = head = page_buffers(page); |
| 697 | BUG_ON(!bh); | 695 | BUG_ON(!bh); |
| 698 | rl = NULL; | 696 | rl = NULL; |
| @@ -1955,7 +1953,7 @@ restore_undo_alloc: | |||
| 1955 | a = ctx->attr; | 1953 | a = ctx->attr; |
| 1956 | a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1); | 1954 | a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1); |
| 1957 | undo_alloc: | 1955 | undo_alloc: |
| 1958 | if (ntfs_cluster_free(vol->mft_ino, old_last_vcn, -1) < 0) { | 1956 | if (ntfs_cluster_free(vol->mft_ino, old_last_vcn, -1, TRUE) < 0) { |
| 1959 | ntfs_error(vol->sb, "Failed to free clusters from mft data " | 1957 | ntfs_error(vol->sb, "Failed to free clusters from mft data " |
| 1960 | "attribute.%s", es); | 1958 | "attribute.%s", es); |
| 1961 | NVolSetErrors(vol); | 1959 | NVolSetErrors(vol); |
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index 758855b0414e..f5b2ac929081 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c | |||
| @@ -35,7 +35,7 @@ static inline void ntfs_rl_mm(runlist_element *base, int dst, int src, | |||
| 35 | int size) | 35 | int size) |
| 36 | { | 36 | { |
| 37 | if (likely((dst != src) && (size > 0))) | 37 | if (likely((dst != src) && (size > 0))) |
| 38 | memmove(base + dst, base + src, size * sizeof (*base)); | 38 | memmove(base + dst, base + src, size * sizeof(*base)); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | /** | 41 | /** |
| @@ -95,6 +95,51 @@ static inline runlist_element *ntfs_rl_realloc(runlist_element *rl, | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | /** | 97 | /** |
| 98 | * ntfs_rl_realloc_nofail - Reallocate memory for runlists | ||
| 99 | * @rl: original runlist | ||
| 100 | * @old_size: number of runlist elements in the original runlist @rl | ||
| 101 | * @new_size: number of runlist elements we need space for | ||
| 102 | * | ||
| 103 | * As the runlists grow, more memory will be required. To prevent the | ||
| 104 | * kernel having to allocate and reallocate large numbers of small bits of | ||
| 105 | * memory, this function returns an entire page of memory. | ||
| 106 | * | ||
| 107 | * This function guarantees that the allocation will succeed. It will sleep | ||
| 108 | * for as long as it takes to complete the allocation. | ||
| 109 | * | ||
| 110 | * It is up to the caller to serialize access to the runlist @rl. | ||
| 111 | * | ||
| 112 | * N.B. If the new allocation doesn't require a different number of pages in | ||
| 113 | * memory, the function will return the original pointer. | ||
| 114 | * | ||
| 115 | * On success, return a pointer to the newly allocated, or recycled, memory. | ||
| 116 | * On error, return -errno. The following error codes are defined: | ||
| 117 | * -ENOMEM - Not enough memory to allocate runlist array. | ||
| 118 | * -EINVAL - Invalid parameters were passed in. | ||
| 119 | */ | ||
| 120 | static inline runlist_element *ntfs_rl_realloc_nofail(runlist_element *rl, | ||
| 121 | int old_size, int new_size) | ||
| 122 | { | ||
| 123 | runlist_element *new_rl; | ||
| 124 | |||
| 125 | old_size = PAGE_ALIGN(old_size * sizeof(*rl)); | ||
| 126 | new_size = PAGE_ALIGN(new_size * sizeof(*rl)); | ||
| 127 | if (old_size == new_size) | ||
| 128 | return rl; | ||
| 129 | |||
| 130 | new_rl = ntfs_malloc_nofs_nofail(new_size); | ||
| 131 | BUG_ON(!new_rl); | ||
| 132 | |||
| 133 | if (likely(rl != NULL)) { | ||
| 134 | if (unlikely(old_size > new_size)) | ||
| 135 | old_size = new_size; | ||
| 136 | memcpy(new_rl, rl, old_size); | ||
| 137 | ntfs_free(rl); | ||
| 138 | } | ||
| 139 | return new_rl; | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 98 | * ntfs_are_rl_mergeable - test if two runlists can be joined together | 143 | * ntfs_are_rl_mergeable - test if two runlists can be joined together |
| 99 | * @dst: original runlist | 144 | * @dst: original runlist |
| 100 | * @src: new runlist to test for mergeability with @dst | 145 | * @src: new runlist to test for mergeability with @dst |
| @@ -497,6 +542,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, | |||
| 497 | /* Scan to the end of the source runlist. */ | 542 | /* Scan to the end of the source runlist. */ |
| 498 | for (dend = 0; likely(drl[dend].length); dend++) | 543 | for (dend = 0; likely(drl[dend].length); dend++) |
| 499 | ; | 544 | ; |
| 545 | dend++; | ||
| 500 | drl = ntfs_rl_realloc(drl, dend, dend + 1); | 546 | drl = ntfs_rl_realloc(drl, dend, dend + 1); |
| 501 | if (IS_ERR(drl)) | 547 | if (IS_ERR(drl)) |
| 502 | return drl; | 548 | return drl; |
| @@ -566,8 +612,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, | |||
| 566 | ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ | 612 | ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ |
| 567 | (srl[send - 1].vcn + srl[send - 1].length))); | 613 | (srl[send - 1].vcn + srl[send - 1].length))); |
| 568 | 614 | ||
| 569 | /* Or we'll lose an end marker */ | 615 | /* Or we will lose an end marker. */ |
| 570 | if (start && finish && (drl[dins].length == 0)) | 616 | if (finish && !drl[dins].length) |
| 571 | ss++; | 617 | ss++; |
| 572 | if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) | 618 | if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) |
| 573 | finish = FALSE; | 619 | finish = FALSE; |
| @@ -621,11 +667,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, | |||
| 621 | if (drl[ds].lcn != LCN_RL_NOT_MAPPED) { | 667 | if (drl[ds].lcn != LCN_RL_NOT_MAPPED) { |
| 622 | /* Add an unmapped runlist element. */ | 668 | /* Add an unmapped runlist element. */ |
| 623 | if (!slots) { | 669 | if (!slots) { |
| 624 | /* FIXME/TODO: We need to have the | 670 | drl = ntfs_rl_realloc_nofail(drl, ds, |
| 625 | * extra memory already! (AIA) */ | 671 | ds + 2); |
| 626 | drl = ntfs_rl_realloc(drl, ds, ds + 2); | ||
| 627 | if (!drl) | ||
| 628 | goto critical_error; | ||
| 629 | slots = 2; | 672 | slots = 2; |
| 630 | } | 673 | } |
| 631 | ds++; | 674 | ds++; |
| @@ -640,13 +683,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, | |||
| 640 | drl[ds].length = marker_vcn - drl[ds].vcn; | 683 | drl[ds].length = marker_vcn - drl[ds].vcn; |
| 641 | /* Finally add the ENOENT terminator. */ | 684 | /* Finally add the ENOENT terminator. */ |
| 642 | ds++; | 685 | ds++; |
| 643 | if (!slots) { | 686 | if (!slots) |
| 644 | /* FIXME/TODO: We need to have the extra | 687 | drl = ntfs_rl_realloc_nofail(drl, ds, ds + 1); |
| 645 | * memory already! (AIA) */ | ||
| 646 | drl = ntfs_rl_realloc(drl, ds, ds + 1); | ||
| 647 | if (!drl) | ||
| 648 | goto critical_error; | ||
| 649 | } | ||
| 650 | drl[ds].vcn = marker_vcn; | 688 | drl[ds].vcn = marker_vcn; |
| 651 | drl[ds].lcn = LCN_ENOENT; | 689 | drl[ds].lcn = LCN_ENOENT; |
| 652 | drl[ds].length = (s64)0; | 690 | drl[ds].length = (s64)0; |
| @@ -659,11 +697,6 @@ finished: | |||
| 659 | ntfs_debug("Merged runlist:"); | 697 | ntfs_debug("Merged runlist:"); |
| 660 | ntfs_debug_dump_runlist(drl); | 698 | ntfs_debug_dump_runlist(drl); |
| 661 | return drl; | 699 | return drl; |
| 662 | |||
| 663 | critical_error: | ||
| 664 | /* Critical error! We cannot afford to fail here. */ | ||
| 665 | ntfs_error(NULL, "Critical error! Not enough memory."); | ||
| 666 | panic("NTFS: Cannot continue."); | ||
| 667 | } | 700 | } |
| 668 | 701 | ||
| 669 | /** | 702 | /** |
| @@ -727,6 +760,9 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, | |||
| 727 | ntfs_error(vol->sb, "Corrupt attribute."); | 760 | ntfs_error(vol->sb, "Corrupt attribute."); |
| 728 | return ERR_PTR(-EIO); | 761 | return ERR_PTR(-EIO); |
| 729 | } | 762 | } |
| 763 | /* If the mapping pairs array is valid but empty, nothing to do. */ | ||
| 764 | if (!vcn && !*buf) | ||
| 765 | return old_rl; | ||
| 730 | /* Current position in runlist array. */ | 766 | /* Current position in runlist array. */ |
| 731 | rlpos = 0; | 767 | rlpos = 0; |
| 732 | /* Allocate first page and set current runlist size to one page. */ | 768 | /* Allocate first page and set current runlist size to one page. */ |
| @@ -1419,6 +1455,7 @@ err_out: | |||
| 1419 | 1455 | ||
| 1420 | /** | 1456 | /** |
| 1421 | * ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn | 1457 | * ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn |
| 1458 | * @vol: ntfs volume (needed for error output) | ||
| 1422 | * @runlist: runlist to truncate | 1459 | * @runlist: runlist to truncate |
| 1423 | * @new_length: the new length of the runlist in VCNs | 1460 | * @new_length: the new length of the runlist in VCNs |
| 1424 | * | 1461 | * |
| @@ -1426,12 +1463,16 @@ err_out: | |||
| 1426 | * holding the runlist elements to a length of @new_length VCNs. | 1463 | * holding the runlist elements to a length of @new_length VCNs. |
| 1427 | * | 1464 | * |
| 1428 | * If @new_length lies within the runlist, the runlist elements with VCNs of | 1465 | * If @new_length lies within the runlist, the runlist elements with VCNs of |
| 1429 | * @new_length and above are discarded. | 1466 | * @new_length and above are discarded. As a special case if @new_length is |
| 1467 | * zero, the runlist is discarded and set to NULL. | ||
| 1430 | * | 1468 | * |
| 1431 | * If @new_length lies beyond the runlist, a sparse runlist element is added to | 1469 | * If @new_length lies beyond the runlist, a sparse runlist element is added to |
| 1432 | * the end of the runlist @runlist or if the last runlist element is a sparse | 1470 | * the end of the runlist @runlist or if the last runlist element is a sparse |
| 1433 | * one already, this is extended. | 1471 | * one already, this is extended. |
| 1434 | * | 1472 | * |
| 1473 | * Note, no checking is done for unmapped runlist elements. It is assumed that | ||
| 1474 | * the caller has mapped any elements that need to be mapped already. | ||
| 1475 | * | ||
| 1435 | * Return 0 on success and -errno on error. | 1476 | * Return 0 on success and -errno on error. |
| 1436 | * | 1477 | * |
| 1437 | * Locking: The caller must hold @runlist->lock for writing. | 1478 | * Locking: The caller must hold @runlist->lock for writing. |
| @@ -1446,6 +1487,13 @@ int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist, | |||
| 1446 | BUG_ON(!runlist); | 1487 | BUG_ON(!runlist); |
| 1447 | BUG_ON(new_length < 0); | 1488 | BUG_ON(new_length < 0); |
| 1448 | rl = runlist->rl; | 1489 | rl = runlist->rl; |
| 1490 | if (!new_length) { | ||
| 1491 | ntfs_debug("Freeing runlist."); | ||
| 1492 | runlist->rl = NULL; | ||
| 1493 | if (rl) | ||
| 1494 | ntfs_free(rl); | ||
| 1495 | return 0; | ||
| 1496 | } | ||
| 1449 | if (unlikely(!rl)) { | 1497 | if (unlikely(!rl)) { |
| 1450 | /* | 1498 | /* |
| 1451 | * Create a runlist consisting of a sparse runlist element of | 1499 | * Create a runlist consisting of a sparse runlist element of |
| @@ -1553,4 +1601,288 @@ int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist, | |||
| 1553 | return 0; | 1601 | return 0; |
| 1554 | } | 1602 | } |
| 1555 | 1603 | ||
| 1604 | /** | ||
| 1605 | * ntfs_rl_punch_nolock - punch a hole into a runlist | ||
| 1606 | * @vol: ntfs volume (needed for error output) | ||
| 1607 | * @runlist: runlist to punch a hole into | ||
| 1608 | * @start: starting VCN of the hole to be created | ||
| 1609 | * @length: size of the hole to be created in units of clusters | ||
| 1610 | * | ||
| 1611 | * Punch a hole into the runlist @runlist starting at VCN @start and of size | ||
| 1612 | * @length clusters. | ||
| 1613 | * | ||
| 1614 | * Return 0 on success and -errno on error, in which case @runlist has not been | ||
| 1615 | * modified. | ||
| 1616 | * | ||
| 1617 | * If @start and/or @start + @length are outside the runlist return error code | ||
| 1618 | * -ENOENT. | ||
| 1619 | * | ||
| 1620 | * If the runlist contains unmapped or error elements between @start and @start | ||
| 1621 | * + @length return error code -EINVAL. | ||
| 1622 | * | ||
| 1623 | * Locking: The caller must hold @runlist->lock for writing. | ||
| 1624 | */ | ||
| 1625 | int ntfs_rl_punch_nolock(const ntfs_volume *vol, runlist *const runlist, | ||
| 1626 | const VCN start, const s64 length) | ||
| 1627 | { | ||
| 1628 | const VCN end = start + length; | ||
| 1629 | s64 delta; | ||
| 1630 | runlist_element *rl, *rl_end, *rl_real_end, *trl; | ||
| 1631 | int old_size; | ||
| 1632 | BOOL lcn_fixup = FALSE; | ||
| 1633 | |||
| 1634 | ntfs_debug("Entering for start 0x%llx, length 0x%llx.", | ||
| 1635 | (long long)start, (long long)length); | ||
| 1636 | BUG_ON(!runlist); | ||
| 1637 | BUG_ON(start < 0); | ||
| 1638 | BUG_ON(length < 0); | ||
| 1639 | BUG_ON(end < 0); | ||
| 1640 | rl = runlist->rl; | ||
| 1641 | if (unlikely(!rl)) { | ||
| 1642 | if (likely(!start && !length)) | ||
| 1643 | return 0; | ||
| 1644 | return -ENOENT; | ||
| 1645 | } | ||
| 1646 | /* Find @start in the runlist. */ | ||
| 1647 | while (likely(rl->length && start >= rl[1].vcn)) | ||
| 1648 | rl++; | ||
| 1649 | rl_end = rl; | ||
| 1650 | /* Find @end in the runlist. */ | ||
| 1651 | while (likely(rl_end->length && end >= rl_end[1].vcn)) { | ||
| 1652 | /* Verify there are no unmapped or error elements. */ | ||
| 1653 | if (unlikely(rl_end->lcn < LCN_HOLE)) | ||
| 1654 | return -EINVAL; | ||
| 1655 | rl_end++; | ||
| 1656 | } | ||
| 1657 | /* Check the last element. */ | ||
| 1658 | if (unlikely(rl_end->length && rl_end->lcn < LCN_HOLE)) | ||
| 1659 | return -EINVAL; | ||
| 1660 | /* This covers @start being out of bounds, too. */ | ||
| 1661 | if (!rl_end->length && end > rl_end->vcn) | ||
| 1662 | return -ENOENT; | ||
| 1663 | if (!length) | ||
| 1664 | return 0; | ||
| 1665 | if (!rl->length) | ||
| 1666 | return -ENOENT; | ||
| 1667 | rl_real_end = rl_end; | ||
| 1668 | /* Determine the runlist size. */ | ||
| 1669 | while (likely(rl_real_end->length)) | ||
| 1670 | rl_real_end++; | ||
| 1671 | old_size = rl_real_end - runlist->rl + 1; | ||
| 1672 | /* If @start is in a hole simply extend the hole. */ | ||
| 1673 | if (rl->lcn == LCN_HOLE) { | ||
| 1674 | /* | ||
| 1675 | * If both @start and @end are in the same sparse run, we are | ||
| 1676 | * done. | ||
| 1677 | */ | ||
| 1678 | if (end <= rl[1].vcn) { | ||
| 1679 | ntfs_debug("Done (requested hole is already sparse)."); | ||
| 1680 | return 0; | ||
| 1681 | } | ||
| 1682 | extend_hole: | ||
| 1683 | /* Extend the hole. */ | ||
| 1684 | rl->length = end - rl->vcn; | ||
| 1685 | /* If @end is in a hole, merge it with the current one. */ | ||
| 1686 | if (rl_end->lcn == LCN_HOLE) { | ||
| 1687 | rl_end++; | ||
| 1688 | rl->length = rl_end->vcn - rl->vcn; | ||
| 1689 | } | ||
| 1690 | /* We have done the hole. Now deal with the remaining tail. */ | ||
| 1691 | rl++; | ||
| 1692 | /* Cut out all runlist elements up to @end. */ | ||
| 1693 | if (rl < rl_end) | ||
| 1694 | memmove(rl, rl_end, (rl_real_end - rl_end + 1) * | ||
| 1695 | sizeof(*rl)); | ||
| 1696 | /* Adjust the beginning of the tail if necessary. */ | ||
| 1697 | if (end > rl->vcn) { | ||
| 1698 | s64 delta = end - rl->vcn; | ||
| 1699 | rl->vcn = end; | ||
| 1700 | rl->length -= delta; | ||
| 1701 | /* Only adjust the lcn if it is real. */ | ||
| 1702 | if (rl->lcn >= 0) | ||
| 1703 | rl->lcn += delta; | ||
| 1704 | } | ||
| 1705 | shrink_allocation: | ||
| 1706 | /* Reallocate memory if the allocation changed. */ | ||
| 1707 | if (rl < rl_end) { | ||
| 1708 | rl = ntfs_rl_realloc(runlist->rl, old_size, | ||
| 1709 | old_size - (rl_end - rl)); | ||
| 1710 | if (IS_ERR(rl)) | ||
| 1711 | ntfs_warning(vol->sb, "Failed to shrink " | ||
| 1712 | "runlist buffer. This just " | ||
| 1713 | "wastes a bit of memory " | ||
| 1714 | "temporarily so we ignore it " | ||
| 1715 | "and return success."); | ||
| 1716 | else | ||
| 1717 | runlist->rl = rl; | ||
| 1718 | } | ||
| 1719 | ntfs_debug("Done (extend hole)."); | ||
| 1720 | return 0; | ||
| 1721 | } | ||
| 1722 | /* | ||
| 1723 | * If @start is at the beginning of a run things are easier as there is | ||
| 1724 | * no need to split the first run. | ||
| 1725 | */ | ||
| 1726 | if (start == rl->vcn) { | ||
| 1727 | /* | ||
| 1728 | * @start is at the beginning of a run. | ||
| 1729 | * | ||
| 1730 | * If the previous run is sparse, extend its hole. | ||
| 1731 | * | ||
| 1732 | * If @end is not in the same run, switch the run to be sparse | ||
| 1733 | * and extend the newly created hole. | ||
| 1734 | * | ||
| 1735 | * Thus both of these cases reduce the problem to the above | ||
| 1736 | * case of "@start is in a hole". | ||
| 1737 | */ | ||
| 1738 | if (rl > runlist->rl && (rl - 1)->lcn == LCN_HOLE) { | ||
| 1739 | rl--; | ||
| 1740 | goto extend_hole; | ||
| 1741 | } | ||
| 1742 | if (end >= rl[1].vcn) { | ||
| 1743 | rl->lcn = LCN_HOLE; | ||
| 1744 | goto extend_hole; | ||
| 1745 | } | ||
| 1746 | /* | ||
| 1747 | * The final case is when @end is in the same run as @start. | ||
| 1748 | * For this need to split the run into two. One run for the | ||
| 1749 | * sparse region between the beginning of the old run, i.e. | ||
| 1750 | * @start, and @end and one for the remaining non-sparse | ||
| 1751 | * region, i.e. between @end and the end of the old run. | ||
| 1752 | */ | ||
| 1753 | trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1); | ||
| 1754 | if (IS_ERR(trl)) | ||
| 1755 | goto enomem_out; | ||
| 1756 | old_size++; | ||
| 1757 | if (runlist->rl != trl) { | ||
| 1758 | rl = trl + (rl - runlist->rl); | ||
| 1759 | rl_end = trl + (rl_end - runlist->rl); | ||
| 1760 | rl_real_end = trl + (rl_real_end - runlist->rl); | ||
| 1761 | runlist->rl = trl; | ||
| 1762 | } | ||
| 1763 | split_end: | ||
| 1764 | /* Shift all the runs up by one. */ | ||
| 1765 | memmove(rl + 1, rl, (rl_real_end - rl + 1) * sizeof(*rl)); | ||
| 1766 | /* Finally, setup the two split runs. */ | ||
| 1767 | rl->lcn = LCN_HOLE; | ||
| 1768 | rl->length = length; | ||
| 1769 | rl++; | ||
| 1770 | rl->vcn += length; | ||
| 1771 | /* Only adjust the lcn if it is real. */ | ||
| 1772 | if (rl->lcn >= 0 || lcn_fixup) | ||
| 1773 | rl->lcn += length; | ||
| 1774 | rl->length -= length; | ||
| 1775 | ntfs_debug("Done (split one)."); | ||
| 1776 | return 0; | ||
| 1777 | } | ||
| 1778 | /* | ||
| 1779 | * @start is neither in a hole nor at the beginning of a run. | ||
| 1780 | * | ||
| 1781 | * If @end is in a hole, things are easier as simply truncating the run | ||
| 1782 | * @start is in to end at @start - 1, deleting all runs after that up | ||
| 1783 | * to @end, and finally extending the beginning of the run @end is in | ||
| 1784 | * to be @start is all that is needed. | ||
| 1785 | */ | ||
| 1786 | if (rl_end->lcn == LCN_HOLE) { | ||
| 1787 | /* Truncate the run containing @start. */ | ||
| 1788 | rl->length = start - rl->vcn; | ||
| 1789 | rl++; | ||
| 1790 | /* Cut out all runlist elements up to @end. */ | ||
| 1791 | if (rl < rl_end) | ||
| 1792 | memmove(rl, rl_end, (rl_real_end - rl_end + 1) * | ||
| 1793 | sizeof(*rl)); | ||
| 1794 | /* Extend the beginning of the run @end is in to be @start. */ | ||
| 1795 | rl->vcn = start; | ||
| 1796 | rl->length = rl[1].vcn - start; | ||
| 1797 | goto shrink_allocation; | ||
| 1798 | } | ||
| 1799 | /* | ||
| 1800 | * If @end is not in a hole there are still two cases to distinguish. | ||
| 1801 | * Either @end is or is not in the same run as @start. | ||
| 1802 | * | ||
| 1803 | * The second case is easier as it can be reduced to an already solved | ||
| 1804 | * problem by truncating the run @start is in to end at @start - 1. | ||
| 1805 | * Then, if @end is in the next run need to split the run into a sparse | ||
| 1806 | * run followed by a non-sparse run (already covered above) and if @end | ||
| 1807 | * is not in the next run switching it to be sparse, again reduces the | ||
| 1808 | * problem to the already covered case of "@start is in a hole". | ||
| 1809 | */ | ||
| 1810 | if (end >= rl[1].vcn) { | ||
| 1811 | /* | ||
| 1812 | * If @end is not in the next run, reduce the problem to the | ||
| 1813 | * case of "@start is in a hole". | ||
| 1814 | */ | ||
| 1815 | if (rl[1].length && end >= rl[2].vcn) { | ||
| 1816 | /* Truncate the run containing @start. */ | ||
| 1817 | rl->length = start - rl->vcn; | ||
| 1818 | rl++; | ||
| 1819 | rl->vcn = start; | ||
| 1820 | rl->lcn = LCN_HOLE; | ||
| 1821 | goto extend_hole; | ||
| 1822 | } | ||
| 1823 | trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1); | ||
| 1824 | if (IS_ERR(trl)) | ||
| 1825 | goto enomem_out; | ||
| 1826 | old_size++; | ||
| 1827 | if (runlist->rl != trl) { | ||
| 1828 | rl = trl + (rl - runlist->rl); | ||
| 1829 | rl_end = trl + (rl_end - runlist->rl); | ||
| 1830 | rl_real_end = trl + (rl_real_end - runlist->rl); | ||
| 1831 | runlist->rl = trl; | ||
| 1832 | } | ||
| 1833 | /* Truncate the run containing @start. */ | ||
| 1834 | rl->length = start - rl->vcn; | ||
| 1835 | rl++; | ||
| 1836 | /* | ||
| 1837 | * @end is in the next run, reduce the problem to the case | ||
| 1838 | * where "@start is at the beginning of a run and @end is in | ||
| 1839 | * the same run as @start". | ||
| 1840 | */ | ||
| 1841 | delta = rl->vcn - start; | ||
| 1842 | rl->vcn = start; | ||
| 1843 | if (rl->lcn >= 0) { | ||
| 1844 | rl->lcn -= delta; | ||
| 1845 | /* Need this in case the lcn just became negative. */ | ||
| 1846 | lcn_fixup = TRUE; | ||
| 1847 | } | ||
| 1848 | rl->length += delta; | ||
| 1849 | goto split_end; | ||
| 1850 | } | ||
| 1851 | /* | ||
| 1852 | * The first case from above, i.e. @end is in the same run as @start. | ||
| 1853 | * We need to split the run into three. One run for the non-sparse | ||
| 1854 | * region between the beginning of the old run and @start, one for the | ||
| 1855 | * sparse region between @start and @end, and one for the remaining | ||
| 1856 | * non-sparse region, i.e. between @end and the end of the old run. | ||
| 1857 | */ | ||
| 1858 | trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 2); | ||
| 1859 | if (IS_ERR(trl)) | ||
| 1860 | goto enomem_out; | ||
| 1861 | old_size += 2; | ||
| 1862 | if (runlist->rl != trl) { | ||
| 1863 | rl = trl + (rl - runlist->rl); | ||
| 1864 | rl_end = trl + (rl_end - runlist->rl); | ||
| 1865 | rl_real_end = trl + (rl_real_end - runlist->rl); | ||
| 1866 | runlist->rl = trl; | ||
| 1867 | } | ||
| 1868 | /* Shift all the runs up by two. */ | ||
| 1869 | memmove(rl + 2, rl, (rl_real_end - rl + 1) * sizeof(*rl)); | ||
| 1870 | /* Finally, setup the three split runs. */ | ||
| 1871 | rl->length = start - rl->vcn; | ||
| 1872 | rl++; | ||
| 1873 | rl->vcn = start; | ||
| 1874 | rl->lcn = LCN_HOLE; | ||
| 1875 | rl->length = length; | ||
| 1876 | rl++; | ||
| 1877 | delta = end - rl->vcn; | ||
| 1878 | rl->vcn = end; | ||
| 1879 | rl->lcn += delta; | ||
| 1880 | rl->length -= delta; | ||
| 1881 | ntfs_debug("Done (split both)."); | ||
| 1882 | return 0; | ||
| 1883 | enomem_out: | ||
| 1884 | ntfs_error(vol->sb, "Not enough memory to extend runlist buffer."); | ||
| 1885 | return -ENOMEM; | ||
| 1886 | } | ||
| 1887 | |||
| 1556 | #endif /* NTFS_RW */ | 1888 | #endif /* NTFS_RW */ |
diff --git a/fs/ntfs/runlist.h b/fs/ntfs/runlist.h index aa0ee6540e7c..47728fbb610b 100644 --- a/fs/ntfs/runlist.h +++ b/fs/ntfs/runlist.h | |||
| @@ -94,6 +94,9 @@ extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst, | |||
| 94 | extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol, | 94 | extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol, |
| 95 | runlist *const runlist, const s64 new_length); | 95 | runlist *const runlist, const s64 new_length); |
| 96 | 96 | ||
| 97 | int ntfs_rl_punch_nolock(const ntfs_volume *vol, runlist *const runlist, | ||
| 98 | const VCN start, const s64 length); | ||
| 99 | |||
| 97 | #endif /* NTFS_RW */ | 100 | #endif /* NTFS_RW */ |
| 98 | 101 | ||
| 99 | #endif /* _LINUX_NTFS_RUNLIST_H */ | 102 | #endif /* _LINUX_NTFS_RUNLIST_H */ |
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 41aa8eb6755b..b2b392961268 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c | |||
| @@ -1133,7 +1133,8 @@ mft_unmap_out: | |||
| 1133 | * | 1133 | * |
| 1134 | * Return TRUE on success or FALSE on error. | 1134 | * Return TRUE on success or FALSE on error. |
| 1135 | */ | 1135 | */ |
| 1136 | static BOOL load_and_check_logfile(ntfs_volume *vol) | 1136 | static BOOL load_and_check_logfile(ntfs_volume *vol, |
| 1137 | RESTART_PAGE_HEADER **rp) | ||
| 1137 | { | 1138 | { |
| 1138 | struct inode *tmp_ino; | 1139 | struct inode *tmp_ino; |
| 1139 | 1140 | ||
| @@ -1145,7 +1146,7 @@ static BOOL load_and_check_logfile(ntfs_volume *vol) | |||
| 1145 | /* Caller will display error message. */ | 1146 | /* Caller will display error message. */ |
| 1146 | return FALSE; | 1147 | return FALSE; |
| 1147 | } | 1148 | } |
| 1148 | if (!ntfs_check_logfile(tmp_ino)) { | 1149 | if (!ntfs_check_logfile(tmp_ino, rp)) { |
| 1149 | iput(tmp_ino); | 1150 | iput(tmp_ino); |
| 1150 | /* ntfs_check_logfile() will have displayed error output. */ | 1151 | /* ntfs_check_logfile() will have displayed error output. */ |
| 1151 | return FALSE; | 1152 | return FALSE; |
| @@ -1689,6 +1690,7 @@ static BOOL load_system_files(ntfs_volume *vol) | |||
| 1689 | VOLUME_INFORMATION *vi; | 1690 | VOLUME_INFORMATION *vi; |
| 1690 | ntfs_attr_search_ctx *ctx; | 1691 | ntfs_attr_search_ctx *ctx; |
| 1691 | #ifdef NTFS_RW | 1692 | #ifdef NTFS_RW |
| 1693 | RESTART_PAGE_HEADER *rp; | ||
| 1692 | int err; | 1694 | int err; |
| 1693 | #endif /* NTFS_RW */ | 1695 | #endif /* NTFS_RW */ |
| 1694 | 1696 | ||
| @@ -1841,8 +1843,9 @@ get_ctx_vol_failed: | |||
| 1841 | * Get the inode for the logfile, check it and determine if the volume | 1843 | * Get the inode for the logfile, check it and determine if the volume |
| 1842 | * was shutdown cleanly. | 1844 | * was shutdown cleanly. |
| 1843 | */ | 1845 | */ |
| 1844 | if (!load_and_check_logfile(vol) || | 1846 | rp = NULL; |
| 1845 | !ntfs_is_logfile_clean(vol->logfile_ino)) { | 1847 | if (!load_and_check_logfile(vol, &rp) || |
| 1848 | !ntfs_is_logfile_clean(vol->logfile_ino, rp)) { | ||
| 1846 | static const char *es1a = "Failed to load $LogFile"; | 1849 | static const char *es1a = "Failed to load $LogFile"; |
| 1847 | static const char *es1b = "$LogFile is not clean"; | 1850 | static const char *es1b = "$LogFile is not clean"; |
| 1848 | static const char *es2 = ". Mount in Windows."; | 1851 | static const char *es2 = ". Mount in Windows."; |
| @@ -1857,6 +1860,10 @@ get_ctx_vol_failed: | |||
| 1857 | "continue nor on_errors=" | 1860 | "continue nor on_errors=" |
| 1858 | "remount-ro was specified%s", | 1861 | "remount-ro was specified%s", |
| 1859 | es1, es2); | 1862 | es1, es2); |
| 1863 | if (vol->logfile_ino) { | ||
| 1864 | BUG_ON(!rp); | ||
| 1865 | ntfs_free(rp); | ||
| 1866 | } | ||
| 1860 | goto iput_logfile_err_out; | 1867 | goto iput_logfile_err_out; |
| 1861 | } | 1868 | } |
| 1862 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; | 1869 | sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; |
| @@ -1867,6 +1874,7 @@ get_ctx_vol_failed: | |||
| 1867 | /* This will prevent a read-write remount. */ | 1874 | /* This will prevent a read-write remount. */ |
| 1868 | NVolSetErrors(vol); | 1875 | NVolSetErrors(vol); |
| 1869 | } | 1876 | } |
| 1877 | ntfs_free(rp); | ||
| 1870 | #endif /* NTFS_RW */ | 1878 | #endif /* NTFS_RW */ |
| 1871 | /* Get the root directory inode so we can do path lookups. */ | 1879 | /* Get the root directory inode so we can do path lookups. */ |
| 1872 | vol->root_ino = ntfs_iget(sb, FILE_root); | 1880 | vol->root_ino = ntfs_iget(sb, FILE_root); |
diff --git a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c index 19c42e231b44..a389a5a16c84 100644 --- a/fs/ntfs/unistr.c +++ b/fs/ntfs/unistr.c | |||
| @@ -372,7 +372,8 @@ retry: wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o, | |||
| 372 | return -EINVAL; | 372 | return -EINVAL; |
| 373 | conversion_err: | 373 | conversion_err: |
| 374 | ntfs_error(vol->sb, "Unicode name contains characters that cannot be " | 374 | ntfs_error(vol->sb, "Unicode name contains characters that cannot be " |
| 375 | "converted to character set %s.", nls->charset); | 375 | "converted to character set %s. You might want to " |
| 376 | "try to use the mount option nls=utf8.", nls->charset); | ||
| 376 | if (ns != *outs) | 377 | if (ns != *outs) |
| 377 | kfree(ns); | 378 | kfree(ns); |
| 378 | if (wc != -ENAMETOOLONG) | 379 | if (wc != -ENAMETOOLONG) |
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/personality.h> | 24 | #include <linux/personality.h> |
| 25 | #include <linux/pagemap.h> | 25 | #include <linux/pagemap.h> |
| 26 | #include <linux/syscalls.h> | 26 | #include <linux/syscalls.h> |
| 27 | #include <linux/rcupdate.h> | ||
| 27 | 28 | ||
| 28 | #include <asm/unistd.h> | 29 | #include <asm/unistd.h> |
| 29 | 30 | ||
| @@ -842,14 +843,16 @@ int get_unused_fd(void) | |||
| 842 | { | 843 | { |
| 843 | struct files_struct * files = current->files; | 844 | struct files_struct * files = current->files; |
| 844 | int fd, error; | 845 | int fd, error; |
| 846 | struct fdtable *fdt; | ||
| 845 | 847 | ||
| 846 | error = -EMFILE; | 848 | error = -EMFILE; |
| 847 | spin_lock(&files->file_lock); | 849 | spin_lock(&files->file_lock); |
| 848 | 850 | ||
| 849 | repeat: | 851 | repeat: |
| 850 | fd = find_next_zero_bit(files->open_fds->fds_bits, | 852 | fdt = files_fdtable(files); |
| 851 | files->max_fdset, | 853 | fd = find_next_zero_bit(fdt->open_fds->fds_bits, |
| 852 | files->next_fd); | 854 | fdt->max_fdset, |
| 855 | fdt->next_fd); | ||
| 853 | 856 | ||
| 854 | /* | 857 | /* |
| 855 | * N.B. For clone tasks sharing a files structure, this test | 858 | * N.B. For clone tasks sharing a files structure, this test |
| @@ -872,14 +875,14 @@ repeat: | |||
| 872 | goto repeat; | 875 | goto repeat; |
| 873 | } | 876 | } |
| 874 | 877 | ||
| 875 | FD_SET(fd, files->open_fds); | 878 | FD_SET(fd, fdt->open_fds); |
| 876 | FD_CLR(fd, files->close_on_exec); | 879 | FD_CLR(fd, fdt->close_on_exec); |
| 877 | files->next_fd = fd + 1; | 880 | fdt->next_fd = fd + 1; |
| 878 | #if 1 | 881 | #if 1 |
| 879 | /* Sanity check */ | 882 | /* Sanity check */ |
| 880 | if (files->fd[fd] != NULL) { | 883 | if (fdt->fd[fd] != NULL) { |
| 881 | printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); | 884 | printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); |
| 882 | files->fd[fd] = NULL; | 885 | fdt->fd[fd] = NULL; |
| 883 | } | 886 | } |
| 884 | #endif | 887 | #endif |
| 885 | error = fd; | 888 | error = fd; |
| @@ -893,9 +896,10 @@ EXPORT_SYMBOL(get_unused_fd); | |||
| 893 | 896 | ||
| 894 | static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) | 897 | static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) |
| 895 | { | 898 | { |
| 896 | __FD_CLR(fd, files->open_fds); | 899 | struct fdtable *fdt = files_fdtable(files); |
| 897 | if (fd < files->next_fd) | 900 | __FD_CLR(fd, fdt->open_fds); |
| 898 | files->next_fd = fd; | 901 | if (fd < fdt->next_fd) |
| 902 | fdt->next_fd = fd; | ||
| 899 | } | 903 | } |
| 900 | 904 | ||
| 901 | void fastcall put_unused_fd(unsigned int fd) | 905 | void fastcall put_unused_fd(unsigned int fd) |
| @@ -924,10 +928,11 @@ EXPORT_SYMBOL(put_unused_fd); | |||
| 924 | void fastcall fd_install(unsigned int fd, struct file * file) | 928 | void fastcall fd_install(unsigned int fd, struct file * file) |
| 925 | { | 929 | { |
| 926 | struct files_struct *files = current->files; | 930 | struct files_struct *files = current->files; |
| 931 | struct fdtable *fdt; | ||
| 927 | spin_lock(&files->file_lock); | 932 | spin_lock(&files->file_lock); |
| 928 | if (unlikely(files->fd[fd] != NULL)) | 933 | fdt = files_fdtable(files); |
| 929 | BUG(); | 934 | BUG_ON(fdt->fd[fd] != NULL); |
| 930 | files->fd[fd] = file; | 935 | rcu_assign_pointer(fdt->fd[fd], file); |
| 931 | spin_unlock(&files->file_lock); | 936 | spin_unlock(&files->file_lock); |
| 932 | } | 937 | } |
| 933 | 938 | ||
| @@ -1010,15 +1015,17 @@ asmlinkage long sys_close(unsigned int fd) | |||
| 1010 | { | 1015 | { |
| 1011 | struct file * filp; | 1016 | struct file * filp; |
| 1012 | struct files_struct *files = current->files; | 1017 | struct files_struct *files = current->files; |
| 1018 | struct fdtable *fdt; | ||
| 1013 | 1019 | ||
| 1014 | spin_lock(&files->file_lock); | 1020 | spin_lock(&files->file_lock); |
| 1015 | if (fd >= files->max_fds) | 1021 | fdt = files_fdtable(files); |
| 1022 | if (fd >= fdt->max_fds) | ||
| 1016 | goto out_unlock; | 1023 | goto out_unlock; |
| 1017 | filp = files->fd[fd]; | 1024 | filp = fdt->fd[fd]; |
| 1018 | if (!filp) | 1025 | if (!filp) |
| 1019 | goto out_unlock; | 1026 | goto out_unlock; |
| 1020 | files->fd[fd] = NULL; | 1027 | rcu_assign_pointer(fdt->fd[fd], NULL); |
| 1021 | FD_CLR(fd, files->close_on_exec); | 1028 | FD_CLR(fd, fdt->close_on_exec); |
| 1022 | __put_unused_fd(files, fd); | 1029 | __put_unused_fd(files, fd); |
| 1023 | spin_unlock(&files->file_lock); | 1030 | spin_unlock(&files->file_lock); |
| 1024 | return filp_close(filp, files); | 1031 | return filp_close(filp, files); |
| @@ -39,7 +39,11 @@ void pipe_wait(struct inode * inode) | |||
| 39 | { | 39 | { |
| 40 | DEFINE_WAIT(wait); | 40 | DEFINE_WAIT(wait); |
| 41 | 41 | ||
| 42 | prepare_to_wait(PIPE_WAIT(*inode), &wait, TASK_INTERRUPTIBLE); | 42 | /* |
| 43 | * Pipes are system-local resources, so sleeping on them | ||
| 44 | * is considered a noninteractive wait: | ||
| 45 | */ | ||
| 46 | prepare_to_wait(PIPE_WAIT(*inode), &wait, TASK_INTERRUPTIBLE|TASK_NONINTERACTIVE); | ||
| 43 | up(PIPE_SEM(*inode)); | 47 | up(PIPE_SEM(*inode)); |
| 44 | schedule(); | 48 | schedule(); |
| 45 | finish_wait(PIPE_WAIT(*inode), &wait); | 49 | finish_wait(PIPE_WAIT(*inode), &wait); |
diff --git a/fs/proc/array.c b/fs/proc/array.c index 37668fe998ad..d88d518d30f6 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
| @@ -159,6 +159,7 @@ static inline char * task_state(struct task_struct *p, char *buffer) | |||
| 159 | { | 159 | { |
| 160 | struct group_info *group_info; | 160 | struct group_info *group_info; |
| 161 | int g; | 161 | int g; |
| 162 | struct fdtable *fdt = NULL; | ||
| 162 | 163 | ||
| 163 | read_lock(&tasklist_lock); | 164 | read_lock(&tasklist_lock); |
| 164 | buffer += sprintf(buffer, | 165 | buffer += sprintf(buffer, |
| @@ -179,10 +180,12 @@ static inline char * task_state(struct task_struct *p, char *buffer) | |||
| 179 | p->gid, p->egid, p->sgid, p->fsgid); | 180 | p->gid, p->egid, p->sgid, p->fsgid); |
| 180 | read_unlock(&tasklist_lock); | 181 | read_unlock(&tasklist_lock); |
| 181 | task_lock(p); | 182 | task_lock(p); |
| 183 | if (p->files) | ||
| 184 | fdt = files_fdtable(p->files); | ||
| 182 | buffer += sprintf(buffer, | 185 | buffer += sprintf(buffer, |
| 183 | "FDSize:\t%d\n" | 186 | "FDSize:\t%d\n" |
| 184 | "Groups:\t", | 187 | "Groups:\t", |
| 185 | p->files ? p->files->max_fds : 0); | 188 | fdt ? fdt->max_fds : 0); |
| 186 | 189 | ||
| 187 | group_info = p->group_info; | 190 | group_info = p->group_info; |
| 188 | get_group_info(group_info); | 191 | get_group_info(group_info); |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 84751f3f52d5..23db452ab428 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -62,6 +62,7 @@ | |||
| 62 | #include <linux/namespace.h> | 62 | #include <linux/namespace.h> |
| 63 | #include <linux/mm.h> | 63 | #include <linux/mm.h> |
| 64 | #include <linux/smp_lock.h> | 64 | #include <linux/smp_lock.h> |
| 65 | #include <linux/rcupdate.h> | ||
| 65 | #include <linux/kallsyms.h> | 66 | #include <linux/kallsyms.h> |
| 66 | #include <linux/mount.h> | 67 | #include <linux/mount.h> |
| 67 | #include <linux/security.h> | 68 | #include <linux/security.h> |
| @@ -283,16 +284,16 @@ static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsm | |||
| 283 | 284 | ||
| 284 | files = get_files_struct(task); | 285 | files = get_files_struct(task); |
| 285 | if (files) { | 286 | if (files) { |
| 286 | spin_lock(&files->file_lock); | 287 | rcu_read_lock(); |
| 287 | file = fcheck_files(files, fd); | 288 | file = fcheck_files(files, fd); |
| 288 | if (file) { | 289 | if (file) { |
| 289 | *mnt = mntget(file->f_vfsmnt); | 290 | *mnt = mntget(file->f_vfsmnt); |
| 290 | *dentry = dget(file->f_dentry); | 291 | *dentry = dget(file->f_dentry); |
| 291 | spin_unlock(&files->file_lock); | 292 | rcu_read_unlock(); |
| 292 | put_files_struct(files); | 293 | put_files_struct(files); |
| 293 | return 0; | 294 | return 0; |
| 294 | } | 295 | } |
| 295 | spin_unlock(&files->file_lock); | 296 | rcu_read_unlock(); |
| 296 | put_files_struct(files); | 297 | put_files_struct(files); |
| 297 | } | 298 | } |
| 298 | return -ENOENT; | 299 | return -ENOENT; |
| @@ -1039,6 +1040,7 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) | |||
| 1039 | int retval; | 1040 | int retval; |
| 1040 | char buf[NUMBUF]; | 1041 | char buf[NUMBUF]; |
| 1041 | struct files_struct * files; | 1042 | struct files_struct * files; |
| 1043 | struct fdtable *fdt; | ||
| 1042 | 1044 | ||
| 1043 | retval = -ENOENT; | 1045 | retval = -ENOENT; |
| 1044 | if (!pid_alive(p)) | 1046 | if (!pid_alive(p)) |
| @@ -1061,15 +1063,16 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) | |||
| 1061 | files = get_files_struct(p); | 1063 | files = get_files_struct(p); |
| 1062 | if (!files) | 1064 | if (!files) |
| 1063 | goto out; | 1065 | goto out; |
| 1064 | spin_lock(&files->file_lock); | 1066 | rcu_read_lock(); |
| 1067 | fdt = files_fdtable(files); | ||
| 1065 | for (fd = filp->f_pos-2; | 1068 | for (fd = filp->f_pos-2; |
| 1066 | fd < files->max_fds; | 1069 | fd < fdt->max_fds; |
| 1067 | fd++, filp->f_pos++) { | 1070 | fd++, filp->f_pos++) { |
| 1068 | unsigned int i,j; | 1071 | unsigned int i,j; |
| 1069 | 1072 | ||
| 1070 | if (!fcheck_files(files, fd)) | 1073 | if (!fcheck_files(files, fd)) |
| 1071 | continue; | 1074 | continue; |
| 1072 | spin_unlock(&files->file_lock); | 1075 | rcu_read_unlock(); |
| 1073 | 1076 | ||
| 1074 | j = NUMBUF; | 1077 | j = NUMBUF; |
| 1075 | i = fd; | 1078 | i = fd; |
| @@ -1081,12 +1084,12 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) | |||
| 1081 | 1084 | ||
| 1082 | ino = fake_ino(tid, PROC_TID_FD_DIR + fd); | 1085 | ino = fake_ino(tid, PROC_TID_FD_DIR + fd); |
| 1083 | if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) { | 1086 | if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) { |
| 1084 | spin_lock(&files->file_lock); | 1087 | rcu_read_lock(); |
| 1085 | break; | 1088 | break; |
| 1086 | } | 1089 | } |
| 1087 | spin_lock(&files->file_lock); | 1090 | rcu_read_lock(); |
| 1088 | } | 1091 | } |
| 1089 | spin_unlock(&files->file_lock); | 1092 | rcu_read_unlock(); |
| 1090 | put_files_struct(files); | 1093 | put_files_struct(files); |
| 1091 | } | 1094 | } |
| 1092 | out: | 1095 | out: |
| @@ -1261,9 +1264,9 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
| 1261 | 1264 | ||
| 1262 | files = get_files_struct(task); | 1265 | files = get_files_struct(task); |
| 1263 | if (files) { | 1266 | if (files) { |
| 1264 | spin_lock(&files->file_lock); | 1267 | rcu_read_lock(); |
| 1265 | if (fcheck_files(files, fd)) { | 1268 | if (fcheck_files(files, fd)) { |
| 1266 | spin_unlock(&files->file_lock); | 1269 | rcu_read_unlock(); |
| 1267 | put_files_struct(files); | 1270 | put_files_struct(files); |
| 1268 | if (task_dumpable(task)) { | 1271 | if (task_dumpable(task)) { |
| 1269 | inode->i_uid = task->euid; | 1272 | inode->i_uid = task->euid; |
| @@ -1275,7 +1278,7 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
| 1275 | security_task_to_inode(task, inode); | 1278 | security_task_to_inode(task, inode); |
| 1276 | return 1; | 1279 | return 1; |
| 1277 | } | 1280 | } |
| 1278 | spin_unlock(&files->file_lock); | 1281 | rcu_read_unlock(); |
| 1279 | put_files_struct(files); | 1282 | put_files_struct(files); |
| 1280 | } | 1283 | } |
| 1281 | d_drop(dentry); | 1284 | d_drop(dentry); |
| @@ -1367,7 +1370,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, | |||
| 1367 | if (!files) | 1370 | if (!files) |
| 1368 | goto out_unlock; | 1371 | goto out_unlock; |
| 1369 | inode->i_mode = S_IFLNK; | 1372 | inode->i_mode = S_IFLNK; |
| 1370 | spin_lock(&files->file_lock); | 1373 | rcu_read_lock(); |
| 1371 | file = fcheck_files(files, fd); | 1374 | file = fcheck_files(files, fd); |
| 1372 | if (!file) | 1375 | if (!file) |
| 1373 | goto out_unlock2; | 1376 | goto out_unlock2; |
| @@ -1375,7 +1378,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, | |||
| 1375 | inode->i_mode |= S_IRUSR | S_IXUSR; | 1378 | inode->i_mode |= S_IRUSR | S_IXUSR; |
| 1376 | if (file->f_mode & 2) | 1379 | if (file->f_mode & 2) |
| 1377 | inode->i_mode |= S_IWUSR | S_IXUSR; | 1380 | inode->i_mode |= S_IWUSR | S_IXUSR; |
| 1378 | spin_unlock(&files->file_lock); | 1381 | rcu_read_unlock(); |
| 1379 | put_files_struct(files); | 1382 | put_files_struct(files); |
| 1380 | inode->i_op = &proc_pid_link_inode_operations; | 1383 | inode->i_op = &proc_pid_link_inode_operations; |
| 1381 | inode->i_size = 64; | 1384 | inode->i_size = 64; |
| @@ -1385,7 +1388,7 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, | |||
| 1385 | return NULL; | 1388 | return NULL; |
| 1386 | 1389 | ||
| 1387 | out_unlock2: | 1390 | out_unlock2: |
| 1388 | spin_unlock(&files->file_lock); | 1391 | rcu_read_unlock(); |
| 1389 | put_files_struct(files); | 1392 | put_files_struct(files); |
| 1390 | out_unlock: | 1393 | out_unlock: |
| 1391 | iput(inode); | 1394 | iput(inode); |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 133c28685105..effa6c0c467a 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
| @@ -60,6 +60,8 @@ static void proc_delete_inode(struct inode *inode) | |||
| 60 | struct proc_dir_entry *de; | 60 | struct proc_dir_entry *de; |
| 61 | struct task_struct *tsk; | 61 | struct task_struct *tsk; |
| 62 | 62 | ||
| 63 | truncate_inode_pages(&inode->i_data, 0); | ||
| 64 | |||
| 63 | /* Let go of any associated process */ | 65 | /* Let go of any associated process */ |
| 64 | tsk = PROC_I(inode)->task; | 66 | tsk = PROC_I(inode)->task; |
| 65 | if (tsk) | 67 | if (tsk) |
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index b79162a35478..80f32911c0cb 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c | |||
| @@ -63,6 +63,7 @@ int qnx4_sync_inode(struct inode *inode) | |||
| 63 | static void qnx4_delete_inode(struct inode *inode) | 63 | static void qnx4_delete_inode(struct inode *inode) |
| 64 | { | 64 | { |
| 65 | QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); | 65 | QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino)); |
| 66 | truncate_inode_pages(&inode->i_data, 0); | ||
| 66 | inode->i_size = 0; | 67 | inode->i_size = 0; |
| 67 | qnx4_truncate(inode); | 68 | qnx4_truncate(inode); |
| 68 | lock_kernel(); | 69 | lock_kernel(); |
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index ff291c973a56..1a8a1bf2154d 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c | |||
| @@ -33,6 +33,8 @@ void reiserfs_delete_inode(struct inode *inode) | |||
| 33 | 2 * REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb); | 33 | 2 * REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb); |
| 34 | struct reiserfs_transaction_handle th; | 34 | struct reiserfs_transaction_handle th; |
| 35 | 35 | ||
| 36 | truncate_inode_pages(&inode->i_data, 0); | ||
| 37 | |||
| 36 | reiserfs_write_lock(inode->i_sb); | 38 | reiserfs_write_lock(inode->i_sb); |
| 37 | 39 | ||
| 38 | /* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */ | 40 | /* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */ |
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index a8e29e9bbbd0..4b15761434bc 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c | |||
| @@ -2868,8 +2868,7 @@ static void let_transaction_grow(struct super_block *sb, unsigned long trans_id) | |||
| 2868 | struct reiserfs_journal *journal = SB_JOURNAL(sb); | 2868 | struct reiserfs_journal *journal = SB_JOURNAL(sb); |
| 2869 | unsigned long bcount = journal->j_bcount; | 2869 | unsigned long bcount = journal->j_bcount; |
| 2870 | while (1) { | 2870 | while (1) { |
| 2871 | set_current_state(TASK_UNINTERRUPTIBLE); | 2871 | schedule_timeout_uninterruptible(1); |
| 2872 | schedule_timeout(1); | ||
| 2873 | journal->j_current_jl->j_state |= LIST_COMMIT_PENDING; | 2872 | journal->j_current_jl->j_state |= LIST_COMMIT_PENDING; |
| 2874 | while ((atomic_read(&journal->j_wcount) > 0 || | 2873 | while ((atomic_read(&journal->j_wcount) > 0 || |
| 2875 | atomic_read(&journal->j_jlock)) && | 2874 | atomic_read(&journal->j_jlock)) && |
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 6951c35755be..44b02fc02ebe 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c | |||
| @@ -1934,8 +1934,7 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent) | |||
| 1934 | if (SB_AP_BITMAP(s)) | 1934 | if (SB_AP_BITMAP(s)) |
| 1935 | brelse(SB_AP_BITMAP(s)[j].bh); | 1935 | brelse(SB_AP_BITMAP(s)[j].bh); |
| 1936 | } | 1936 | } |
| 1937 | if (SB_AP_BITMAP(s)) | 1937 | vfree(SB_AP_BITMAP(s)); |
| 1938 | vfree(SB_AP_BITMAP(s)); | ||
| 1939 | } | 1938 | } |
| 1940 | if (SB_BUFFER_WITH_SB(s)) | 1939 | if (SB_BUFFER_WITH_SB(s)) |
| 1941 | brelse(SB_BUFFER_WITH_SB(s)); | 1940 | brelse(SB_BUFFER_WITH_SB(s)); |
diff --git a/fs/select.c b/fs/select.c index b80e7eb0ac0d..f10a10317d54 100644 --- a/fs/select.c +++ b/fs/select.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/personality.h> /* for STICKY_TIMEOUTS */ | 22 | #include <linux/personality.h> /* for STICKY_TIMEOUTS */ |
| 23 | #include <linux/file.h> | 23 | #include <linux/file.h> |
| 24 | #include <linux/fs.h> | 24 | #include <linux/fs.h> |
| 25 | #include <linux/rcupdate.h> | ||
| 25 | 26 | ||
| 26 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
| 27 | 28 | ||
| @@ -132,11 +133,13 @@ static int max_select_fd(unsigned long n, fd_set_bits *fds) | |||
| 132 | unsigned long *open_fds; | 133 | unsigned long *open_fds; |
| 133 | unsigned long set; | 134 | unsigned long set; |
| 134 | int max; | 135 | int max; |
| 136 | struct fdtable *fdt; | ||
| 135 | 137 | ||
| 136 | /* handle last in-complete long-word first */ | 138 | /* handle last in-complete long-word first */ |
| 137 | set = ~(~0UL << (n & (__NFDBITS-1))); | 139 | set = ~(~0UL << (n & (__NFDBITS-1))); |
| 138 | n /= __NFDBITS; | 140 | n /= __NFDBITS; |
| 139 | open_fds = current->files->open_fds->fds_bits+n; | 141 | fdt = files_fdtable(current->files); |
| 142 | open_fds = fdt->open_fds->fds_bits+n; | ||
| 140 | max = 0; | 143 | max = 0; |
| 141 | if (set) { | 144 | if (set) { |
| 142 | set &= BITS(fds, n); | 145 | set &= BITS(fds, n); |
| @@ -183,9 +186,9 @@ int do_select(int n, fd_set_bits *fds, long *timeout) | |||
| 183 | int retval, i; | 186 | int retval, i; |
| 184 | long __timeout = *timeout; | 187 | long __timeout = *timeout; |
| 185 | 188 | ||
| 186 | spin_lock(¤t->files->file_lock); | 189 | rcu_read_lock(); |
| 187 | retval = max_select_fd(n, fds); | 190 | retval = max_select_fd(n, fds); |
| 188 | spin_unlock(¤t->files->file_lock); | 191 | rcu_read_unlock(); |
| 189 | 192 | ||
| 190 | if (retval < 0) | 193 | if (retval < 0) |
| 191 | return retval; | 194 | return retval; |
| @@ -299,6 +302,7 @@ sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s | |||
| 299 | char *bits; | 302 | char *bits; |
| 300 | long timeout; | 303 | long timeout; |
| 301 | int ret, size, max_fdset; | 304 | int ret, size, max_fdset; |
| 305 | struct fdtable *fdt; | ||
| 302 | 306 | ||
| 303 | timeout = MAX_SCHEDULE_TIMEOUT; | 307 | timeout = MAX_SCHEDULE_TIMEOUT; |
| 304 | if (tvp) { | 308 | if (tvp) { |
| @@ -326,7 +330,10 @@ sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s | |||
| 326 | goto out_nofds; | 330 | goto out_nofds; |
| 327 | 331 | ||
| 328 | /* max_fdset can increase, so grab it once to avoid race */ | 332 | /* max_fdset can increase, so grab it once to avoid race */ |
| 329 | max_fdset = current->files->max_fdset; | 333 | rcu_read_lock(); |
| 334 | fdt = files_fdtable(current->files); | ||
| 335 | max_fdset = fdt->max_fdset; | ||
| 336 | rcu_read_unlock(); | ||
| 330 | if (n > max_fdset) | 337 | if (n > max_fdset) |
| 331 | n = max_fdset; | 338 | n = max_fdset; |
| 332 | 339 | ||
| @@ -464,9 +471,15 @@ asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long ti | |||
| 464 | unsigned int i; | 471 | unsigned int i; |
| 465 | struct poll_list *head; | 472 | struct poll_list *head; |
| 466 | struct poll_list *walk; | 473 | struct poll_list *walk; |
| 474 | struct fdtable *fdt; | ||
| 475 | int max_fdset; | ||
| 467 | 476 | ||
| 468 | /* Do a sanity check on nfds ... */ | 477 | /* Do a sanity check on nfds ... */ |
| 469 | if (nfds > current->files->max_fdset && nfds > OPEN_MAX) | 478 | rcu_read_lock(); |
| 479 | fdt = files_fdtable(current->files); | ||
| 480 | max_fdset = fdt->max_fdset; | ||
| 481 | rcu_read_unlock(); | ||
| 482 | if (nfds > max_fdset && nfds > OPEN_MAX) | ||
| 470 | return -EINVAL; | 483 | return -EINVAL; |
| 471 | 484 | ||
| 472 | if (timeout) { | 485 | if (timeout) { |
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 4765aaac9fd2..10b994428fef 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c | |||
| @@ -331,6 +331,7 @@ static void | |||
| 331 | smb_delete_inode(struct inode *ino) | 331 | smb_delete_inode(struct inode *ino) |
| 332 | { | 332 | { |
| 333 | DEBUG1("ino=%ld\n", ino->i_ino); | 333 | DEBUG1("ino=%ld\n", ino->i_ino); |
| 334 | truncate_inode_pages(&ino->i_data, 0); | ||
| 334 | lock_kernel(); | 335 | lock_kernel(); |
| 335 | if (smb_close(ino)) | 336 | if (smb_close(ino)) |
| 336 | PARANOIA("could not close inode %ld\n", ino->i_ino); | 337 | PARANOIA("could not close inode %ld\n", ino->i_ino); |
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 220babe91efd..38ab558835c4 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c | |||
| @@ -2397,8 +2397,7 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, | |||
| 2397 | if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { | 2397 | if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { |
| 2398 | /* a damn Win95 bug - sometimes it clags if you | 2398 | /* a damn Win95 bug - sometimes it clags if you |
| 2399 | ask it too fast */ | 2399 | ask it too fast */ |
| 2400 | current->state = TASK_INTERRUPTIBLE; | 2400 | schedule_timeout_interruptible(msecs_to_jiffies(200)); |
| 2401 | schedule_timeout(HZ/5); | ||
| 2402 | continue; | 2401 | continue; |
| 2403 | } | 2402 | } |
| 2404 | 2403 | ||
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 0530077d9dd8..fa33eceb0011 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c | |||
| @@ -292,6 +292,7 @@ int sysv_sync_inode(struct inode * inode) | |||
| 292 | 292 | ||
| 293 | static void sysv_delete_inode(struct inode *inode) | 293 | static void sysv_delete_inode(struct inode *inode) |
| 294 | { | 294 | { |
| 295 | truncate_inode_pages(&inode->i_data, 0); | ||
| 295 | inode->i_size = 0; | 296 | inode->i_size = 0; |
| 296 | sysv_truncate(inode); | 297 | sysv_truncate(inode); |
| 297 | lock_kernel(); | 298 | lock_kernel(); |
diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 3d68de39fad6..b83890beaaac 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c | |||
| @@ -87,6 +87,8 @@ static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); | |||
| 87 | */ | 87 | */ |
| 88 | void udf_delete_inode(struct inode * inode) | 88 | void udf_delete_inode(struct inode * inode) |
| 89 | { | 89 | { |
| 90 | truncate_inode_pages(&inode->i_data, 0); | ||
| 91 | |||
| 90 | if (is_bad_inode(inode)) | 92 | if (is_bad_inode(inode)) |
| 91 | goto no_delete; | 93 | goto no_delete; |
| 92 | 94 | ||
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 718627ca8b5c..55f4aa16e3fc 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c | |||
| @@ -804,6 +804,7 @@ int ufs_sync_inode (struct inode *inode) | |||
| 804 | 804 | ||
| 805 | void ufs_delete_inode (struct inode * inode) | 805 | void ufs_delete_inode (struct inode * inode) |
| 806 | { | 806 | { |
| 807 | truncate_inode_pages(&inode->i_data, 0); | ||
| 807 | /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ | 808 | /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ |
| 808 | lock_kernel(); | 809 | lock_kernel(); |
| 809 | mark_inode_dirty(inode); | 810 | mark_inode_dirty(inode); |
diff --git a/fs/xfs/linux-2.6/time.h b/fs/xfs/linux-2.6/time.h index 6c6fd0faa8e1..b0d2873ab274 100644 --- a/fs/xfs/linux-2.6/time.h +++ b/fs/xfs/linux-2.6/time.h | |||
| @@ -39,8 +39,7 @@ typedef struct timespec timespec_t; | |||
| 39 | 39 | ||
| 40 | static inline void delay(long ticks) | 40 | static inline void delay(long ticks) |
| 41 | { | 41 | { |
| 42 | set_current_state(TASK_UNINTERRUPTIBLE); | 42 | schedule_timeout_uninterruptible(ticks); |
| 43 | schedule_timeout(ticks); | ||
| 44 | } | 43 | } |
| 45 | 44 | ||
| 46 | static inline void nanotime(struct timespec *tvp) | 45 | static inline void nanotime(struct timespec *tvp) |
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 655bf4a78afe..e82cf72ac599 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c | |||
| @@ -1780,10 +1780,10 @@ xfsbufd( | |||
| 1780 | xfsbufd_force_sleep = 0; | 1780 | xfsbufd_force_sleep = 0; |
| 1781 | } | 1781 | } |
| 1782 | 1782 | ||
| 1783 | set_current_state(TASK_INTERRUPTIBLE); | 1783 | schedule_timeout_interruptible |
| 1784 | schedule_timeout((xfs_buf_timer_centisecs * HZ) / 100); | 1784 | (xfs_buf_timer_centisecs * msecs_to_jiffies(10)); |
| 1785 | 1785 | ||
| 1786 | age = (xfs_buf_age_centisecs * HZ) / 100; | 1786 | age = xfs_buf_age_centisecs * msecs_to_jiffies(10); |
| 1787 | spin_lock(&pbd_delwrite_lock); | 1787 | spin_lock(&pbd_delwrite_lock); |
| 1788 | list_for_each_entry_safe(pb, n, &pbd_delwrite_queue, pb_list) { | 1788 | list_for_each_entry_safe(pb, n, &pbd_delwrite_queue, pb_list) { |
| 1789 | PB_TRACE(pb, "walkq1", (long)pagebuf_ispin(pb)); | 1789 | PB_TRACE(pb, "walkq1", (long)pagebuf_ispin(pb)); |
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 0da87bfc9999..2302454d8d47 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c | |||
| @@ -467,7 +467,7 @@ xfs_flush_inode( | |||
| 467 | 467 | ||
| 468 | igrab(inode); | 468 | igrab(inode); |
| 469 | xfs_syncd_queue_work(vfs, inode, xfs_flush_inode_work); | 469 | xfs_syncd_queue_work(vfs, inode, xfs_flush_inode_work); |
| 470 | delay(HZ/2); | 470 | delay(msecs_to_jiffies(500)); |
| 471 | } | 471 | } |
| 472 | 472 | ||
| 473 | /* | 473 | /* |
| @@ -492,7 +492,7 @@ xfs_flush_device( | |||
| 492 | 492 | ||
| 493 | igrab(inode); | 493 | igrab(inode); |
| 494 | xfs_syncd_queue_work(vfs, inode, xfs_flush_device_work); | 494 | xfs_syncd_queue_work(vfs, inode, xfs_flush_device_work); |
| 495 | delay(HZ/2); | 495 | delay(msecs_to_jiffies(500)); |
| 496 | xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC); | 496 | xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC); |
| 497 | } | 497 | } |
| 498 | 498 | ||
| @@ -520,10 +520,9 @@ xfssyncd( | |||
| 520 | struct vfs_sync_work *work, *n; | 520 | struct vfs_sync_work *work, *n; |
| 521 | LIST_HEAD (tmp); | 521 | LIST_HEAD (tmp); |
| 522 | 522 | ||
| 523 | timeleft = (xfs_syncd_centisecs * HZ) / 100; | 523 | timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10); |
| 524 | for (;;) { | 524 | for (;;) { |
| 525 | set_current_state(TASK_INTERRUPTIBLE); | 525 | timeleft = schedule_timeout_interruptible(timeleft); |
| 526 | timeleft = schedule_timeout(timeleft); | ||
| 527 | /* swsusp */ | 526 | /* swsusp */ |
| 528 | try_to_freeze(); | 527 | try_to_freeze(); |
| 529 | if (kthread_should_stop()) | 528 | if (kthread_should_stop()) |
| @@ -537,7 +536,8 @@ xfssyncd( | |||
| 537 | */ | 536 | */ |
| 538 | if (!timeleft || list_empty(&vfsp->vfs_sync_list)) { | 537 | if (!timeleft || list_empty(&vfsp->vfs_sync_list)) { |
| 539 | if (!timeleft) | 538 | if (!timeleft) |
| 540 | timeleft = (xfs_syncd_centisecs * HZ) / 100; | 539 | timeleft = xfs_syncd_centisecs * |
| 540 | msecs_to_jiffies(10); | ||
| 541 | INIT_LIST_HEAD(&vfsp->vfs_sync_work.w_list); | 541 | INIT_LIST_HEAD(&vfsp->vfs_sync_work.w_list); |
| 542 | list_add_tail(&vfsp->vfs_sync_work.w_list, | 542 | list_add_tail(&vfsp->vfs_sync_work.w_list, |
| 543 | &vfsp->vfs_sync_list); | 543 | &vfsp->vfs_sync_list); |
diff --git a/fs/xfs/support/ktrace.c b/fs/xfs/support/ktrace.c index 3dae14c8c55a..fa8394f9437d 100644 --- a/fs/xfs/support/ktrace.c +++ b/fs/xfs/support/ktrace.c | |||
| @@ -170,7 +170,7 @@ ktrace_enter( | |||
| 170 | void *val14, | 170 | void *val14, |
| 171 | void *val15) | 171 | void *val15) |
| 172 | { | 172 | { |
| 173 | static lock_t wrap_lock = SPIN_LOCK_UNLOCKED; | 173 | static DEFINE_SPINLOCK(wrap_lock); |
| 174 | unsigned long flags; | 174 | unsigned long flags; |
| 175 | int index; | 175 | int index; |
| 176 | ktrace_entry_t *ktep; | 176 | ktrace_entry_t *ktep; |
