L4Re/departure

Annotated libfsclient/lib/src/client.cc

236:b769dcb3f4b3
2022-01-27 Paul Boddie Added initial support for renaming filesystem objects.
paul@90 1
/*
paul@90 2
 * Filesystem client functions.
paul@90 3
 *
paul@236 4
 * Copyright (C) 2018, 2019, 2020, 2021, 2022 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@236 380
/* Rename a file in the filesystem. */
paul@236 381
paul@236 382
long client_rename(const char *source, const char *target)
paul@236 383
{
paul@236 384
  l4_cap_idx_t server = l4re_env_get_cap("server");
paul@236 385
paul@236 386
  return client_rename_using(source, target, server);
paul@236 387
}
paul@236 388
paul@236 389
/* Rename a file in the filesystem via a named capability. */
paul@236 390
paul@236 391
long client_rename_using(const char *source, const char *target, l4_cap_idx_t server)
paul@236 392
{
paul@236 393
  return file_rename(source, target, server);
paul@236 394
}
paul@236 395
paul@236 396
paul@236 397
paul@174 398
/* Obtain the current region of a pipe. */
paul@174 399
paul@174 400
long client_current_region(file_t *file)
paul@174 401
{
paul@174 402
  if (file == NULL)
paul@174 403
    return -L4_EINVAL;
paul@174 404
paul@174 405
  return pipe_current(file);
paul@174 406
}
paul@174 407
paul@174 408
paul@174 409
paul@90 410
/* Flush data explicitly to the filesystem object. */
paul@90 411
paul@90 412
long client_flush(file_t *file)
paul@90 413
{
paul@90 414
  if (file == NULL)
paul@90 415
    return -L4_EINVAL;
paul@90 416
paul@90 417
  /* Flush and retain most buffer settings. */
paul@90 418
paul@90 419
  return file_flush(file);
paul@90 420
}
paul@90 421
paul@90 422
paul@90 423
paul@90 424
/* Map a memory region to a file. */
paul@90 425
paul@90 426
void *client_mmap(file_t *file, offset_t position, offset_t length)
paul@90 427
{
paul@90 428
  if ((file == NULL) || (file_mmap(file, position, length)))
paul@90 429
    return NULL;
paul@90 430
paul@90 431
  return file->memory;
paul@90 432
}
paul@90 433
paul@90 434
paul@90 435
paul@90 436
/* Obtain the next region of a pipe. */
paul@90 437
paul@116 438
long client_next_region(file_t *file)
paul@90 439
{
paul@116 440
  if (file == NULL)
paul@116 441
    return -L4_EINVAL;
paul@90 442
paul@116 443
  return pipe_next(file);
paul@90 444
}
paul@90 445
paul@90 446
paul@90 447
paul@180 448
/* Close a notifier object. */
paul@180 449
paul@180 450
void client_notifier_close(file_notifier_t *notifier)
paul@180 451
{
paul@180 452
  file_notify_close(notifier);
paul@180 453
}
paul@180 454
paul@180 455
/* Obtain a local notifier object. */
paul@180 456
paul@180 457
file_notifier_t *client_notifier_local()
paul@180 458
{
paul@180 459
  return file_notify_local();
paul@180 460
}
paul@180 461
paul@180 462
/* Obtain a task-wide notifier object. */
paul@180 463
paul@180 464
file_notifier_t *client_notifier_task()
paul@180 465
{
paul@180 466
  return file_notify_task();
paul@180 467
}
paul@180 468
paul@180 469
paul@180 470
paul@174 471
/* Read a directory entry. This must be freed by the caller after use. */
paul@174 472
paul@174 473
struct dirent *client_readdir(file_t *file)
paul@174 474
{
paul@174 475
  char buffer[DIRENT_CORE_SIZE];
paul@174 476
  offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE);
paul@174 477
paul@174 478
  /* Stop if no new structure can be successfully read. */
paul@174 479
paul@174 480
  if (nread != DIRENT_CORE_SIZE)
