13cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich/*
23cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * Copyright (C) 2015 The Android Open Source Project
33cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * All rights reserved.
43cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *
53cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * Redistribution and use in source and binary forms, with or without
63cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * modification, are permitted provided that the following conditions
73cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * are met:
83cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *  * Redistributions of source code must retain the above copyright
93cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *    notice, this list of conditions and the following disclaimer.
103cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *  * Redistributions in binary form must reproduce the above copyright
113cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *    notice, this list of conditions and the following disclaimer in
123cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *    the documentation and/or other materials provided with the
133cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *    distribution.
143cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich *
153cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
163cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
173cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
183cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
193cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
203cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
213cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
223cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
233cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
243cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
253cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich * SUCH DAMAGE.
273cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich */
283cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
293cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include <fcntl.h>
303cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include <sys/stat.h>
313cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include <sys/types.h>
323cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include <errno.h>
333cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include <unistd.h>
343cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
353cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich#include "private/ErrnoRestorer.h"
363cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
3700490ae3f351e07ed4cc2a94b11cba6a22f37311Nick Kralevichextern "C" int ___fchmodat(int, const char*, mode_t);
383cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
393cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevichint fchmodat(int dirfd, const char* pathname, mode_t mode, int flags) {
403cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich  if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) {
413cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    errno = EINVAL;
423cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    return -1;
433cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich  }
443cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
453cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich  if (flags & AT_SYMLINK_NOFOLLOW) {
463cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // Emulate AT_SYMLINK_NOFOLLOW using the mechanism described
473cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // at https://sourceware.org/bugzilla/show_bug.cgi?id=14578
483cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // comment #10
493cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
503cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    int fd = openat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
513cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    if (fd == -1) {
523cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich      return -1; // returns errno from openat
533cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    }
543cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
553cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // POSIX requires that ENOTSUP be returned when the system
563cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // doesn't support setting the mode of a symbolic link.
573cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // This is true for all Linux kernels.
583cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // We rely on the O_PATH compatibility layer added in the
593cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    // fchmod() function to get errno correct.
603cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    int result = fchmod(fd, mode);
613cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    ErrnoRestorer errno_restorer; // don't let close() clobber errno
623cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    close(fd);
633cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich    return result;
643cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich  }
653cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich
6600490ae3f351e07ed4cc2a94b11cba6a22f37311Nick Kralevich  return ___fchmodat(dirfd, pathname, mode);
673cbc6c627fe57c9a9783c52d148078f8d52f7b96Nick Kralevich}
68