sysctl_binary.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #include <linux/stat.h>
  2. #include <linux/sysctl.h>
  3. #include "../fs/xfs/linux-2.6/xfs_sysctl.h"
  4. #include <linux/sunrpc/debug.h>
  5. #include <linux/string.h>
  6. #include <net/ip_vs.h>
  7. #include <linux/syscalls.h>
  8. #include <linux/namei.h>
  9. #include <linux/mount.h>
  10. #include <linux/fs.h>
  11. #include <linux/nsproxy.h>
  12. #include <linux/pid_namespace.h>
  13. #include <linux/file.h>
  14. #include <linux/ctype.h>
  15. #include <linux/smp_lock.h>
  16. #ifdef CONFIG_SYSCTL_SYSCALL
  17. /* Perform the actual read/write of a sysctl table entry. */
  18. static int do_sysctl_strategy(struct ctl_table_root *root,
  19. struct ctl_table *table,
  20. void __user *oldval, size_t __user *oldlenp,
  21. void __user *newval, size_t newlen)
  22. {
  23. int op = 0, rc;
  24. if (oldval)
  25. op |= MAY_READ;
  26. if (newval)
  27. op |= MAY_WRITE;
  28. if (sysctl_perm(root, table, op))
  29. return -EPERM;
  30. if (table->strategy) {
  31. rc = table->strategy(table, oldval, oldlenp, newval, newlen);
  32. if (rc < 0)
  33. return rc;
  34. if (rc > 0)
  35. return 0;
  36. }
  37. /* If there is no strategy routine, or if the strategy returns
  38. * zero, proceed with automatic r/w */
  39. if (table->data && table->maxlen) {
  40. rc = sysctl_data(table, oldval, oldlenp, newval, newlen);
  41. if (rc < 0)
  42. return rc;
  43. }
  44. return 0;
  45. }
  46. static int parse_table(const int *name, int nlen,
  47. void __user *oldval, size_t __user *oldlenp,
  48. void __user *newval, size_t newlen,
  49. struct ctl_table_root *root,
  50. struct ctl_table *table)
  51. {
  52. int n;
  53. repeat:
  54. if (!nlen)
  55. return -ENOTDIR;
  56. n = *name;
  57. for ( ; table->ctl_name || table->procname; table++) {
  58. if (!table->ctl_name)
  59. continue;
  60. if (n == table->ctl_name) {
  61. int error;
  62. if (table->child) {
  63. if (sysctl_perm(root, table, MAY_EXEC))
  64. return -EPERM;
  65. name++;
  66. nlen--;
  67. table = table->child;
  68. goto repeat;
  69. }
  70. error = do_sysctl_strategy(root, table,
  71. oldval, oldlenp,
  72. newval, newlen);
  73. return error;
  74. }
  75. }
  76. return -ENOTDIR;
  77. }
  78. static ssize_t binary_sysctl(const int *name, int nlen,
  79. void __user *oldval, size_t __user *oldlenp,
  80. void __user *newval, size_t newlen)
  81. {
  82. struct ctl_table_header *head;
  83. ssize_t error = -ENOTDIR;
  84. for (head = sysctl_head_next(NULL); head;
  85. head = sysctl_head_next(head)) {
  86. error = parse_table(name, nlen, oldval, oldlenp,
  87. newval, newlen,
  88. head->root, head->ctl_table);
  89. if (error != -ENOTDIR) {
  90. sysctl_head_finish(head);
  91. break;
  92. }
  93. }
  94. return error;
  95. }
  96. #else /* CONFIG_SYSCTL_SYSCALL */
  97. static ssize_t binary_sysctl(const int *ctl_name, int nlen,
  98. void __user *oldval, size_t __user *oldlenp,
  99. void __user *newval, size_t newlen)
  100. {
  101. return -ENOSYS;
  102. }
  103. #endif /* CONFIG_SYSCTL_SYSCALL */
  104. static void deprecated_sysctl_warning(const int *name, int nlen)
  105. {
  106. static int msg_count;
  107. int i;
  108. /* Ignore accesses to kernel.version */
  109. if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
  110. return;
  111. if (msg_count < 5) {
  112. msg_count++;
  113. printk(KERN_INFO
  114. "warning: process `%s' used the deprecated sysctl "
  115. "system call with ", current->comm);
  116. for (i = 0; i < nlen; i++)
  117. printk("%d.", name[i]);
  118. printk("\n");
  119. }
  120. return;
  121. }
  122. static int do_sysctl(int __user *args_name, int nlen,
  123. void __user *oldval, size_t __user *oldlenp,
  124. void __user *newval, size_t newlen)
  125. {
  126. int name[CTL_MAXNAME];
  127. size_t oldlen = 0;
  128. int i;
  129. if (nlen <= 0 || nlen >= CTL_MAXNAME)
  130. return -ENOTDIR;
  131. if (oldval && !oldlenp)
  132. return -EFAULT;
  133. if (oldlenp && get_user(oldlen, oldlenp))
  134. return -EFAULT;
  135. /* Read in the sysctl name for simplicity */
  136. for (i = 0; i < nlen; i++)
  137. if (get_user(name[i], args_name + i))
  138. return -EFAULT;
  139. deprecated_sysctl_warning(name, nlen);
  140. return binary_sysctl(name, nlen, oldval, oldlenp, newval, newlen);
  141. }
  142. SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
  143. {
  144. struct __sysctl_args tmp;
  145. int error;
  146. if (copy_from_user(&tmp, args, sizeof(tmp)))
  147. return -EFAULT;
  148. lock_kernel();
  149. error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
  150. tmp.newval, tmp.newlen);
  151. unlock_kernel();
  152. return error;
  153. }
  154. #ifdef CONFIG_COMPAT
  155. #include <asm/compat.h>
  156. struct compat_sysctl_args {
  157. compat_uptr_t name;
  158. int nlen;
  159. compat_uptr_t oldval;
  160. compat_uptr_t oldlenp;
  161. compat_uptr_t newval;
  162. compat_size_t newlen;
  163. compat_ulong_t __unused[4];
  164. };
  165. asmlinkage long compat_sys_sysctl(struct compat_sysctl_args __user *args)
  166. {
  167. struct compat_sysctl_args tmp;
  168. compat_size_t __user *compat_oldlenp;
  169. size_t __user *oldlenp = NULL;
  170. size_t oldlen = 0;
  171. ssize_t result;
  172. if (copy_from_user(&tmp, args, sizeof(tmp)))
  173. return -EFAULT;
  174. compat_oldlenp = compat_ptr(tmp.oldlenp);
  175. if (compat_oldlenp) {
  176. oldlenp = compat_alloc_user_space(sizeof(*compat_oldlenp));
  177. if (get_user(oldlen, compat_oldlenp) ||
  178. put_user(oldlen, oldlenp))
  179. return -EFAULT;
  180. }
  181. lock_kernel();
  182. result = do_sysctl(compat_ptr(tmp.name), tmp.nlen,
  183. compat_ptr(tmp.oldval), oldlenp,
  184. compat_ptr(tmp.newval), tmp.newlen);
  185. unlock_kernel();
  186. if (oldlenp && !result) {
  187. if (get_user(oldlen, oldlenp) ||
  188. put_user(oldlen, compat_oldlenp))
  189. return -EFAULT;
  190. }
  191. return result;
  192. }
  193. #endif /* CONFIG_COMPAT */