L4Re/departure

Annotated libfsclient/lib/src/client.cc

232:9cfd2fb90c33
2022-01-04 Paul Boddie Added initial support for file removal.
paul@90 1
/*
paul@90 2
 * Filesystem client functions.
paul@90 3
 *
paul@90 4
 * Copyright (C) 2018, 2019, 2020, 2021 Paul Boddie <paul@boddie.org.uk>
paul@90 5
 *
paul@90 6
 * This program is free software; you can redistribute it and/or
paul@90 7
 * modify it under the terms of the GNU General Public License as
paul@90 8
 * published by the Free Software Foundation; either version 2 of
paul@90 9
 * the License, or (at your option) any later version.
paul@90 10
 *
paul@90 11
 * This program is distributed in the hope that it will be useful,
paul@90 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@90 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@90 14
 * GNU General Public License for more details.
paul@90 15
 *
paul@90 16
 * You should have received a copy of the GNU General Public License
paul@90 17
 * along with this program; if not, write to the Free Software
paul@90 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@90 19
 * Boston, MA  02110-1301, USA
paul@90 20
 */
paul@90 21
paul@90 22
#include <l4/re/env.h>
paul@90 23
paul@90 24
#include <stdio.h>
paul@90 25
#include <stdlib.h>
paul@174 26
#include <string.h>
paul@90 27
paul@175 28
#include <systypes/fcntl.h>
paul@175 29
paul@90 30
#include "client.h"
paul@90 31
paul@90 32
paul@90 33
paul@90 34
/* Default size of pipe regions. */
paul@90 35
paul@90 36
const offset_t DEFAULT_PIPE_SIZE = 4096;
paul@90 37
paul@174 38
/* Size of the core member region of a directory entry structure. */
paul@174 39
paul@174 40
const offset_t DIRENT_CORE_SIZE = (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name));
paul@174 41
paul@90 42
paul@90 43
paul@169 44
/* Access the given position and synchronise state with the file object. Pipe
paul@169 45
   objects may return busy conditions indicating that the desired access cannot
paul@169 46
   yet be fulfilled. */
paul@117 47
paul@117 48
static long _access(file_t *file, offset_t position)
paul@117 49
{
paul@117 50
  long err;
paul@117 51
paul@171 52
  if (file->object_flags & OBJECT_SUPPORTS_MMAP)
paul@117 53
  {
paul@117 54
    /* Where the position is outside the current region, re-map. */
paul@117 55
paul@117 56
    if ((position < file->start_pos) || (position >= file->end_pos))
paul@117 57
    {
paul@117 58
      if (file_mmap(file, position, file_span(file)))
paul@117 59
        return -L4_EIO;
paul@117 60
    }
paul@117 61
paul@117 62
    /* Otherwise, flush any written data in the current region and update the
paul@117 63
       file size details. */
paul@117 64
paul@117 65
    else
paul@117 66
    {
paul@117 67
      err = client_flush(file);
paul@117 68
      if (err)
paul@117 69
        return err;
paul@117 70
    }
paul@165 71
paul@165 72
    /* Update the current data offset. */
paul@165 73
paul@165 74
    file->data_current = position - file->start_pos;
paul@165 75
paul@165 76
    return L4_EOK;
paul@117 77
  }
paul@117 78
  else
paul@117 79
  {
paul@192 80
    /* Handle the initial condition with no current region. */
paul@192 81
paul@192 82
    if (file->memory == NULL)
paul@192 83
    {
paul@192 84
      err = client_current_region(file);
paul@192 85
      if (err)
paul@192 86
        return err;
paul@192 87
    }
paul@192 88
paul@117 89
    /* Strict conditions for region navigation in pipes. */
paul@117 90
paul@117 91
    if ((position < file->start_pos) || (position > file->end_pos))
paul@117 92
    {
paul@117 93
      return -L4_EIO;
paul@117 94
    }
paul@117 95
paul@117 96
    /* The next region is only available at the end of the mapped memory. */
paul@117 97
paul@117 98
    else if (position == file->end_pos)
paul@117 99
    {
paul@117 100
      err = client_next_region(file);
paul@117 101
      if (err)
paul@117 102
        return err;
paul@117 103
paul@117 104
      file->data_current = 0;
paul@117 105
      return L4_EOK;
paul@117 106
    }
paul@117 107
paul@117 108
    /* Within the current pipe region, synchronise with the pipe object. */
paul@117 109
paul@117 110
    else
paul@165 111
      return client_current_region(file);
paul@117 112
  }
paul@117 113
}
paul@117 114
paul@117 115
paul@117 116
paul@162 117
/* Return whether an operation on file should block for more content or more
paul@169 118
   space. A file must be configured for blocking, not be closed, and must either
paul@169 119
   be lacking content (if reading) or space (if writing). */
