source: mainline/uspace/lib/c/generic/vfs/vfs.c@ 39c3b7f9

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

Add the FS name field to struct statfs

  • Property mode set to 100644
File size: 25.0 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 ent->service_id = stat->service_id;
1118
1119 struct statfs stfs;
1120 if (statfs(path, &stfs) == EOK)
1121 str_cpy(ent->fs_name, sizeof(ent->fs_name), stfs.fs_name);
1122 else
1123 str_cpy(ent->fs_name, sizeof(ent->fs_name), "?");
1124
1125 list_append(&ent->link, mtab_list);
1126}
1127
1128static int vfs_get_mtab_visit(const char *path, list_t *mtab_list,
1129 fs_handle_t fs_handle, service_id_t service_id)
1130{
1131 DIR *dir;
1132 struct dirent *dirent;
1133
1134 dir = opendir(path);
1135 if (!dir)
1136 return -1;
1137
1138 while ((dirent = readdir(dir)) != NULL) {
1139 char *child;
1140 struct stat st;
1141 int rc;
1142
1143 rc = asprintf(&child, "%s/%s", path, dirent->d_name);
1144 if (rc < 0) {
1145 closedir(dir);
1146 errno = rc;
1147 return -1;
1148 }
1149
1150 rc = stat(child, &st);
1151 if (rc != 0) {
1152 free(child);
1153 closedir(dir);
1154 errno = rc;
1155 return -1;
1156 }
1157
1158 if (st.fs_handle != fs_handle || st.service_id != service_id) {
1159 /*
1160 * We have discovered a mountpoint.
1161 */
1162 process_mp(child, &st, mtab_list);
1163 }
1164
1165 if (st.is_directory) {
1166 (void) vfs_get_mtab_visit(child, mtab_list,
1167 st.fs_handle, st.service_id);
1168 }
1169
1170 free(child);
1171 }
1172
1173 closedir(dir);
1174 return EOK;
1175}
1176
1177int vfs_get_mtab_list(list_t *mtab_list)
1178{
1179 struct stat st;
1180
1181 int rc = stat("/", &st);
1182 if (rc != 0)
1183 return rc;
1184
1185 process_mp("/", &st, mtab_list);
1186
1187 return vfs_get_mtab_visit("", mtab_list, st.fs_handle, st.service_id);
1188}
1189
1190/** Get filesystem statistics.
1191 *
1192 * @param path Mount point path
1193 * @param st Buffer for storing information
1194 * @return 0 on success. On error -1 is returned and errno is set.
1195 */
1196int statfs(const char *path, struct statfs *st)
1197{
1198 int fd = vfs_lookup(path, 0);
1199 if (fd < 0) {
1200 errno = fd;
1201 return -1;
1202 }
1203
1204 sysarg_t rc, ret;
1205 aid_t req;
1206
1207 async_exch_t *exch = vfs_exchange_begin();
1208
1209 req = async_send_1(exch, VFS_IN_STATFS, fd, NULL);
1210 rc = async_data_read_start(exch, (void *) st, sizeof(*st));
1211
1212 vfs_exchange_end(exch);
1213 async_wait_for(req, &ret);
1214 close(fd);
1215
1216 rc = (ret != EOK ? ret : rc);
1217 if (rc != EOK) {
1218 errno = rc;
1219 return -1;
1220 }
1221
1222 return 0;
1223}
1224
1225int vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
1226{
1227 return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t)file, 0, vfs_exch);
1228}
1229
1230int vfs_receive_handle(bool high_descriptor)
1231{
1232 ipc_callid_t callid;
1233 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
1234 async_answer_0(callid, EINVAL);
1235 return EINVAL;
1236 }
1237
1238 async_exch_t *vfs_exch = vfs_exchange_begin();
1239
1240 async_state_change_finalize(callid, vfs_exch);
1241
1242 sysarg_t ret;
1243 sysarg_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high_descriptor, &ret);
1244
1245 async_exchange_end(vfs_exch);
1246
1247 if (rc != EOK) {
1248 return rc;
1249 }
1250 return ret;
1251}
1252
1253int vfs_clone(int file, bool high_descriptor)
1254{
1255 async_exch_t *vfs_exch = vfs_exchange_begin();
1256 int rc = async_req_2_0(vfs_exch, VFS_IN_CLONE, (sysarg_t) file, (sysarg_t) high_descriptor);
1257 vfs_exchange_end(vfs_exch);
1258 return rc;
1259}
1260
1261/** @}
1262 */
Note: See TracBrowser for help on using the repository browser.