diff options
author | David S. Miller <davem@davemloft.net> | 2006-03-20 20:13:49 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-03-20 20:13:49 -0500 |
commit | 39d8c1b6fbaeb8d6adec4a8c08365cc9eaca6ae4 (patch) | |
tree | e604ee025c6711caca1bf6ec2a406197bf5be843 /net/socket.c | |
parent | 77d2ca350018c507815f5d38a40ffb597eb9ae25 (diff) |
[NET]: Do not lose accepted socket when -ENFILE/-EMFILE.
Try to allocate the struct file and an unused file
descriptor before we try to pull a newly accepted
socket out of the protocol layer.
Based upon a patch by Prassana Meda.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/socket.c')
-rw-r--r-- | net/socket.c | 113 |
1 files changed, 71 insertions, 42 deletions
diff --git a/net/socket.c b/net/socket.c index 7e1bdef8b09e..74283610db15 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -348,8 +348,8 @@ static struct dentry_operations sockfs_dentry_operations = { | |||
348 | /* | 348 | /* |
349 | * Obtains the first available file descriptor and sets it up for use. | 349 | * Obtains the first available file descriptor and sets it up for use. |
350 | * | 350 | * |
351 | * This function creates file structure and maps it to fd space | 351 | * These functions create file structures and maps them to fd space |
352 | * of current process. On success it returns file descriptor | 352 | * of the current process. On success it returns file descriptor |
353 | * and file struct implicitly stored in sock->file. | 353 | * and file struct implicitly stored in sock->file. |
354 | * Note that another thread may close file descriptor before we return | 354 | * Note that another thread may close file descriptor before we return |
355 | * from this function. We use the fact that now we do not refer | 355 | * from this function. We use the fact that now we do not refer |
@@ -362,52 +362,67 @@ static struct dentry_operations sockfs_dentry_operations = { | |||
362 | * but we take care of internal coherence yet. | 362 | * but we take care of internal coherence yet. |
363 | */ | 363 | */ |
364 | 364 | ||
365 | int sock_map_fd(struct socket *sock) | 365 | static int sock_alloc_fd(struct file **filep) |
366 | { | 366 | { |
367 | int fd; | 367 | int fd; |
368 | struct qstr this; | ||
369 | char name[32]; | ||
370 | |||
371 | /* | ||
372 | * Find a file descriptor suitable for return to the user. | ||
373 | */ | ||
374 | 368 | ||
375 | fd = get_unused_fd(); | 369 | fd = get_unused_fd(); |
376 | if (fd >= 0) { | 370 | if (likely(fd >= 0)) { |
377 | struct file *file = get_empty_filp(); | 371 | struct file *file = get_empty_filp(); |
378 | 372 | ||
379 | if (!file) { | 373 | *filep = file; |
374 | if (unlikely(!file)) { | ||
380 | put_unused_fd(fd); | 375 | put_unused_fd(fd); |
381 | fd = -ENFILE; | 376 | return -ENFILE; |
382 | goto out; | ||
383 | } | 377 | } |
378 | } else | ||
379 | *filep = NULL; | ||
380 | return fd; | ||
381 | } | ||
384 | 382 | ||
385 | this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino); | 383 | static int sock_attach_fd(struct socket *sock, struct file *file) |
386 | this.name = name; | 384 | { |
387 | this.hash = SOCK_INODE(sock)->i_ino; | 385 | struct qstr this; |
386 | char name[32]; | ||
387 | |||
388 | this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino); | ||
389 | this.name = name; | ||
390 | this.hash = SOCK_INODE(sock)->i_ino; | ||
391 | |||
392 | file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); | ||
393 | if (unlikely(!file->f_dentry)) | ||
394 | return -ENOMEM; | ||
395 | |||
396 | file->f_dentry->d_op = &sockfs_dentry_operations; | ||
397 | d_add(file->f_dentry, SOCK_INODE(sock)); | ||
398 | file->f_vfsmnt = mntget(sock_mnt); | ||
399 | file->f_mapping = file->f_dentry->d_inode->i_mapping; | ||
400 | |||
401 | sock->file = file; | ||
402 | file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops; | ||
403 | file->f_mode = FMODE_READ | FMODE_WRITE; | ||
404 | file->f_flags = O_RDWR; | ||
405 | file->f_pos = 0; | ||
406 | file->private_data = sock; | ||
388 | 407 | ||
389 | file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); | 408 | return 0; |
390 | if (!file->f_dentry) { | 409 | } |
391 | put_filp(file); | 410 | |
411 | int sock_map_fd(struct socket *sock) | ||
412 | { | ||
413 | struct file *newfile; | ||
414 | int fd = sock_alloc_fd(&newfile); | ||
415 | |||
416 | if (likely(fd >= 0)) { | ||
417 | int err = sock_attach_fd(sock, newfile); | ||
418 | |||
419 | if (unlikely(err < 0)) { | ||
420 | put_filp(newfile); | ||
392 | put_unused_fd(fd); | 421 | put_unused_fd(fd); |
393 | fd = -ENOMEM; | 422 | return err; |
394 | goto out; | ||
395 | } | 423 | } |
396 | file->f_dentry->d_op = &sockfs_dentry_operations; | 424 | fd_install(fd, newfile); |
397 | d_add(file->f_dentry, SOCK_INODE(sock)); | ||
398 | file->f_vfsmnt = mntget(sock_mnt); | ||
399 | file->f_mapping = file->f_dentry->d_inode->i_mapping; | ||
400 | |||
401 | sock->file = file; | ||
402 | file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops; | ||
403 | file->f_mode = FMODE_READ | FMODE_WRITE; | ||
404 | file->f_flags = O_RDWR; | ||
405 | file->f_pos = 0; | ||
406 | file->private_data = sock; | ||
407 | fd_install(fd, file); | ||
408 | } | 425 | } |
409 | |||
410 | out: | ||
411 | return fd; | 426 | return fd; |
412 | } | 427 | } |
413 | 428 | ||
@@ -1349,7 +1364,8 @@ asmlinkage long sys_listen(int fd, int backlog) | |||
1349 | asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) | 1364 | asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) |
1350 | { | 1365 | { |
1351 | struct socket *sock, *newsock; | 1366 | struct socket *sock, *newsock; |
1352 | int err, len; | 1367 | struct file *newfile; |
1368 | int err, len, newfd; | ||
1353 | char address[MAX_SOCK_ADDR]; | 1369 | char address[MAX_SOCK_ADDR]; |
1354 | 1370 | ||
1355 | sock = sockfd_lookup(fd, &err); | 1371 | sock = sockfd_lookup(fd, &err); |
@@ -1369,28 +1385,38 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _ | |||
1369 | */ | 1385 | */ |
1370 | __module_get(newsock->ops->owner); | 1386 | __module_get(newsock->ops->owner); |
1371 | 1387 | ||
1388 | newfd = sock_alloc_fd(&newfile); | ||
1389 | if (unlikely(newfd < 0)) { | ||
1390 | err = newfd; | ||
1391 | goto out_release; | ||
1392 | } | ||
1393 | |||
1394 | err = sock_attach_fd(newsock, newfile); | ||
1395 | if (err < 0) | ||
1396 | goto out_fd; | ||
1397 | |||
1372 | err = security_socket_accept(sock, newsock); | 1398 | err = security_socket_accept(sock, newsock); |
1373 | if (err) | 1399 | if (err) |
1374 | goto out_release; | 1400 | goto out_fd; |
1375 | 1401 | ||
1376 | err = sock->ops->accept(sock, newsock, sock->file->f_flags); | 1402 | err = sock->ops->accept(sock, newsock, sock->file->f_flags); |
1377 | if (err < 0) | 1403 | if (err < 0) |
1378 | goto out_release; | 1404 | goto out_fd; |
1379 | 1405 | ||
1380 | if (upeer_sockaddr) { | 1406 | if (upeer_sockaddr) { |
1381 | if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) { | 1407 | if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) { |
1382 | err = -ECONNABORTED; | 1408 | err = -ECONNABORTED; |
1383 | goto out_release; | 1409 | goto out_fd; |
1384 | } | 1410 | } |
1385 | err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen); | 1411 | err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen); |
1386 | if (err < 0) | 1412 | if (err < 0) |
1387 | goto out_release; | 1413 | goto out_fd; |
1388 | } | 1414 | } |
1389 | 1415 | ||
1390 | /* File flags are not inherited via accept() unlike another OSes. */ | 1416 | /* File flags are not inherited via accept() unlike another OSes. */ |
1391 | 1417 | ||
1392 | if ((err = sock_map_fd(newsock)) < 0) | 1418 | fd_install(newfd, newfile); |
1393 | goto out_release; | 1419 | err = newfd; |
1394 | 1420 | ||
1395 | security_socket_post_accept(sock, newsock); | 1421 | security_socket_post_accept(sock, newsock); |
1396 | 1422 | ||
@@ -1398,6 +1424,9 @@ out_put: | |||
1398 | sockfd_put(sock); | 1424 | sockfd_put(sock); |
1399 | out: | 1425 | out: |
1400 | return err; | 1426 | return err; |
1427 | out_fd: | ||
1428 | put_filp(newfile); | ||
1429 | put_unused_fd(newfd); | ||
1401 | out_release: | 1430 | out_release: |
1402 | sock_release(newsock); | 1431 | sock_release(newsock); |
1403 | goto out_put; | 1432 | goto out_put; |