paul@162 120
paul@162 121
static int _operation_blocking(file_t *file, int reading)
paul@162 122
{
paul@165 123
  return (file->can_block && !(file->notifications & NOTIFY_PEER_CLOSED) && (
paul@162 124
           (reading && !file_data_available(file)) ||
paul@162 125
           (!reading && !file_data_space(file))));
paul@162 126
}
paul@162 127
paul@162 128
paul@162 129
paul@117 130
/* Return whether an access could occur, blocking if necessary. */
paul@117 131
paul@162 132
static int _access_blocking(file_t *file, offset_t position, int reading)
paul@117 133
{
paul@117 134
  long err;
paul@117 135
paul@162 136
  /* Attempt to access the position, handling an error condition or a blocking
paul@162 137
     condition. */
paul@162 138
paul@162 139
  while ((err = _access(file, position)) || _operation_blocking(file, reading))
paul@117 140
  {
paul@165 141
    position = file->data_current;
paul@165 142
paul@117 143
    /* Exit if blocking is not configured or suitable. */
paul@117 144
paul@162 145
    if ((err && (err != -L4_EBUSY)) || !file->can_block)
paul@117 146
      return 0;
paul@117 147
paul@117 148
    /* Handle an inability to access by blocking, exiting if waiting failed. */
paul@117 149
paul@180 150
    if (client_wait_file(file, client_notifier_task()))
paul@117 151
      return 0;
paul@117 152
  }
paul@117 153
paul@117 154
  return 1;
paul@117 155
}
paul@117 156
paul@117 157
paul@117 158
paul@117 159
/* Ensure that memory is mapped for accessing the given file, using the
paul@117 160
   indicated count as a region size hint. */
paul@117 161
paul@117 162
static void *_map_memory(file_t *file, offset_t count)
paul@117 163
{
paul@117 164
  if (file->memory == NULL)
paul@117 165
  {
paul@171 166
    if (file->object_flags & OBJECT_SUPPORTS_MMAP)
paul@117 167
      return client_mmap(file, client_tell(file), count);
paul@117 168
    else if (pipe_current(file))
paul@117 169
      return NULL;
paul@117 170
  }
paul@117 171
paul@117 172
  return file->memory;
paul@117 173
}
paul@117 174
paul@117 175
paul@117 176
paul@144 177
/* Open a file opening object. */
paul@144 178
paul@150 179
l4_cap_idx_t client_open_for_user(user_t user)
paul@144 180
{
paul@144 181
  l4_cap_idx_t server = l4re_env_get_cap("server");
paul@144 182
paul@150 183
  return client_open_for_user_using(user, server);
paul@144 184
}
paul@144 185
paul@144 186
/* Open a file opening object via a named capability. */
paul@144 187
paul@150 188
l4_cap_idx_t client_open_for_user_using(user_t user, l4_cap_idx_t server)
paul@144 189
{
paul@144 190
  if (l4_is_invalid_cap(server))
paul@144 191
    return L4_INVALID_CAP;
paul@144 192
paul@144 193
  l4_cap_idx_t opener;
paul@150 194
  long err = file_open_for_user(user, server, &opener);
paul@144 195
paul@144 196
  if (err)
paul@144 197
    return L4_INVALID_CAP;
paul@144 198
paul@144 199
  return opener;
paul@144 200
}
paul@144 201
paul@144 202
paul@144 203
paul@90 204
/* Close a filesystem object. */
paul@90 205
paul@90 206
void client_close(file_t *file)
paul@90 207
{
paul@90 208
  if (file == NULL)
paul@90 209
    return;
paul@90 210
paul@159 211
  file_flush(file);
paul@90 212
  file_close(file);
paul@90 213
  free(file);
paul@90 214
}
paul@90 215
paul@90 216
paul@90 217
paul@90 218
/* Open a filesystem object. */
paul@90 219
paul@90 220
file_t *client_open(const char *name, flags_t flags)
paul@90 221
{
paul@144 222
  l4_cap_idx_t server = l4re_env_get_cap("server");
paul@144 223
paul@144 224
  return client_open_using(name, flags, server);
paul@103 225
}
paul@103 226
paul@103 227
/* Open a filesystem object via a named capability. */
paul@103 228
paul@144 229
file_t *client_open_using(const char *name, flags_t flags, l4_cap_idx_t server)
paul@103 230
{
paul@144 231
  if (l4_is_invalid_cap(server))
paul@144 232
    return NULL;
paul@144 233
paul@90 234
  file_t *file = (file_t *) malloc(sizeof(file_t));
paul@90 235
paul@90 236
  if (file == NULL)
paul@90 237
    return NULL;
paul@90 238
paul@90 239
  if (file_open(file, name, flags, server))
paul@90 240
  {
paul@90 241
    free(file);
paul@90 242
    return NULL;
paul@90 243
  }
paul@90 244
paul@90 245
  return file;
paul@90 246
}
paul@90 247
paul@90 248
paul@90 249
paul@204 250
/* Open a directory listing stream via the given named directory. */
paul@204 251
paul@204 252
file_t *client_opendir(const char *name)
paul@204 253
{
paul@204 254
  l4_cap_idx_t server = l4re_env_get_cap("server");
paul@204 255
paul@204 256
  return client_opendir_using(name, server);
paul@204 257
}
paul@204 258
paul@204 259
/* Open a directory listing stream via the given named directory and a named
paul@204 260
   capability. */
