1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef __STDC_LIMIT_MACROS
6#define __STDC_LIMIT_MACROS
7#endif
8
9#include "nacl_io/memfs/mem_fs_node.h"
10
11#include <assert.h>
12#include <errno.h>
13#include <string.h>
14
15#include <algorithm>
16
17#include "nacl_io/kernel_handle.h"
18#include "nacl_io/osinttypes.h"
19#include "nacl_io/osstat.h"
20#include "sdk_util/auto_lock.h"
21
22namespace nacl_io {
23
24namespace {
25
26// The maximum size to reserve in addition to the requested size. Resize() will
27// allocate twice as much as requested, up to this value.
28const size_t kMaxResizeIncrement = 16 * 1024 * 1024;
29
30}  // namespace
31
32MemFsNode::MemFsNode(Filesystem* filesystem)
33  : Node(filesystem),
34    data_(NULL),
35    data_capacity_(0) {
36  SetType(S_IFREG);
37}
38
39MemFsNode::~MemFsNode() {
40  free(data_);
41}
42
43Error MemFsNode::Read(const HandleAttr& attr,
44                      void* buf,
45                      size_t count,
46                      int* out_bytes) {
47  *out_bytes = 0;
48
49  AUTO_LOCK(node_lock_);
50  if (count == 0)
51    return 0;
52
53  size_t size = stat_.st_size;
54
55  if (attr.offs + count > size) {
56    count = size - attr.offs;
57  }
58
59  memcpy(buf, data_ + attr.offs, count);
60  *out_bytes = static_cast<int>(count);
61  return 0;
62}
63
64Error MemFsNode::Write(const HandleAttr& attr,
65                       const void* buf,
66                       size_t count,
67                       int* out_bytes) {
68  *out_bytes = 0;
69
70  if (count == 0)
71    return 0;
72
73  AUTO_LOCK(node_lock_);
74  off_t new_size = attr.offs + count;
75  if (new_size > stat_.st_size) {
76    Error error = Resize(new_size);
77    if (error) {
78      LOG_ERROR("memfs: resize (%" PRIoff ") failed: %s", new_size,
79          strerror(error));
80      return error;
81    }
82  }
83
84  memcpy(data_ + attr.offs, buf, count);
85  *out_bytes = static_cast<int>(count);
86  return 0;
87}
88
89Error MemFsNode::FTruncate(off_t new_size) {
90  AUTO_LOCK(node_lock_);
91  return Resize(new_size);
92}
93
94Error MemFsNode::Resize(off_t new_length) {
95  if (new_length < 0)
96    return EINVAL;
97  size_t new_size = static_cast<size_t>(new_length);
98
99  size_t new_capacity = data_capacity_;
100  if (new_size > data_capacity_) {
101    // While the node size is small, grow exponentially. When it starts to get
102    // larger, grow linearly.
103    size_t extra = std::min(new_size, kMaxResizeIncrement);
104    new_capacity = new_size + extra;
105  } else if (new_length < stat_.st_size) {
106    // Shrinking capacity
107    new_capacity = new_size;
108  }
109
110  if (new_capacity != data_capacity_) {
111    data_ = (char*)realloc(data_, new_capacity);
112    if (new_capacity != 0) {
113      assert(data_ != NULL);
114      if (data_ == NULL)
115        return ENOMEM;
116    }
117    data_capacity_ = new_capacity;
118  }
119
120  if (new_length > stat_.st_size)
121    memset(data_ + stat_.st_size, 0, new_length - stat_.st_size);
122  stat_.st_size = new_length;
123  return 0;
124}
125
126Error MemFsNode::Fchmod(mode_t mode) {
127  AUTO_LOCK(node_lock_);
128  SetMode(mode);
129  return 0;
130}
131
132}  // namespace nacl_io
133