-
Notifications
You must be signed in to change notification settings - Fork 0
/
mfs_client.c
314 lines (258 loc) · 5.7 KB
/
mfs_client.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include "mfs_client.h"
#include "mfs_imap.h"
#define MFS_DFT_PORT 143
/**
* -------------------------------------
* File handling network operations
* -------------------------------------
*/
/**
* Close a connected socket
*/
static inline void client_close_socket(struct mfs_client *clt)
{
if(clt->cs != NULL)
sock_release(clt->cs);
clt->cs = NULL;
}
/**
* Create a new socket and connect it
*/
static inline struct socket *client_open_socket(struct mfs_client *clt)
{
struct socket *cs;
int e = 0;
/**
* XXX should I use sock_create() instead ?
*/
e = __sock_create(read_pnet(¤t->nsproxy->net_ns), PF_INET,
SOCK_STREAM, IPPROTO_TCP, &cs, 1);
if(e != 0)
goto err;
e = cs->ops->connect(cs, (struct sockaddr *)&clt->sin,
sizeof(clt->sin), 0);
if(e < 0)
goto sockrelease;
return cs;
sockrelease:
sock_release(cs);
err:
return ERR_PTR(e);
}
static int mfs_client_parse_opt(struct mfs_client *clt, char *opt)
{
char *p;
unsigned short port;
int ret;
clt->opt.port = MFS_DFT_PORT;
if(opt == NULL)
return 0;
p = strstr(opt, "port=");
if(p != NULL) {
ret = sscanf(p, "port=%hu", &port);
if(ret == 1)
clt->opt.port = port;
}
return 0;
}
/**
* Init a network session with server
*/
int mfs_client_init_session(struct mfs_client *clt, char const *addr,
char *data)
{
struct sockaddr_in *sin = &clt->sin;
struct socket *cs;
int e;
e = mfs_client_parse_opt(clt, data);
if(e != 0)
goto err;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = in_aton(addr);
sin->sin_port = htons(clt->opt.port);
cs = client_open_socket(clt);
if(IS_ERR_OR_NULL(cs)) {
e = PTR_ERR(cs);
goto err;
}
clt->cs = cs;
clt->ops = &imapops;
e = clt->ops->connect(clt, data);
if(e < 0)
goto err;
return 0;
err:
return e;
}
/**
* Close network session
*/
void mfs_client_close_session(struct mfs_client *clt)
{
if(clt->ops)
clt->ops->close(clt);
client_close_socket(clt);
}
/**
* Restart network session
*/
int mfs_client_restart_session(struct mfs_client *clt)
{
struct socket *cs = NULL;
int e;
client_close_socket(clt);
cs = client_open_socket(clt);
if(IS_ERR_OR_NULL(cs)) {
e = PTR_ERR(cs);
goto err;
}
clt->cs = cs;
clt->ops = &imapops;
e = clt->ops->reconnect(clt);
if(e < 0)
goto closesock;
return 0;
closesock:
client_close_socket(clt);
err:
return e;
}
/**
* Associate a superblock with client
*/
void mfs_client_set_sb(struct mfs_client *clt, struct super_block *sb)
{
clt->sb = sb;
}
/**
* Receive a network message through session
* XXX For userspace buffer
*/
ssize_t mfs_client_net_recv(struct mfs_client *clt, char __user *buf,
size_t size)
{
/**
* Struct iovec is for userspace buffers. If it were for kernel space
* buffer, struct kvec would have been used as well as kernel_recvmsg()
* instread of socket_recvmsg().
*/
struct iovec iov = {
.iov_base = buf,
.iov_len = size,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
return sock_recvmsg(clt->cs, &msg, size, 0);
}
/**
* Receive a network message through session
* XXX For kernel space buffer
*/
ssize_t mfs_client_kernel_net_recv(struct mfs_client *clt, char *buf,
size_t size)
{
struct msghdr msg = {
.msg_flags = MSG_NOSIGNAL
};
struct kvec iov = {
.iov_base = buf,
.iov_len = size,
};
return kernel_recvmsg(clt->cs, &msg, &iov, 1, size, msg.msg_flags);
}
/**
* Generic function called to read from a file
*/
ssize_t mfs_client_read(struct mfs_client *clt, struct file *f,
char __user *buf, size_t size, loff_t off)
{
struct inode *i = file_inode(f);
if(i->i_private == NULL)
return mfs_client_net_recv(clt, buf, size);
return clt->ops->read(clt, f, i->i_private, buf, size, off);
}
/**
* Send a message through client session
* XXX For userspace buffer
*/
ssize_t mfs_client_net_send(struct mfs_client *clt, const char __user *buf,
size_t size)
{
/**
* Struct iovec is for userspace buffers. If it were for kernel space
* buffer, struct kvec would have been used as well as kernel_sendmsg()
* instread of socket_sendmsg().
*/
struct iovec iov = {
.iov_base = (void __user *)buf,
.iov_len = size,
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
};
return sock_sendmsg(clt->cs, &msg, size);
}
/**
* Send a message through client session
* XXX For kernelspace buffer
*/
ssize_t mfs_client_kernel_net_send(struct mfs_client *clt, const char *buf,
size_t size)
{
struct msghdr msg = {
.msg_flags = MSG_NOSIGNAL
};
struct kvec iov = {
.iov_base = (void *)buf,
.iov_len = size,
};
return kernel_sendmsg(clt->cs, &msg, &iov, 1, size);
}
/**
* Generic function called to write into a file
*/
ssize_t mfs_client_write(struct mfs_client *clt, struct file *f,
const char __user *buf, size_t size)
{
struct inode *i = file_inode(f);
if(i->i_private == NULL)
return mfs_client_net_send(clt, buf, size);
return clt->ops->write(clt, f, i->i_private, buf, size);
}
/**
* Wait for socket to have message to be read
*/
int mfs_client_kernel_wait_recv(struct mfs_client *clt, long timeout)
{
char b;
ssize_t ret;
long timeo_old;
struct msghdr msg = {
.msg_flags = MSG_NOSIGNAL | MSG_PEEK
};
struct kvec iov = {
.iov_base = &b,
.iov_len = 1,
};
timeo_old = clt->cs->sk->sk_rcvtimeo;
clt->cs->sk->sk_rcvtimeo = timeout;
ret = kernel_recvmsg(clt->cs, &msg, &iov, 1, 1, msg.msg_flags);
clt->cs->sk->sk_rcvtimeo = timeo_old;
return ret;
}
int mfs_client_readdir(struct mfs_client *clt, struct file *f,
void *dirent, filldir_t filldir)
{
struct inode *i = file_inode(f);
if(i->i_private == NULL)
return -EINVAL;
return clt->ops->readdir(clt, f, i->i_private, dirent, filldir);
}