paul@204 261
paul@204 262
file_t *client_opendir_using(const char *name, l4_cap_idx_t server)
paul@204 263
{
paul@204 264
  file_t *file = client_open_using(name, O_DIRECTORY, server);
paul@204 265
paul@204 266
  if (file == NULL)
paul@204 267
    return NULL;
paul@204 268
paul@204 269
  file_t *reader = client_opendir_at(file);
paul@204 270
paul@204 271
  if (reader == NULL)
paul@204 272
    return NULL;
paul@204 273
paul@204 274
  /* Release the directory and return the reader. */
paul@204 275
paul@204 276
  client_close(file);
paul@204 277
  return reader;
paul@204 278
}
paul@204 279
paul@204 280
paul@204 281
paul@202 282
/* Open a directory listing stream via the given directory. */
paul@175 283
paul@204 284
file_t *client_opendir_at(file_t *file)
paul@175 285
{
paul@202 286
  if (file == NULL)
paul@202 287
    return NULL;
paul@175 288
paul@202 289
  file_t *reader = (file_t *) malloc(sizeof(file_t));
paul@175 290
paul@202 291
  if (reader == NULL)
paul@202 292
    return NULL;
paul@175 293
paul@202 294
  long err = directory_opendir(file, reader);
paul@202 295
paul@202 296
  if (err)
paul@175 297
    return NULL;
paul@175 298
paul@175 299
  /* Set blocking read mode to be able to conveniently read directory entries
paul@175 300
     from the stream. */
paul@175 301
paul@202 302
  if (client_set_blocking(reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))
paul@175 303
  {
paul@202 304
    client_close(reader);
paul@175 305
    return NULL;
paul@175 306
  }
paul@175 307
paul@202 308
  return reader;
paul@175 309
}
paul@175 310
paul@175 311
paul@175 312
paul@90 313
/* Open a pipe object. */
paul@90 314
paul@178 315
long client_pipe(file_t **reader, file_t **writer, flags_t flags)
paul@90 316
{
paul@144 317
  l4_cap_idx_t server = l4re_env_get_cap("pipes");
paul@144 318
paul@178 319
  return client_pipe_using(reader, writer, flags, server);
paul@135 320
}
paul@135 321
paul@178 322
long client_pipe_using(file_t **reader, file_t **writer, flags_t flags, l4_cap_idx_t server)
paul@135 323
{
paul@144 324
  if (l4_is_invalid_cap(server))
paul@144 325
    return -L4_EINVAL;
paul@144 326
paul@90 327
  *reader = (file_t *) malloc(sizeof(file_t));
paul@90 328
paul@90 329
  if (*reader == NULL)
paul@90 330
    return -L4_ENOMEM;
paul@90 331
paul@90 332
  *writer = (file_t *) malloc(sizeof(file_t));
paul@90 333
paul@90 334
  if (*writer == NULL)
paul@90 335
  {
paul@90 336
    free(*reader);
paul@90 337
    return -L4_ENOMEM;
paul@90 338
  }
paul@90 339
paul@90 340
  long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server);
