source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 8ffedd8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8ffedd8 was 8ffedd8, checked in by Jakub Jermar <jakub@…>, 8 years ago

Partially revive support for mount table listing and walking

This implementation is purely client-based. Using a simple file system
traversal, the library figures out what are the actual mountpoints. It
only discovers mountpoints that it can reach from its VFS root.

The benefit is that this works and produces reasonable results even in
the environment of per-client VFS roots, whereas a global mount table
would present paths that might not make any sense to the client
(notwithstanding the fact that VFS no longer knows the mountpoint
paths).

Listing of file system names is still broken as of this commit.

  • Property mode set to 100644
File size: 24.9 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 */
34
35#include <vfs/canonify.h>
36#include <vfs/vfs.h>
37#include <vfs/vfs_mtab.h>
38#include <vfs/vfs_sess.h>
39#include <macros.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <dirent.h>
43#include <fcntl.h>
44#include <stdio.h>
45#include <sys/stat.h>
46#include <sys/statfs.h>
47#include <sys/types.h>
48#include <ipc/services.h>
49#include <ns.h>
50#include <async.h>
51#include <fibril_synch.h>
52#include <errno.h>
53#include <assert.h>
54#include <str.h>
55#include <loc.h>
56#include <ipc/vfs.h>
57#include <ipc/loc.h>
58
59static FIBRIL_MUTEX_INITIALIZE(vfs_mutex);
60static async_sess_t *vfs_sess = NULL;
61
62static FIBRIL_MUTEX_INITIALIZE(cwd_mutex);
63
64static int cwd_fd = -1;
65static char *cwd_path = NULL;
66static size_t cwd_size = 0;
67
68static FIBRIL_MUTEX_INITIALIZE(root_mutex);
69static int root_fd = -1;
70
71int vfs_root(void)
72{
73 fibril_mutex_lock(&root_mutex);
74 int r;
75 if (root_fd < 0) {
76 r = ENOENT;
77 } else {
78 r = vfs_clone(root_fd, true);
79 }
80 fibril_mutex_unlock(&root_mutex);
81 return r;
82}
83
84void vfs_root_set(int nroot)
85{
86 fibril_mutex_lock(&root_mutex);
87 if (root_fd >= 0) {
88 close(root_fd);
89 }
90 root_fd = vfs_clone(nroot, true);
91 fibril_mutex_unlock(&root_mutex);
92}
93
94/** Start an async exchange on the VFS session.
95 *
96 * @return New exchange.
97 *
98 */
99async_exch_t *vfs_exchange_begin(void)
100{
101 fibril_mutex_lock(&vfs_mutex);
102
103 while (vfs_sess == NULL)
104 vfs_sess = service_connect_blocking(SERVICE_VFS, INTERFACE_VFS,
105 0);
106
107 fibril_mutex_unlock(&vfs_mutex);
108
109 return async_exchange_begin(vfs_sess);
110}
111
112/** Finish an async exchange on the VFS session.
113 *
114 * @param exch Exchange to be finished.
115 *
116 */
117void vfs_exchange_end(async_exch_t *exch)
118{
119 async_exchange_end(exch);
120}
121
122int _vfs_walk(int parent, const char *path, int flags)
123{
124 async_exch_t *exch = vfs_exchange_begin();
125
126 ipc_call_t answer;
127 aid_t req = async_send_2(exch, VFS_IN_WALK, parent, flags, &answer);
128 sysarg_t rc = async_data_write_start(exch, path, str_size(path));
129 vfs_exchange_end(exch);
130
131 sysarg_t rc_orig;
132 async_wait_for(req, &rc_orig);
133
134 if (rc_orig != EOK) {
135 return (int) rc_orig;
136 }
137
138 if (rc != EOK) {
139 return (int) rc;
140 }
141
142 return (int) IPC_GET_ARG1(answer);
143}
144
145int vfs_lookup(const char *path, int flags)
146{
147 size_t size;
148 char *p = vfs_absolutize(path, &size);
149 if (!p) {
150 return ENOMEM;
151 }
152 int root = vfs_root();
153 if (root == -1) {
154 free(p);
155 return ENOENT;
156 }
157 int rc = _vfs_walk(root, p, flags);
158 close(root);
159 free(p);
160 return rc;
161}
162
163int _vfs_open(int fildes, int mode)
164{
165 async_exch_t *exch = vfs_exchange_begin();
166 sysarg_t rc = async_req_2_0(exch, VFS_IN_OPEN2, fildes, mode);
167 vfs_exchange_end(exch);
168
169 return (int) rc;
170}
171
172char *vfs_absolutize(const char *path, size_t *retlen)
173{
174 char *ncwd_path;
175 char *ncwd_path_nc;
176
177 fibril_mutex_lock(&cwd_mutex);
178 size_t size = str_size(path);
179 if (*path != '/') {
180 if (cwd_path == NULL) {
181 fibril_mutex_unlock(&cwd_mutex);
182 return NULL;
183 }
184 ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
185 if (ncwd_path_nc == NULL) {
186 fibril_mutex_unlock(&cwd_mutex);
187 return NULL;
188 }
189 str_cpy(ncwd_path_nc, cwd_size + 1 + size + 1, cwd_path);
190 ncwd_path_nc[cwd_size] = '/';
191 ncwd_path_nc[cwd_size + 1] = '\0';
192 } else {
193 ncwd_path_nc = malloc(size + 1);
194 if (ncwd_path_nc == NULL) {
195 fibril_mutex_unlock(&cwd_mutex);
196 return NULL;
197 }
198 ncwd_path_nc[0] = '\0';
199 }
200 str_append(ncwd_path_nc, cwd_size + 1 + size + 1, path);
201 ncwd_path = canonify(ncwd_path_nc, retlen);
202 if (ncwd_path == NULL) {
203 fibril_mutex_unlock(&cwd_mutex);
204 free(ncwd_path_nc);
205 return NULL;
206 }
207 /*
208 * We need to clone ncwd_path because canonify() works in-place and thus
209 * the address in ncwd_path need not be the same as ncwd_path_nc, even
210 * though they both point into the same dynamically allocated buffer.
211 */
212 ncwd_path = str_dup(ncwd_path);
213 free(ncwd_path_nc);
214 if (ncwd_path == NULL) {
215 fibril_mutex_unlock(&cwd_mutex);
216 return NULL;
217 }
218 fibril_mutex_unlock(&cwd_mutex);
219 return ncwd_path;
220}
221
222int vfs_mount(int mp, const char *fs_name, service_id_t serv, const char *opts,
223unsigned int flags, unsigned int instance, int *mountedfd)
224{
225 sysarg_t rc, rc1;
226
227 if (!mountedfd) {
228 flags |= VFS_MOUNT_NO_REF;
229 }
230 if (mp < 0) {
231 flags |= VFS_MOUNT_CONNECT_ONLY;
232 }
233
234 ipc_call_t answer;
235 async_exch_t *exch = vfs_exchange_begin();
236 aid_t req = async_send_4(exch, VFS_IN_MOUNT, mp, serv, flags, instance, &answer);
237
238 rc1 = async_data_write_start(exch, (void *) opts, str_size(opts));
239
240 if (rc1 == EOK) {
241 rc1 = async_data_write_start(exch, (void *) fs_name, str_size(fs_name));
242 }
243
244 vfs_exchange_end(exch);
245
246 async_wait_for(req, &rc);
247
248 if (mountedfd) {
249 *mountedfd = (int) IPC_GET_ARG1(answer);
250 }
251
252 if (rc != EOK) {
253 return rc;
254 }
255 return rc1;
256}
257
258int vfs_unmount(int mp)
259{
260 async_exch_t *exch = vfs_exchange_begin();
261 int rc = async_req_1_0(exch, VFS_IN_UNMOUNT, mp);
262 vfs_exchange_end(exch);
263 return rc;
264}
265
266int mount(const char *fs_name, const char *mp, const char *fqsn,
267 const char *opts, unsigned int flags, unsigned int instance)
268{
269 int null_id = -1;
270 char null[LOC_NAME_MAXLEN];
271
272 if (str_cmp(fqsn, "") == 0) {
273 /* No device specified, create a fresh
274 null/%d device instead */
275 null_id = loc_null_create();
276
277 if (null_id == -1)
278 return ENOMEM;
279
280 snprintf(null, LOC_NAME_MAXLEN, "null/%d", null_id);
281 fqsn = null;
282 }
283
284 if (flags & IPC_FLAG_BLOCKING) {
285 flags = VFS_MOUNT_BLOCKING;
286 } else {
287 flags = 0;
288 }
289
290 service_id_t service_id;
291 int res = loc_service_get_id(fqsn, &service_id, flags);
292 if (res != EOK) {
293 if (null_id != -1)
294 loc_null_destroy(null_id);
295
296 return res;
297 }
298
299 size_t mpa_size;
300 char *mpa = vfs_absolutize(mp, &mpa_size);
301 if (mpa == NULL) {
302 if (null_id != -1)
303 loc_null_destroy(null_id);
304
305 return ENOMEM;
306 }
307
308 fibril_mutex_lock(&root_mutex);
309
310 int rc;
311
312 if (str_cmp(mpa, "/") == 0) {
313 /* Mounting root. */
314
315 if (root_fd >= 0) {
316 fibril_mutex_unlock(&root_mutex);
317 if (null_id != -1) {
318 loc_null_destroy(null_id);
319 }
320 return EBUSY;
321 }
322
323 int root;
324 rc = vfs_mount(-1, fs_name, service_id, opts, flags, instance, &root);
325 if (rc == EOK) {
326 root_fd = root;
327 }
328 } else {
329 if (root_fd < 0) {
330 fibril_mutex_unlock(&root_mutex);
331 if (null_id != -1) {
332 loc_null_destroy(null_id);
333 }
334 return EINVAL;
335 }
336
337 int mpfd = _vfs_walk(root_fd, mpa, WALK_DIRECTORY);
338 if (mpfd >= 0) {
339 rc = vfs_mount(mpfd, fs_name, service_id, opts, flags, instance, NULL);
340 close(mpfd);
341 } else {
342 rc = mpfd;
343 }
344 }
345
346 fibril_mutex_unlock(&root_mutex);
347
348 if ((rc != EOK) && (null_id != -1))
349 loc_null_destroy(null_id);
350
351 return (int) rc;
352}
353
354int unmount(const char *mpp)
355{
356 int mp = vfs_lookup(mpp, WALK_MOUNT_POINT | WALK_DIRECTORY);
357 if (mp < 0) {
358 return mp;
359 }
360
361 int rc = vfs_unmount(mp);
362 close(mp);
363 return rc;
364}
365
366static int walk_flags(int oflags)
367{
368 int flags = 0;
369 if (oflags & O_CREAT) {
370 if (oflags & O_EXCL) {
371 flags |= WALK_MUST_CREATE;
372 } else {
373 flags |= WALK_MAY_CREATE;
374 }
375 }
376 return flags;
377}
378
379/** Open file.
380 *
381 * @param path File path
382 * @param oflag O_xxx flags
383 * @param mode File mode (only with O_CREAT)
384 *
385 * @return Nonnegative file descriptor on success. On error -1 is returned
386 * and errno is set.
387 */
388int open(const char *path, int oflag, ...)
389{
390 // FIXME: Some applications call this incorrectly.
391 if ((oflag & (O_RDONLY|O_WRONLY|O_RDWR)) == 0) {
392 oflag |= O_RDWR;
393 }
394
395 assert((((oflag & O_RDONLY) != 0) + ((oflag & O_WRONLY) != 0) + ((oflag & O_RDWR) != 0)) == 1);
396
397 int ret = vfs_lookup(path, walk_flags(oflag) | WALK_REGULAR);
398 if (ret < 0) {
399 return ret;
400 }
401
402 int mode =
403 ((oflag & O_RDWR) ? MODE_READ|MODE_WRITE : 0) |
404 ((oflag & O_RDONLY) ? MODE_READ : 0) |
405 ((oflag & O_WRONLY) ? MODE_WRITE : 0) |
406 ((oflag & O_APPEND) ? MODE_APPEND : 0);
407
408 int rc = _vfs_open(ret, mode);
409 if (rc < 0) {
410 // _vfs_put(ret);
411 close(ret);
412 return rc;
413 }
414
415 if (oflag & O_TRUNC) {
416 assert(oflag & O_WRONLY || oflag & O_RDWR);
417 assert(!(oflag & O_APPEND));
418
419 // _vfs_resize
420 (void) ftruncate(ret, 0);
421 }
422
423 return ret;
424}
425
426/** Close file.
427 *
428 * @param fildes File descriptor
429 * @return Zero on success. On error -1 is returned and errno is set.
430 */
431int close(int fildes)
432{
433 sysarg_t rc;
434
435 async_exch_t *exch = vfs_exchange_begin();
436 rc = async_req_1_0(exch, VFS_IN_CLOSE, fildes);
437 vfs_exchange_end(exch);
438
439 if (rc != EOK) {
440 errno = rc;
441 return -1;
442 }
443
444 return 0;
445}
446
447/** Read bytes from file.
448 *
449 * Read up to @a nbyte bytes from file. The actual number of bytes read
450 * may be lower, but greater than zero if there are any bytes available.
451 * If there are no bytes available for reading, then the function will
452 * return success with zero bytes read.
453 *
454 * @param fildes File descriptor
455 * @param buf Buffer
456 * @param nbyte Maximum number of bytes to read
457 * @param nread Place to store actual number of bytes read (0 or more)
458 *
459 * @return EOK on success, non-zero error code on error.
460 */
461static int _read_short(int fildes, void *buf, size_t nbyte, ssize_t *nread)
462{
463 sysarg_t rc;
464 ipc_call_t answer;
465 aid_t req;
466
467 if (nbyte > DATA_XFER_LIMIT)
468 nbyte = DATA_XFER_LIMIT;
469
470 async_exch_t *exch = vfs_exchange_begin();
471
472 req = async_send_1(exch, VFS_IN_READ, fildes, &answer);
473 rc = async_data_read_start(exch, (void *) buf, nbyte);
474
475 vfs_exchange_end(exch);
476
477 if (rc == EOK) {
478 async_wait_for(req, &rc);
479 } else {
480 async_forget(req);
481 }
482
483 if (rc != EOK)
484 return rc;
485
486 *nread = (ssize_t) IPC_GET_ARG1(answer);
487 return EOK;
488}
489
490/** Write bytes to file.
491 *
492 * Write up to @a nbyte bytes from file. The actual number of bytes written
493 * may be lower, but greater than zero.
494 *
495 * @param fildes File descriptor
496 * @param buf Buffer
497 * @param nbyte Maximum number of bytes to write
498 * @param nread Place to store actual number of bytes written (0 or more)
499 *
500 * @return EOK on success, non-zero error code on error.
501 */
502static int _write_short(int fildes, const void *buf, size_t nbyte,
503 ssize_t *nwritten)
504{
505 sysarg_t rc;
506 ipc_call_t answer;
507 aid_t req;
508
509 if (nbyte > DATA_XFER_LIMIT)
510 nbyte = DATA_XFER_LIMIT;
511
512 async_exch_t *exch = vfs_exchange_begin();
513
514 req = async_send_1(exch, VFS_IN_WRITE, fildes, &answer);
515 rc = async_data_write_start(exch, (void *) buf, nbyte);
516
517 vfs_exchange_end(exch);
518
519 if (rc == EOK) {
520 async_wait_for(req, &rc);
521 } else {
522 async_forget(req);
523 }
524
525 if (rc != EOK)
526 return rc;
527
528 *nwritten = (ssize_t) IPC_GET_ARG1(answer);
529 return EOK;
530}
531
532/** Read data.
533 *
534 * Read up to @a nbytes bytes from file if available. This function always reads
535 * all the available bytes up to @a nbytes.
536 *
537 * @param fildes File descriptor
538 * @param buf Buffer, @a nbytes bytes long
539 * @param nbytes Number of bytes to read
540 *
541 * @return On success, nonnegative number of bytes read.
542 * On failure, -1 and sets errno.
543 */
544ssize_t read(int fildes, void *buf, size_t nbyte)
545{
546 ssize_t cnt = 0;
547 size_t nread = 0;
548 uint8_t *bp = (uint8_t *) buf;
549 int rc;
550
551 do {
552 bp += cnt;
553 nread += cnt;
554 rc = _read_short(fildes, bp, nbyte - nread, &cnt);
555 } while (rc == EOK && cnt > 0 && (nbyte - nread - cnt) > 0);
556
557 if (rc != EOK) {
558 errno = rc;
559 return -1;
560 }
561
562 return nread + cnt;
563}
564
565/** Write data.
566 *
567 * This function fails if it cannot write exactly @a len bytes to the file.
568 *
569 * @param fildes File descriptor
570 * @param buf Data, @a nbytes bytes long
571 * @param nbytes Number of bytes to write
572 *
573 * @return On success, nonnegative number of bytes written.
574 * On failure, -1 and sets errno.
575 */
576ssize_t write(int fildes, const void *buf, size_t nbyte)
577{
578 ssize_t cnt = 0;
579 ssize_t nwritten = 0;
580 const uint8_t *bp = (uint8_t *) buf;
581 int rc;
582
583 do {
584 bp += cnt;
585 nwritten += cnt;
586 rc = _write_short(fildes, bp, nbyte - nwritten, &cnt);
587 } while (rc == EOK && ((ssize_t )nbyte - nwritten - cnt) > 0);
588
589 if (rc != EOK) {
590 errno = rc;
591 return -1;
592 }
593
594 return nbyte;
595}
596
597/** Synchronize file.
598 *
599 * @param fildes File descriptor
600 * @return 0 on success. On error returns -1 and sets errno.
601 */
602int fsync(int fildes)
603{
604 async_exch_t *exch = vfs_exchange_begin();
605 sysarg_t rc = async_req_1_0(exch, VFS_IN_SYNC, fildes);
606 vfs_exchange_end(exch);
607
608 if (rc != EOK) {
609 errno = rc;
610 return -1;
611 }
612
613 return 0;
614}
615
616/** Seek to a position.
617 *
618 * @param fildes File descriptor
619 * @param offset Offset
620 * @param whence SEEK_SET, SEEK_CUR or SEEK_END
621 *
622 * @return On success the nonnegative offset from start of file. On error
623 * returns (off64_t)-1 and sets errno.
624 */
625off64_t lseek(int fildes, off64_t offset, int whence)
626{
627 async_exch_t *exch = vfs_exchange_begin();
628
629 sysarg_t newoff_lo;
630 sysarg_t newoff_hi;
631 sysarg_t rc = async_req_4_2(exch, VFS_IN_SEEK, fildes,
632 LOWER32(offset), UPPER32(offset), whence,
633 &newoff_lo, &newoff_hi);
634
635 vfs_exchange_end(exch);
636
637 if (rc != EOK) {
638 errno = rc;
639 return (off64_t) -1;
640 }
641
642 return (off64_t) MERGE_LOUP32(newoff_lo, newoff_hi);
643}
644
645/** Truncate file to a specified length.
646 *
647 * Truncate file so that its size is exactly @a length
648 *
649 * @param fildes File descriptor
650 * @param length Length
651 *
652 * @return 0 on success, -1 on error and sets errno.
653 */
654int ftruncate(int fildes, aoff64_t length)
655{
656 sysarg_t rc;
657
658 async_exch_t *exch = vfs_exchange_begin();
659 rc = async_req_3_0(exch, VFS_IN_TRUNCATE, fildes,
660 LOWER32(length), UPPER32(length));
661 vfs_exchange_end(exch);
662
663 if (rc != EOK) {
664 errno = rc;
665 return -1;
666 }
667
668 return 0;
669}
670
671/** Get file status.
672 *
673 * @param fildes File descriptor
674 * @param stat Place to store file information
675 *
676 * @return 0 on success, -1 on error and sets errno.
677 */
678int fstat(int fildes, struct stat *stat)
679{
680 sysarg_t rc;
681 aid_t req;
682
683 async_exch_t *exch = vfs_exchange_begin();
684
685 req = async_send_1(exch, VFS_IN_FSTAT, fildes, NULL);
686 rc = async_data_read_start(exch, (void *) stat, sizeof(struct stat));
687 if (rc != EOK) {
688 vfs_exchange_end(exch);
689
690 sysarg_t rc_orig;
691 async_wait_for(req, &rc_orig);
692
693 if (rc_orig != EOK)
694 rc = rc_orig;
695 if (rc != EOK) {
696 errno = rc;
697 return -1;
698 }
699
700 return 0;
701 }
702
703 vfs_exchange_end(exch);
704 async_wait_for(req, &rc);
705
706 if (rc != EOK) {
707 errno = rc;
708 return -1;
709 }
710
711 return 0;
712}
713
714/** Get file status.
715 *
716 * @param path Path to file
717 * @param stat Place to store file information
718 *
719 * @return 0 on success, -1 on error and sets errno.
720 */
721int stat(const char *path, struct stat *stat)
722{
723 int fd = vfs_lookup(path, 0);
724 if (fd < 0) {
725 return fd;
726 }
727
728 int rc = fstat(fd, stat);
729 close(fd);
730 return rc;
731}
732
733/** Open directory.
734 *
735 * @param dirname Directory pathname
736 *
737 * @return Non-NULL pointer on success. On error returns @c NULL and sets errno.
738 */
739DIR *opendir(const char *dirname)
740{
741 DIR *dirp = malloc(sizeof(DIR));
742 if (!dirp) {
743 errno = ENOMEM;
744 return NULL;
745 }
746
747 int ret = vfs_lookup(dirname, WALK_DIRECTORY);
748 if (ret < 0) {
749 free(dirp);
750 errno = ret;
751 return NULL;
752 }
753
754 int rc = _vfs_open(ret, MODE_READ);
755 if (rc < 0) {
756 free(dirp);
757 close(ret);
758 errno = rc;
759 return NULL;
760 }
761
762 dirp->fd = ret;
763 return dirp;
764}
765
766/** Read directory entry.
767 *
768 * @param dirp Open directory
769 * @return Non-NULL pointer to directory entry on success. On error returns
770 * @c NULL and sets errno.
771 */
772struct dirent *readdir(DIR *dirp)
773{
774 int rc;
775 ssize_t len;
776
777 rc = _read_short(dirp->fd, &dirp->res.d_name[0], NAME_MAX + 1, &len);
778 if (rc != EOK) {
779 errno = rc;
780 return NULL;
781 }
782
783 (void) len;
784 return &dirp->res;
785}
786
787/** Rewind directory position to the beginning.
788 *
789 * @param dirp Open directory
790 */
791void rewinddir(DIR *dirp)
792{
793 (void) lseek(dirp->fd, 0, SEEK_SET);
794}
795
796/** Close directory.
797 *
798 * @param dirp Open directory
799 * @return 0 on success. On error returns -1 and sets errno.
800 */
801int closedir(DIR *dirp)
802{
803 int rc;
804
805 rc = close(dirp->fd);
806 free(dirp);
807
808 /* On error errno was set by close() */
809 return rc;
810}
811
812/** Create directory.
813 *
814 * @param path Path
815 * @param mode File mode
816 * @return 0 on success. On error returns -1 and sets errno.
817 */
818int mkdir(const char *path, mode_t mode)
819{
820 int ret = vfs_lookup(path, WALK_MUST_CREATE | WALK_DIRECTORY);
821 if (ret < 0) {
822 return ret;
823 }
824
825 close(ret);
826 return EOK;
827}
828
829static int _vfs_unlink2(int parent, const char *path, int expect, int wflag)
830{
831 sysarg_t rc;
832 aid_t req;
833
834 async_exch_t *exch = vfs_exchange_begin();
835
836 req = async_send_3(exch, VFS_IN_UNLINK2, parent, expect, wflag, NULL);
837 rc = async_data_write_start(exch, path, str_size(path));
838
839 vfs_exchange_end(exch);
840
841 sysarg_t rc_orig;
842 async_wait_for(req, &rc_orig);
843
844 if (rc_orig != EOK) {
845 return (int) rc_orig;
846 }
847 return rc;
848}
849
850/** Unlink file or directory.
851 *
852 * @param path Path
853 * @return EOk on success, error code on error
854 */
855int unlink(const char *path)
856{
857 size_t pa_size;
858 char *pa = vfs_absolutize(path, &pa_size);
859 if (!pa) {
860 return ENOMEM;
861 }
862
863 int root = vfs_root();
864 if (root < 0) {
865 free(pa);
866 return ENOENT;
867 }
868
869 int rc = _vfs_unlink2(root, pa, -1, 0);
870
871 free(pa);
872 close(root);
873 return rc;
874}
875
876/** Remove empty directory.
877 *
878 * @param path Path
879 * @return 0 on success. On error returns -1 and sets errno.
880 */
881int rmdir(const char *path)
882{
883 size_t pa_size;
884 char *pa = vfs_absolutize(path, &pa_size);
885 if (!pa) {
886 return ENOMEM;
887 }
888
889 int root = vfs_root();
890 if (root < 0) {
891 free(pa);
892 return ENOENT;
893 }
894
895 int rc = _vfs_unlink2(root, pa, -1, WALK_DIRECTORY);
896
897 free(pa);
898 close(root);
899 return rc;
900}
901
902/** Rename directory entry.
903 *
904 * @param old Old name
905 * @param new New name
906 *
907 * @return 0 on success. On error returns -1 and sets errno.
908 */
909int rename(const char *old, const char *new)
910{
911 sysarg_t rc;
912 sysarg_t rc_orig;
913 aid_t req;
914
915 size_t olda_size;
916 char *olda = vfs_absolutize(old, &olda_size);
917 if (olda == NULL) {
918 errno = ENOMEM;
919 return -1;
920 }
921
922 size_t newa_size;
923 char *newa = vfs_absolutize(new, &newa_size);
924 if (newa == NULL) {
925 free(olda);
926 errno = ENOMEM;
927 return -1;
928 }
929
930 async_exch_t *exch = vfs_exchange_begin();
931 int root = vfs_root();
932 if (root < 0) {
933 free(olda);
934 free(newa);
935 return ENOENT;
936 }
937
938 req = async_send_1(exch, VFS_IN_RENAME, root, NULL);
939 rc = async_data_write_start(exch, olda, olda_size);
940 if (rc != EOK) {
941 vfs_exchange_end(exch);
942 free(olda);
943 free(newa);
944 close(root);
945 async_wait_for(req, &rc_orig);
946 if (rc_orig != EOK)
947 rc = rc_orig;
948 if (rc != EOK) {
949 errno = rc;
950 return -1;
951 }
952 return 0;
953 }
954 rc = async_data_write_start(exch, newa, newa_size);
955 if (rc != EOK) {
956 vfs_exchange_end(exch);
957 free(olda);
958 free(newa);
959 close(root);
960 async_wait_for(req, &rc_orig);
961 if (rc_orig != EOK)
962 rc = rc_orig;
963 if (rc != EOK) {
964 errno = rc;
965 return -1;
966 }
967 return 0;
968 }
969 vfs_exchange_end(exch);
970 free(olda);
971 free(newa);
972 close(root);
973 async_wait_for(req, &rc);
974
975 if (rc != EOK) {
976 errno = rc;
977 return -1;
978 }
979
980 return 0;
981}
982
983/** Remove directory entry.
984 *
985 * @param path Path
986 * @return 0 on success. On error returns -1 and sets errno.
987 */
988int remove(const char *path)
989{
990 return unlink(path);
991}
992
993/** Change working directory.
994 *
995 * @param path Path
996 * @return 0 on success. On error returns -1 and sets errno.
997 */
998int chdir(const char *path)
999{
1000 size_t abs_size;
1001 char *abs = vfs_absolutize(path, &abs_size);
1002 if (!abs)
1003 return ENOMEM;
1004
1005 int fd = vfs_lookup(abs, WALK_DIRECTORY);
1006 if (fd < 0) {
1007 free(abs);
1008 errno = fd;
1009 return -1;
1010 }
1011
1012 fibril_mutex_lock(&cwd_mutex);
1013
1014 if (cwd_fd >= 0) {
1015 close(cwd_fd);
1016 }
1017
1018 if (cwd_path) {
1019 free(cwd_path);
1020 }
1021
1022 cwd_fd = fd;
1023 cwd_path = abs;
1024 cwd_size = abs_size;
1025
1026 fibril_mutex_unlock(&cwd_mutex);
1027 return 0;
1028}
1029
1030/** Get current working directory path.
1031 *
1032 * @param buf Buffer
1033 * @param size Size of @a buf
1034 * @return On success returns @a buf. On failure returns @c NULL and sets errno.
1035 */
1036char *getcwd(char *buf, size_t size)
1037{
1038 if (size == 0) {
1039 errno = EINVAL;
1040 return NULL;
1041 }
1042
1043 fibril_mutex_lock(&cwd_mutex);
1044
1045 if ((cwd_size == 0) || (size < cwd_size + 1)) {
1046 fibril_mutex_unlock(&cwd_mutex);
1047 errno = ERANGE;
1048 return NULL;
1049 }
1050
1051 str_cpy(buf, size, cwd_path);
1052 fibril_mutex_unlock(&cwd_mutex);
1053
1054 return buf;
1055}
1056
1057/** Open session to service represented by a special file.
1058 *
1059 * Given that the file referred to by @a fildes represents a service,
1060 * open a session to that service.
1061 *
1062 * @param fildes File descriptor
1063 * @param iface Interface to connect to (XXX Should be automatic)
1064 * @return On success returns session pointer. On error returns @c NULL.
1065 */
1066async_sess_t *vfs_fd_session(int fildes, iface_t iface)
1067{
1068 struct stat stat;
1069 int rc = fstat(fildes, &stat);
1070 if (rc != 0)
1071 return NULL;
1072
1073 if (stat.service == 0)
1074 return NULL;
1075
1076 return loc_service_connect(stat.service, iface, 0);
1077}
1078
1079/** Duplicate open file.
1080 *
1081 * Duplicate open file under a new file descriptor.
1082 *
1083 * @param oldfd Old file descriptor
1084 * @param newfd New file descriptor
1085 * @return 0 on success. On error -1 is returned and errno is set
1086 */
1087int dup2(int oldfd, int newfd)
1088{
1089 async_exch_t *exch = vfs_exchange_begin();
1090
1091 sysarg_t ret;
1092 sysarg_t rc = async_req_2_1(exch, VFS_IN_DUP, oldfd, newfd, &ret);
1093
1094 vfs_exchange_end(exch);
1095
1096 if (rc == EOK)
1097 rc = ret;
1098
1099 if (rc != EOK) {
1100 errno = rc;
1101 return -1;
1102 }
1103
1104 return 0;
1105}
1106
1107static void process_mp(const char *path, struct stat *stat, list_t *mtab_list)
1108{
1109 mtab_ent_t *ent;
1110
1111 ent = (mtab_ent_t *) malloc(sizeof(mtab_ent_t));
1112 if (!ent)
1113 return;
1114
1115 link_initialize(&ent->link);
1116 str_cpy(ent->mp, sizeof(ent->mp), path);
1117 str_cpy(ent->fs_name, sizeof(ent->fs_name), "fixme");
1118 ent->service_id = stat->service_id;
1119
1120 list_append(&ent->link, mtab_list);
1121}
1122
1123static int vfs_get_mtab_visit(const char *path, list_t *mtab_list,
1124 fs_handle_t fs_handle, service_id_t service_id)
1125{
1126 DIR *dir;
1127 struct dirent *dirent;
1128
1129 dir = opendir(path);
1130 if (!dir)
1131 return -1;
1132
1133 while ((dirent = readdir(dir)) != NULL) {
1134 char *child;
1135 struct stat st;
1136 int rc;
1137
1138 rc = asprintf(&child, "%s/%s", path, dirent->d_name);
1139 if (rc < 0) {
1140 closedir(dir);
1141 errno = rc;
1142 return -1;
1143 }
1144
1145 rc = stat(child, &st);
1146 if (rc != 0) {
1147 free(child);
1148 closedir(dir);
1149 errno = rc;
1150 return -1;
1151 }
1152
1153 if (st.fs_handle != fs_handle || st.service_id != service_id) {
1154 /*
1155 * We have discovered a mountpoint.
1156 */
1157 process_mp(child, &st, mtab_list);
1158 }
1159
1160 if (st.is_directory) {
1161 (void) vfs_get_mtab_visit(child, mtab_list,
1162 st.fs_handle, st.service_id);
1163 }
1164
1165 free(child);
1166 }
1167
1168 closedir(dir);
1169 return EOK;
1170}
1171
1172int vfs_get_mtab_list(list_t *mtab_list)
1173{
1174 struct stat st;
1175
1176 int rc = stat("/", &st);
1177 if (rc != 0)
1178 return rc;
1179
1180 process_mp("/", &st, mtab_list);
1181
1182 return vfs_get_mtab_visit("", mtab_list, st.fs_handle, st.service_id);
1183}
1184
1185/** Get filesystem statistics.
1186 *
1187 * @param path Mount point path
1188 * @param st Buffer for storing information
1189 * @return 0 on success. On error -1 is returned and errno is set.
1190 */
1191int statfs(const char *path, struct statfs *st)
1192{
1193 int fd = vfs_lookup(path, 0);
1194 if (fd < 0) {
1195 errno = fd;
1196 return -1;
1197 }
1198
1199 sysarg_t rc, ret;
1200 aid_t req;
1201
1202 async_exch_t *exch = vfs_exchange_begin();
1203
1204 req = async_send_1(exch, VFS_IN_STATFS, fd, NULL);
1205 rc = async_data_read_start(exch, (void *) st, sizeof(*st));
1206
1207 vfs_exchange_end(exch);
1208 async_wait_for(req, &ret);
1209 close(fd);
1210
1211 rc = (ret != EOK ? ret : rc);
1212 if (rc != EOK) {
1213 errno = rc;
1214 return -1;
1215 }
1216
1217 return 0;
1218}
1219
1220int vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
1221{
1222 return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t)file, 0, vfs_exch);
1223}
1224
1225int vfs_receive_handle(bool high_descriptor)
1226{
1227 ipc_callid_t callid;
1228 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
1229 async_answer_0(callid, EINVAL);
1230 return EINVAL;
1231 }
1232
1233 async_exch_t *vfs_exch = vfs_exchange_begin();
1234
1235 async_state_change_finalize(callid, vfs_exch);
1236
1237 sysarg_t ret;
1238 sysarg_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high_descriptor, &ret);
1239
1240 async_exchange_end(vfs_exch);
1241
1242 if (rc != EOK) {
1243 return rc;
1244 }
1245 return ret;
1246}
1247
1248int vfs_clone(int file, bool high_descriptor)
1249{
1250 async_exch_t *vfs_exch = vfs_exchange_begin();
1251 int rc = async_req_2_0(vfs_exch, VFS_IN_CLONE, (sysarg_t) file, (sysarg_t) high_descriptor);
1252 vfs_exchange_end(vfs_exch);
1253 return rc;
1254}
1255
1256/** @}
1257 */
Note: See TracBrowser for help on using the repository browser.