paul@174 481
    return NULL;
paul@174 482
paul@174 483
  struct dirent *dirent = (struct dirent *) buffer;
paul@174 484
  offset_t remaining = dirent->d_reclen - DIRENT_CORE_SIZE;
paul@174 485
paul@174 486
  /* Allocate a buffer for the complete structure. */
paul@174 487
paul@174 488
  char *entry = (char *) calloc(DIRENT_CORE_SIZE + remaining, sizeof(char));
paul@174 489
paul@174 490
  if (entry == NULL)
paul@174 491
    return NULL;
paul@174 492
paul@174 493
  /* Copy the start of the entry into a new buffer. */
paul@174 494
paul@174 495
  memcpy(entry, buffer, DIRENT_CORE_SIZE);
paul@174 496
paul@174 497
  /* Append to the entry buffer. */
paul@174 498
paul@174 499
  char *current = entry + DIRENT_CORE_SIZE;
paul@174 500
paul@174 501
  nread = client_read(file, current, remaining);
paul@174 502
paul@174 503
  /* Stop if no complete structure can be successfully read. */
paul@174 504
paul@174 505
  if (nread != remaining)
paul@174 506
  {
paul@174 507
    free(entry);
paul@174 508
    return NULL;
paul@174 509
  }
paul@174 510
paul@174 511
  return (struct dirent *) entry;