paul@90 341
paul@178 342
  /* Set blocking if successful and non-blocking is not indicated. */
paul@178 343
paul@178 344
  if (!err && !(flags & O_NONBLOCK))
paul@178 345
  {
paul@178 346
    err = client_set_blocking(*reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED);
paul@178 347
    if (!err)
paul@178 348
      err = client_set_blocking(*writer, NOTIFY_SPACE_AVAILABLE | NOTIFY_PEER_CLOSED);
paul@178 349
  }
paul@178 350
paul@90 351
  if (err)
paul@90 352
  {
paul@90 353
    free(*reader);
paul@90 354
    free(*writer);
paul@90 355
  }
paul@90 356
paul@90 357
  return err;
paul@90 358
}
paul@90 359
paul@90 360
paul@90 361
paul@232 362
/* Remove a file from the filesystem. */
paul@232 363
paul@232 364
long client_remove(const char *name)
paul@232 365
{
paul@232 366
  l4_cap_idx_t server = l4re_env_get_cap("server");
paul@232 367
paul@232 368
  return client_remove_using(name, server);
paul@232 369
}
paul@232 370
paul@232 371
/* Remove a file from the filesystem via a named capability. */
paul@232 372
paul@232 373
long client_remove_using(const char *name, l4_cap_idx_t server)
paul@232 374
{
paul@232 375
  return file_remove(name, server);
paul@232 376
}
paul@232 377
paul@232 378
paul@232 379
paul@174 380
/* Obtain the current region of a pipe. */
paul@174 381
paul@174 382
long client_current_region(file_t *file)
paul@174 383
{
paul@174 384
  if (file == NULL)
paul@174 385
    return -L4_EINVAL;
paul@174 386
paul@174 387
  return pipe_current(file);
paul@174 388
}
paul@174 389
paul@174 390
paul@174 391
paul@90 392
/* Flush data explicitly to the filesystem object. */
paul@90 393
paul@90 394
long client_flush(file_t *file)
paul@90 395
{
paul@90 396
  if (file == NULL)
paul@90 397
    return -L4_EINVAL;
paul@90 398
paul@90 399
  /* Flush and retain most buffer settings. */
paul@90 400
paul@90 401
  return file_flush(file);
paul@90 402
}
paul@90 403
paul@90 404
paul@90 405
paul@90 406
/* Map a memory region to a file. */
paul@90 407
paul@90 408
void *client_mmap(file_t *file, offset_t position, offset_t length)
paul@90 409
{
paul@90 410
  if ((file == NULL) || (file_mmap(file, position, length)))
paul@90 411
    return NULL;
paul@90 412
paul@90 413
  return file->memory;
paul@90 414
}
paul@90 415
paul@90 416
paul@90 417
paul@90 418
/* Obtain the next region of a pipe. */
paul@90 419
paul@116 420
long client_next_region(file_t *file)
paul@90 421
{
paul@116 422
  if (file == NULL)
paul@116 423
    return -L4_EINVAL;
paul@90 424
paul@116 425
  return pipe_next(file);
paul@90 426
}
paul@90 427
paul@90 428
paul@90 429
paul@180 430
/* Close a notifier object. */
paul@180 431
paul@180 432
void client_notifier_close(file_notifier_t *notifier)
paul@180 433
{
paul@180 434
  file_notify_close(notifier);
paul@180 435
}
paul@180 436
paul@180 437
/* Obtain a local notifier object. */
paul@180 438
paul@180 439
file_notifier_t *client_notifier_local()
paul@180 440
{
paul@180 441
  return file_notify_local();
paul@180 442
}
paul@180 443
paul@180 444
/* Obtain a task-wide notifier object. */
paul@180 445
paul@180 446
file_notifier_t *client_notifier_task()
paul@180 447
{
paul@180 448
  return file_notify_task();
paul@180 449
}
paul@180 450
paul@180 451
paul@180 452
paul@174 453
/* Read a directory entry. This must be freed by the caller after use. */
paul@174 454
paul@174 455
struct dirent *client_readdir(file_t *file)
paul@174 456
{
paul@174 457
  char buffer[DIRENT_CORE_SIZE];
paul@174 458
  offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE);
