source: mainline/uspace/lib/c/generic/vfs/vfs.c@ b7f69f2

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

Callers of open() must use exactly one of O_RDONLY, O_WRONLY and O_RDWR

  • Property mode set to 100644
File size: 25.2 KB
RevLine 
[2f02aa17]1/*
[64d2b10]2 * Copyright (c) 2008 Jakub Jermar
[2f02aa17]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 */
[19b28b0]34
[5fec355]35#include <vfs/canonify.h>
[79ae36dd]36#include <vfs/vfs.h>
[8ffedd8]37#include <vfs/vfs_mtab.h>
[79ae36dd]38#include <vfs/vfs_sess.h>
[ed903174]39#include <macros.h>
[d0dc74ae]40#include <stdlib.h>
[449c246]41#include <unistd.h>
[d0dc74ae]42#include <dirent.h>
[449c246]43#include <fcntl.h>
[923c39e]44#include <stdio.h>
[852b801]45#include <sys/stat.h>
[9dc6083]46#include <sys/statfs.h>
[72bde81]47#include <sys/types.h>
[2f02aa17]48#include <ipc/services.h>
[79ae36dd]49#include <ns.h>
[2f02aa17]50#include <async.h>
[a28ab12]51#include <fibril_synch.h>
[2f02aa17]52#include <errno.h>
[a28ab12]53#include <assert.h>
[19f857a]54#include <str.h>
[15f3c3f]55#include <loc.h>
[2595dab]56#include <ipc/vfs.h>
[15f3c3f]57#include <ipc/loc.h>
[2f02aa17]58
[79ae36dd]59static FIBRIL_MUTEX_INITIALIZE(vfs_mutex);
60static async_sess_t *vfs_sess = NULL;
[a28ab12]61
62static FIBRIL_MUTEX_INITIALIZE(cwd_mutex);
[5fec355]63
[2b88074b]64static int cwd_fd = -1;
65static char *cwd_path = NULL;
66static size_t cwd_size = 0;
[5fec355]67
[5126f80]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
[79ae36dd]94/** Start an async exchange on the VFS session.
95 *
96 * @return New exchange.
97 *
98 */
[866e627]99async_exch_t *vfs_exchange_begin(void)
[79ae36dd]100{
101 fibril_mutex_lock(&vfs_mutex);
102
103 while (vfs_sess == NULL)
[f9b2cb4c]104 vfs_sess = service_connect_blocking(SERVICE_VFS, INTERFACE_VFS,
[2133e02]105 0);
[79ae36dd]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 */
[866e627]117void vfs_exchange_end(async_exch_t *exch)
[79ae36dd]118{
119 async_exchange_end(exch);
120}
121
[0b18364]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);
[3ef62df]128 sysarg_t rc = async_data_write_start(exch, path, str_size(path));
[0b18364]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
[5126f80]145int vfs_lookup(const char *path, int flags)
[eca9fd0]146{
147 size_t size;
148 char *p = vfs_absolutize(path, &size);
149 if (!p) {
150 return ENOMEM;
151 }
[5126f80]152 int root = vfs_root();
[c725619]153 if (root < 0) {
[5126f80]154 free(p);
155 return ENOENT;
156 }
157 int rc = _vfs_walk(root, p, flags);
158 close(root);
[eca9fd0]159 free(p);
160 return rc;
161}
162
[0b18364]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
[6afc9d7]172char *vfs_absolutize(const char *path, size_t *retlen)
[5fec355]173{
174 char *ncwd_path;
[34a74ab]175 char *ncwd_path_nc;
[5fec355]176
[a28ab12]177 fibril_mutex_lock(&cwd_mutex);
[9eb3623]178 size_t size = str_size(path);
[5fec355]179 if (*path != '/') {
[6afc9d7]180 if (cwd_path == NULL) {
[a28ab12]181 fibril_mutex_unlock(&cwd_mutex);
[5fec355]182 return NULL;
183 }
[79ae36dd]184 ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
[6afc9d7]185 if (ncwd_path_nc == NULL) {
[a28ab12]186 fibril_mutex_unlock(&cwd_mutex);
[5fec355]187 return NULL;
188 }
[79ae36dd]189 str_cpy(ncwd_path_nc, cwd_size + 1 + size + 1, cwd_path);
[9eb3623]190 ncwd_path_nc[cwd_size] = '/';
191 ncwd_path_nc[cwd_size + 1] = '\0';
[5fec355]192 } else {
[79ae36dd]193 ncwd_path_nc = malloc(size + 1);
[6afc9d7]194 if (ncwd_path_nc == NULL) {
[a28ab12]195 fibril_mutex_unlock(&cwd_mutex);
[5fec355]196 return NULL;
197 }
[34a74ab]198 ncwd_path_nc[0] = '\0';
[5fec355]199 }
[79ae36dd]200 str_append(ncwd_path_nc, cwd_size + 1 + size + 1, path);
[34a74ab]201 ncwd_path = canonify(ncwd_path_nc, retlen);
[6afc9d7]202 if (ncwd_path == NULL) {
[a28ab12]203 fibril_mutex_unlock(&cwd_mutex);
[34a74ab]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 */
[095003a8]212 ncwd_path = str_dup(ncwd_path);
[34a74ab]213 free(ncwd_path_nc);
[6afc9d7]214 if (ncwd_path == NULL) {
[a28ab12]215 fibril_mutex_unlock(&cwd_mutex);
[923c39e]216 return NULL;
217 }
[a28ab12]218 fibril_mutex_unlock(&cwd_mutex);
[5fec355]219 return ncwd_path;
220}
[2f02aa17]221
[5126f80]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,
[4979403]267 const char *opts, unsigned int flags, unsigned int instance)
[2f02aa17]268{
[210e50a]269 int null_id = -1;
[15f3c3f]270 char null[LOC_NAME_MAXLEN];
[210e50a]271
[15f3c3f]272 if (str_cmp(fqsn, "") == 0) {
[210e50a]273 /* No device specified, create a fresh
274 null/%d device instead */
[15f3c3f]275 null_id = loc_null_create();
[210e50a]276
277 if (null_id == -1)
278 return ENOMEM;
279
[15f3c3f]280 snprintf(null, LOC_NAME_MAXLEN, "null/%d", null_id);
281 fqsn = null;
[210e50a]282 }
[82405266]283
[5126f80]284 if (flags & IPC_FLAG_BLOCKING) {
285 flags = VFS_MOUNT_BLOCKING;
286 } else {
287 flags = 0;
288 }
289
[15f3c3f]290 service_id_t service_id;
291 int res = loc_service_get_id(fqsn, &service_id, flags);
[210e50a]292 if (res != EOK) {
293 if (null_id != -1)
[15f3c3f]294 loc_null_destroy(null_id);
[210e50a]295
[82405266]296 return res;
[210e50a]297 }
[82405266]298
[9eb3623]299 size_t mpa_size;
[6afc9d7]300 char *mpa = vfs_absolutize(mp, &mpa_size);
301 if (mpa == NULL) {
[210e50a]302 if (null_id != -1)
[15f3c3f]303 loc_null_destroy(null_id);
[210e50a]304
[5fec355]305 return ENOMEM;
[210e50a]306 }
[19b28b0]307
[5126f80]308 fibril_mutex_lock(&root_mutex);
[19b28b0]309
[5126f80]310 int rc;
[210e50a]311
[5126f80]312 if (str_cmp(mpa, "/") == 0) {
313 /* Mounting root. */
[210e50a]314
[5126f80]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 }
[210e50a]322
[5126f80]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 }
[2f02aa17]344 }
[210e50a]345
[5126f80]346 fibril_mutex_unlock(&root_mutex);
[19b28b0]347
[210e50a]348 if ((rc != EOK) && (null_id != -1))
[15f3c3f]349 loc_null_destroy(null_id);
[210e50a]350
[2f02aa17]351 return (int) rc;
352}
353
[5126f80]354int unmount(const char *mpp)
[21f32ee1]355{
[5126f80]356 int mp = vfs_lookup(mpp, WALK_MOUNT_POINT | WALK_DIRECTORY);
357 if (mp < 0) {
358 return mp;
[b9067dfa]359 }
360
[5126f80]361 int rc = vfs_unmount(mp);
362 close(mp);
363 return rc;
[21f32ee1]364}
365
[3ef62df]366static int walk_flags(int oflags)
[2f02aa17]367{
[3ef62df]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 }
[2f02aa17]375 }
[3ef62df]376 return flags;
[2f02aa17]377}
378
[6afc9d7]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 */
[ae78b530]388int open(const char *path, int oflag, ...)
389{
[38aaf005]390 if (((oflag & (O_RDONLY | O_WRONLY | O_RDWR)) == 0) ||
391 ((oflag & (O_RDONLY | O_WRONLY)) == (O_RDONLY | O_WRONLY)) ||
392 ((oflag & (O_RDONLY | O_RDWR)) == (O_RDONLY | O_RDWR)) ||
393 ((oflag & (O_WRONLY | O_RDWR)) == (O_WRONLY | O_RDWR))) {
394 errno = EINVAL;
395 return -1;
[3ef62df]396 }
397
[0b97336]398 int fd = vfs_lookup(path, walk_flags(oflag) | WALK_REGULAR);
399 if (fd < 0) {
400 errno = fd;
401 return -1;
[6afc9d7]402 }
[2b88074b]403
[3ef62df]404 int mode =
405 ((oflag & O_RDWR) ? MODE_READ|MODE_WRITE : 0) |
406 ((oflag & O_RDONLY) ? MODE_READ : 0) |
407 ((oflag & O_WRONLY) ? MODE_WRITE : 0) |
408 ((oflag & O_APPEND) ? MODE_APPEND : 0);
[2b88074b]409
[0b97336]410 int rc = _vfs_open(fd, mode);
[3ef62df]411 if (rc < 0) {
[0b97336]412 close(fd);
413 errno = rc;
414 return -1;
[6afc9d7]415 }
416
[3ef62df]417 if (oflag & O_TRUNC) {
418 assert(oflag & O_WRONLY || oflag & O_RDWR);
419 assert(!(oflag & O_APPEND));
420
[0b97336]421 (void) ftruncate(fd, 0);
[3ef62df]422 }
423
[0b97336]424 return fd;
[ae78b530]425}
426
[6afc9d7]427/** Close file.
428 *
429 * @param fildes File descriptor
430 * @return Zero on success. On error -1 is returned and errno is set.
431 */
[72bde81]432int close(int fildes)
433{
[96b02eb9]434 sysarg_t rc;
[19b28b0]435
[79ae36dd]436 async_exch_t *exch = vfs_exchange_begin();
437 rc = async_req_1_0(exch, VFS_IN_CLOSE, fildes);
438 vfs_exchange_end(exch);
[19b28b0]439
[6afc9d7]440 if (rc != EOK) {
441 errno = rc;
442 return -1;
443 }
444
445 return 0;
[72bde81]446}
447
[6afc9d7]448/** Read bytes from file.
449 *
450 * Read up to @a nbyte bytes from file. The actual number of bytes read
451 * may be lower, but greater than zero if there are any bytes available.
452 * If there are no bytes available for reading, then the function will
453 * return success with zero bytes read.
454 *
455 * @param fildes File descriptor
456 * @param buf Buffer
457 * @param nbyte Maximum number of bytes to read
458 * @param nread Place to store actual number of bytes read (0 or more)
459 *
460 * @return EOK on success, non-zero error code on error.
461 */
462static int _read_short(int fildes, void *buf, size_t nbyte, ssize_t *nread)
[2f02aa17]463{
[96b02eb9]464 sysarg_t rc;
[2f02aa17]465 ipc_call_t answer;
466 aid_t req;
[19b28b0]467
[fa24efa]468 if (nbyte > DATA_XFER_LIMIT)
469 nbyte = DATA_XFER_LIMIT;
470
[79ae36dd]471 async_exch_t *exch = vfs_exchange_begin();
472
473 req = async_send_1(exch, VFS_IN_READ, fildes, &answer);
[fa24efa]474 rc = async_data_read_start(exch, (void *) buf, nbyte);
[354b642]475
[79ae36dd]476 vfs_exchange_end(exch);
[354b642]477
478 if (rc == EOK) {
479 async_wait_for(req, &rc);
480 } else {
481 async_forget(req);
482 }
[6afc9d7]483
484 if (rc != EOK)
[25becee8]485 return rc;
[6afc9d7]486
487 *nread = (ssize_t) IPC_GET_ARG1(answer);
488 return EOK;
[2f02aa17]489}
490
[6afc9d7]491/** Write bytes to file.
492 *
493 * Write up to @a nbyte bytes from file. The actual number of bytes written
494 * may be lower, but greater than zero.
495 *
496 * @param fildes File descriptor
497 * @param buf Buffer
498 * @param nbyte Maximum number of bytes to write
499 * @param nread Place to store actual number of bytes written (0 or more)
500 *
501 * @return EOK on success, non-zero error code on error.
502 */
503static int _write_short(int fildes, const void *buf, size_t nbyte,
504 ssize_t *nwritten)
[449c246]505{
[96b02eb9]506 sysarg_t rc;
[449c246]507 ipc_call_t answer;
508 aid_t req;
[19b28b0]509
[fa24efa]510 if (nbyte > DATA_XFER_LIMIT)
511 nbyte = DATA_XFER_LIMIT;
512
[79ae36dd]513 async_exch_t *exch = vfs_exchange_begin();
514
515 req = async_send_1(exch, VFS_IN_WRITE, fildes, &answer);
[fa24efa]516 rc = async_data_write_start(exch, (void *) buf, nbyte);
[6afc9d7]517
[79ae36dd]518 vfs_exchange_end(exch);
[6afc9d7]519
[354b642]520 if (rc == EOK) {
521 async_wait_for(req, &rc);
522 } else {
523 async_forget(req);
524 }
525
[6afc9d7]526 if (rc != EOK)
527 return rc;
528
529 *nwritten = (ssize_t) IPC_GET_ARG1(answer);
530 return EOK;
[449c246]531}
[222e57c]532
[6afc9d7]533/** Read data.
[8fd04ba9]534 *
[6afc9d7]535 * Read up to @a nbytes bytes from file if available. This function always reads
536 * all the available bytes up to @a nbytes.
[8fd04ba9]537 *
538 * @param fildes File descriptor
539 * @param buf Buffer, @a nbytes bytes long
540 * @param nbytes Number of bytes to read
541 *
[6afc9d7]542 * @return On success, nonnegative number of bytes read.
543 * On failure, -1 and sets errno.
[8fd04ba9]544 */
[6afc9d7]545ssize_t read(int fildes, void *buf, size_t nbyte)
[8fd04ba9]546{
547 ssize_t cnt = 0;
548 size_t nread = 0;
549 uint8_t *bp = (uint8_t *) buf;
[6afc9d7]550 int rc;
551
[8fd04ba9]552 do {
553 bp += cnt;
554 nread += cnt;
[6afc9d7]555 rc = _read_short(fildes, bp, nbyte - nread, &cnt);
556 } while (rc == EOK && cnt > 0 && (nbyte - nread - cnt) > 0);
557
558 if (rc != EOK) {
559 errno = rc;
560 return -1;
561 }
562
[8fd04ba9]563 return nread + cnt;
564}
565
[6afc9d7]566/** Write data.
[8fd04ba9]567 *
568 * This function fails if it cannot write exactly @a len bytes to the file.
569 *
570 * @param fildes File descriptor
571 * @param buf Data, @a nbytes bytes long
572 * @param nbytes Number of bytes to write
573 *
[6afc9d7]574 * @return On success, nonnegative number of bytes written.
575 * On failure, -1 and sets errno.
[8fd04ba9]576 */
[6afc9d7]577ssize_t write(int fildes, const void *buf, size_t nbyte)
[8fd04ba9]578{
579 ssize_t cnt = 0;
580 ssize_t nwritten = 0;
581 const uint8_t *bp = (uint8_t *) buf;
[6afc9d7]582 int rc;
[8fd04ba9]583
584 do {
585 bp += cnt;
586 nwritten += cnt;
[6afc9d7]587 rc = _write_short(fildes, bp, nbyte - nwritten, &cnt);
588 } while (rc == EOK && ((ssize_t )nbyte - nwritten - cnt) > 0);
[8fd04ba9]589
[6afc9d7]590 if (rc != EOK) {
591 errno = rc;
592 return -1;
593 }
[8fd04ba9]594
595 return nbyte;
596}
597
[6afc9d7]598/** Synchronize file.
599 *
600 * @param fildes File descriptor
601 * @return 0 on success. On error returns -1 and sets errno.
602 */
[2595dab]603int fsync(int fildes)
604{
[79ae36dd]605 async_exch_t *exch = vfs_exchange_begin();
606 sysarg_t rc = async_req_1_0(exch, VFS_IN_SYNC, fildes);
607 vfs_exchange_end(exch);
[2595dab]608
[6afc9d7]609 if (rc != EOK) {
610 errno = rc;
611 return -1;
612 }
613
614 return 0;
[2595dab]615}
616
[6afc9d7]617/** Seek to a position.
618 *
619 * @param fildes File descriptor
620 * @param offset Offset
621 * @param whence SEEK_SET, SEEK_CUR or SEEK_END
622 *
623 * @return On success the nonnegative offset from start of file. On error
624 * returns (off64_t)-1 and sets errno.
625 */
[ed903174]626off64_t lseek(int fildes, off64_t offset, int whence)
[222e57c]627{
[79ae36dd]628 async_exch_t *exch = vfs_exchange_begin();
[19b28b0]629
[96b02eb9]630 sysarg_t newoff_lo;
631 sysarg_t newoff_hi;
[79ae36dd]632 sysarg_t rc = async_req_4_2(exch, VFS_IN_SEEK, fildes,
[ed903174]633 LOWER32(offset), UPPER32(offset), whence,
634 &newoff_lo, &newoff_hi);
635
[79ae36dd]636 vfs_exchange_end(exch);
[ed903174]637
[6afc9d7]638 if (rc != EOK) {
639 errno = rc;
[ed903174]640 return (off64_t) -1;
[6afc9d7]641 }
[222e57c]642
[ed903174]643 return (off64_t) MERGE_LOUP32(newoff_lo, newoff_hi);
[222e57c]644}
645
[6afc9d7]646/** Truncate file to a specified length.
647 *
648 * Truncate file so that its size is exactly @a length
649 *
650 * @param fildes File descriptor
651 * @param length Length
652 *
653 * @return 0 on success, -1 on error and sets errno.
654 */
[ed903174]655int ftruncate(int fildes, aoff64_t length)
[0ee4322]656{
[96b02eb9]657 sysarg_t rc;
[0ee4322]658
[79ae36dd]659 async_exch_t *exch = vfs_exchange_begin();
660 rc = async_req_3_0(exch, VFS_IN_TRUNCATE, fildes,
[ed903174]661 LOWER32(length), UPPER32(length));
[79ae36dd]662 vfs_exchange_end(exch);
[ed903174]663
[6afc9d7]664 if (rc != EOK) {
665 errno = rc;
666 return -1;
667 }
668
669 return 0;
[0ee4322]670}
671
[6afc9d7]672/** Get file status.
673 *
674 * @param fildes File descriptor
675 * @param stat Place to store file information
676 *
677 * @return 0 on success, -1 on error and sets errno.
678 */
[852b801]679int fstat(int fildes, struct stat *stat)
680{
[96b02eb9]681 sysarg_t rc;
[852b801]682 aid_t req;
683
[79ae36dd]684 async_exch_t *exch = vfs_exchange_begin();
685
686 req = async_send_1(exch, VFS_IN_FSTAT, fildes, NULL);
687 rc = async_data_read_start(exch, (void *) stat, sizeof(struct stat));
[852b801]688 if (rc != EOK) {
[79ae36dd]689 vfs_exchange_end(exch);
[6afc9d7]690
[96b02eb9]691 sysarg_t rc_orig;
[3734106]692 async_wait_for(req, &rc_orig);
[6afc9d7]693
694 if (rc_orig != EOK)
695 rc = rc_orig;
696 if (rc != EOK) {
697 errno = rc;
698 return -1;
699 }
700
701 return 0;
[852b801]702 }
[6afc9d7]703
[79ae36dd]704 vfs_exchange_end(exch);
[852b801]705 async_wait_for(req, &rc);
[6afc9d7]706
707 if (rc != EOK) {
708 errno = rc;
709 return -1;
710 }
711
712 return 0;
[852b801]713}
714
[6afc9d7]715/** Get file status.
716 *
717 * @param path Path to file
718 * @param stat Place to store file information
719 *
720 * @return 0 on success, -1 on error and sets errno.
721 */
[415c7e0d]722int stat(const char *path, struct stat *stat)
723{
[5126f80]724 int fd = vfs_lookup(path, 0);
[0ed9cb6]725 if (fd < 0) {
[0b97336]726 errno = fd;
727 return -1;
[415c7e0d]728 }
[0ed9cb6]729
730 int rc = fstat(fd, stat);
[0b97336]731 if (rc != EOK) {
732 close(fd);
733 errno = rc;
734 rc = -1;
735 } else
736 rc = close(fd);
737
[415c7e0d]738 return rc;
739}
740
[6afc9d7]741/** Open directory.
742 *
743 * @param dirname Directory pathname
744 *
745 * @return Non-NULL pointer on success. On error returns @c NULL and sets errno.
746 */
[d0dc74ae]747DIR *opendir(const char *dirname)
748{
749 DIR *dirp = malloc(sizeof(DIR));
[9b48c06]750 if (!dirp) {
[6afc9d7]751 errno = ENOMEM;
[d0dc74ae]752 return NULL;
[6afc9d7]753 }
[2b88074b]754
[0b97336]755 int fd = vfs_lookup(dirname, WALK_DIRECTORY);
756 if (fd < 0) {
[d0dc74ae]757 free(dirp);
[0b97336]758 errno = fd;
[9b48c06]759 return NULL;
760 }
761
[0b97336]762 int rc = _vfs_open(fd, MODE_READ);
[9b48c06]763 if (rc < 0) {
764 free(dirp);
[0b97336]765 close(fd);
[6afc9d7]766 errno = rc;
[d0dc74ae]767 return NULL;
768 }
[2b88074b]769
[0b97336]770 dirp->fd = fd;
[d0dc74ae]771 return dirp;
772}
773
[6afc9d7]774/** Read directory entry.
775 *
776 * @param dirp Open directory
777 * @return Non-NULL pointer to directory entry on success. On error returns
778 * @c NULL and sets errno.
779 */
[d0dc74ae]780struct dirent *readdir(DIR *dirp)
781{
[6afc9d7]782 int rc;
783 ssize_t len;
784
785 rc = _read_short(dirp->fd, &dirp->res.d_name[0], NAME_MAX + 1, &len);
786 if (rc != EOK) {
787 errno = rc;
[5973fd0]788 return NULL;
[6afc9d7]789 }
790
791 (void) len;
[5973fd0]792 return &dirp->res;
[d0dc74ae]793}
794
[6afc9d7]795/** Rewind directory position to the beginning.
796 *
797 * @param dirp Open directory
798 */
[d0dc74ae]799void rewinddir(DIR *dirp)
800{
[5973fd0]801 (void) lseek(dirp->fd, 0, SEEK_SET);
[d0dc74ae]802}
803
[6afc9d7]804/** Close directory.
805 *
806 * @param dirp Open directory
807 * @return 0 on success. On error returns -1 and sets errno.
808 */
[d0dc74ae]809int closedir(DIR *dirp)
810{
[6afc9d7]811 int rc;
812
813 rc = close(dirp->fd);
[d0dc74ae]814 free(dirp);
[6afc9d7]815
816 /* On error errno was set by close() */
817 return rc;
[d0dc74ae]818}
819
[6afc9d7]820/** Create directory.
821 *
822 * @param path Path
823 * @param mode File mode
824 * @return 0 on success. On error returns -1 and sets errno.
825 */
[72bde81]826int mkdir(const char *path, mode_t mode)
[d0dc74ae]827{
[0b97336]828 int fd = vfs_lookup(path, WALK_MUST_CREATE | WALK_DIRECTORY);
829 if (fd < 0) {
830 errno = fd;
831 return -1;
[6afc9d7]832 }
833
[0b97336]834 return close(fd);
[d0dc74ae]835}
836
[d18c404]837static int _vfs_unlink2(int parent, const char *path, int expect, int wflag)
[f15cf1a6]838{
[96b02eb9]839 sysarg_t rc;
[f15cf1a6]840 aid_t req;
841
[79ae36dd]842 async_exch_t *exch = vfs_exchange_begin();
843
[d18c404]844 req = async_send_3(exch, VFS_IN_UNLINK2, parent, expect, wflag, NULL);
845 rc = async_data_write_start(exch, path, str_size(path));
846
[79ae36dd]847 vfs_exchange_end(exch);
[d18c404]848
849 sysarg_t rc_orig;
850 async_wait_for(req, &rc_orig);
851
852 if (rc_orig != EOK) {
853 return (int) rc_orig;
854 }
[2595dab]855 return rc;
[f15cf1a6]856}
857
[6afc9d7]858/** Unlink file or directory.
859 *
860 * @param path Path
861 * @return EOk on success, error code on error
862 */
[f15cf1a6]863int unlink(const char *path)
864{
[d18c404]865 size_t pa_size;
[1dff985]866 char *pa = vfs_absolutize(path, &pa_size);
[d18c404]867 if (!pa) {
[0b97336]868 errno = ENOMEM;
869 return -1;
[6afc9d7]870 }
[d18c404]871
[5126f80]872 int root = vfs_root();
873 if (root < 0) {
874 free(pa);
[0b97336]875 errno = ENOENT;
876 return -1;
[5126f80]877 }
878
879 int rc = _vfs_unlink2(root, pa, -1, 0);
880
[0b97336]881 if (rc != EOK) {
882 errno = rc;
883 rc = -1;
884 }
885
[5126f80]886 free(pa);
887 close(root);
888 return rc;
[f15cf1a6]889}
890
[6afc9d7]891/** Remove empty directory.
892 *
893 * @param path Path
894 * @return 0 on success. On error returns -1 and sets errno.
895 */
[f15cf1a6]896int rmdir(const char *path)
897{
[d18c404]898 size_t pa_size;
[1dff985]899 char *pa = vfs_absolutize(path, &pa_size);
[d18c404]900 if (!pa) {
[0b97336]901 errno = ENOMEM;
902 return -1;
[6afc9d7]903 }
[d18c404]904
[5126f80]905 int root = vfs_root();
906 if (root < 0) {
907 free(pa);
[0b97336]908 errno = ENOENT;
909 return -1;
[5126f80]910 }
911
912 int rc = _vfs_unlink2(root, pa, -1, WALK_DIRECTORY);
[0b97336]913
914 if (rc != EOK) {
915 errno = rc;
916 rc = -1;
917 }
[5126f80]918
919 free(pa);
920 close(root);
921 return rc;
[f15cf1a6]922}
923
[6afc9d7]924/** Rename directory entry.
925 *
926 * @param old Old name
927 * @param new New name
928 *
929 * @return 0 on success. On error returns -1 and sets errno.
930 */
[a8e9ab8d]931int rename(const char *old, const char *new)
932{
[96b02eb9]933 sysarg_t rc;
934 sysarg_t rc_orig;
[a8e9ab8d]935 aid_t req;
936
[9eb3623]937 size_t olda_size;
[6afc9d7]938 char *olda = vfs_absolutize(old, &olda_size);
939 if (olda == NULL) {
940 errno = ENOMEM;
941 return -1;
942 }
[923c39e]943
[9eb3623]944 size_t newa_size;
[6afc9d7]945 char *newa = vfs_absolutize(new, &newa_size);
946 if (newa == NULL) {
[a8e9ab8d]947 free(olda);
[6afc9d7]948 errno = ENOMEM;
949 return -1;
[a8e9ab8d]950 }
[19b28b0]951
[79ae36dd]952 async_exch_t *exch = vfs_exchange_begin();
[5126f80]953 int root = vfs_root();
954 if (root < 0) {
955 free(olda);
956 free(newa);
[0b97336]957 errno = ENOENT;
958 return -1;
[5126f80]959 }
[79ae36dd]960
[5126f80]961 req = async_send_1(exch, VFS_IN_RENAME, root, NULL);
[79ae36dd]962 rc = async_data_write_start(exch, olda, olda_size);
[a8e9ab8d]963 if (rc != EOK) {
[79ae36dd]964 vfs_exchange_end(exch);
[a8e9ab8d]965 free(olda);
966 free(newa);
[5126f80]967 close(root);
[a28ab12]968 async_wait_for(req, &rc_orig);
[6afc9d7]969 if (rc_orig != EOK)
970 rc = rc_orig;
971 if (rc != EOK) {
972 errno = rc;
973 return -1;
974 }
975 return 0;
[a8e9ab8d]976 }
[79ae36dd]977 rc = async_data_write_start(exch, newa, newa_size);
[a8e9ab8d]978 if (rc != EOK) {
[79ae36dd]979 vfs_exchange_end(exch);
[a8e9ab8d]980 free(olda);
981 free(newa);
[5126f80]982 close(root);
[a28ab12]983 async_wait_for(req, &rc_orig);
[6afc9d7]984 if (rc_orig != EOK)
985 rc = rc_orig;
986 if (rc != EOK) {
987 errno = rc;
988 return -1;
989 }
990 return 0;
[a8e9ab8d]991 }
[79ae36dd]992 vfs_exchange_end(exch);
[a8e9ab8d]993 free(olda);
994 free(newa);
[5126f80]995 close(root);
[a28ab12]996 async_wait_for(req, &rc);
[6afc9d7]997
998 if (rc != EOK) {
999 errno = rc;
1000 return -1;
1001 }
1002
1003 return 0;
[a8e9ab8d]1004}
1005
[6afc9d7]1006/** Remove directory entry.
1007 *
1008 * @param path Path
1009 * @return 0 on success. On error returns -1 and sets errno.
1010 */
[38db6288]1011int remove(const char *path)
1012{
1013 return unlink(path);
1014}
1015
[6afc9d7]1016/** Change working directory.
1017 *
1018 * @param path Path
1019 * @return 0 on success. On error returns -1 and sets errno.
1020 */
[5fec355]1021int chdir(const char *path)
1022{
[2b88074b]1023 size_t abs_size;
[6afc9d7]1024 char *abs = vfs_absolutize(path, &abs_size);
[0b97336]1025 if (!abs) {
1026 errno = ENOMEM;
1027 return -1;
1028 }
[2b88074b]1029
[5126f80]1030 int fd = vfs_lookup(abs, WALK_DIRECTORY);
[2b88074b]1031 if (fd < 0) {
1032 free(abs);
[1dff985]1033 errno = fd;
[6afc9d7]1034 return -1;
[5fec355]1035 }
[2b88074b]1036
[a28ab12]1037 fibril_mutex_lock(&cwd_mutex);
[2b88074b]1038
[5126f80]1039 if (cwd_fd >= 0) {
[2b88074b]1040 close(cwd_fd);
[5126f80]1041 }
[2b88074b]1042
[5126f80]1043 if (cwd_path) {
[2b88074b]1044 free(cwd_path);
[5126f80]1045 }
[2b88074b]1046
1047 cwd_fd = fd;
1048 cwd_path = abs;
1049 cwd_size = abs_size;
1050
[a28ab12]1051 fibril_mutex_unlock(&cwd_mutex);
[6afc9d7]1052 return 0;
[5fec355]1053}
1054
[6afc9d7]1055/** Get current working directory path.
1056 *
1057 * @param buf Buffer
1058 * @param size Size of @a buf
1059 * @return On success returns @a buf. On failure returns @c NULL and sets errno.
1060 */
[5fec355]1061char *getcwd(char *buf, size_t size)
1062{
[6afc9d7]1063 if (size == 0) {
1064 errno = EINVAL;
[5fec355]1065 return NULL;
[6afc9d7]1066 }
[2b88074b]1067
[a28ab12]1068 fibril_mutex_lock(&cwd_mutex);
[2b88074b]1069
1070 if ((cwd_size == 0) || (size < cwd_size + 1)) {
[a28ab12]1071 fibril_mutex_unlock(&cwd_mutex);
[6afc9d7]1072 errno = ERANGE;
[5fec355]1073 return NULL;
1074 }
[2b88074b]1075
[6eb2e96]1076 str_cpy(buf, size, cwd_path);
[a28ab12]1077 fibril_mutex_unlock(&cwd_mutex);
[2b88074b]1078
[5fec355]1079 return buf;
1080}
1081
[6afc9d7]1082/** Open session to service represented by a special file.
1083 *
1084 * Given that the file referred to by @a fildes represents a service,
1085 * open a session to that service.
1086 *
1087 * @param fildes File descriptor
1088 * @param iface Interface to connect to (XXX Should be automatic)
1089 * @return On success returns session pointer. On error returns @c NULL.
1090 */
1091async_sess_t *vfs_fd_session(int fildes, iface_t iface)
[852b801]1092{
1093 struct stat stat;
[8caaea7]1094 int rc = fstat(fildes, &stat);
[6afc9d7]1095 if (rc != 0)
[79ae36dd]1096 return NULL;
[8caaea7]1097
[6afc9d7]1098 if (stat.service == 0)
[79ae36dd]1099 return NULL;
[852b801]1100
[f9b2cb4c]1101 return loc_service_connect(stat.service, iface, 0);
[852b801]1102}
1103
[6afc9d7]1104/** Duplicate open file.
1105 *
1106 * Duplicate open file under a new file descriptor.
1107 *
1108 * @param oldfd Old file descriptor
1109 * @param newfd New file descriptor
1110 * @return 0 on success. On error -1 is returned and errno is set
1111 */
[2b88074b]1112int dup2(int oldfd, int newfd)
[852b801]1113{
[79ae36dd]1114 async_exch_t *exch = vfs_exchange_begin();
[852b801]1115
[96b02eb9]1116 sysarg_t ret;
[79ae36dd]1117 sysarg_t rc = async_req_2_1(exch, VFS_IN_DUP, oldfd, newfd, &ret);
[852b801]1118
[79ae36dd]1119 vfs_exchange_end(exch);
[2b88074b]1120
1121 if (rc == EOK)
[6afc9d7]1122 rc = ret;
[2b88074b]1123
[6afc9d7]1124 if (rc != EOK) {
1125 errno = rc;
1126 return -1;
1127 }
1128
1129 return 0;
[852b801]1130}
1131
[8ffedd8]1132static void process_mp(const char *path, struct stat *stat, list_t *mtab_list)
[10e4cd7]1133{
[8ffedd8]1134 mtab_ent_t *ent;
1135
1136 ent = (mtab_ent_t *) malloc(sizeof(mtab_ent_t));
1137 if (!ent)
1138 return;
1139
1140 link_initialize(&ent->link);
1141 str_cpy(ent->mp, sizeof(ent->mp), path);
1142 ent->service_id = stat->service_id;
[5a2b765]1143
1144 struct statfs stfs;
1145 if (statfs(path, &stfs) == EOK)
1146 str_cpy(ent->fs_name, sizeof(ent->fs_name), stfs.fs_name);
1147 else
1148 str_cpy(ent->fs_name, sizeof(ent->fs_name), "?");
[10e4cd7]1149
[8ffedd8]1150 list_append(&ent->link, mtab_list);
1151}
1152
1153static int vfs_get_mtab_visit(const char *path, list_t *mtab_list,
1154 fs_handle_t fs_handle, service_id_t service_id)
1155{
1156 DIR *dir;
1157 struct dirent *dirent;
1158
1159 dir = opendir(path);
1160 if (!dir)
[0b97336]1161 return ENOENT;
[8ffedd8]1162
1163 while ((dirent = readdir(dir)) != NULL) {
1164 char *child;
1165 struct stat st;
1166 int rc;
1167
1168 rc = asprintf(&child, "%s/%s", path, dirent->d_name);
1169 if (rc < 0) {
1170 closedir(dir);
[0b97336]1171 return rc;
[8ffedd8]1172 }
1173
1174 rc = stat(child, &st);
1175 if (rc != 0) {
1176 free(child);
1177 closedir(dir);
[0b97336]1178 return rc;
[8ffedd8]1179 }
1180
1181 if (st.fs_handle != fs_handle || st.service_id != service_id) {
1182 /*
1183 * We have discovered a mountpoint.
1184 */
1185 process_mp(child, &st, mtab_list);
1186 }
1187
1188 if (st.is_directory) {
1189 (void) vfs_get_mtab_visit(child, mtab_list,
1190 st.fs_handle, st.service_id);
1191 }
1192
1193 free(child);
1194 }
1195
1196 closedir(dir);
1197 return EOK;
1198}
1199
1200int vfs_get_mtab_list(list_t *mtab_list)
1201{
1202 struct stat st;
1203
1204 int rc = stat("/", &st);
1205 if (rc != 0)
1206 return rc;
1207
1208 process_mp("/", &st, mtab_list);
1209
1210 return vfs_get_mtab_visit("", mtab_list, st.fs_handle, st.service_id);
[10e4cd7]1211}
1212
[6afc9d7]1213/** Get filesystem statistics.
1214 *
1215 * @param path Mount point path
1216 * @param st Buffer for storing information
1217 * @return 0 on success. On error -1 is returned and errno is set.
1218 */
[73fbcbb]1219int statfs(const char *path, struct statfs *st)
[66366470]1220{
[5126f80]1221 int fd = vfs_lookup(path, 0);
[a737667e]1222 if (fd < 0) {
1223 errno = fd;
1224 return -1;
1225 }
1226
1227 sysarg_t rc, ret;
1228 aid_t req;
[73fbcbb]1229
[a737667e]1230 async_exch_t *exch = vfs_exchange_begin();
[73fbcbb]1231
[a737667e]1232 req = async_send_1(exch, VFS_IN_STATFS, fd, NULL);
[73fbcbb]1233 rc = async_data_read_start(exch, (void *) st, sizeof(*st));
1234
[66366470]1235 vfs_exchange_end(exch);
[a737667e]1236 async_wait_for(req, &ret);
1237 close(fd);
[6afc9d7]1238
[a737667e]1239 rc = (ret != EOK ? ret : rc);
[6afc9d7]1240 if (rc != EOK) {
1241 errno = rc;
1242 return -1;
1243 }
1244
1245 return 0;
[66366470]1246}
1247
[354b642]1248int vfs_pass_handle(async_exch_t *vfs_exch, int file, async_exch_t *exch)
1249{
1250 return async_state_change_start(exch, VFS_PASS_HANDLE, (sysarg_t)file, 0, vfs_exch);
1251}
1252
[bb9ec2d]1253int vfs_receive_handle(bool high_descriptor)
[354b642]1254{
1255 ipc_callid_t callid;
1256 if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
1257 async_answer_0(callid, EINVAL);
1258 return EINVAL;
1259 }
1260
1261 async_exch_t *vfs_exch = vfs_exchange_begin();
1262
1263 async_state_change_finalize(callid, vfs_exch);
1264
1265 sysarg_t ret;
[bb9ec2d]1266 sysarg_t rc = async_req_1_1(vfs_exch, VFS_IN_WAIT_HANDLE, high_descriptor, &ret);
[354b642]1267
1268 async_exchange_end(vfs_exch);
1269
1270 if (rc != EOK) {
1271 return rc;
1272 }
1273 return ret;
1274}
1275
1276int vfs_clone(int file, bool high_descriptor)
1277{
1278 async_exch_t *vfs_exch = vfs_exchange_begin();
1279 int rc = async_req_2_0(vfs_exch, VFS_IN_CLONE, (sysarg_t) file, (sysarg_t) high_descriptor);
1280 vfs_exchange_end(vfs_exch);
1281 return rc;
1282}
1283
[2f02aa17]1284/** @}
1285 */
Note: See TracBrowser for help on using the repository browser.