1 /* PTYFS - file system for Unix98 pseudoterminal slave nodes (/dev/pts) */ 2 3 #include <minix/drivers.h> 4 #include <minix/fsdriver.h> 5 #include <minix/vfsif.h> 6 #include <minix/ds.h> 7 #include <sys/dirent.h> 8 #include <assert.h> 9 10 #include "node.h" 11 12 #define ROOT_INO_NR 1 /* inode number of the root directory */ 13 #define BASE_INO_NR 2 /* first inode number for slave nodes */ 14 15 #define GETDENTS_BUF 1024 /* size of the temporary buffer for getdents */ 16 17 static struct node_data root_data = { 18 .mode = S_IFDIR | 0755, 19 .uid = 0, 20 .gid = 0, 21 .dev = NO_DEV 22 }; 23 24 /* 25 * Mount the file system. 26 */ 27 static int 28 ptyfs_mount(dev_t __unused dev, unsigned int flags, 29 struct fsdriver_node * root_node, unsigned int * res_flags) 30 { 31 32 /* This file system can not be used as a root file system. */ 33 if (flags & REQ_ISROOT) 34 return EINVAL; 35 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; 43 44 *res_flags = RES_NOFLAGS; 45 46 return OK; 47 } 48 49 /* 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. 53 */ 54 static int 55 make_name(char * name, size_t size, node_t index) 56 { 57 ssize_t r; 58 59 if ((r = snprintf(name, sizeof(name), "%u", index)) < 0) 60 return EINVAL; 61 62 if (r >= size) 63 return ENAMETOOLONG; 64 65 return OK; 66 } 67 68 /* 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. 74 */ 75 static int 76 parse_name(const char * name, node_t * indexp) 77 { 78 node_t index; 79 const char *p; 80 81 index = 0; 82 for (p = name; *p; p++) { 83 /* Digits only. */ 84 if (*p < '0' || *p > '9') 85 return FALSE; 86 87 /* No leading zeroes. */ 88 if (p != name && index == 0) 89 return FALSE; 90 91 /* No overflow. */ 92 if (index * 10 < index) 93 return FALSE; 94 95 index = index * 10 + *p - '0'; 96 } 97 98 *indexp = index; 99 return TRUE; 100 } 101 102 /* 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. 106 */ 107 static int 108 ptyfs_lookup(ino_t dir_nr, char * name, struct fsdriver_node * node, 109 int * is_mountpt) 110 { 111 struct node_data *data; 112 node_t index; 113 ino_t ino_nr; 114 115 assert(name[0] != '\0'); 116 117 if (dir_nr != ROOT_INO_NR) 118 return ENOENT; 119 120 if (name[0] == '.' && name[1] == '\0') { 121 /* The root directory itself is requested. */ 122 ino_nr = ROOT_INO_NR; 123 124 data = &root_data; 125 } else { 126 /* Parse the user-provided name, which must be a number. */ 127 if (!parse_name(name, &index)) 128 return ENOENT; 129 130 ino_nr = BASE_INO_NR + index; 131 132 /* See if the number is in use, and get its details. */ 133 if ((data = get_node(index)) == NULL) 134 return ENOENT; 135 } 136 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; 141 node->fn_size = 0; 142 node->fn_dev = data->dev; 143 144 *is_mountpt = FALSE; 145 146 return OK; 147 } 148 149 /* 150 * Enumerate directory contents. 151 */ 152 static ssize_t 153 ptyfs_getdents(ino_t ino_nr, struct fsdriver_data * data, 154 size_t bytes, off_t * posp) 155 { 156 struct fsdriver_dentry fsdentry; 157 static char buf[GETDENTS_BUF]; 158 char name[NAME_MAX + 1]; 159 struct node_data *node_data; 160 unsigned int type; 161 off_t pos; 162 node_t index; 163 ssize_t r; 164 165 if (ino_nr != ROOT_INO_NR) 166 return EINVAL; 167 168 fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); 169 170 for (;;) { 171 pos = (*posp)++; 172 173 if (pos < 2) { 174 strlcpy(name, (pos == 0) ? "." : "..", sizeof(name)); 175 ino_nr = ROOT_INO_NR; 176 type = DT_DIR; 177 } else { 178 if (pos - 2 >= get_max_node()) 179 break; /* EOF */ 180 index = (node_t)(pos - 2); 181 182 if ((node_data = get_node(index)) == NULL) 183 continue; /* index not in use */ 184 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); 189 } 190 191 if ((r = fsdriver_dentry_add(&fsdentry, ino_nr, name, 192 strlen(name), type)) < 0) 193 return r; 194 if (r == 0) 195 break; /* result buffer full */ 196 } 197 198 return fsdriver_dentry_finish(&fsdentry); 199 } 200 201 /* 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. 204 */ 205 static struct node_data * 206 get_data(ino_t ino_nr) 207 { 208 node_t index; 209 210 if (ino_nr == ROOT_INO_NR) 211 return &root_data; 212 213 if (ino_nr < BASE_INO_NR || ino_nr >= BASE_INO_NR + get_max_node()) 214 return NULL; 215 216 index = (node_t)(ino_nr - BASE_INO_NR); 217 218 return get_node(index); 219 } 220 221 /* 222 * Change file ownership. 223 */ 224 static int 225 ptyfs_chown(ino_t ino_nr, uid_t uid, gid_t gid, mode_t * mode) 226 { 227 struct node_data *data; 228 229 if ((data = get_data(ino_nr)) == NULL) 230 return EINVAL; 231 232 data->uid = uid; 233 data->gid = gid; 234 data->mode &= ~(S_ISUID | S_ISGID); 235 236 *mode = data->mode; 237 238 return OK; 239 } 240 241 /* 242 * Change file mode. 243 */ 244 static int 245 ptyfs_chmod(ino_t ino_nr, mode_t * mode) 246 { 247 struct node_data *data; 248 249 if ((data = get_data(ino_nr)) == NULL) 250 return EINVAL; 251 252 data->mode = (data->mode & ~ALLPERMS) | (*mode & ALLPERMS); 253 254 *mode = data->mode; 255 256 return OK; 257 } 258 259 /* 260 * Return node details. 261 */ 262 static int 263 ptyfs_stat(ino_t ino_nr, struct stat * buf) 264 { 265 struct node_data *data; 266 267 if ((data = get_data(ino_nr)) == NULL) 268 return EINVAL; 269 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; 278 279 return OK; 280 } 281 282 /* 283 * Return file system statistics. 284 */ 285 static int 286 ptyfs_statvfs(struct statvfs * buf) 287 { 288 289 buf->f_flag = ST_NOTRUNC; 290 buf->f_namemax = NAME_MAX; 291 292 return OK; 293 } 294 295 /* 296 * Process non-filesystem messages, in particular slave node creation and 297 * deletion requests from the PTY service. 298 */ 299 static void 300 ptyfs_other(const message * m_ptr, int ipc_status) 301 { 302 char label[DS_MAX_KEYLEN]; 303 struct node_data data; 304 message m_reply; 305 int r; 306 307 /* 308 * We only accept requests from the service with the label "pty". 309 * More sophisticated access checks are part of future work. 310 */ 311 if ((r = ds_retrieve_label_name(label, m_ptr->m_source)) != OK) { 312 printf("PTYFS: unable to obtain label for %u (%d)\n", 313 m_ptr->m_source, r); 314 return; 315 } 316 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); 320 return; 321 } 322 323 /* Process the request from PTY. */ 324 memset(&m_reply, 0, sizeof(m_reply)); 325 326 switch (m_ptr->m_type) { 327 case PTYFS_SET: 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); 334 335 r = set_node(m_ptr->m_pty_ptyfs_req.index, &data); 336 337 break; 338 339 case PTYFS_CLEAR: 340 clear_node(m_ptr->m_pty_ptyfs_req.index); 341 r = OK; 342 343 break; 344 345 case PTYFS_NAME: 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); 349 350 break; 351 352 default: 353 printf("PTYFS: invalid request %x from PTY\n", m_ptr->m_type); 354 r = ENOSYS; 355 } 356 357 /* 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 361 * to open it. 362 */ 363 m_reply.m_type = r; 364 365 if (IPC_STATUS_CALL(ipc_status) == SENDREC) 366 r = ipc_sendnb(m_ptr->m_source, &m_reply); 367 else 368 r = asynsend3(m_ptr->m_source, &m_reply, AMF_NOREPLY); 369 370 if (r != OK) 371 printf("PTYFS: unable to reply to PTY (%d)\n", r); 372 } 373 374 /* 375 * Initialize the service. 376 */ 377 static int 378 ptyfs_init(int __unused type, sef_init_info_t * __unused info) 379 { 380 381 init_nodes(); 382 383 root_data.ctime = clock_time(NULL); 384 385 return OK; 386 } 387 388 /* 389 * Process an incoming signal. 390 */ 391 static void 392 ptyfs_signal(int sig) 393 { 394 395 if (sig == SIGTERM) 396 fsdriver_terminate(); 397 } 398 399 /* 400 * Perform SEF initialization. 401 */ 402 static void 403 ptyfs_startup(void) 404 { 405 406 sef_setcb_init_fresh(ptyfs_init); 407 sef_setcb_signal_handler(ptyfs_signal); 408 sef_startup(); 409 } 410 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 420 }; 421 422 /* 423 * The PTYFS service. 424 */ 425 int 426 main(void) 427 { 428 429 ptyfs_startup(); 430 431 fsdriver_task(&ptyfs_table); 432 433 return 0; 434 } 435