paul@174 459
paul@174 460
  /* Stop if no new structure can be successfully read. */
paul@174 461
paul@174 462
  if (nread != DIRENT_CORE_SIZE)
paul@174 463
    return NULL;
paul@174 464
paul@174 465
  struct dirent *dirent = (struct dirent *) buffer;
paul@174 466
  offset_t remaining = dirent->d_reclen - DIRENT_CORE_SIZE;
paul@174 467
paul@174 468
  /* Allocate a buffer for the complete structure. */
paul@174 469
paul@174 470
  char *entry = (char *) calloc(DIRENT_CORE_SIZE + remaining, sizeof(char));
paul@174 471
paul@174 472
  if (entry == NULL)
paul@174 473
    return NULL;
paul@174 474
paul@174 475
  /* Copy the start of the entry into a new buffer. */
paul@174 476
paul@174 477
  memcpy(entry, buffer, DIRENT_CORE_SIZE);
paul@174 478
paul@174 479
  /* Append to the entry buffer. */
paul@174 480
paul@174 481
  char *current = entry + DIRENT_CORE_SIZE;
paul@174 482
paul@174 483
  nread = client_read(file, current, remaining);
paul@174 484
paul@174 485
  /* Stop if no complete structure can be successfully read. */
paul@174 486
paul@174 487
  if (nread != remaining)
paul@174 488
  {
paul@174 489
    free(entry);
paul@174 490
    return NULL;
paul@174 491
  }
paul@174 492
paul@174 493
  return (struct dirent *) entry;
