/* nwattrib.c 07-Oct-00 */
/* (C)opyright (C) 1998,2000  Martin Stover, Marburg, Germany
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

 /* Attrib routines for mars_nwe */

/* history since 07-Oct-00
 *
 * mst:07-Oct-00: unix dot files are hidden as default.
 *                
 */

#include "net.h"
#include <dirent.h>
#include "unxfile.h"
#include "nwvolume.h"
#include "connect.h"
#include "trustee.h"
#include "nwattrib.h"

static void put_attr_to_disk(int dev, ino_t inode, uint32 attrib)
{
  char   buf[255];
  char   battrib[255];
  int    l;
  uint8  buf_uc[4];
  U32_TO_BE32(inode, buf_uc);
  l=snprintf(buf, sizeof(buf), "%s/%x/%x/%x/%x", path_attributes,
            dev,
            (int) buf_uc[0],
            (int) buf_uc[1],
            (int) buf_uc[2]);
  if (seteuid(0)) {}
  unx_xmkdir(buf, 0755);
  sprintf(buf+l, "/%x", (int) buf_uc[3]);
  unlink(buf);
  l=sprintf(battrib, "%08x", (unsigned int) attrib);
  if (symlink(battrib, buf)) {}
  (void)reseteuid();
}

static void free_attr_from_disk(int dev, ino_t inode)
{
  char   buf[255];
  uint8  buf_uc[4];
  U32_TO_BE32(inode, buf_uc);
  snprintf(buf, sizeof(buf), "%s/%x/%x/%x/%x/%x", path_attributes,
            dev,
            (int) buf_uc[0],
            (int) buf_uc[1],
            (int) buf_uc[2],
            (int) buf_uc[3]);
  if (seteuid(0)) {}
  unlink(buf);
  (void)reseteuid();
}

static int get_attr_from_disk(int dev, ino_t inode, uint32 *attrib)
/* returns 0 if all ok */
{
  char   buf[255];
  char   battrib[255];
  int    l;
  uint8  buf_uc[4];
  U32_TO_BE32(inode, buf_uc);
  snprintf(buf, sizeof(buf), "%s/%x/%x/%x/%x/%x", path_attributes,
            dev,
            (int) buf_uc[0],
            (int) buf_uc[1],
            (int) buf_uc[2],
            (int) buf_uc[3]);
  if (seteuid(0)) {}
  l=readlink(buf, battrib, 224);
  (void)reseteuid();
  if (l > 0) {
    unsigned int uattrib=0;
    battrib[l]='\0';
    if (1 == sscanf(battrib, "%x", &uattrib)) {
      *attrib = uattrib;
      return(0);
    }
  }
  return(-1);
}


uint32 get_nw_attrib_dword(int volume, char *unixname, struct stat *stb)
/* returns full attrib_dword */
{
  uint32 attrib = S_ISDIR(stb->st_mode) ? FILE_ATTR_DIR
                                        : FILE_ATTR_A;
  
  int voloptions = get_volume_options(volume);
  
  if (voloptions & VOL_OPTION_IS_PIPE) {
    attrib |= (FILE_ATTR_DELETE_INH|FILE_ATTR_RENAME_INH);
    if (!S_ISDIR(stb->st_mode)){
      attrib|=FILE_ATTR_SHARE;
    }
    return(attrib);
  }
  
  if (  (voloptions & VOL_OPTION_ATTRIBUTES) &&
       !get_attr_from_disk(stb->st_dev, stb->st_ino, &attrib)) {
  
    if (S_ISDIR(stb->st_mode)) attrib |= FILE_ATTR_DIR;
    else attrib &= (~FILE_ATTR_DIR);
  } else {  /* mst: 07-Oct-00, mark unix dot files as hidden */
    char *p = strrchr(unixname, '/');
    if (p && *(p+1) == '.' && *(p+2) != 0 && *(p+2) != '.' ) 
      attrib |= FILE_ATTR_H;   /* We say hidden here */
  } 
  
  if (voloptions & VOL_OPTION_READONLY){
    attrib |= (FILE_ATTR_DELETE_INH|FILE_ATTR_RENAME_INH|FILE_ATTR_R);
  }

  if (!(voloptions & VOL_OPTION_ATTRIBUTES)) { 
    /* only for volumes without attribute handling */
    if (act_uid) {
      /* if not root */
      int acc=get_unix_eff_rights(stb);
      if (!(acc & W_OK)) {
        attrib |= FILE_ATTR_R;    /* RO    */
      }
      if (!(acc & R_OK)) {
        attrib |= FILE_ATTR_H;   /* We say hidden here */
      }
    }
  }
  return(attrib);
}

