/*
 * SPDX-FileCopyrightText: 2026 Didier Kryn <kryn@in2p3.fr>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
/* open or create /media/username/target                       */
/* First create /media/username if it does not exist           */
/* return a file descriptor to the target or -1                */
/* In case of error, appropriate message is written to stderr. */
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/capability.h>
/*------------- Internationalization -------------*/
#include <libintl.h>
#define _(StRiNg) gettext(StRiNg)
#define gettext_noop(String) String
#define N_(String) gettext_noop(String)
/*------------------------------------------------*/

static int open_or_create_dirat(int dirfd, const char *name, uid_t uid, gid_t gid)
{
  int fd = -1;
  // S_IRWXU ~ S_IRUSR | S_IWUSR | S_IXUSR (0700)
  const int mode = S_IRWXU;

  fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY);
  if(fd < 0 && errno == ENOENT)
    {
      /* mkdirat uses permissions afected by umask */
      if(mkdirat(dirfd, name, mode)) goto error;
      fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY);
    }
  if(fd < 0) goto error;

  /* Set ownership over the new fd (not over dirfd)
   * Requires CAP_CHOWN to be activated */
  if(fchown(fd, uid, gid)) {
      fprintf(stderr, "Error en fchown de %s: ", name);
      goto error;
  }

  /* Set mode:
   * Requires CAP_FSETID to keep special bits,
   * however, for 0700 CAP_DAC_OVERRIDE is enough */
  if(fchmod(fd, mode)) goto error;

  return fd;

 error:
  if(fd >= 0) close(fd);
  perror(name);
  return -1;
}



int install_target(const char *username, const char *targetname, uid_t uid,
		   gid_t gid)
{
  int media; /* file descriptor for /media                  */
  int user;  /* file descriptor for /media/username         */
  int target; /* file descriptor for /media/username/target */
  struct stat sbuser, sbtarget;
  char buf[PATH_MAX];
  /* open /media */
  media = open("/media", O_DIRECTORY);
  if(media < 0) goto media_error;

  /* open or create /media/username */
  user = open_or_create_dirat(media, username, uid, gid);
  if(user<0) goto user_error;

  /* open or create /media/username/target */
  target = open_or_create_dirat(user, targetname, uid, gid);
  if(target<0) goto target_error;

  /* check that target is not already a mount point */
  if( fstat(user,   &sbuser  ) ) goto user_error;
  if( fstat(target, &sbtarget) ) goto target_error;
  if(sbtarget.st_dev != sbuser.st_dev) goto mpoint_error;
  /* clean up and return */
  close(user);
  close(media);
  return target;

 media_error:
  perror("/media");

 user_error:
  snprintf(buf, sizeof(buf), "/media/%s", username);
  perror(buf);
  close(media);
  return -1;

 target_error:
  snprintf(buf, sizeof(buf), "/media/%s/%s", username, targetname);
  perror(buf);
  close(user);
  close(media);
  return -1;

 mpoint_error:
  fprintf(stderr,_("/media/%s/%s is already a mount point.\n"),
          username,targetname);
  close(target);
  close(user);
  close(media);
  return -1;
}