paul@174 512
}
paul@174 513
paul@174 514
paul@174 515
paul@90 516
/* Read from the filesystem object into the buffer provided. */
paul@90 517
paul@90 518
offset_t client_read(file_t *file, void *buf, offset_t count)
paul@90 519
{
paul@90 520
  if (file == NULL)
paul@90 521
    return 0;
paul@90 522
paul@100 523
  /* Map memory if none has been mapped so far. */
paul@100 524
paul@108 525
  if (_map_memory(file, count) == NULL)
paul@100 526
    return 0;
paul@100 527
paul@90 528
  /* Amount available in the descriptor buffer already. */
paul@90 529
paul@90 530
  offset_t available = file_data_available(file);
paul@90 531
  offset_t to_transfer, total = 0;
paul@90 532
paul@90 533
  while (count > 0)
paul@90 534
  {
paul@90 535
    /* If there is no data, try and obtain more data. */
paul@90 536
paul@90 537
    if (!available)
paul@90 538
    {
paul@90 539
      /* Flush any unwritten data, preparing to read from the file position at
paul@90 540
         the end of the data, and returning if no new data is available. */
paul@90 541
paul@162 542
      if (!_access_blocking(file, file_data_end_position(file), 1))
paul@90 543
        break;
paul@90 544
paul@90 545
      available = file_data_available(file);
paul@90 546
paul@90 547
      if (!available)
paul@90 548
        break;
paul@90 549
    }
paul@90 550
paul@90 551
    /* Transfer data into the supplied buffer. */
paul@90 552
paul@90 553
    to_transfer = available <= count ? available : count;
paul@90 554
paul@90 555
    file_data_read(file, (char *) buf, to_transfer);
paul@90 556
paul@90 557
    /* Update counters. */
paul@90 558
paul@90 559
    available -= to_transfer;
paul@90 560
paul@90 561
    count -= to_transfer;
paul@90 562
    total += to_transfer;
paul@90 563
paul@90 564
    buf = ((char *) buf + to_transfer);
paul@90 565
  }
paul@90 566
paul@90 567
  return total;
paul@90 568
}
paul@90 569
paul@90 570
paul@90 571
paul@90 572
/* Ensure that the buffer can provide the needed data. */
paul@90 573
paul@90 574
offset_t client_seek(file_t *file, offset_t offset, int whence)
paul@90 575
{
paul@90 576
  if (file == NULL)
paul@90 577
    return 0;
paul@90 578
paul@90 579
  offset_t position, current = file_data_current_position(file), change;
paul@90 580
paul@90 581
  switch (whence)
paul@90 582
  {
paul@90 583
    case SEEK_SET:
paul@90 584
      position = offset;
paul@90 585
      break;
paul@90 586
paul@90 587
    case SEEK_CUR:
paul@90 588
      position = current + offset;
paul@90 589
      break;
paul@90 590
paul@90 591
    case SEEK_END:
paul@90 592
      position = file->size + offset;
paul@90 593
      break;
paul@90 594
paul@90 595
    default:
paul@90 596
      /* NOTE: Set errno to EINVAL. */
paul@117 597
      return current;
paul@90 598
  }
paul@90 599
paul@90 600
  /* Retain the current position if unchanged. */
paul@90 601
paul@90 602
  if (position == current)
paul@90 603
    return position;
paul@90 604
paul@90 605
  /* Move forward in the file. */
paul@90 606
paul@90 607
  if (position > current)
paul@90 608
  {
paul@90 609
    change = position - current;
paul@90 610
paul@90 611
    /* Move towards the end of available data.
paul@90 612
       Request new data if not enough is available. */
paul@90 613
paul@90 614
    if (change <= file_data_available(file))
paul@90 615
    {
paul@90 616
      file->data_current += change;
paul@90 617
      return position;
paul@90 618
    }
paul@90 619
  }
paul@90 620
paul@90 621
  /* Move backward in the file. */
paul@90 622
paul@90 623
  else
paul@90 624
  {
paul@90 625
    change = current - position;
paul@90 626
paul@90 627
    /* Move towards the start of available data.
paul@90 628
       Request new data if moving beyond the start of the data. */
paul@90 629
paul@90 630
    if (change <= file->data_current)
paul@90 631
    {
paul@90 632
      file->data_current -= change;
paul@90 633
      return position;
paul@90 634
    }
paul@90 635
  }
paul@90 636
paul@90 637
  /* Handle unwritten data and reset the buffer for reading. */
paul@90 638
paul@117 639
  if (_access(file, position))
paul@117 640
    return current;
paul@117 641
paul@90 642
  return position;
paul@90 643
}
paul@90 644
paul@90 645
paul@90 646
paul@117 647
/* Set or unset blocking access for a file. */
paul@117 648
paul@122 649
long client_set_blocking(file_t *file, notify_flags_t flags)
paul@117 650
{
paul@117 651
  long err;
paul@117 652
paul@122 653
  if (file->can_block == flags)
paul@117 654
    return L4_EOK;
paul@117 655
paul@177 656
  /* Since blocking access is used with specific file notifications, the
paul@177 657
     per-task notifier is used. */
paul@117 658
paul@180 659
  file_notifier_t *notifier = client_notifier_task();
paul@180 660
paul@122 661
  if (flags)
paul@180 662
    err = client_subscribe(file, flags, notifier);
paul@117 663
  else
paul@180 664
    err = client_unsubscribe(file, notifier);
paul@117 665
paul@117 666
  if (err)
paul@117 667
    return err;
paul@117 668
paul@122 669
  file->can_block = flags;
paul@117 670
  return L4_EOK;
paul@117 671
}
paul@117 672
paul@117 673
paul@117 674
paul@117 675
/* Subscribe from events concerning a file. */
paul@117 676
paul@180 677
long client_subscribe(file_t *file, notify_flags_t flags, file_notifier_t *notifier)
paul@117 678
{
paul@117 679
  if (file == NULL)
paul@117 680
    return -L4_EINVAL;
paul@117 681
paul@180 682
  return file_notify_subscribe(file, flags, notifier);
paul@117 683
}
paul@117 684
paul@117 685
paul@117 686
paul@117 687
/* Return the current position in the file. */
paul@117 688
paul@90 689
long client_tell(file_t *file)
paul@90 690
{
paul@90 691
  if (file == NULL)
paul@90 692
    return -L4_EINVAL;
paul@90 693
paul@90 694
  return file_data_current_position(file);
paul@90 695
}
paul@90 696
paul@90 697
paul@90 698
paul@117 699
/* Unsubscribe from events concerning a file. */
paul@117 700
paul@180 701
long client_unsubscribe(file_t *file, file_notifier_t *notifier)
paul@117 702
{
paul@117 703
  if (file == NULL)
paul@117 704
    return -L4_EINVAL;
paul@117 705
paul@180 706
  return file_notify_unsubscribe(file, notifier);
paul@117 707
}
paul@117 708
paul@117 709
paul@117 710
paul@123 711
/* Wait for events involving a specific file. */
paul@123 712
paul@180 713
long client_wait_file(file_t *file, file_notifier_t *notifier)
paul@123 714
{
paul@123 715
  if (file == NULL)
paul@123 716
    return -L4_EINVAL;
paul@123 717
paul@180 718
  return file_notify_wait_file(file, notifier);
paul@123 719
}
paul@123 720
paul@123 721
/* Wait for events concerning files, referencing a file object if an event is
paul@123 722
   delivered. */