static int set_nw_attrib(
  int volume, char *unixname, struct stat *stb, uint32 attrib, uint32 mode)
  /* mode == 0 : set_nw_attrib_dword
             1 : set_nw_attrib_byte
             2 : set_nw_attrib_word
   */
{
  int is_dir=S_ISDIR(stb->st_mode);
  uint32 oldattrib,newattrib;
  int voloptions=get_volume_options(volume);
  if (voloptions & VOL_OPTION_IS_PIPE)
     return(0); /* we return with no error */
  if (tru_eff_rights_exists(volume, unixname, stb, TRUSTEE_M))
     return(-0x8c); /* no modify rights */
  if (!(voloptions & VOL_OPTION_ATTRIBUTES)) {
    if (!(get_unix_eff_rights(stb) & W_OK)) {
      if (!(attrib & FILE_ATTR_R)) { /* if not setting RO */
        if (!chmod(unixname, stb->st_mode | S_IWUSR)) {
          stb->st_mode |= S_IWUSR;
        } else
         return(-0x8c); /* no modify rights */
      }
    }
  }
  if (mode || !(voloptions & VOL_OPTION_ATTRIBUTES)) {
    oldattrib=get_nw_attrib_dword(volume, unixname, stb);
    if (mode==1) {  /* byte */
      newattrib  = (oldattrib & ~0xff);
      newattrib |= (attrib    &  0xff);
    } else if (mode==2) {  /* word */
      newattrib  = (oldattrib & ~0xffff);
      newattrib |= (attrib    &  0xffff);
    } else newattrib=attrib;
  } else {
    newattrib=attrib;
  }
  
  if ((!mode) || (newattrib != oldattrib) )  {
    if (!(voloptions & VOL_OPTION_ATTRIBUTES)) {
      if ((!is_dir) && ((newattrib&FILE_ATTR_R)!=(oldattrib&FILE_ATTR_R))) {
        int oldmode=stb->st_mode;
        if (newattrib & FILE_ATTR_R)   /* R/O */
          stb->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
        else
          stb->st_mode |= (S_IWUSR | S_IWGRP);
        if (chmod(unixname, stb->st_mode)) {
          stb->st_mode = oldmode;
          return(-0x8c); /* no modify rights */
        }
      }
    } else {
      if (is_dir) newattrib |=  0x10;
      else newattrib        &= ~0x10;
      put_attr_to_disk(stb->st_dev, stb->st_ino, newattrib);
    }
  }
  return(0);
}

int set_nw_attrib_dword(int volume, char *unixname, struct stat *stb, uint32 attrib)
/* wattrib = ext_attrib, attrib */
{
  return(set_nw_attrib(volume,unixname,stb,attrib, 0));
}

int set_nw_attrib_byte(int volume, char *unixname, struct stat *stb, int battrib)
{
  return(set_nw_attrib(volume,unixname,stb,(uint32)battrib, 1));
}

int set_nw_attrib_word(int volume, char *unixname, struct stat *stb, int wattrib)
/* wattrib = ext_attrib, attrib */
{
  return(set_nw_attrib(volume,unixname,stb,(uint32)wattrib, 2));
}

void set_nw_archive_bit(int volume, char *unixname, int dev, ino_t inode)
/* sets archive bit on files, is called when file is closed */
{
  uint32 attrib=0L;
  int voloptions=get_volume_options(volume);
  if (voloptions & VOL_OPTION_ATTRIBUTES) {
    if (  (!get_attr_from_disk(dev, inode, &attrib))
       && !(attrib & FILE_ATTR_A)) {
      attrib|=FILE_ATTR_A;
      put_attr_to_disk(dev, inode, attrib);
    }
  }
}

void free_nw_ext_inode(int volume, char *unixname, int dev, ino_t inode)
/* removes all attrib or trustees entries for files or dirs */
/* must be called after deleting nw file or dir             */
{
  int voloptions=get_volume_options(volume);
  if (voloptions & VOL_OPTION_ATTRIBUTES) 
    free_attr_from_disk(dev, inode);
  if (voloptions & VOL_OPTION_TRUSTEES) {
    int i=used_nw_volumes;
    while(i--) 
     tru_free_file_trustees_from_disk(i, dev, inode);
  }
}

