ソケットスペシャルファイル
sockfd_lookup_light()でfdからfile->private_dataのsockを取得し、sockコールバックバック.bindがコールされ、ソケットファイルが作成されます。
socketをソケットファイルinode番号をlist headとするunix_socket_table[]のインデックスにリストし、他プロセスがこのinode番号を介してsockの取得を可能とします。
なお、umyaddr->patname[0]=NULLまたはaddrlen=sizeof(short)の時、ソケットスペシャルファイルを作成する事なく、addrlen長でのumyaddrのチェックサム値をinode番号に代わるunix_socket_table[]のインデックスとします。
また、addr_len == sizeof(short)ならunix_autobind()がコールされ、 sprintf(addr->name->sun_path+1, "%05x", ordernum++)とする、ハッシュとなります。addr->name->sun_path+1はaddr->name->sun_path[0]=NULLです。
umyaddrをカーネルメモリに転送し、そのアドレスをsock->sk->addrに設定されます。getsockname()システムコールはsock->sk->addrを取得し、子プロセスとUNIX ドメインソケットを介してやり取りする場合、ソケットファイルを作成することなく実装できます。
動的にファイル名が作成され、uaddr->sun_path[0]=NULLの時、addr_lenとするuaddrをチェックサム値をhashとします。
そうでない場合、ソケットファイルが作成されます。kern_path_create()でソケットファイルの親ディレクトリpathを取得し、S_IFSOCKとするvfs_mknod()でソケットスペシャルファイルを作成、そのinode番号をインデックスとしてunix_socket_table[]をヘッドとするリストに登録します。
unix_find_other()でstruct sockaddr *addrのソケット(サーバサイド)otherを取得し、unix_peer(sk) = otherでsk->peer=otherとし、両ソケットを接続します。
mknodシステムコールもvfs_mknod()でスペシャルファイルが作成されるため、mknodシステムコールからでもソケットファイルは作成可能ですが、vfs_mknod()をユーザ/カーネル両空間からコールする故、mknodシステムコールでソケットファイルの作成は実用故でありません。
socketをソケットファイルinode番号をlist headとするunix_socket_table[]のインデックスにリストし、他プロセスがこのinode番号を介してsockの取得を可能とします。
なお、umyaddr->patname[0]=NULLまたはaddrlen=sizeof(short)の時、ソケットスペシャルファイルを作成する事なく、addrlen長でのumyaddrのチェックサム値をinode番号に代わるunix_socket_table[]のインデックスとします。
umyaddr->patname[0]=NUL; strcpy(&umyaddr->patname[1], "babakaka"); addrlen= strlen("babakaka") + 1 + sizeof(short);とするstruct sockaddrでbind/connectすると、ソケットファイルが作成される事なく、UNIXドメインソケットが利用できます。
また、addr_len == sizeof(short)ならunix_autobind()がコールされ、 sprintf(addr->name->sun_path+1, "%05x", ordernum++)とする、ハッシュとなります。addr->name->sun_path+1はaddr->name->sun_path[0]=NULLです。
umyaddrをカーネルメモリに転送し、そのアドレスをsock->sk->addrに設定されます。getsockname()システムコールはsock->sk->addrを取得し、子プロセスとUNIX ドメインソケットを介してやり取りする場合、ソケットファイルを作成することなく実装できます。
SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen) { struct socket *sock; struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address); if (err >= 0) { err = security_socket_bind(sock, (struct sockaddr *)&address, addrlen); if (!err) err = sock->ops->bind(sock, (struct sockaddr *) &address, addrlen); } fput_light(sock->file, fput_needed); } return err; }unix_bind()はUNIXドメインソケットの.bindコールバックです。addr_len == sizeof(short)の時、unix_autobind()で
動的にファイル名が作成され、uaddr->sun_path[0]=NULLの時、addr_lenとするuaddrをチェックサム値をhashとします。
そうでない場合、ソケットファイルが作成されます。kern_path_create()でソケットファイルの親ディレクトリpathを取得し、S_IFSOCKとするvfs_mknod()でソケットスペシャルファイルを作成、そのinode番号をインデックスとしてunix_socket_table[]をヘッドとするリストに登録します。
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr; char *sun_path = sunaddr->sun_path; struct dentry *dentry = NULL; struct path path; int err; unsigned hash; struct unix_address *addr; struct hlist_head *list; err = -EINVAL; if (sunaddr->sun_family != AF_UNIX) goto out; if (addr_len == sizeof(short)) { err = unix_autobind(sock); goto out; } err = unix_mkname(sunaddr, addr_len, &hash); if (err < 0) goto out; addr_len = err; mutex_lock(&u->readlock); err = -EINVAL; if (u->addr) goto out_up; err = -ENOMEM; addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL); if (!addr) goto out_up; memcpy(addr->name, sunaddr, addr_len); addr->len = addr_len; addr->hash = hash ^ sk->sk_type; atomic_set(&addr->refcnt, 1); if (sun_path[0]) { umode_t mode; err = 0; dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_mknod_parent; mode = S_IFSOCK | (SOCK_INODE(sock)->i_mode & ~current_umask()); err = mnt_want_write(path.mnt); if (err) goto out_mknod_dput; err = security_path_mknod(&path, dentry, mode, 0); if (err) goto out_mknod_drop_write; err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0); out_mknod_drop_write: mnt_drop_write(path.mnt); if (err) goto out_mknod_dput; mutex_unlock(&path.dentry->d_inode->i_mutex); dput(path.dentry); path.dentry = dentry; addr->hash = UNIX_HASH_SIZE; } spin_lock(&unix_table_lock); if (!sun_path[0]) { err = -EADDRINUSE; if (__unix_find_socket_byname(net, sunaddr, addr_len, sk->sk_type, hash)) { unix_release_addr(addr); goto out_unlock; } list = &unix_socket_table[addr->hash]; } else { list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)]; u->dentry = path.dentry; u->mnt = path.mnt; } err = 0; __unix_remove_socket(sk); u->addr = addr; __unix_insert_socket(list, sk); out_unlock: spin_unlock(&unix_table_lock); out_up: mutex_unlock(&u->readlock); out: return err; out_mknod_dput: dput(dentry); mutex_unlock(&path.dentry->d_inode->i_mutex); path_put(&path); out_mknod_parent: if (err == -EEXIST) err = -EADDRINUSE; unix_release_addr(addr); goto out_up; }connectシステムコールは、ソケットの.connectコールバックがコールされ、unix_dgram_connect()はソケットタイプがSOCK_DGRAMの時は、unix_dgram_connect()がコールされます。
unix_find_other()でstruct sockaddr *addrのソケット(サーバサイド)otherを取得し、unix_peer(sk) = otherでsk->peer=otherとし、両ソケットを接続します。
static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr; struct sock *other; unsigned hash; int err; if (addr->sa_family != AF_UNSPEC) { err = unix_mkname(sunaddr, alen, &hash); if (err < 0) goto out; alen = err; if (test_bit(SOCK_PASSCRED, &sock->flags) && !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0) goto out; restart: other = unix_find_other(net, sunaddr, alen, sock->type, hash, &err); if (!other) goto out; unix_state_double_lock(sk, other); if (sock_flag(other, SOCK_DEAD)) { unix_state_double_unlock(sk, other); sock_put(other); goto restart; } err = -EPERM; if (!unix_may_send(sk, other)) goto out_unlock; err = security_unix_may_send(sk->sk_socket, other->sk_socket); if (err) goto out_unlock; } else { other = NULL; unix_state_double_lock(sk, other); } if (unix_peer(sk)) { struct sock *old_peer = unix_peer(sk); unix_peer(sk) = other; unix_state_double_unlock(sk, other); if (other != old_peer) unix_dgram_disconnected(sk, old_peer); sock_put(old_peer); } else { unix_peer(sk) = other; unix_state_double_unlock(sk, other); } return 0; out_unlock: unix_state_double_unlock(sk, other); sock_put(other); out: return err; }unix_find_other()からコールされ、ソケットファイルinodeでのsock取得で、unix_socket_table[]リストのsock->sk->dentryのinodeと一致するsockを取得します。
static struct sock *unix_find_socket_byinode(struct inode *i) { struct sock *s; struct hlist_node *node; spin_lock(&unix_table_lock); sk_for_each(s, node, &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) { struct dentry *dentry = unix_sk(s)->dentry; if (dentry && dentry->d_inode == i) { sock_hold(s); goto found; } } s = NULL; found: spin_unlock(&unix_table_lock); return s; }ハッシュによるsock取得は、unix_socket_table[]リストのsock->sk->addr->nameとstruct sockaddr_un *sunnameの一致するsockを取得します。
static struct sock *__unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, int len, int type, unsigned hash) { struct sock *s; struct hlist_node *node; sk_for_each(s, node, &unix_socket_table[hash ^ type]) { struct unix_sock *u = unix_sk(s); if (!net_eq(sock_net(s), net)) continue; if (u->addr->len == len && !memcmp(u->addr->name, sunname, len)) goto found; } s = NULL; found: return s; } static inline struct sock *unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, int len, int type, unsigned hash) { struct sock *s; spin_lock(&unix_table_lock); s = __unix_find_socket_byname(net, sunname, len, type, hash); if (s) sock_hold(s); spin_unlock(&unix_table_lock); return s; }
補足
ソケットファイル作成は、 mode | S_IFSOCKとし、vfs_mknod()で作成されます。作成する親ディレクトリ.mknodコールバックがコールされ、ファイルシステム依存したinodeを取得し、init_special_inode()でinode->i_fop = &bad_sock_fopsとします。bad_sock_fopsの.openコールバックはエラーとし、ファイルとしての機能を有しません。mknodシステムコールもvfs_mknod()でスペシャルファイルが作成されるため、mknodシステムコールからでもソケットファイルは作成可能ですが、vfs_mknod()をユーザ/カーネル両空間からコールする故、mknodシステムコールでソケットファイルの作成は実用故でありません。
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { int error = may_create(dir, dentry); if (error) return error; if ((S_ISCHR(mode) || S_ISBLK(mode)) && !ns_capable(inode_userns(dir), CAP_MKNOD)) return -EPERM; if (!dir->i_op->mknod) return -EPERM; error = devcgroup_inode_mknod(mode, dev); if (error) return error; error = security_inode_mknod(dir, dentry, mode, dev); if (error) return error; error = dir->i_op->mknod(dir, dentry, mode, dev); if (!error) fsnotify_create(dir, dentry); return error; } const struct file_operations bad_sock_fops = { .owner = THIS_MODULE, .open = sock_no_open, .llseek = noop_llseek, }; static int sock_no_open(struct inode *irrelevant, struct file *dontcare) { return -ENXIO; } void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { inode->i_fop = &def_chr_fops; inode->i_rdev = rdev; } else if (S_ISBLK(mode)) { inode->i_fop = &def_blk_fops; inode->i_rdev = rdev; } else if (S_ISFIFO(mode)) inode->i_fop = &def_fifo_fops; else if (S_ISSOCK(mode)) inode->i_fop = &bad_sock_fops; else printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for" " inode %s:%lu\n", mode, inode->i_sb->s_id, inode->i_ino); }