paul@123 723
paul@180 724
long client_wait_files(file_t **file, file_notifier_t *notifier)
paul@123 725
{
paul@180 726
  return file_notify_wait_files(file, notifier);
paul@117 727
}
paul@117 728
paul@117 729
paul@117 730
paul@90 731
/* Write to the filesystem object from the buffer provided. */
paul@90 732
paul@90 733
offset_t client_write(file_t *file, const void *buf, offset_t count)
paul@90 734
{
paul@90 735
  if (file == NULL)
paul@90 736
    return 0;
paul@90 737
paul@100 738
  /* Map memory if none has been mapped so far. */
paul@100 739
paul@108 740
  if (_map_memory(file, count) == NULL)
paul@100 741
    return 0;
paul@100 742
paul@90 743
  /* Attempt to ensure that the file can accept the amount of data to be
paul@90 744
     written. This may not resize to the needed amount if a file has a fixed
paul@90 745
     size, but data will still be written to any available space. */
paul@90 746
paul@90 747
  offset_t needed_size = file_data_current_position(file) + count;
paul@90 748
paul@171 749
  if (file->object_flags & OBJECT_HAS_SIZE)
paul@90 750
  {
paul@108 751
    if (file->size < needed_size)
paul@108 752
    {
paul@108 753
      file_resize(file, needed_size);
paul@90 754
paul@108 755
      if (file->size < needed_size)
paul@108 756
        count = file->size - file_data_current_position(file);
paul@108 757
    }
paul@90 758
  }
paul@90 759
paul@90 760
  /* Space remaining in the descriptor buffer. */
paul@90 761
paul@90 762
  offset_t space = file_data_space(file);
paul@90 763
  offset_t to_transfer, total = 0;
paul@90 764
paul@90 765
  while (count > 0)
paul@90 766
  {
paul@90 767
    /* If no space is available, try and send data, reset the buffer. */
paul@90 768
paul@90 769
    if (!space)
paul@90 770
    {
paul@90 771
      /* Flush any unwritten data and continue writing from the current data
paul@90 772
         position. */
paul@90 773
paul@162 774
      if (!_access_blocking(file, file_data_current_position(file), 0))
paul@90 775
        break;
paul@90 776
paul@90 777
      space = file_data_space(file);
paul@90 778
    }
paul@90 779
paul@90 780
    /* Transfer data into the supplied buffer. */
paul@90 781
paul@90 782
    to_transfer = space <= count ? space : count;
paul@90 783
paul@90 784
    file_data_write(file, (char *) buf, to_transfer);
paul@90 785
paul@90 786
    /* Update counters. */
paul@90 787
paul@90 788
    space -= to_transfer;
paul@90 789
paul@90 790
    count -= to_transfer;
paul@90 791
    total += to_transfer;
paul@90 792
paul@90 793
    buf = ((char *) buf + to_transfer);
paul@90 794
  }
paul@90 795
paul@90 796
  return total;
paul@90 797
}
paul@90 798
paul@90 799
// vim: tabstop=2 expandtab shiftwidth=2