ソケットスペシャルファイル
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);
}





