diff options
-rw-r--r-- | fs/afs/callback.c | 7 | ||||
-rw-r--r-- | fs/afs/cmservice.c | 11 | ||||
-rw-r--r-- | fs/afs/file.c | 20 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 77 | ||||
-rw-r--r-- | fs/afs/inode.c | 42 | ||||
-rw-r--r-- | fs/afs/internal.h | 23 | ||||
-rw-r--r-- | fs/afs/misc.c | 2 | ||||
-rw-r--r-- | fs/afs/mntpt.c | 53 | ||||
-rw-r--r-- | fs/afs/rxrpc.c | 149 | ||||
-rw-r--r-- | fs/afs/security.c | 9 | ||||
-rw-r--r-- | fs/afs/server.c | 6 | ||||
-rw-r--r-- | fs/afs/vlocation.c | 16 | ||||
-rw-r--r-- | fs/afs/write.c | 76 |
13 files changed, 269 insertions, 222 deletions
diff --git a/fs/afs/callback.c b/fs/afs/callback.c index b29447e03ede..25d404d22cae 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c | |||
@@ -362,7 +362,7 @@ static void afs_callback_updater(struct work_struct *work) | |||
362 | { | 362 | { |
363 | struct afs_server *server; | 363 | struct afs_server *server; |
364 | struct afs_vnode *vnode, *xvnode; | 364 | struct afs_vnode *vnode, *xvnode; |
365 | time_t now; | 365 | time64_t now; |
366 | long timeout; | 366 | long timeout; |
367 | int ret; | 367 | int ret; |
368 | 368 | ||
@@ -370,7 +370,7 @@ static void afs_callback_updater(struct work_struct *work) | |||
370 | 370 | ||
371 | _enter(""); | 371 | _enter(""); |
372 | 372 | ||
373 | now = get_seconds(); | 373 | now = ktime_get_real_seconds(); |
374 | 374 | ||
375 | /* find the first vnode to update */ | 375 | /* find the first vnode to update */ |
376 | spin_lock(&server->cb_lock); | 376 | spin_lock(&server->cb_lock); |
@@ -424,7 +424,8 @@ static void afs_callback_updater(struct work_struct *work) | |||
424 | 424 | ||
425 | /* and then reschedule */ | 425 | /* and then reschedule */ |
426 | _debug("reschedule"); | 426 | _debug("reschedule"); |
427 | vnode->update_at = get_seconds() + afs_vnode_update_timeout; | 427 | vnode->update_at = ktime_get_real_seconds() + |
428 | afs_vnode_update_timeout; | ||
428 | 429 | ||
429 | spin_lock(&server->cb_lock); | 430 | spin_lock(&server->cb_lock); |
430 | 431 | ||
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 2edbdcbf6432..3062cceb5c2a 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c | |||
@@ -187,7 +187,6 @@ static int afs_deliver_cb_callback(struct afs_call *call) | |||
187 | struct afs_callback *cb; | 187 | struct afs_callback *cb; |
188 | struct afs_server *server; | 188 | struct afs_server *server; |
189 | __be32 *bp; | 189 | __be32 *bp; |
190 | u32 tmp; | ||
191 | int ret, loop; | 190 | int ret, loop; |
192 | 191 | ||
193 | _enter("{%u}", call->unmarshall); | 192 | _enter("{%u}", call->unmarshall); |
@@ -249,9 +248,9 @@ static int afs_deliver_cb_callback(struct afs_call *call) | |||
249 | if (ret < 0) | 248 | if (ret < 0) |
250 | return ret; | 249 | return ret; |
251 | 250 | ||
252 | tmp = ntohl(call->tmp); | 251 | call->count2 = ntohl(call->tmp); |
253 | _debug("CB count: %u", tmp); | 252 | _debug("CB count: %u", call->count2); |
254 | if (tmp != call->count && tmp != 0) | 253 | if (call->count2 != call->count && call->count2 != 0) |
255 | return -EBADMSG; | 254 | return -EBADMSG; |
256 | call->offset = 0; | 255 | call->offset = 0; |
257 | call->unmarshall++; | 256 | call->unmarshall++; |
@@ -259,14 +258,14 @@ static int afs_deliver_cb_callback(struct afs_call *call) | |||
259 | case 4: | 258 | case 4: |
260 | _debug("extract CB array"); | 259 | _debug("extract CB array"); |
261 | ret = afs_extract_data(call, call->buffer, | 260 | ret = afs_extract_data(call, call->buffer, |
262 | call->count * 3 * 4, false); | 261 | call->count2 * 3 * 4, false); |
263 | if (ret < 0) | 262 | if (ret < 0) |
264 | return ret; | 263 | return ret; |
265 | 264 | ||
266 | _debug("unmarshall CB array"); | 265 | _debug("unmarshall CB array"); |
267 | cb = call->request; | 266 | cb = call->request; |
268 | bp = call->buffer; | 267 | bp = call->buffer; |
269 | for (loop = call->count; loop > 0; loop--, cb++) { | 268 | for (loop = call->count2; loop > 0; loop--, cb++) { |
270 | cb->version = ntohl(*bp++); | 269 | cb->version = ntohl(*bp++); |
271 | cb->expiry = ntohl(*bp++); | 270 | cb->expiry = ntohl(*bp++); |
272 | cb->type = ntohl(*bp++); | 271 | cb->type = ntohl(*bp++); |
diff --git a/fs/afs/file.c b/fs/afs/file.c index ba7b71fba34b..0d5b8508869b 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
@@ -30,6 +30,7 @@ static int afs_readpages(struct file *filp, struct address_space *mapping, | |||
30 | 30 | ||
31 | const struct file_operations afs_file_operations = { | 31 | const struct file_operations afs_file_operations = { |
32 | .open = afs_open, | 32 | .open = afs_open, |
33 | .flush = afs_flush, | ||
33 | .release = afs_release, | 34 | .release = afs_release, |
34 | .llseek = generic_file_llseek, | 35 | .llseek = generic_file_llseek, |
35 | .read_iter = generic_file_read_iter, | 36 | .read_iter = generic_file_read_iter, |
@@ -184,10 +185,13 @@ int afs_page_filler(void *data, struct page *page) | |||
184 | if (!req) | 185 | if (!req) |
185 | goto enomem; | 186 | goto enomem; |
186 | 187 | ||
188 | /* We request a full page. If the page is a partial one at the | ||
189 | * end of the file, the server will return a short read and the | ||
190 | * unmarshalling code will clear the unfilled space. | ||
191 | */ | ||
187 | atomic_set(&req->usage, 1); | 192 | atomic_set(&req->usage, 1); |
188 | req->pos = (loff_t)page->index << PAGE_SHIFT; | 193 | req->pos = (loff_t)page->index << PAGE_SHIFT; |
189 | req->len = min_t(size_t, i_size_read(inode) - req->pos, | 194 | req->len = PAGE_SIZE; |
190 | PAGE_SIZE); | ||
191 | req->nr_pages = 1; | 195 | req->nr_pages = 1; |
192 | req->pages[0] = page; | 196 | req->pages[0] = page; |
193 | get_page(page); | 197 | get_page(page); |
@@ -208,7 +212,13 @@ int afs_page_filler(void *data, struct page *page) | |||
208 | fscache_uncache_page(vnode->cache, page); | 212 | fscache_uncache_page(vnode->cache, page); |
209 | #endif | 213 | #endif |
210 | BUG_ON(PageFsCache(page)); | 214 | BUG_ON(PageFsCache(page)); |
211 | goto error; | 215 | |
216 | if (ret == -EINTR || | ||
217 | ret == -ENOMEM || | ||
218 | ret == -ERESTARTSYS || | ||
219 | ret == -EAGAIN) | ||
220 | goto error; | ||
221 | goto io_error; | ||
212 | } | 222 | } |
213 | 223 | ||
214 | SetPageUptodate(page); | 224 | SetPageUptodate(page); |
@@ -227,10 +237,12 @@ int afs_page_filler(void *data, struct page *page) | |||
227 | _leave(" = 0"); | 237 | _leave(" = 0"); |
228 | return 0; | 238 | return 0; |
229 | 239 | ||
240 | io_error: | ||
241 | SetPageError(page); | ||
242 | goto error; | ||
230 | enomem: | 243 | enomem: |
231 | ret = -ENOMEM; | 244 | ret = -ENOMEM; |
232 | error: | 245 | error: |
233 | SetPageError(page); | ||
234 | unlock_page(page); | 246 | unlock_page(page); |
235 | _leave(" = %d", ret); | 247 | _leave(" = %d", ret); |
236 | return ret; | 248 | return ret; |
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index ac8e766978dc..19f76ae36982 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c | |||
@@ -17,6 +17,12 @@ | |||
17 | #include "afs_fs.h" | 17 | #include "afs_fs.h" |
18 | 18 | ||
19 | /* | 19 | /* |
20 | * We need somewhere to discard into in case the server helpfully returns more | ||
21 | * than we asked for in FS.FetchData{,64}. | ||
22 | */ | ||
23 | static u8 afs_discard_buffer[64]; | ||
24 | |||
25 | /* | ||
20 | * decode an AFSFid block | 26 | * decode an AFSFid block |
21 | */ | 27 | */ |
22 | static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) | 28 | static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) |
@@ -105,7 +111,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, | |||
105 | vnode->vfs_inode.i_mode = mode; | 111 | vnode->vfs_inode.i_mode = mode; |
106 | } | 112 | } |
107 | 113 | ||
108 | vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; | 114 | vnode->vfs_inode.i_ctime.tv_sec = status->mtime_client; |
109 | vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; | 115 | vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; |
110 | vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; | 116 | vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; |
111 | vnode->vfs_inode.i_version = data_version; | 117 | vnode->vfs_inode.i_version = data_version; |
@@ -139,7 +145,7 @@ static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode) | |||
139 | vnode->cb_version = ntohl(*bp++); | 145 | vnode->cb_version = ntohl(*bp++); |
140 | vnode->cb_expiry = ntohl(*bp++); | 146 | vnode->cb_expiry = ntohl(*bp++); |
141 | vnode->cb_type = ntohl(*bp++); | 147 | vnode->cb_type = ntohl(*bp++); |
142 | vnode->cb_expires = vnode->cb_expiry + get_seconds(); | 148 | vnode->cb_expires = vnode->cb_expiry + ktime_get_real_seconds(); |
143 | *_bp = bp; | 149 | *_bp = bp; |
144 | } | 150 | } |
145 | 151 | ||
@@ -315,7 +321,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
315 | void *buffer; | 321 | void *buffer; |
316 | int ret; | 322 | int ret; |
317 | 323 | ||
318 | _enter("{%u,%zu/%u;%u/%llu}", | 324 | _enter("{%u,%zu/%u;%llu/%llu}", |
319 | call->unmarshall, call->offset, call->count, | 325 | call->unmarshall, call->offset, call->count, |
320 | req->remain, req->actual_len); | 326 | req->remain, req->actual_len); |
321 | 327 | ||
@@ -353,12 +359,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
353 | 359 | ||
354 | req->actual_len |= ntohl(call->tmp); | 360 | req->actual_len |= ntohl(call->tmp); |
355 | _debug("DATA length: %llu", req->actual_len); | 361 | _debug("DATA length: %llu", req->actual_len); |
356 | /* Check that the server didn't want to send us extra. We | ||
357 | * might want to just discard instead, but that requires | ||
358 | * cooperation from AF_RXRPC. | ||
359 | */ | ||
360 | if (req->actual_len > req->len) | ||
361 | return -EBADMSG; | ||
362 | 362 | ||
363 | req->remain = req->actual_len; | 363 | req->remain = req->actual_len; |
364 | call->offset = req->pos & (PAGE_SIZE - 1); | 364 | call->offset = req->pos & (PAGE_SIZE - 1); |
@@ -368,6 +368,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
368 | call->unmarshall++; | 368 | call->unmarshall++; |
369 | 369 | ||
370 | begin_page: | 370 | begin_page: |
371 | ASSERTCMP(req->index, <, req->nr_pages); | ||
371 | if (req->remain > PAGE_SIZE - call->offset) | 372 | if (req->remain > PAGE_SIZE - call->offset) |
372 | size = PAGE_SIZE - call->offset; | 373 | size = PAGE_SIZE - call->offset; |
373 | else | 374 | else |
@@ -378,7 +379,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
378 | 379 | ||
379 | /* extract the returned data */ | 380 | /* extract the returned data */ |
380 | case 3: | 381 | case 3: |
381 | _debug("extract data %u/%llu %zu/%u", | 382 | _debug("extract data %llu/%llu %zu/%u", |
382 | req->remain, req->actual_len, call->offset, call->count); | 383 | req->remain, req->actual_len, call->offset, call->count); |
383 | 384 | ||
384 | buffer = kmap(req->pages[req->index]); | 385 | buffer = kmap(req->pages[req->index]); |
@@ -389,19 +390,40 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
389 | if (call->offset == PAGE_SIZE) { | 390 | if (call->offset == PAGE_SIZE) { |
390 | if (req->page_done) | 391 | if (req->page_done) |
391 | req->page_done(call, req); | 392 | req->page_done(call, req); |
393 | req->index++; | ||
392 | if (req->remain > 0) { | 394 | if (req->remain > 0) { |
393 | req->index++; | ||
394 | call->offset = 0; | 395 | call->offset = 0; |
396 | if (req->index >= req->nr_pages) { | ||
397 | call->unmarshall = 4; | ||
398 | goto begin_discard; | ||
399 | } | ||
395 | goto begin_page; | 400 | goto begin_page; |
396 | } | 401 | } |
397 | } | 402 | } |
403 | goto no_more_data; | ||
404 | |||
405 | /* Discard any excess data the server gave us */ | ||
406 | begin_discard: | ||
407 | case 4: | ||
408 | size = min_t(loff_t, sizeof(afs_discard_buffer), req->remain); | ||
409 | call->count = size; | ||
410 | _debug("extract discard %llu/%llu %zu/%u", | ||
411 | req->remain, req->actual_len, call->offset, call->count); | ||
412 | |||
413 | call->offset = 0; | ||
414 | ret = afs_extract_data(call, afs_discard_buffer, call->count, true); | ||
415 | req->remain -= call->offset; | ||
416 | if (ret < 0) | ||
417 | return ret; | ||
418 | if (req->remain > 0) | ||
419 | goto begin_discard; | ||
398 | 420 | ||
399 | no_more_data: | 421 | no_more_data: |
400 | call->offset = 0; | 422 | call->offset = 0; |
401 | call->unmarshall++; | 423 | call->unmarshall = 5; |
402 | 424 | ||
403 | /* extract the metadata */ | 425 | /* extract the metadata */ |
404 | case 4: | 426 | case 5: |
405 | ret = afs_extract_data(call, call->buffer, | 427 | ret = afs_extract_data(call, call->buffer, |
406 | (21 + 3 + 6) * 4, false); | 428 | (21 + 3 + 6) * 4, false); |
407 | if (ret < 0) | 429 | if (ret < 0) |
@@ -416,16 +438,17 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) | |||
416 | call->offset = 0; | 438 | call->offset = 0; |
417 | call->unmarshall++; | 439 | call->unmarshall++; |
418 | 440 | ||
419 | case 5: | 441 | case 6: |
420 | break; | 442 | break; |
421 | } | 443 | } |
422 | 444 | ||
423 | if (call->count < PAGE_SIZE) { | 445 | for (; req->index < req->nr_pages; req->index++) { |
424 | buffer = kmap(req->pages[req->index]); | 446 | if (call->count < PAGE_SIZE) |
425 | memset(buffer + call->count, 0, PAGE_SIZE - call->count); | 447 | zero_user_segment(req->pages[req->index], |
426 | kunmap(req->pages[req->index]); | 448 | call->count, PAGE_SIZE); |
427 | if (req->page_done) | 449 | if (req->page_done) |
428 | req->page_done(call, req); | 450 | req->page_done(call, req); |
451 | call->count = 0; | ||
429 | } | 452 | } |
430 | 453 | ||
431 | _leave(" = 0 [done]"); | 454 | _leave(" = 0 [done]"); |
@@ -711,8 +734,8 @@ int afs_fs_create(struct afs_server *server, | |||
711 | memset(bp, 0, padsz); | 734 | memset(bp, 0, padsz); |
712 | bp = (void *) bp + padsz; | 735 | bp = (void *) bp + padsz; |
713 | } | 736 | } |
714 | *bp++ = htonl(AFS_SET_MODE); | 737 | *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); |
715 | *bp++ = 0; /* mtime */ | 738 | *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ |
716 | *bp++ = 0; /* owner */ | 739 | *bp++ = 0; /* owner */ |
717 | *bp++ = 0; /* group */ | 740 | *bp++ = 0; /* group */ |
718 | *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ | 741 | *bp++ = htonl(mode & S_IALLUGO); /* unix mode */ |
@@ -980,8 +1003,8 @@ int afs_fs_symlink(struct afs_server *server, | |||
980 | memset(bp, 0, c_padsz); | 1003 | memset(bp, 0, c_padsz); |
981 | bp = (void *) bp + c_padsz; | 1004 | bp = (void *) bp + c_padsz; |
982 | } | 1005 | } |
983 | *bp++ = htonl(AFS_SET_MODE); | 1006 | *bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME); |
984 | *bp++ = 0; /* mtime */ | 1007 | *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ |
985 | *bp++ = 0; /* owner */ | 1008 | *bp++ = 0; /* owner */ |
986 | *bp++ = 0; /* group */ | 1009 | *bp++ = 0; /* group */ |
987 | *bp++ = htonl(S_IRWXUGO); /* unix mode */ | 1010 | *bp++ = htonl(S_IRWXUGO); /* unix mode */ |
@@ -1180,8 +1203,8 @@ static int afs_fs_store_data64(struct afs_server *server, | |||
1180 | *bp++ = htonl(vnode->fid.vnode); | 1203 | *bp++ = htonl(vnode->fid.vnode); |
1181 | *bp++ = htonl(vnode->fid.unique); | 1204 | *bp++ = htonl(vnode->fid.unique); |
1182 | 1205 | ||
1183 | *bp++ = 0; /* mask */ | 1206 | *bp++ = htonl(AFS_SET_MTIME); /* mask */ |
1184 | *bp++ = 0; /* mtime */ | 1207 | *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ |
1185 | *bp++ = 0; /* owner */ | 1208 | *bp++ = 0; /* owner */ |
1186 | *bp++ = 0; /* group */ | 1209 | *bp++ = 0; /* group */ |
1187 | *bp++ = 0; /* unix mode */ | 1210 | *bp++ = 0; /* unix mode */ |
@@ -1213,7 +1236,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb, | |||
1213 | _enter(",%x,{%x:%u},,", | 1236 | _enter(",%x,{%x:%u},,", |
1214 | key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode); | 1237 | key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode); |
1215 | 1238 | ||
1216 | size = to - offset; | 1239 | size = (loff_t)to - (loff_t)offset; |
1217 | if (first != last) | 1240 | if (first != last) |
1218 | size += (loff_t)(last - first) << PAGE_SHIFT; | 1241 | size += (loff_t)(last - first) << PAGE_SHIFT; |
1219 | pos = (loff_t)first << PAGE_SHIFT; | 1242 | pos = (loff_t)first << PAGE_SHIFT; |
@@ -1257,8 +1280,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb, | |||
1257 | *bp++ = htonl(vnode->fid.vnode); | 1280 | *bp++ = htonl(vnode->fid.vnode); |
1258 | *bp++ = htonl(vnode->fid.unique); | 1281 | *bp++ = htonl(vnode->fid.unique); |
1259 | 1282 | ||
1260 | *bp++ = 0; /* mask */ | 1283 | *bp++ = htonl(AFS_SET_MTIME); /* mask */ |
1261 | *bp++ = 0; /* mtime */ | 1284 | *bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */ |
1262 | *bp++ = 0; /* owner */ | 1285 | *bp++ = 0; /* owner */ |
1263 | *bp++ = 0; /* group */ | 1286 | *bp++ = 0; /* group */ |
1264 | *bp++ = 0; /* unix mode */ | 1287 | *bp++ = 0; /* unix mode */ |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 1e4897a048d2..aae55dd15108 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -54,8 +54,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) | |||
54 | inode->i_fop = &afs_dir_file_operations; | 54 | inode->i_fop = &afs_dir_file_operations; |
55 | break; | 55 | break; |
56 | case AFS_FTYPE_SYMLINK: | 56 | case AFS_FTYPE_SYMLINK: |
57 | inode->i_mode = S_IFLNK | vnode->status.mode; | 57 | /* Symlinks with a mode of 0644 are actually mountpoints. */ |
58 | inode->i_op = &page_symlink_inode_operations; | 58 | if ((vnode->status.mode & 0777) == 0644) { |
59 | inode->i_flags |= S_AUTOMOUNT; | ||
60 | |||
61 | spin_lock(&vnode->lock); | ||
62 | set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); | ||
63 | spin_unlock(&vnode->lock); | ||
64 | |||
65 | inode->i_mode = S_IFDIR | 0555; | ||
66 | inode->i_op = &afs_mntpt_inode_operations; | ||
67 | inode->i_fop = &afs_mntpt_file_operations; | ||
68 | } else { | ||
69 | inode->i_mode = S_IFLNK | vnode->status.mode; | ||
70 | inode->i_op = &page_symlink_inode_operations; | ||
71 | } | ||
59 | inode_nohighmem(inode); | 72 | inode_nohighmem(inode); |
60 | break; | 73 | break; |
61 | default: | 74 | default: |
@@ -70,27 +83,15 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) | |||
70 | 83 | ||
71 | set_nlink(inode, vnode->status.nlink); | 84 | set_nlink(inode, vnode->status.nlink); |
72 | inode->i_uid = vnode->status.owner; | 85 | inode->i_uid = vnode->status.owner; |
73 | inode->i_gid = GLOBAL_ROOT_GID; | 86 | inode->i_gid = vnode->status.group; |
74 | inode->i_size = vnode->status.size; | 87 | inode->i_size = vnode->status.size; |
75 | inode->i_ctime.tv_sec = vnode->status.mtime_server; | 88 | inode->i_ctime.tv_sec = vnode->status.mtime_client; |
76 | inode->i_ctime.tv_nsec = 0; | 89 | inode->i_ctime.tv_nsec = 0; |
77 | inode->i_atime = inode->i_mtime = inode->i_ctime; | 90 | inode->i_atime = inode->i_mtime = inode->i_ctime; |
78 | inode->i_blocks = 0; | 91 | inode->i_blocks = 0; |
79 | inode->i_generation = vnode->fid.unique; | 92 | inode->i_generation = vnode->fid.unique; |
80 | inode->i_version = vnode->status.data_version; | 93 | inode->i_version = vnode->status.data_version; |
81 | inode->i_mapping->a_ops = &afs_fs_aops; | 94 | inode->i_mapping->a_ops = &afs_fs_aops; |
82 | |||
83 | /* check to see whether a symbolic link is really a mountpoint */ | ||
84 | if (vnode->status.type == AFS_FTYPE_SYMLINK) { | ||
85 | afs_mntpt_check_symlink(vnode, key); | ||
86 | |||
87 | if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) { | ||
88 | inode->i_mode = S_IFDIR | vnode->status.mode; | ||
89 | inode->i_op = &afs_mntpt_inode_operations; | ||
90 | inode->i_fop = &afs_mntpt_file_operations; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | return 0; | 95 | return 0; |
95 | } | 96 | } |
96 | 97 | ||
@@ -245,12 +246,13 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, | |||
245 | vnode->cb_version = 0; | 246 | vnode->cb_version = 0; |
246 | vnode->cb_expiry = 0; | 247 | vnode->cb_expiry = 0; |
247 | vnode->cb_type = 0; | 248 | vnode->cb_type = 0; |
248 | vnode->cb_expires = get_seconds(); | 249 | vnode->cb_expires = ktime_get_real_seconds(); |
249 | } else { | 250 | } else { |
250 | vnode->cb_version = cb->version; | 251 | vnode->cb_version = cb->version; |
251 | vnode->cb_expiry = cb->expiry; | 252 | vnode->cb_expiry = cb->expiry; |
252 | vnode->cb_type = cb->type; | 253 | vnode->cb_type = cb->type; |
253 | vnode->cb_expires = vnode->cb_expiry + get_seconds(); | 254 | vnode->cb_expires = vnode->cb_expiry + |
255 | ktime_get_real_seconds(); | ||
254 | } | 256 | } |
255 | } | 257 | } |
256 | 258 | ||
@@ -323,7 +325,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) | |||
323 | !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && | 325 | !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && |
324 | !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) && | 326 | !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) && |
325 | !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | 327 | !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { |
326 | if (vnode->cb_expires < get_seconds() + 10) { | 328 | if (vnode->cb_expires < ktime_get_real_seconds() + 10) { |
327 | _debug("callback expired"); | 329 | _debug("callback expired"); |
328 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | 330 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); |
329 | } else { | 331 | } else { |
@@ -444,7 +446,7 @@ void afs_evict_inode(struct inode *inode) | |||
444 | 446 | ||
445 | mutex_lock(&vnode->permits_lock); | 447 | mutex_lock(&vnode->permits_lock); |
446 | permits = vnode->permits; | 448 | permits = vnode->permits; |
447 | rcu_assign_pointer(vnode->permits, NULL); | 449 | RCU_INIT_POINTER(vnode->permits, NULL); |
448 | mutex_unlock(&vnode->permits_lock); | 450 | mutex_unlock(&vnode->permits_lock); |
449 | if (permits) | 451 | if (permits) |
450 | call_rcu(&permits->rcu, afs_zap_permits); | 452 | call_rcu(&permits->rcu, afs_zap_permits); |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5dfa56903a2d..a6901360fb81 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <linux/compiler.h> | 12 | #include <linux/compiler.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/ktime.h> | ||
14 | #include <linux/fs.h> | 15 | #include <linux/fs.h> |
15 | #include <linux/pagemap.h> | 16 | #include <linux/pagemap.h> |
16 | #include <linux/rxrpc.h> | 17 | #include <linux/rxrpc.h> |
@@ -90,7 +91,10 @@ struct afs_call { | |||
90 | unsigned request_size; /* size of request data */ | 91 | unsigned request_size; /* size of request data */ |
91 | unsigned reply_max; /* maximum size of reply */ | 92 | unsigned reply_max; /* maximum size of reply */ |
92 | unsigned first_offset; /* offset into mapping[first] */ | 93 | unsigned first_offset; /* offset into mapping[first] */ |
93 | unsigned last_to; /* amount of mapping[last] */ | 94 | union { |
95 | unsigned last_to; /* amount of mapping[last] */ | ||
96 | unsigned count2; /* count used in unmarshalling */ | ||
97 | }; | ||
94 | unsigned char unmarshall; /* unmarshalling phase */ | 98 | unsigned char unmarshall; /* unmarshalling phase */ |
95 | bool incoming; /* T if incoming call */ | 99 | bool incoming; /* T if incoming call */ |
96 | bool send_pages; /* T if data from mapping should be sent */ | 100 | bool send_pages; /* T if data from mapping should be sent */ |
@@ -127,12 +131,11 @@ struct afs_call_type { | |||
127 | */ | 131 | */ |
128 | struct afs_read { | 132 | struct afs_read { |
129 | loff_t pos; /* Where to start reading */ | 133 | loff_t pos; /* Where to start reading */ |
130 | loff_t len; /* How much to read */ | 134 | loff_t len; /* How much we're asking for */ |
131 | loff_t actual_len; /* How much we're actually getting */ | 135 | loff_t actual_len; /* How much we're actually getting */ |
136 | loff_t remain; /* Amount remaining */ | ||
132 | atomic_t usage; | 137 | atomic_t usage; |
133 | unsigned int remain; /* Amount remaining */ | ||
134 | unsigned int index; /* Which page we're reading into */ | 138 | unsigned int index; /* Which page we're reading into */ |
135 | unsigned int pg_offset; /* Offset in page we're at */ | ||
136 | unsigned int nr_pages; | 139 | unsigned int nr_pages; |
137 | void (*page_done)(struct afs_call *, struct afs_read *); | 140 | void (*page_done)(struct afs_call *, struct afs_read *); |
138 | struct page *pages[]; | 141 | struct page *pages[]; |
@@ -247,7 +250,7 @@ struct afs_cache_vhash { | |||
247 | */ | 250 | */ |
248 | struct afs_vlocation { | 251 | struct afs_vlocation { |
249 | atomic_t usage; | 252 | atomic_t usage; |
250 | time_t time_of_death; /* time at which put reduced usage to 0 */ | 253 | time64_t time_of_death; /* time at which put reduced usage to 0 */ |
251 | struct list_head link; /* link in cell volume location list */ | 254 | struct list_head link; /* link in cell volume location list */ |
252 | struct list_head grave; /* link in master graveyard list */ | 255 | struct list_head grave; /* link in master graveyard list */ |
253 | struct list_head update; /* link in master update list */ | 256 | struct list_head update; /* link in master update list */ |
@@ -258,7 +261,7 @@ struct afs_vlocation { | |||
258 | struct afs_cache_vlocation vldb; /* volume information DB record */ | 261 | struct afs_cache_vlocation vldb; /* volume information DB record */ |
259 | struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ | 262 | struct afs_volume *vols[3]; /* volume access record pointer (index by type) */ |
260 | wait_queue_head_t waitq; /* status change waitqueue */ | 263 | wait_queue_head_t waitq; /* status change waitqueue */ |
261 | time_t update_at; /* time at which record should be updated */ | 264 | time64_t update_at; /* time at which record should be updated */ |
262 | spinlock_t lock; /* access lock */ | 265 | spinlock_t lock; /* access lock */ |
263 | afs_vlocation_state_t state; /* volume location state */ | 266 | afs_vlocation_state_t state; /* volume location state */ |
264 | unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */ | 267 | unsigned short upd_rej_cnt; /* ENOMEDIUM count during update */ |
@@ -271,7 +274,7 @@ struct afs_vlocation { | |||
271 | */ | 274 | */ |
272 | struct afs_server { | 275 | struct afs_server { |
273 | atomic_t usage; | 276 | atomic_t usage; |
274 | time_t time_of_death; /* time at which put reduced usage to 0 */ | 277 | time64_t time_of_death; /* time at which put reduced usage to 0 */ |
275 | struct in_addr addr; /* server address */ | 278 | struct in_addr addr; /* server address */ |
276 | struct afs_cell *cell; /* cell in which server resides */ | 279 | struct afs_cell *cell; /* cell in which server resides */ |
277 | struct list_head link; /* link in cell's server list */ | 280 | struct list_head link; /* link in cell's server list */ |
@@ -374,8 +377,8 @@ struct afs_vnode { | |||
374 | struct rb_node server_rb; /* link in server->fs_vnodes */ | 377 | struct rb_node server_rb; /* link in server->fs_vnodes */ |
375 | struct rb_node cb_promise; /* link in server->cb_promises */ | 378 | struct rb_node cb_promise; /* link in server->cb_promises */ |
376 | struct work_struct cb_broken_work; /* work to be done on callback break */ | 379 | struct work_struct cb_broken_work; /* work to be done on callback break */ |
377 | time_t cb_expires; /* time at which callback expires */ | 380 | time64_t cb_expires; /* time at which callback expires */ |
378 | time_t cb_expires_at; /* time used to order cb_promise */ | 381 | time64_t cb_expires_at; /* time used to order cb_promise */ |
379 | unsigned cb_version; /* callback version */ | 382 | unsigned cb_version; /* callback version */ |
380 | unsigned cb_expiry; /* callback expiry time */ | 383 | unsigned cb_expiry; /* callback expiry time */ |
381 | afs_callback_type_t cb_type; /* type of callback */ | 384 | afs_callback_type_t cb_type; /* type of callback */ |
@@ -557,7 +560,6 @@ extern const struct inode_operations afs_autocell_inode_operations; | |||
557 | extern const struct file_operations afs_mntpt_file_operations; | 560 | extern const struct file_operations afs_mntpt_file_operations; |
558 | 561 | ||
559 | extern struct vfsmount *afs_d_automount(struct path *); | 562 | extern struct vfsmount *afs_d_automount(struct path *); |
560 | extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *); | ||
561 | extern void afs_mntpt_kill_timer(void); | 563 | extern void afs_mntpt_kill_timer(void); |
562 | 564 | ||
563 | /* | 565 | /* |
@@ -718,6 +720,7 @@ extern int afs_writepages(struct address_space *, struct writeback_control *); | |||
718 | extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); | 720 | extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *); |
719 | extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); | 721 | extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); |
720 | extern int afs_writeback_all(struct afs_vnode *); | 722 | extern int afs_writeback_all(struct afs_vnode *); |
723 | extern int afs_flush(struct file *, fl_owner_t); | ||
721 | extern int afs_fsync(struct file *, loff_t, loff_t, int); | 724 | extern int afs_fsync(struct file *, loff_t, loff_t, int); |
722 | 725 | ||
723 | 726 | ||
diff --git a/fs/afs/misc.c b/fs/afs/misc.c index 91ea1aa0d8b3..100b207efc9e 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c | |||
@@ -84,6 +84,8 @@ int afs_abort_to_error(u32 abort_code) | |||
84 | case RXKADDATALEN: return -EKEYREJECTED; | 84 | case RXKADDATALEN: return -EKEYREJECTED; |
85 | case RXKADILLEGALLEVEL: return -EKEYREJECTED; | 85 | case RXKADILLEGALLEVEL: return -EKEYREJECTED; |
86 | 86 | ||
87 | case RXGEN_OPCODE: return -ENOTSUPP; | ||
88 | |||
87 | default: return -EREMOTEIO; | 89 | default: return -EREMOTEIO; |
88 | } | 90 | } |
89 | } | 91 | } |
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index d4fb0afc0097..bd3b65cde282 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c | |||
@@ -47,59 +47,6 @@ static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); | |||
47 | static unsigned long afs_mntpt_expiry_timeout = 10 * 60; | 47 | static unsigned long afs_mntpt_expiry_timeout = 10 * 60; |
48 | 48 | ||
49 | /* | 49 | /* |
50 | * check a symbolic link to see whether it actually encodes a mountpoint | ||
51 | * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately | ||
52 | */ | ||
53 | int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) | ||
54 | { | ||
55 | struct page *page; | ||
56 | size_t size; | ||
57 | char *buf; | ||
58 | int ret; | ||
59 | |||
60 | _enter("{%x:%u,%u}", | ||
61 | vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); | ||
62 | |||
63 | /* read the contents of the symlink into the pagecache */ | ||
64 | page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, | ||
65 | afs_page_filler, key); | ||
66 | if (IS_ERR(page)) { | ||
67 | ret = PTR_ERR(page); | ||
68 | goto out; | ||
69 | } | ||
70 | |||
71 | ret = -EIO; | ||
72 | if (PageError(page)) | ||
73 | goto out_free; | ||
74 | |||
75 | buf = kmap(page); | ||
76 | |||
77 | /* examine the symlink's contents */ | ||
78 | size = vnode->status.size; | ||
79 | _debug("symlink to %*.*s", (int) size, (int) size, buf); | ||
80 | |||
81 | if (size > 2 && | ||
82 | (buf[0] == '%' || buf[0] == '#') && | ||
83 | buf[size - 1] == '.' | ||
84 | ) { | ||
85 | _debug("symlink is a mountpoint"); | ||
86 | spin_lock(&vnode->lock); | ||
87 | set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); | ||
88 | vnode->vfs_inode.i_flags |= S_AUTOMOUNT; | ||
89 | spin_unlock(&vnode->lock); | ||
90 | } | ||
91 | |||
92 | ret = 0; | ||
93 | |||
94 | kunmap(page); | ||
95 | out_free: | ||
96 | put_page(page); | ||
97 | out: | ||
98 | _leave(" = %d", ret); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * no valid lookup procedure on this sort of dir | 50 | * no valid lookup procedure on this sort of dir |
104 | */ | 51 | */ |
105 | static struct dentry *afs_mntpt_lookup(struct inode *dir, | 52 | static struct dentry *afs_mntpt_lookup(struct inode *dir, |
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 419ef05dcb5e..8f76b13d5549 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c | |||
@@ -259,67 +259,74 @@ void afs_flat_call_destructor(struct afs_call *call) | |||
259 | call->buffer = NULL; | 259 | call->buffer = NULL; |
260 | } | 260 | } |
261 | 261 | ||
262 | #define AFS_BVEC_MAX 8 | ||
263 | |||
264 | /* | ||
265 | * Load the given bvec with the next few pages. | ||
266 | */ | ||
267 | static void afs_load_bvec(struct afs_call *call, struct msghdr *msg, | ||
268 | struct bio_vec *bv, pgoff_t first, pgoff_t last, | ||
269 | unsigned offset) | ||
270 | { | ||
271 | struct page *pages[AFS_BVEC_MAX]; | ||
272 | unsigned int nr, n, i, to, bytes = 0; | ||
273 | |||
274 | nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX); | ||
275 | n = find_get_pages_contig(call->mapping, first, nr, pages); | ||
276 | ASSERTCMP(n, ==, nr); | ||
277 | |||
278 | msg->msg_flags |= MSG_MORE; | ||
279 | for (i = 0; i < nr; i++) { | ||
280 | to = PAGE_SIZE; | ||
281 | if (first + i >= last) { | ||
282 | to = call->last_to; | ||
283 | msg->msg_flags &= ~MSG_MORE; | ||
284 | } | ||
285 | bv[i].bv_page = pages[i]; | ||
286 | bv[i].bv_len = to - offset; | ||
287 | bv[i].bv_offset = offset; | ||
288 | bytes += to - offset; | ||
289 | offset = 0; | ||
290 | } | ||
291 | |||
292 | iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC, bv, nr, bytes); | ||
293 | } | ||
294 | |||
262 | /* | 295 | /* |
263 | * attach the data from a bunch of pages on an inode to a call | 296 | * attach the data from a bunch of pages on an inode to a call |
264 | */ | 297 | */ |
265 | static int afs_send_pages(struct afs_call *call, struct msghdr *msg) | 298 | static int afs_send_pages(struct afs_call *call, struct msghdr *msg) |
266 | { | 299 | { |
267 | struct page *pages[8]; | 300 | struct bio_vec bv[AFS_BVEC_MAX]; |
268 | unsigned count, n, loop, offset, to; | 301 | unsigned int bytes, nr, loop, offset; |
269 | pgoff_t first = call->first, last = call->last; | 302 | pgoff_t first = call->first, last = call->last; |
270 | int ret; | 303 | int ret; |
271 | 304 | ||
272 | _enter(""); | ||
273 | |||
274 | offset = call->first_offset; | 305 | offset = call->first_offset; |
275 | call->first_offset = 0; | 306 | call->first_offset = 0; |
276 | 307 | ||
277 | do { | 308 | do { |
278 | _debug("attach %lx-%lx", first, last); | 309 | afs_load_bvec(call, msg, bv, first, last, offset); |
279 | 310 | offset = 0; | |
280 | count = last - first + 1; | 311 | bytes = msg->msg_iter.count; |
281 | if (count > ARRAY_SIZE(pages)) | 312 | nr = msg->msg_iter.nr_segs; |
282 | count = ARRAY_SIZE(pages); | 313 | |
283 | n = find_get_pages_contig(call->mapping, first, count, pages); | 314 | /* Have to change the state *before* sending the last |
284 | ASSERTCMP(n, ==, count); | 315 | * packet as RxRPC might give us the reply before it |
285 | 316 | * returns from sending the request. | |
286 | loop = 0; | 317 | */ |
287 | do { | 318 | if (first + nr - 1 >= last) |
288 | struct bio_vec bvec = {.bv_page = pages[loop], | 319 | call->state = AFS_CALL_AWAIT_REPLY; |
289 | .bv_offset = offset}; | 320 | ret = rxrpc_kernel_send_data(afs_socket, call->rxcall, |
290 | msg->msg_flags = 0; | 321 | msg, bytes); |
291 | to = PAGE_SIZE; | 322 | for (loop = 0; loop < nr; loop++) |
292 | if (first + loop >= last) | 323 | put_page(bv[loop].bv_page); |
293 | to = call->last_to; | ||
294 | else | ||
295 | msg->msg_flags = MSG_MORE; | ||
296 | bvec.bv_len = to - offset; | ||
297 | offset = 0; | ||
298 | |||
299 | _debug("- range %u-%u%s", | ||
300 | offset, to, msg->msg_flags ? " [more]" : ""); | ||
301 | iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC, | ||
302 | &bvec, 1, to - offset); | ||
303 | |||
304 | /* have to change the state *before* sending the last | ||
305 | * packet as RxRPC might give us the reply before it | ||
306 | * returns from sending the request */ | ||
307 | if (first + loop >= last) | ||
308 | call->state = AFS_CALL_AWAIT_REPLY; | ||
309 | ret = rxrpc_kernel_send_data(afs_socket, call->rxcall, | ||
310 | msg, to - offset); | ||
311 | if (ret < 0) | ||
312 | break; | ||
313 | } while (++loop < count); | ||
314 | first += count; | ||
315 | |||
316 | for (loop = 0; loop < count; loop++) | ||
317 | put_page(pages[loop]); | ||
318 | if (ret < 0) | 324 | if (ret < 0) |
319 | break; | 325 | break; |
326 | |||
327 | first += nr; | ||
320 | } while (first <= last); | 328 | } while (first <= last); |
321 | 329 | ||
322 | _leave(" = %d", ret); | ||
323 | return ret; | 330 | return ret; |
324 | } | 331 | } |
325 | 332 | ||
@@ -333,6 +340,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, | |||
333 | struct rxrpc_call *rxcall; | 340 | struct rxrpc_call *rxcall; |
334 | struct msghdr msg; | 341 | struct msghdr msg; |
335 | struct kvec iov[1]; | 342 | struct kvec iov[1]; |
343 | size_t offset; | ||
344 | u32 abort_code; | ||
336 | int ret; | 345 | int ret; |
337 | 346 | ||
338 | _enter("%x,{%d},", addr->s_addr, ntohs(call->port)); | 347 | _enter("%x,{%d},", addr->s_addr, ntohs(call->port)); |
@@ -381,9 +390,11 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, | |||
381 | msg.msg_controllen = 0; | 390 | msg.msg_controllen = 0; |
382 | msg.msg_flags = (call->send_pages ? MSG_MORE : 0); | 391 | msg.msg_flags = (call->send_pages ? MSG_MORE : 0); |
383 | 392 | ||
384 | /* have to change the state *before* sending the last packet as RxRPC | 393 | /* We have to change the state *before* sending the last packet as |
385 | * might give us the reply before it returns from sending the | 394 | * rxrpc might give us the reply before it returns from sending the |
386 | * request */ | 395 | * request. Further, if the send fails, we may already have been given |
396 | * a notification and may have collected it. | ||
397 | */ | ||
387 | if (!call->send_pages) | 398 | if (!call->send_pages) |
388 | call->state = AFS_CALL_AWAIT_REPLY; | 399 | call->state = AFS_CALL_AWAIT_REPLY; |
389 | ret = rxrpc_kernel_send_data(afs_socket, rxcall, | 400 | ret = rxrpc_kernel_send_data(afs_socket, rxcall, |
@@ -405,7 +416,17 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, | |||
405 | return afs_wait_for_call_to_complete(call); | 416 | return afs_wait_for_call_to_complete(call); |
406 | 417 | ||
407 | error_do_abort: | 418 | error_do_abort: |
408 | rxrpc_kernel_abort_call(afs_socket, rxcall, RX_USER_ABORT, -ret, "KSD"); | 419 | call->state = AFS_CALL_COMPLETE; |
420 | if (ret != -ECONNABORTED) { | ||
421 | rxrpc_kernel_abort_call(afs_socket, rxcall, RX_USER_ABORT, | ||
422 | -ret, "KSD"); | ||
423 | } else { | ||
424 | abort_code = 0; | ||
425 | offset = 0; | ||
426 | rxrpc_kernel_recv_data(afs_socket, rxcall, NULL, 0, &offset, | ||
427 | false, &abort_code); | ||
428 | ret = call->type->abort_to_error(abort_code); | ||
429 | } | ||
409 | error_kill_call: | 430 | error_kill_call: |
410 | afs_put_call(call); | 431 | afs_put_call(call); |
411 | _leave(" = %d", ret); | 432 | _leave(" = %d", ret); |
@@ -452,16 +473,18 @@ static void afs_deliver_to_call(struct afs_call *call) | |||
452 | case -EINPROGRESS: | 473 | case -EINPROGRESS: |
453 | case -EAGAIN: | 474 | case -EAGAIN: |
454 | goto out; | 475 | goto out; |
476 | case -ECONNABORTED: | ||
477 | goto call_complete; | ||
455 | case -ENOTCONN: | 478 | case -ENOTCONN: |
456 | abort_code = RX_CALL_DEAD; | 479 | abort_code = RX_CALL_DEAD; |
457 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, | 480 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, |
458 | abort_code, -ret, "KNC"); | 481 | abort_code, -ret, "KNC"); |
459 | goto do_abort; | 482 | goto save_error; |
460 | case -ENOTSUPP: | 483 | case -ENOTSUPP: |
461 | abort_code = RX_INVALID_OPERATION; | 484 | abort_code = RXGEN_OPCODE; |
462 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, | 485 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, |
463 | abort_code, -ret, "KIV"); | 486 | abort_code, -ret, "KIV"); |
464 | goto do_abort; | 487 | goto save_error; |
465 | case -ENODATA: | 488 | case -ENODATA: |
466 | case -EBADMSG: | 489 | case -EBADMSG: |
467 | case -EMSGSIZE: | 490 | case -EMSGSIZE: |
@@ -471,7 +494,7 @@ static void afs_deliver_to_call(struct afs_call *call) | |||
471 | abort_code = RXGEN_SS_UNMARSHAL; | 494 | abort_code = RXGEN_SS_UNMARSHAL; |
472 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, | 495 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, |
473 | abort_code, EBADMSG, "KUM"); | 496 | abort_code, EBADMSG, "KUM"); |
474 | goto do_abort; | 497 | goto save_error; |
475 | } | 498 | } |
476 | } | 499 | } |
477 | 500 | ||
@@ -482,8 +505,9 @@ out: | |||
482 | _leave(""); | 505 | _leave(""); |
483 | return; | 506 | return; |
484 | 507 | ||
485 | do_abort: | 508 | save_error: |
486 | call->error = ret; | 509 | call->error = ret; |
510 | call_complete: | ||
487 | call->state = AFS_CALL_COMPLETE; | 511 | call->state = AFS_CALL_COMPLETE; |
488 | goto done; | 512 | goto done; |
489 | } | 513 | } |
@@ -493,7 +517,6 @@ do_abort: | |||
493 | */ | 517 | */ |
494 | static int afs_wait_for_call_to_complete(struct afs_call *call) | 518 | static int afs_wait_for_call_to_complete(struct afs_call *call) |
495 | { | 519 | { |
496 | const char *abort_why; | ||
497 | int ret; | 520 | int ret; |
498 | 521 | ||
499 | DECLARE_WAITQUEUE(myself, current); | 522 | DECLARE_WAITQUEUE(myself, current); |
@@ -512,13 +535,8 @@ static int afs_wait_for_call_to_complete(struct afs_call *call) | |||
512 | continue; | 535 | continue; |
513 | } | 536 | } |
514 | 537 | ||
515 | abort_why = "KWC"; | 538 | if (call->state == AFS_CALL_COMPLETE || |
516 | ret = call->error; | 539 | signal_pending(current)) |
517 | if (call->state == AFS_CALL_COMPLETE) | ||
518 | break; | ||
519 | abort_why = "KWI"; | ||
520 | ret = -EINTR; | ||
521 | if (signal_pending(current)) | ||
522 | break; | 540 | break; |
523 | schedule(); | 541 | schedule(); |
524 | } | 542 | } |
@@ -526,13 +544,14 @@ static int afs_wait_for_call_to_complete(struct afs_call *call) | |||
526 | remove_wait_queue(&call->waitq, &myself); | 544 | remove_wait_queue(&call->waitq, &myself); |
527 | __set_current_state(TASK_RUNNING); | 545 | __set_current_state(TASK_RUNNING); |
528 | 546 | ||
529 | /* kill the call */ | 547 | /* Kill off the call if it's still live. */ |
530 | if (call->state < AFS_CALL_COMPLETE) { | 548 | if (call->state < AFS_CALL_COMPLETE) { |
531 | _debug("call incomplete"); | 549 | _debug("call interrupted"); |
532 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, | 550 | rxrpc_kernel_abort_call(afs_socket, call->rxcall, |
533 | RX_CALL_DEAD, -ret, abort_why); | 551 | RX_USER_ABORT, -EINTR, "KWI"); |
534 | } | 552 | } |
535 | 553 | ||
554 | ret = call->error; | ||
536 | _debug("call complete"); | 555 | _debug("call complete"); |
537 | afs_put_call(call); | 556 | afs_put_call(call); |
538 | _leave(" = %d", ret); | 557 | _leave(" = %d", ret); |
diff --git a/fs/afs/security.c b/fs/afs/security.c index 8d010422dc89..ecb86a670180 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c | |||
@@ -114,7 +114,7 @@ void afs_clear_permits(struct afs_vnode *vnode) | |||
114 | 114 | ||
115 | mutex_lock(&vnode->permits_lock); | 115 | mutex_lock(&vnode->permits_lock); |
116 | permits = vnode->permits; | 116 | permits = vnode->permits; |
117 | rcu_assign_pointer(vnode->permits, NULL); | 117 | RCU_INIT_POINTER(vnode->permits, NULL); |
118 | mutex_unlock(&vnode->permits_lock); | 118 | mutex_unlock(&vnode->permits_lock); |
119 | 119 | ||
120 | if (permits) | 120 | if (permits) |
@@ -340,17 +340,22 @@ int afs_permission(struct inode *inode, int mask) | |||
340 | } else { | 340 | } else { |
341 | if (!(access & AFS_ACE_LOOKUP)) | 341 | if (!(access & AFS_ACE_LOOKUP)) |
342 | goto permission_denied; | 342 | goto permission_denied; |
343 | if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR)) | ||
344 | goto permission_denied; | ||
343 | if (mask & (MAY_EXEC | MAY_READ)) { | 345 | if (mask & (MAY_EXEC | MAY_READ)) { |
344 | if (!(access & AFS_ACE_READ)) | 346 | if (!(access & AFS_ACE_READ)) |
345 | goto permission_denied; | 347 | goto permission_denied; |
348 | if (!(inode->i_mode & S_IRUSR)) | ||
349 | goto permission_denied; | ||
346 | } else if (mask & MAY_WRITE) { | 350 | } else if (mask & MAY_WRITE) { |
347 | if (!(access & AFS_ACE_WRITE)) | 351 | if (!(access & AFS_ACE_WRITE)) |
348 | goto permission_denied; | 352 | goto permission_denied; |
353 | if (!(inode->i_mode & S_IWUSR)) | ||
354 | goto permission_denied; | ||
349 | } | 355 | } |
350 | } | 356 | } |
351 | 357 | ||
352 | key_put(key); | 358 | key_put(key); |
353 | ret = generic_permission(inode, mask); | ||
354 | _leave(" = %d", ret); | 359 | _leave(" = %d", ret); |
355 | return ret; | 360 | return ret; |
356 | 361 | ||
diff --git a/fs/afs/server.c b/fs/afs/server.c index d4066ab7dd55..c001b1f2455f 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c | |||
@@ -242,7 +242,7 @@ void afs_put_server(struct afs_server *server) | |||
242 | spin_lock(&afs_server_graveyard_lock); | 242 | spin_lock(&afs_server_graveyard_lock); |
243 | if (atomic_read(&server->usage) == 0) { | 243 | if (atomic_read(&server->usage) == 0) { |
244 | list_move_tail(&server->grave, &afs_server_graveyard); | 244 | list_move_tail(&server->grave, &afs_server_graveyard); |
245 | server->time_of_death = get_seconds(); | 245 | server->time_of_death = ktime_get_real_seconds(); |
246 | queue_delayed_work(afs_wq, &afs_server_reaper, | 246 | queue_delayed_work(afs_wq, &afs_server_reaper, |
247 | afs_server_timeout * HZ); | 247 | afs_server_timeout * HZ); |
248 | } | 248 | } |
@@ -277,9 +277,9 @@ static void afs_reap_server(struct work_struct *work) | |||
277 | LIST_HEAD(corpses); | 277 | LIST_HEAD(corpses); |
278 | struct afs_server *server; | 278 | struct afs_server *server; |
279 | unsigned long delay, expiry; | 279 | unsigned long delay, expiry; |
280 | time_t now; | 280 | time64_t now; |
281 | 281 | ||
282 | now = get_seconds(); | 282 | now = ktime_get_real_seconds(); |
283 | spin_lock(&afs_server_graveyard_lock); | 283 | spin_lock(&afs_server_graveyard_lock); |
284 | 284 | ||
285 | while (!list_empty(&afs_server_graveyard)) { | 285 | while (!list_empty(&afs_server_graveyard)) { |
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index d7d8dd8c0b31..37b7c3b342a6 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c | |||
@@ -340,7 +340,8 @@ static void afs_vlocation_queue_for_updates(struct afs_vlocation *vl) | |||
340 | struct afs_vlocation *xvl; | 340 | struct afs_vlocation *xvl; |
341 | 341 | ||
342 | /* wait at least 10 minutes before updating... */ | 342 | /* wait at least 10 minutes before updating... */ |
343 | vl->update_at = get_seconds() + afs_vlocation_update_timeout; | 343 | vl->update_at = ktime_get_real_seconds() + |
344 | afs_vlocation_update_timeout; | ||
344 | 345 | ||
345 | spin_lock(&afs_vlocation_updates_lock); | 346 | spin_lock(&afs_vlocation_updates_lock); |
346 | 347 | ||
@@ -506,7 +507,7 @@ void afs_put_vlocation(struct afs_vlocation *vl) | |||
506 | if (atomic_read(&vl->usage) == 0) { | 507 | if (atomic_read(&vl->usage) == 0) { |
507 | _debug("buried"); | 508 | _debug("buried"); |
508 | list_move_tail(&vl->grave, &afs_vlocation_graveyard); | 509 | list_move_tail(&vl->grave, &afs_vlocation_graveyard); |
509 | vl->time_of_death = get_seconds(); | 510 | vl->time_of_death = ktime_get_real_seconds(); |
510 | queue_delayed_work(afs_wq, &afs_vlocation_reap, | 511 | queue_delayed_work(afs_wq, &afs_vlocation_reap, |
511 | afs_vlocation_timeout * HZ); | 512 | afs_vlocation_timeout * HZ); |
512 | 513 | ||
@@ -543,11 +544,11 @@ static void afs_vlocation_reaper(struct work_struct *work) | |||
543 | LIST_HEAD(corpses); | 544 | LIST_HEAD(corpses); |
544 | struct afs_vlocation *vl; | 545 | struct afs_vlocation *vl; |
545 | unsigned long delay, expiry; | 546 | unsigned long delay, expiry; |
546 | time_t now; | 547 | time64_t now; |
547 | 548 | ||
548 | _enter(""); | 549 | _enter(""); |
549 | 550 | ||
550 | now = get_seconds(); | 551 | now = ktime_get_real_seconds(); |
551 | spin_lock(&afs_vlocation_graveyard_lock); | 552 | spin_lock(&afs_vlocation_graveyard_lock); |
552 | 553 | ||
553 | while (!list_empty(&afs_vlocation_graveyard)) { | 554 | while (!list_empty(&afs_vlocation_graveyard)) { |
@@ -622,13 +623,13 @@ static void afs_vlocation_updater(struct work_struct *work) | |||
622 | { | 623 | { |
623 | struct afs_cache_vlocation vldb; | 624 | struct afs_cache_vlocation vldb; |
624 | struct afs_vlocation *vl, *xvl; | 625 | struct afs_vlocation *vl, *xvl; |
625 | time_t now; | 626 | time64_t now; |
626 | long timeout; | 627 | long timeout; |
627 | int ret; | 628 | int ret; |
628 | 629 | ||
629 | _enter(""); | 630 | _enter(""); |
630 | 631 | ||
631 | now = get_seconds(); | 632 | now = ktime_get_real_seconds(); |
632 | 633 | ||
633 | /* find a record to update */ | 634 | /* find a record to update */ |
634 | spin_lock(&afs_vlocation_updates_lock); | 635 | spin_lock(&afs_vlocation_updates_lock); |
@@ -684,7 +685,8 @@ static void afs_vlocation_updater(struct work_struct *work) | |||
684 | 685 | ||
685 | /* and then reschedule */ | 686 | /* and then reschedule */ |
686 | _debug("reschedule"); | 687 | _debug("reschedule"); |
687 | vl->update_at = get_seconds() + afs_vlocation_update_timeout; | 688 | vl->update_at = ktime_get_real_seconds() + |
689 | afs_vlocation_update_timeout; | ||
688 | 690 | ||
689 | spin_lock(&afs_vlocation_updates_lock); | 691 | spin_lock(&afs_vlocation_updates_lock); |
690 | 692 | ||
diff --git a/fs/afs/write.c b/fs/afs/write.c index c83c1a0e851f..2d2fccd5044b 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c | |||
@@ -84,10 +84,9 @@ void afs_put_writeback(struct afs_writeback *wb) | |||
84 | * partly or wholly fill a page that's under preparation for writing | 84 | * partly or wholly fill a page that's under preparation for writing |
85 | */ | 85 | */ |
86 | static int afs_fill_page(struct afs_vnode *vnode, struct key *key, | 86 | static int afs_fill_page(struct afs_vnode *vnode, struct key *key, |
87 | loff_t pos, struct page *page) | 87 | loff_t pos, unsigned int len, struct page *page) |
88 | { | 88 | { |
89 | struct afs_read *req; | 89 | struct afs_read *req; |
90 | loff_t i_size; | ||
91 | int ret; | 90 | int ret; |
92 | 91 | ||
93 | _enter(",,%llu", (unsigned long long)pos); | 92 | _enter(",,%llu", (unsigned long long)pos); |
@@ -99,14 +98,10 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key, | |||
99 | 98 | ||
100 | atomic_set(&req->usage, 1); | 99 | atomic_set(&req->usage, 1); |
101 | req->pos = pos; | 100 | req->pos = pos; |
101 | req->len = len; | ||
102 | req->nr_pages = 1; | 102 | req->nr_pages = 1; |
103 | req->pages[0] = page; | 103 | req->pages[0] = page; |
104 | 104 | get_page(page); | |
105 | i_size = i_size_read(&vnode->vfs_inode); | ||
106 | if (pos + PAGE_SIZE > i_size) | ||
107 | req->len = i_size - pos; | ||
108 | else | ||
109 | req->len = PAGE_SIZE; | ||
110 | 105 | ||
111 | ret = afs_vnode_fetch_data(vnode, key, req); | 106 | ret = afs_vnode_fetch_data(vnode, key, req); |
112 | afs_put_read(req); | 107 | afs_put_read(req); |
@@ -159,12 +154,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping, | |||
159 | kfree(candidate); | 154 | kfree(candidate); |
160 | return -ENOMEM; | 155 | return -ENOMEM; |
161 | } | 156 | } |
162 | *pagep = page; | ||
163 | /* page won't leak in error case: it eventually gets cleaned off LRU */ | ||
164 | 157 | ||
165 | if (!PageUptodate(page) && len != PAGE_SIZE) { | 158 | if (!PageUptodate(page) && len != PAGE_SIZE) { |
166 | ret = afs_fill_page(vnode, key, index << PAGE_SHIFT, page); | 159 | ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page); |
167 | if (ret < 0) { | 160 | if (ret < 0) { |
161 | unlock_page(page); | ||
162 | put_page(page); | ||
168 | kfree(candidate); | 163 | kfree(candidate); |
169 | _leave(" = %d [prep]", ret); | 164 | _leave(" = %d [prep]", ret); |
170 | return ret; | 165 | return ret; |
@@ -172,6 +167,9 @@ int afs_write_begin(struct file *file, struct address_space *mapping, | |||
172 | SetPageUptodate(page); | 167 | SetPageUptodate(page); |
173 | } | 168 | } |
174 | 169 | ||
170 | /* page won't leak in error case: it eventually gets cleaned off LRU */ | ||
171 | *pagep = page; | ||
172 | |||
175 | try_again: | 173 | try_again: |
176 | spin_lock(&vnode->writeback_lock); | 174 | spin_lock(&vnode->writeback_lock); |
177 | 175 | ||
@@ -233,7 +231,7 @@ flush_conflicting_wb: | |||
233 | if (wb->state == AFS_WBACK_PENDING) | 231 | if (wb->state == AFS_WBACK_PENDING) |
234 | wb->state = AFS_WBACK_CONFLICTING; | 232 | wb->state = AFS_WBACK_CONFLICTING; |
235 | spin_unlock(&vnode->writeback_lock); | 233 | spin_unlock(&vnode->writeback_lock); |
236 | if (PageDirty(page)) { | 234 | if (clear_page_dirty_for_io(page)) { |
237 | ret = afs_write_back_from_locked_page(wb, page); | 235 | ret = afs_write_back_from_locked_page(wb, page); |
238 | if (ret < 0) { | 236 | if (ret < 0) { |
239 | afs_put_writeback(candidate); | 237 | afs_put_writeback(candidate); |
@@ -257,7 +255,9 @@ int afs_write_end(struct file *file, struct address_space *mapping, | |||
257 | struct page *page, void *fsdata) | 255 | struct page *page, void *fsdata) |
258 | { | 256 | { |
259 | struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); | 257 | struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); |
258 | struct key *key = file->private_data; | ||
260 | loff_t i_size, maybe_i_size; | 259 | loff_t i_size, maybe_i_size; |
260 | int ret; | ||
261 | 261 | ||
262 | _enter("{%x:%u},{%lx}", | 262 | _enter("{%x:%u},{%lx}", |
263 | vnode->fid.vid, vnode->fid.vnode, page->index); | 263 | vnode->fid.vid, vnode->fid.vnode, page->index); |
@@ -273,6 +273,20 @@ int afs_write_end(struct file *file, struct address_space *mapping, | |||
273 | spin_unlock(&vnode->writeback_lock); | 273 | spin_unlock(&vnode->writeback_lock); |
274 | } | 274 | } |
275 | 275 | ||
276 | if (!PageUptodate(page)) { | ||
277 | if (copied < len) { | ||
278 | /* Try and load any missing data from the server. The | ||
279 | * unmarshalling routine will take care of clearing any | ||
280 | * bits that are beyond the EOF. | ||
281 | */ | ||
282 | ret = afs_fill_page(vnode, key, pos + copied, | ||
283 | len - copied, page); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | } | ||
287 | SetPageUptodate(page); | ||
288 | } | ||
289 | |||
276 | set_page_dirty(page); | 290 | set_page_dirty(page); |
277 | if (PageDirty(page)) | 291 | if (PageDirty(page)) |
278 | _debug("dirtied"); | 292 | _debug("dirtied"); |
@@ -307,10 +321,14 @@ static void afs_kill_pages(struct afs_vnode *vnode, bool error, | |||
307 | ASSERTCMP(pv.nr, ==, count); | 321 | ASSERTCMP(pv.nr, ==, count); |
308 | 322 | ||
309 | for (loop = 0; loop < count; loop++) { | 323 | for (loop = 0; loop < count; loop++) { |
310 | ClearPageUptodate(pv.pages[loop]); | 324 | struct page *page = pv.pages[loop]; |
325 | ClearPageUptodate(page); | ||
311 | if (error) | 326 | if (error) |
312 | SetPageError(pv.pages[loop]); | 327 | SetPageError(page); |
313 | end_page_writeback(pv.pages[loop]); | 328 | if (PageWriteback(page)) |
329 | end_page_writeback(page); | ||
330 | if (page->index >= first) | ||
331 | first = page->index + 1; | ||
314 | } | 332 | } |
315 | 333 | ||
316 | __pagevec_release(&pv); | 334 | __pagevec_release(&pv); |
@@ -335,8 +353,6 @@ static int afs_write_back_from_locked_page(struct afs_writeback *wb, | |||
335 | _enter(",%lx", primary_page->index); | 353 | _enter(",%lx", primary_page->index); |
336 | 354 | ||
337 | count = 1; | 355 | count = 1; |
338 | if (!clear_page_dirty_for_io(primary_page)) | ||
339 | BUG(); | ||
340 | if (test_set_page_writeback(primary_page)) | 356 | if (test_set_page_writeback(primary_page)) |
341 | BUG(); | 357 | BUG(); |
342 | 358 | ||
@@ -502,17 +518,17 @@ static int afs_writepages_region(struct address_space *mapping, | |||
502 | */ | 518 | */ |
503 | lock_page(page); | 519 | lock_page(page); |
504 | 520 | ||
505 | if (page->mapping != mapping) { | 521 | if (page->mapping != mapping || !PageDirty(page)) { |
506 | unlock_page(page); | 522 | unlock_page(page); |
507 | put_page(page); | 523 | put_page(page); |
508 | continue; | 524 | continue; |
509 | } | 525 | } |
510 | 526 | ||
511 | if (wbc->sync_mode != WB_SYNC_NONE) | 527 | if (PageWriteback(page)) { |
512 | wait_on_page_writeback(page); | ||
513 | |||
514 | if (PageWriteback(page) || !PageDirty(page)) { | ||
515 | unlock_page(page); | 528 | unlock_page(page); |
529 | if (wbc->sync_mode != WB_SYNC_NONE) | ||
530 | wait_on_page_writeback(page); | ||
531 | put_page(page); | ||
516 | continue; | 532 | continue; |
517 | } | 533 | } |
518 | 534 | ||
@@ -523,6 +539,8 @@ static int afs_writepages_region(struct address_space *mapping, | |||
523 | wb->state = AFS_WBACK_WRITING; | 539 | wb->state = AFS_WBACK_WRITING; |
524 | spin_unlock(&wb->vnode->writeback_lock); | 540 | spin_unlock(&wb->vnode->writeback_lock); |
525 | 541 | ||
542 | if (!clear_page_dirty_for_io(page)) | ||
543 | BUG(); | ||
526 | ret = afs_write_back_from_locked_page(wb, page); | 544 | ret = afs_write_back_from_locked_page(wb, page); |
527 | unlock_page(page); | 545 | unlock_page(page); |
528 | put_page(page); | 546 | put_page(page); |
@@ -746,6 +764,20 @@ out: | |||
746 | } | 764 | } |
747 | 765 | ||
748 | /* | 766 | /* |
767 | * Flush out all outstanding writes on a file opened for writing when it is | ||
768 | * closed. | ||
769 | */ | ||
770 | int afs_flush(struct file *file, fl_owner_t id) | ||
771 | { | ||
772 | _enter(""); | ||
773 | |||
774 | if ((file->f_mode & FMODE_WRITE) == 0) | ||
775 | return 0; | ||
776 | |||
777 | return vfs_fsync(file, 0); | ||
778 | } | ||
779 | |||
780 | /* | ||
749 | * notification that a previously read-only page is about to become writable | 781 | * notification that a previously read-only page is about to become writable |
750 | * - if it returns an error, the caller will deliver a bus error signal | 782 | * - if it returns an error, the caller will deliver a bus error signal |
751 | */ | 783 | */ |