diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 110 |
1 files changed, 62 insertions, 48 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 641f2e47f165..79981d97bc9c 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
| @@ -115,15 +115,24 @@ | |||
| 115 | #include <net/checksum.h> | 115 | #include <net/checksum.h> |
| 116 | #include <linux/security.h> | 116 | #include <linux/security.h> |
| 117 | 117 | ||
| 118 | struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1]; | 118 | struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; |
| 119 | EXPORT_SYMBOL_GPL(unix_socket_table); | 119 | EXPORT_SYMBOL_GPL(unix_socket_table); |
| 120 | DEFINE_SPINLOCK(unix_table_lock); | 120 | DEFINE_SPINLOCK(unix_table_lock); |
| 121 | EXPORT_SYMBOL_GPL(unix_table_lock); | 121 | EXPORT_SYMBOL_GPL(unix_table_lock); |
| 122 | static atomic_long_t unix_nr_socks; | 122 | static atomic_long_t unix_nr_socks; |
| 123 | 123 | ||
| 124 | #define unix_sockets_unbound (&unix_socket_table[UNIX_HASH_SIZE]) | ||
| 125 | 124 | ||
| 126 | #define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash != UNIX_HASH_SIZE) | 125 | static struct hlist_head *unix_sockets_unbound(void *addr) |
| 126 | { | ||
| 127 | unsigned long hash = (unsigned long)addr; | ||
| 128 | |||
| 129 | hash ^= hash >> 16; | ||
| 130 | hash ^= hash >> 8; | ||
| 131 | hash %= UNIX_HASH_SIZE; | ||
| 132 | return &unix_socket_table[UNIX_HASH_SIZE + hash]; | ||
| 133 | } | ||
| 134 | |||
| 135 | #define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash < UNIX_HASH_SIZE) | ||
| 127 | 136 | ||
| 128 | #ifdef CONFIG_SECURITY_NETWORK | 137 | #ifdef CONFIG_SECURITY_NETWORK |
| 129 | static void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb) | 138 | static void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb) |
| @@ -645,7 +654,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock) | |||
| 645 | INIT_LIST_HEAD(&u->link); | 654 | INIT_LIST_HEAD(&u->link); |
| 646 | mutex_init(&u->readlock); /* single task reading lock */ | 655 | mutex_init(&u->readlock); /* single task reading lock */ |
| 647 | init_waitqueue_head(&u->peer_wait); | 656 | init_waitqueue_head(&u->peer_wait); |
| 648 | unix_insert_socket(unix_sockets_unbound, sk); | 657 | unix_insert_socket(unix_sockets_unbound(sk), sk); |
| 649 | out: | 658 | out: |
| 650 | if (sk == NULL) | 659 | if (sk == NULL) |
| 651 | atomic_long_dec(&unix_nr_socks); | 660 | atomic_long_dec(&unix_nr_socks); |
| @@ -2239,47 +2248,54 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, | |||
| 2239 | } | 2248 | } |
| 2240 | 2249 | ||
| 2241 | #ifdef CONFIG_PROC_FS | 2250 | #ifdef CONFIG_PROC_FS |
| 2242 | static struct sock *first_unix_socket(int *i) | 2251 | |
| 2252 | #define BUCKET_SPACE (BITS_PER_LONG - (UNIX_HASH_BITS + 1) - 1) | ||
| 2253 | |||
| 2254 | #define get_bucket(x) ((x) >> BUCKET_SPACE) | ||
| 2255 | #define get_offset(x) ((x) & ((1L << BUCKET_SPACE) - 1)) | ||
| 2256 | #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) | ||
| 2257 | |||
| 2258 | static struct sock *unix_from_bucket(struct seq_file *seq, loff_t *pos) | ||
| 2243 | { | 2259 | { |
| 2244 | for (*i = 0; *i <= UNIX_HASH_SIZE; (*i)++) { | 2260 | unsigned long offset = get_offset(*pos); |
| 2245 | if (!hlist_empty(&unix_socket_table[*i])) | 2261 | unsigned long bucket = get_bucket(*pos); |
| 2246 | return __sk_head(&unix_socket_table[*i]); | 2262 | struct sock *sk; |
| 2263 | unsigned long count = 0; | ||
| 2264 | |||
| 2265 | for (sk = sk_head(&unix_socket_table[bucket]); sk; sk = sk_next(sk)) { | ||
| 2266 | if (sock_net(sk) != seq_file_net(seq)) | ||
| 2267 | continue; | ||
| 2268 | if (++count == offset) | ||
| 2269 | break; | ||
| 2247 | } | 2270 | } |
| 2248 | return NULL; | 2271 | |
| 2272 | return sk; | ||
| 2249 | } | 2273 | } |
| 2250 | 2274 | ||
| 2251 | static struct sock *next_unix_socket(int *i, struct sock *s) | 2275 | static struct sock *unix_next_socket(struct seq_file *seq, |
| 2276 | struct sock *sk, | ||
| 2277 | loff_t *pos) | ||
| 2252 | { | 2278 | { |
| 2253 | struct sock *next = sk_next(s); | 2279 | unsigned long bucket; |
| 2254 | /* More in this chain? */ | 2280 | |
| 2255 | if (next) | 2281 | while (sk > (struct sock *)SEQ_START_TOKEN) { |
| 2256 | return next; | 2282 | sk = sk_next(sk); |
| 2257 | /* Look for next non-empty chain. */ | 2283 | if (!sk) |
| 2258 | for ((*i)++; *i <= UNIX_HASH_SIZE; (*i)++) { | 2284 | goto next_bucket; |
| 2259 | if (!hlist_empty(&unix_socket_table[*i])) | 2285 | if (sock_net(sk) == seq_file_net(seq)) |
| 2260 | return __sk_head(&unix_socket_table[*i]); | 2286 | return sk; |
| 2261 | } | 2287 | } |
| 2262 | return NULL; | ||
| 2263 | } | ||
| 2264 | 2288 | ||
| 2265 | struct unix_iter_state { | 2289 | do { |
| 2266 | struct seq_net_private p; | 2290 | sk = unix_from_bucket(seq, pos); |
| 2267 | int i; | 2291 | if (sk) |
| 2268 | }; | 2292 | return sk; |
| 2269 | 2293 | ||
| 2270 | static struct sock *unix_seq_idx(struct seq_file *seq, loff_t pos) | 2294 | next_bucket: |
| 2271 | { | 2295 | bucket = get_bucket(*pos) + 1; |
| 2272 | struct unix_iter_state *iter = seq->private; | 2296 | *pos = set_bucket_offset(bucket, 1); |
| 2273 | loff_t off = 0; | 2297 | } while (bucket < ARRAY_SIZE(unix_socket_table)); |
| 2274 | struct sock *s; | ||
| 2275 | 2298 | ||
| 2276 | for (s = first_unix_socket(&iter->i); s; s = next_unix_socket(&iter->i, s)) { | ||
| 2277 | if (sock_net(s) != seq_file_net(seq)) | ||
| 2278 | continue; | ||
| 2279 | if (off == pos) | ||
| 2280 | return s; | ||
| 2281 | ++off; | ||
| 2282 | } | ||
| 2283 | return NULL; | 2299 | return NULL; |
| 2284 | } | 2300 | } |
| 2285 | 2301 | ||
| @@ -2287,22 +2303,20 @@ static void *unix_seq_start(struct seq_file *seq, loff_t *pos) | |||
| 2287 | __acquires(unix_table_lock) | 2303 | __acquires(unix_table_lock) |
| 2288 | { | 2304 | { |
| 2289 | spin_lock(&unix_table_lock); | 2305 | spin_lock(&unix_table_lock); |
| 2290 | return *pos ? unix_seq_idx(seq, *pos - 1) : SEQ_START_TOKEN; | 2306 | |
| 2307 | if (!*pos) | ||
| 2308 | return SEQ_START_TOKEN; | ||
| 2309 | |||
| 2310 | if (get_bucket(*pos) >= ARRAY_SIZE(unix_socket_table)) | ||
| 2311 | return NULL; | ||
| 2312 | |||
| 2313 | return unix_next_socket(seq, NULL, pos); | ||
| 2291 | } | 2314 | } |
| 2292 | 2315 | ||
| 2293 | static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 2316 | static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
| 2294 | { | 2317 | { |
| 2295 | struct unix_iter_state *iter = seq->private; | ||
| 2296 | struct sock *sk = v; | ||
| 2297 | ++*pos; | 2318 | ++*pos; |
| 2298 | 2319 | return unix_next_socket(seq, v, pos); | |
| 2299 | if (v == SEQ_START_TOKEN) | ||
| 2300 | sk = first_unix_socket(&iter->i); | ||
| 2301 | else | ||
| 2302 | sk = next_unix_socket(&iter->i, sk); | ||
| 2303 | while (sk && (sock_net(sk) != seq_file_net(seq))) | ||
| 2304 | sk = next_unix_socket(&iter->i, sk); | ||
| 2305 | return sk; | ||
| 2306 | } | 2320 | } |
| 2307 | 2321 | ||
| 2308 | static void unix_seq_stop(struct seq_file *seq, void *v) | 2322 | static void unix_seq_stop(struct seq_file *seq, void *v) |
| @@ -2365,7 +2379,7 @@ static const struct seq_operations unix_seq_ops = { | |||
| 2365 | static int unix_seq_open(struct inode *inode, struct file *file) | 2379 | static int unix_seq_open(struct inode *inode, struct file *file) |
| 2366 | { | 2380 | { |
| 2367 | return seq_open_net(inode, file, &unix_seq_ops, | 2381 | return seq_open_net(inode, file, &unix_seq_ops, |
| 2368 | sizeof(struct unix_iter_state)); | 2382 | sizeof(struct seq_net_private)); |
| 2369 | } | 2383 | } |
| 2370 | 2384 | ||
| 2371 | static const struct file_operations unix_seq_fops = { | 2385 | static const struct file_operations unix_seq_fops = { |
