aboutsummaryrefslogtreecommitdiff
path: root/libc/src/__support/OSUtil/linux/fcntl.cpp
blob: 08db4859c6417ba5ba8f0d368db5e7bbc10ccc31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//===-- Implementation of internal fcntl ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/OSUtil/fcntl.h"

#include "hdr/errno_macros.h"
#include "hdr/fcntl_macros.h"
#include "hdr/types/mode_t.h"
#include "hdr/types/off_t.h"
#include "hdr/types/struct_f_owner_ex.h"
#include "hdr/types/struct_flock.h"
#include "hdr/types/struct_flock64.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"

#include <sys/syscall.h> // For syscall numbers.

namespace LIBC_NAMESPACE_DECL {
namespace internal {

ErrorOr<int> fcntl(int fd, int cmd, void *arg) {
#if SYS_fcntl
  constexpr auto FCNTL_SYSCALL_ID = SYS_fcntl;
#elif defined(SYS_fcntl64)
  constexpr auto FCNTL_SYSCALL_ID = SYS_fcntl64;
#else
#error "fcntl and fcntl64 syscalls not available."
#endif

  switch (cmd) {
  case F_OFD_SETLKW: {
    struct flock *flk = reinterpret_cast<struct flock *>(arg);
    // convert the struct to a flock64
    struct flock64 flk64;
    flk64.l_type = flk->l_type;
    flk64.l_whence = flk->l_whence;
    flk64.l_start = flk->l_start;
    flk64.l_len = flk->l_len;
    flk64.l_pid = flk->l_pid;
    // create a syscall
    int ret =
        LIBC_NAMESPACE::syscall_impl<int>(FCNTL_SYSCALL_ID, fd, cmd, &flk64);
    if (ret < 0)
      return Error(-ret);
    return ret;
  }
  case F_OFD_GETLK:
  case F_OFD_SETLK: {
    struct flock *flk = reinterpret_cast<struct flock *>(arg);
    // convert the struct to a flock64
    struct flock64 flk64;
    flk64.l_type = flk->l_type;
    flk64.l_whence = flk->l_whence;
    flk64.l_start = flk->l_start;
    flk64.l_len = flk->l_len;
    flk64.l_pid = flk->l_pid;
    // create a syscall
    int ret =
        LIBC_NAMESPACE::syscall_impl<int>(FCNTL_SYSCALL_ID, fd, cmd, &flk64);
    // On failure, return
    if (ret < 0)
      return Error(-ret);
    // Check for overflow, i.e. the offsets are not the same when cast
    // to off_t from off64_t.
    if (static_cast<off_t>(flk64.l_len) != flk64.l_len ||
        static_cast<off_t>(flk64.l_start) != flk64.l_start)
      return Error(EOVERFLOW);

    // Now copy back into flk, in case flk64 got modified
    flk->l_type = flk64.l_type;
    flk->l_whence = flk64.l_whence;
    flk->l_start = static_cast<decltype(flk->l_start)>(flk64.l_start);
    flk->l_len = static_cast<decltype(flk->l_len)>(flk64.l_len);
    flk->l_pid = flk64.l_pid;
    return ret;
  }
  case F_GETOWN: {
    struct f_owner_ex fex;
    int ret = LIBC_NAMESPACE::syscall_impl<int>(FCNTL_SYSCALL_ID, fd,
                                                F_GETOWN_EX, &fex);
    if (ret < 0)
      return Error(-ret);
    return fex.type == F_OWNER_PGRP ? -fex.pid : fex.pid;
  }
#ifdef SYS_fcntl64
  case F_GETLK: {
    if constexpr (FCNTL_SYSCALL_ID == SYS_fcntl64)
      cmd = F_GETLK64;
    break;
  }
  case F_SETLK: {
    if constexpr (FCNTL_SYSCALL_ID == SYS_fcntl64)
      cmd = F_SETLK64;
    break;
  }
  case F_SETLKW: {
    if constexpr (FCNTL_SYSCALL_ID == SYS_fcntl64)
      cmd = F_SETLKW64;
    break;
  }
#endif
  }

  // default, but may use rewritten cmd from above.
  int ret = LIBC_NAMESPACE::syscall_impl<int>(FCNTL_SYSCALL_ID, fd, cmd,
                                              reinterpret_cast<void *>(arg));
  if (ret < 0)
    return Error(-ret);
  return ret;
}

ErrorOr<int> open(const char *path, int flags, mode_t mode_flags) {
#ifdef SYS_open
  int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, flags, mode_flags);
#else
  int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, flags,
                                             mode_flags);
#endif
  if (fd < 0)
    return Error(-fd);

  return fd;
}

ErrorOr<int> close(int fd) {
  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_close, fd);

  if (ret < 0)
    return Error(-ret);

  return ret;
}

} // namespace internal
} // namespace LIBC_NAMESPACE_DECL