1 /* PTYFS - file system for Unix98 pseudoterminal slave nodes (/dev/pts) */
3 #include <minix/drivers.h>
4 #include <minix/fsdriver.h>
5 #include <minix/vfsif.h>
7 #include <sys/dirent.h>
12 #define ROOT_INO_NR 1 /* inode number of the root directory */
13 #define BASE_INO_NR 2 /* first inode number for slave nodes */
15 #define GETDENTS_BUF 1024 /* size of the temporary buffer for getdents */
17 static struct node_data root_data
= {
18 .mode
= S_IFDIR
| 0755,
25 * Mount the file system.
28 ptyfs_mount(dev_t __unused dev
, unsigned int flags
,
29 struct fsdriver_node
* root_node
, unsigned int * res_flags
)
32 /* This file system can not be used as a root file system. */
33 if (flags
& REQ_ISROOT
)
36 /* Return the details of the root node. */
37 root_node
->fn_ino_nr
= ROOT_INO_NR
;
38 root_node
->fn_mode
= root_data
.mode
;
39 root_node
->fn_uid
= root_data
.uid
;
40 root_node
->fn_gid
= root_data
.gid
;
41 root_node
->fn_size
= 0;
42 root_node
->fn_dev
= root_data
.dev
;
44 *res_flags
= RES_NOFLAGS
;
50 * Generate the name string of a slave node based on its node number. Return
51 * OK on success, with the null-terminated name stored in the buffer 'name'
52 * which is 'size' bytes in size. Return an error code on failure.
55 make_name(char * name
, size_t size
, node_t index
)
59 if ((r
= snprintf(name
, sizeof(name
), "%u", index
)) < 0)
69 * Parse the name of a slave node as given by a user, and check whether it is a
70 * valid slave node number. A valid slave number is any name that can be
71 * produced by make_name(). Return TRUE if the string was successfully parsed
72 * as a slave node number (which may or may not actually be allocated), with
73 * the number stored in 'indexp'. Return FALSE if the name is not a number.
76 parse_name(const char * name
, node_t
* indexp
)
82 for (p
= name
; *p
; p
++) {
84 if (*p
< '0' || *p
> '9')
87 /* No leading zeroes. */
88 if (p
!= name
&& index
== 0)
92 if (index
* 10 < index
)
95 index
= index
* 10 + *p
- '0';
103 * Look up a name in a directory, yielding a node on success. For a successful
104 * lookup, the given name must either be a single dot, which resolves to the
105 * file system root directory, or the number of an allocated slave node.
108 ptyfs_lookup(ino_t dir_nr
, char * name
, struct fsdriver_node
* node
,
111 struct node_data
*data
;
115 assert(name
[0] != '\0');
117 if (dir_nr
!= ROOT_INO_NR
)
120 if (name
[0] == '.' && name
[1] == '\0') {
121 /* The root directory itself is requested. */
122 ino_nr
= ROOT_INO_NR
;
126 /* Parse the user-provided name, which must be a number. */
127 if (!parse_name(name
, &index
))
130 ino_nr
= BASE_INO_NR
+ index
;
132 /* See if the number is in use, and get its details. */
133 if ((data
= get_node(index
)) == NULL
)
137 node
->fn_ino_nr
= ino_nr
;
138 node
->fn_mode
= data
->mode
;
139 node
->fn_uid
= data
->uid
;
140 node
->fn_gid
= data
->gid
;
142 node
->fn_dev
= data
->dev
;
150 * Enumerate directory contents.
153 ptyfs_getdents(ino_t ino_nr
, struct fsdriver_data
* data
,
154 size_t bytes
, off_t
* posp
)
156 struct fsdriver_dentry fsdentry
;
157 static char buf
[GETDENTS_BUF
];
158 char name
[NAME_MAX
+ 1];
159 struct node_data
*node_data
;
165 if (ino_nr
!= ROOT_INO_NR
)
168 fsdriver_dentry_init(&fsdentry
, data
, bytes
, buf
, sizeof(buf
));
174 strlcpy(name
, (pos
== 0) ? "." : "..", sizeof(name
));
175 ino_nr
= ROOT_INO_NR
;
178 if (pos
- 2 >= get_max_node())
180 index
= (node_t
)(pos
- 2);
182 if ((node_data
= get_node(index
)) == NULL
)
183 continue; /* index not in use */
185 if (make_name(name
, sizeof(name
), index
) != OK
)
186 continue; /* could not generate name string */
187 ino_nr
= BASE_INO_NR
+ index
;
188 type
= IFTODT(node_data
->mode
);
191 if ((r
= fsdriver_dentry_add(&fsdentry
, ino_nr
, name
,
192 strlen(name
), type
)) < 0)
195 break; /* result buffer full */
198 return fsdriver_dentry_finish(&fsdentry
);
202 * Return a pointer to the node data structure for the given inode number, or
203 * NULL if no node exists for the given inode number.
205 static struct node_data
*
206 get_data(ino_t ino_nr
)
210 if (ino_nr
== ROOT_INO_NR
)
213 if (ino_nr
< BASE_INO_NR
|| ino_nr
>= BASE_INO_NR
+ get_max_node())
216 index
= (node_t
)(ino_nr
- BASE_INO_NR
);
218 return get_node(index
);
222 * Change file ownership.
225 ptyfs_chown(ino_t ino_nr
, uid_t uid
, gid_t gid
, mode_t
* mode
)
227 struct node_data
*data
;
229 if ((data
= get_data(ino_nr
)) == NULL
)
234 data
->mode
&= ~(S_ISUID
| S_ISGID
);
245 ptyfs_chmod(ino_t ino_nr
, mode_t
* mode
)
247 struct node_data
*data
;
249 if ((data
= get_data(ino_nr
)) == NULL
)
252 data
->mode
= (data
->mode
& ~ALLPERMS
) | (*mode
& ALLPERMS
);
260 * Return node details.
263 ptyfs_stat(ino_t ino_nr
, struct stat
* buf
)
265 struct node_data
*data
;
267 if ((data
= get_data(ino_nr
)) == NULL
)
270 buf
->st_mode
= data
->mode
;
271 buf
->st_uid
= data
->uid
;
272 buf
->st_gid
= data
->gid
;
273 buf
->st_nlink
= S_ISDIR(data
->mode
) ? 2 : 1;
274 buf
->st_rdev
= data
->dev
;
275 buf
->st_atime
= data
->ctime
;
276 buf
->st_mtime
= data
->ctime
;
277 buf
->st_ctime
= data
->ctime
;
283 * Return file system statistics.
286 ptyfs_statvfs(struct statvfs
* buf
)
289 buf
->f_flag
= ST_NOTRUNC
;
290 buf
->f_namemax
= NAME_MAX
;
296 * Process non-filesystem messages, in particular slave node creation and
297 * deletion requests from the PTY service.
300 ptyfs_other(const message
* m_ptr
, int ipc_status
)
302 char label
[DS_MAX_KEYLEN
];
303 struct node_data data
;
308 * We only accept requests from the service with the label "pty".
309 * More sophisticated access checks are part of future work.
311 if ((r
= ds_retrieve_label_name(label
, m_ptr
->m_source
)) != OK
) {
312 printf("PTYFS: unable to obtain label for %u (%d)\n",
317 if (strcmp(label
, "pty")) {
318 printf("PTYFS: unexpected request %x from %s/%u\n",
319 m_ptr
->m_type
, label
, m_ptr
->m_source
);
323 /* Process the request from PTY. */
324 memset(&m_reply
, 0, sizeof(m_reply
));
326 switch (m_ptr
->m_type
) {
328 memset(&data
, 0, sizeof(data
));
329 data
.dev
= m_ptr
->m_pty_ptyfs_req
.dev
;
330 data
.mode
= m_ptr
->m_pty_ptyfs_req
.mode
;
331 data
.uid
= m_ptr
->m_pty_ptyfs_req
.uid
;
332 data
.gid
= m_ptr
->m_pty_ptyfs_req
.gid
;
333 data
.ctime
= clock_time(NULL
);
335 r
= set_node(m_ptr
->m_pty_ptyfs_req
.index
, &data
);
340 clear_node(m_ptr
->m_pty_ptyfs_req
.index
);
346 r
= make_name(m_reply
.m_ptyfs_pty_name
.name
,
347 sizeof(m_reply
.m_ptyfs_pty_name
.name
),
348 m_ptr
->m_pty_ptyfs_req
.index
);
353 printf("PTYFS: invalid request %x from PTY\n", m_ptr
->m_type
);
358 * Send a reply to the request. In particular slave node addition
359 * requests must be blocking for the PTY service, so as to avoid race
360 * conditions between PTYFS creating the slave node and userland trying
365 if (IPC_STATUS_CALL(ipc_status
) == SENDREC
)
366 r
= ipc_sendnb(m_ptr
->m_source
, &m_reply
);
368 r
= asynsend3(m_ptr
->m_source
, &m_reply
, AMF_NOREPLY
);
371 printf("PTYFS: unable to reply to PTY (%d)\n", r
);
375 * Initialize the service.
378 ptyfs_init(int __unused type
, sef_init_info_t
* __unused info
)
383 root_data
.ctime
= clock_time(NULL
);
389 * Process an incoming signal.
392 ptyfs_signal(int sig
)
396 fsdriver_terminate();
400 * Perform SEF initialization.
406 sef_setcb_init_fresh(ptyfs_init
);
407 sef_setcb_signal_handler(ptyfs_signal
);
411 static struct fsdriver ptyfs_table
= {
412 .fdr_mount
= ptyfs_mount
,
413 .fdr_lookup
= ptyfs_lookup
,
414 .fdr_getdents
= ptyfs_getdents
,
415 .fdr_stat
= ptyfs_stat
,
416 .fdr_chown
= ptyfs_chown
,
417 .fdr_chmod
= ptyfs_chmod
,
418 .fdr_statvfs
= ptyfs_statvfs
,
419 .fdr_other
= ptyfs_other
431 fsdriver_task(&ptyfs_table
);