paul@174 494
}
paul@174 495
paul@174 496
paul@174 497
paul@90 498
/* Read from the filesystem object into the buffer provided. */
paul@90 499
paul@90 500
offset_t client_read(file_t *file, void *buf, offset_t count)
paul@90 501
{
paul@90 502
  if (file == NULL)
paul@90 503
    return 0;
paul@90 504
paul@100 505
  /* Map memory if none has been mapped so far. */
paul@100 506
paul@108 507
  if (_map_memory(file, count) == NULL)
paul@100 508
    return 0;
paul@100 509
paul@90 510
  /* Amount available in the descriptor buffer already. */
paul@90 511
paul@90 512
  offset_t available = file_data_available(file);
paul@90 513
  offset_t to_transfer, total = 0;
paul@90 514
paul@90 515
  while (count > 0)
paul@90 516
  {
paul@90 517
    /* If there is no data, try and obtain more data. */
paul@90 518
paul@90 519
    if (!available)
paul@90 520
    {
paul@90 521
      /* Flush any unwritten data, preparing to read from the file position at
paul@90 522
         the end of the data, and returning if no new data is available. */
paul@90 523
paul@162 524
      if (!_access_blocking(file, file_data_end_position(file), 1))
paul@90 525
        break;
paul@90 526
paul@90 527
      available = file_data_available(file);
paul@90 528
paul@90 529
      if (!available)
paul@90 530
        break;
paul@90 531
    }
paul@90 532
paul@90 533
    /* Transfer data into the supplied buffer. */
paul@90 534
paul@90 535
    to_transfer = available <= count ? available : count;
paul@90 536
paul@90 537
    file_data_read(file, (char *) buf, to_transfer);
paul@90 538
paul@90 539
    /* Update counters. */
paul@90 540
paul@90 541
    available -= to_transfer;
paul@90 542
paul@90 543
    count -= to_transfer;
paul@90 544
    total += to_transfer;
paul@90 545
paul@90 546
    buf = ((char *) buf + to_transfer);
paul@90 547
  }
paul@90 548
paul@90 549
  return total;
paul@90 550
}
paul@90 551
paul@90 552
paul@90 553
paul@90 554
/* Ensure that the buffer can provide the needed data. */
paul@90 555
paul@90 556
offset_t client_seek(file_t *file, offset_t offset, int whence)
paul@90 557
{
paul@90 558
  if (file == NULL)
paul@90 559
    return 0;
paul@90 560
paul@90 561
  offset_t position, current = file_data_current_position(file), change;
paul@90 562
paul@90 563
  switch (whence)
paul@90 564
  {
paul@90 565
    case SEEK_SET:
paul@90 566
      position = offset;
paul@90 567
      break;
paul@90 568
paul@90 569
    case SEEK_CUR:
paul@90 570
      position = current + offset;
paul@90 571
      break;
paul@90 572
paul@90 573
    case SEEK_END:
paul@90 574
      position = file->size + offset;
paul@90 575
      break;
paul@90 576
paul@90 577
    default:
paul@90 578
      /* NOTE: Set errno to EINVAL. */
paul@117 579
      return current;
paul@90 580
  }
paul@90 581
paul@90 582
  /* Retain the current position if unchanged. */
paul@90 583
paul@90 584
  if (position == current)
paul@90 585
    return position;
paul@90 586
paul@90 587
  /* Move forward in the file. */
paul@90 588
paul@90 589
  if (position > current)
paul@90 590
  {
paul@90 591
    change = position - current;
paul@90 592
paul@90 593
    /* Move towards the end of available data.
paul@90 594
       Request new data if not enough is available. */
paul@90 595
paul@90 596
    if (change <= file_data_available(file))
paul@90 597
    {
paul@90 598
      file->data_current += change;
paul@90 599
      return position;
paul@90 600
    }
paul@90 601
  }
paul@90 602
paul@90 603
  /* Move backward in the file. */
paul@90 604
paul@90 605
  else
paul@90 606
  {
paul@90 607
    change = current - position;
paul@90 608
paul@90 609
    /* Move towards the start of available data.
paul@90 610
       Request new data if moving beyond the start of the data. */
paul@90 611
paul@90 612
    if (change <= file->data_current)
paul@90 613
    {
paul@90 614
      file->data_current -= change;
paul@90 615
      return position;
paul@90 616
    }
paul@90 617
  }
paul@90 618
paul@90 619
  /* Handle unwritten data and reset the buffer for reading. */
paul@90 620
paul@117 621
  if (_access(file, position))
paul@117 622
    return current;
paul@117 623
paul@90 624
  return position;
paul@90 625
}
paul@90 626
paul@90 627
paul@90 628
paul@117 629
/* Set or unset blocking access for a file. */
paul@117 630
paul@122 631
long client_set_blocking(file_t *file, notify_flags_t flags)
paul@117 632
{
paul@117 633
  long err;
paul@117 634
paul@122 635
  if (file->can_block == flags)
paul@117 636
    return L4_EOK;
paul@117 637
paul@177 638
  /* Since blocking access is used with specific file notifications, the
paul@177 639
     per-task notifier is used. */
paul@117 640
paul@180 641
  file_notifier_t *notifier = client_notifier_task();
paul@180 642
paul@122 643
  if (flags)
paul@180 644
    err = client_subscribe(file, flags, notifier);
paul@117 645
  else
paul@180 646
    err = client_unsubscribe(file, notifier);
paul@117 647
paul@117 648
  if (err)
paul@117 649
    return err;
paul@117 650
paul@122 651
  file->can_block = flags;
paul@117 652
  return L4_EOK;
paul@117 653
}
paul@117 654
paul@117 655
paul@117 656
paul@117 657
/* Subscribe from events concerning a file. */
paul@117 658
paul@180 659
long client_subscribe(file_t *file, notify_flags_t flags, file_notifier_t *notifier)
paul@117 660
{
paul@117 661
  if (file == NULL)
paul@117 662
    return -L4_EINVAL;
paul@117 663
paul@180 664
  return file_notify_subscribe(file, flags, notifier);
paul@117 665
}
paul@117 666
paul@117 667
paul@117 668
paul@117 669
/* Return the current position in the file. */
paul@117 670
paul@90 671
long client_tell(file_t *file)
paul@90 672
{
paul@90 673
  if (file == NULL)
paul@90 674
    return -L4_EINVAL;
paul@90 675
paul@90 676
  return file_data_current_position(file);
paul@90 677
}
paul@90 678
paul@90 679
paul@90 680
paul@117 681
/* Unsubscribe from events concerning a file. */
paul@117 682
paul@180 683
long client_unsubscribe(file_t *file, file_notifier_t *notifier)
paul@117 684
{
paul@117 685
  if (file == NULL)
paul@117 686
    return -L4_EINVAL;
paul@117 687
paul@180 688
  return file_notify_unsubscribe(file, notifier);
paul@117 689
}
paul@117 690
paul@117 691
paul@117 692
paul@123 693
/* Wait for events involving a specific file. */
paul@123 694
paul@180 695
long client_wait_file(file_t *file, file_notifier_t *notifier)
paul@123 696
{
paul@123 697
  if (file == NULL)
paul@123 698
    return -L4_EINVAL;
paul@123 699
paul@180 700
  return file_notify_wait_file(file, notifier);
paul@123 701
}
paul@123 702
paul@123 703
/* Wait for events concerning files, referencing a file object if an event is
paul@123 704
   delivered. */
