1//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_consumer/file_descriptor.h"
18
19#include <fcntl.h>
20#include <linux/fs.h>
21#include <sys/ioctl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <base/posix/eintr_wrapper.h>
26
27namespace chromeos_update_engine {
28
29bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) {
30  CHECK_EQ(fd_, -1);
31  return ((fd_ = HANDLE_EINTR(open(path, flags, mode))) >= 0);
32}
33
34bool EintrSafeFileDescriptor::Open(const char* path, int flags) {
35  CHECK_EQ(fd_, -1);
36  return ((fd_ = HANDLE_EINTR(open(path, flags))) >= 0);
37}
38
39ssize_t EintrSafeFileDescriptor::Read(void* buf, size_t count) {
40  CHECK_GE(fd_, 0);
41  return HANDLE_EINTR(read(fd_, buf, count));
42}
43
44ssize_t EintrSafeFileDescriptor::Write(const void* buf, size_t count) {
45  CHECK_GE(fd_, 0);
46
47  // Attempt repeated writes, as long as some progress is being made.
48  char* char_buf = const_cast<char*>(reinterpret_cast<const char*>(buf));
49  ssize_t written = 0;
50  while (count > 0) {
51    ssize_t ret = HANDLE_EINTR(write(fd_, char_buf, count));
52
53    // Fail on either an error or no progress.
54    if (ret <= 0)
55      return (written ? written : ret);
56    written += ret;
57    count -= ret;
58    char_buf += ret;
59  }
60  return written;
61}
62
63off64_t EintrSafeFileDescriptor::Seek(off64_t offset, int whence) {
64  CHECK_GE(fd_, 0);
65  return lseek64(fd_, offset, whence);
66}
67
68bool EintrSafeFileDescriptor::BlkIoctl(int request,
69                                       uint64_t start,
70                                       uint64_t length,
71                                       int* result) {
72  // If the ioctl BLKZEROOUT is not defined, just fail to perform any of these
73  // operations.
74#ifndef BLKZEROOUT
75  return false;
76#else  // defined(BLKZEROOUT)
77  DCHECK(request == BLKDISCARD || request == BLKZEROOUT ||
78         request == BLKSECDISCARD);
79  // On some devices, the BLKDISCARD will actually read back as zeros, instead
80  // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
81  // case, so we issue a BLKDISCARD in those cases to speed up the writes.
82  unsigned int arg;
83  if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
84    request = BLKDISCARD;
85
86  // Ensure the |fd_| is in O_DIRECT mode during this operation, so the write
87  // cache for this region is invalidated. This is required since otherwise
88  // reading back this region could consume stale data from the cache.
89  int flags = fcntl(fd_, F_GETFL, 0);
90  if (flags == -1) {
91    PLOG(WARNING) << "Couldn't get flags on fd " << fd_;
92    return false;
93  }
94  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags | O_DIRECT) == -1) {
95    PLOG(WARNING) << "Couldn't set O_DIRECT on fd " << fd_;
96    return false;
97  }
98
99  uint64_t range[2] = {start, length};
100  *result = ioctl(fd_, request, range);
101
102  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags) == -1) {
103    PLOG(WARNING) << "Couldn't remove O_DIRECT on fd " << fd_;
104    return false;
105  }
106  return true;
107#endif  // defined(BLKZEROOUT)
108}
109
110bool EintrSafeFileDescriptor::Close() {
111  CHECK_GE(fd_, 0);
112  if (IGNORE_EINTR(close(fd_)))
113    return false;
114  Reset();
115  return true;
116}
117
118void EintrSafeFileDescriptor::Reset() {
119  fd_ = -1;
120}
121
122}  // namespace chromeos_update_engine
123