paul@123 705
paul@180 706
long client_wait_files(file_t **file, file_notifier_t *notifier)
paul@123 707
{
paul@180 708
  return file_notify_wait_files(file, notifier);
paul@117 709
}
paul@117 710
paul@117 711
paul@117 712
paul@90 713
/* Write to the filesystem object from the buffer provided. */
paul@90 714
paul@90 715
offset_t client_write(file_t *file, const void *buf, offset_t count)
paul@90 716
{
paul@90 717
  if (file == NULL)
paul@90 718
    return 0;
paul@90 719
paul@100 720
  /* Map memory if none has been mapped so far. */
paul@100 721
paul@108 722
  if (_map_memory(file, count) == NULL)
paul@100 723
    return 0;
paul@100 724
paul@90 725
  /* Attempt to ensure that the file can accept the amount of data to be
paul@90 726
     written. This may not resize to the needed amount if a file has a fixed
paul@90 727
     size, but data will still be written to any available space. */
paul@90 728
paul@90 729
  offset_t needed_size = file_data_current_position(file) + count;
paul@90 730
paul@171 731
  if (file->object_flags & OBJECT_HAS_SIZE)
paul@90 732
  {
paul@108 733
    if (file->size < needed_size)
paul@108 734
    {
paul@108 735
      file_resize(file, needed_size);
paul@90 736
paul@108 737
      if (file->size < needed_size)
paul@108 738
        count = file->size - file_data_current_position(file);
paul@108 739
    }
paul@90 740
  }
paul@90 741
paul@90 742
  /* Space remaining in the descriptor buffer. */
paul@90 743
paul@90 744
  offset_t space = file_data_space(file);
paul@90 745
  offset_t to_transfer, total = 0;
paul@90 746
paul@90 747
  while (count > 0)
paul@90 748
  {
paul@90 749
    /* If no space is available, try and send data, reset the buffer. */
paul@90 750
paul@90 751
    if (!space)
paul@90 752
    {
paul@90 753
      /* Flush any unwritten data and continue writing from the current data
paul@90 754
         position. */
paul@90 755
paul@162 756
      if (!_access_blocking(file, file_data_current_position(file), 0))
paul@90 757
        break;
paul@90 758
paul@90 759
      space = file_data_space(file);
paul@90 760
    }
paul@90 761
paul@90 762
    /* Transfer data into the supplied buffer. */
paul@90 763
paul@90 764
    to_transfer = space <= count ? space : count;
paul@90 765
paul@90 766
    file_data_write(file, (char *) buf, to_transfer);
paul@90 767
paul@90 768
    /* Update counters. */
paul@90 769
paul@90 770
    space -= to_transfer;
paul@90 771
paul@90 772
    count -= to_transfer;
paul@90 773
    total += to_transfer;
paul@90 774
paul@90 775
    buf = ((char *) buf + to_transfer);
paul@90 776
  }
paul@90 777
paul@90 778
  return total;
paul@90 779
}
paul@90 780
paul@90 781
// vim: tabstop=2 expandtab shiftwidth=2