Unix SMB/CIFS implementation.
dos mode handling functions
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) James Peach 2006
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
#include "includes.h"
+static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
+{
+#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
+ return FILE_ATTRIBUTE_SPARSE;
+ }
+#endif
+ return 0;
+}
+
+/****************************************************************************
+ Work out whether this file is offline
+****************************************************************************/
+
+static uint32 set_offline_flag(connection_struct *conn, const char *const path)
+{
+ if (ISDOT(path) || ISDOTDOT(path)) {
+ return 0;
+ }
+
+ if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
+ return 0;
+ }
+
+ return dmapi_file_flags(path);
+}
+
/****************************************************************************
Change a dos mode to a unix mode.
Base permission for files:
- if inheriting
+ if creating file and inheriting (i.e. parent_dir != NULL)
apply read/write bits from parent directory.
else
everybody gets read bit set
}
****************************************************************************/
-mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname)
+mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
+ const char *inherit_from_dir)
{
mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
- mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
+ mode_t dir_mode = 0; /* Mode of the inherit_from directory if
+ * inheriting. */
if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
}
- if (fname && lp_inherit_perms(SNUM(conn))) {
- char *dname;
+ if (fname && (inherit_from_dir != NULL)
+ && lp_inherit_perms(SNUM(conn))) {
SMB_STRUCT_STAT sbuf;
- dname = parent_dirname(fname);
- DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
- if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
- DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
+ DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
+ inherit_from_dir));
+ if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
+ DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
+ inherit_from_dir, strerror(errno)));
return(0); /* *** shouldn't happen! *** */
}
Change a unix mode to a dos mode.
****************************************************************************/
-uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
+static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
{
int result = 0;
+ enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
+
+ if (ro_opts == MAP_READONLY_YES) {
+ /* Original Samba method - map inverse of user "w" bit. */
+ if ((sbuf->st_mode & S_IWUSR) == 0) {
+ result |= aRONLY;
+ }
+ } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
+ /* Check actual permissions for read-only. */
+ if (!can_write_to_file(conn, path, sbuf)) {
+ result |= aRONLY;
+ }
+ } /* Else never set the readonly bit. */
- if ((sbuf->st_mode & S_IWUSR) == 0)
- result |= aRONLY;
-
if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
result |= aARCH;
if (S_ISDIR(sbuf->st_mode))
result = aDIR | (result & aRONLY);
-#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
- if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
- result |= FILE_ATTRIBUTE_SPARSE;
- }
-#endif
+ result |= set_sparse_flag(sbuf);
#ifdef S_ISLNK
#if LINKS_READ_ONLY
return False;
}
- *pattr = 0;
+ /* Don't reset pattr to zero as we may already have filename-based attributes we
+ need to preserve. */
sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
if (sizeret == -1) {
#if defined(ENOTSUP) && defined(ENOATTR)
- if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
+ if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
path, strerror(errno) ));
set_store_dos_attributes(SNUM(conn), False);
* are not violating security in doing the setxattr.
*/
- fsp = open_file_fchmod(conn,path,sbuf);
- if (!fsp)
+ if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
return ret;
become_root();
if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
}
/****************************************************************************
- Change a unix mode to a dos mode.
+ Change a unix mode to a dos mode for an ms dfs link.
****************************************************************************/
-uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
+uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
{
uint32 result = 0;
- DEBUG(8,("dos_mode: %s\n", path));
+ DEBUG(8,("dos_mode_msdfs: %s\n", path));
if (!VALID_STAT(*sbuf)) {
return 0;
}
- /* Get the DOS attributes from an EA by preference. */
- if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
- return result;
+ /* First do any modifications that depend on the path name. */
+ /* hide files with a name starting with a . */
+ if (lp_hide_dot_files(SNUM(conn))) {
+ const char *p = strrchr_m(path,'/');
+ if (p) {
+ p++;
+ } else {
+ p = path;
+ }
+
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
+ result |= aHIDDEN;
+ }
+ }
+
+ result |= dos_mode_from_sbuf(conn, path, sbuf);
+
+ /* Optimization : Only call is_hidden_path if it's not already
+ hidden. */
+ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
+ result |= aHIDDEN;
}
- result = dos_mode_from_sbuf(conn, sbuf);
+ DEBUG(8,("dos_mode_msdfs returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+ if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
+
+ DEBUG(8,("\n"));
+
+ return(result);
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+
+uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
+{
+ uint32 result = 0;
+
+ DEBUG(8,("dos_mode: %s\n", path));
+
+ if (!VALID_STAT(*sbuf)) {
+ return 0;
+ }
- /* Now do any modifications that depend on the path name. */
+ /* First do any modifications that depend on the path name. */
/* hide files with a name starting with a . */
if (lp_hide_dot_files(SNUM(conn))) {
const char *p = strrchr_m(path,'/');
- if (p)
+ if (p) {
p++;
- else
+ } else {
p = path;
+ }
- if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
result |= aHIDDEN;
+ }
}
+ /* Get the DOS attributes from an EA by preference. */
+ if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
+ result |= set_sparse_flag(sbuf);
+ } else {
+ result |= dos_mode_from_sbuf(conn, path, sbuf);
+ }
+
+ if (S_ISREG(sbuf->st_mode)) {
+ result |= set_offline_flag(conn, path);
+ }
+
/* Optimization : Only call is_hidden_path if it's not already
hidden. */
if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
if (result & aSYSTEM) DEBUG(8, ("s"));
if (result & aDIR ) DEBUG(8, ("d"));
if (result & aARCH ) DEBUG(8, ("a"));
+ if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
DEBUG(8,("\n"));
chmod a file - but preserve some bits.
********************************************************************/
-int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
+int file_set_dosmode(connection_struct *conn, const char *fname,
+ uint32 dosmode, SMB_STRUCT_STAT *st,
+ const char *parent_dir)
{
SMB_STRUCT_STAT st1;
int mask=0;
mode_t unixmode;
int ret = -1;
+ /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
+ dosmode &= SAMBA_ATTRIBUTES_MASK;
+
DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
- if (!st) {
+ if (!st || (st && !VALID_STAT(*st))) {
st = &st1;
if (SMB_VFS_STAT(conn,fname,st))
return(-1);
return 0;
}
- unixmode = unix_mode(conn,dosmode,fname);
+ unixmode = unix_mode(conn,dosmode,fname, parent_dir);
/* preserve the s bits */
mask |= (S_ISUID | S_ISGID);
* holding. We need to review this.... may need to
* break batch oplocks open by others. JRA.
*/
- files_struct *fsp = open_file_fchmod(conn,fname,st);
- if (!fsp)
+ files_struct *fsp;
+ if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
return -1;
become_root();
- ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
+ ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
unbecome_root();
close_file_fchmod(fsp);
}
than POSIX.
*******************************************************************/
-int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
+int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times)
{
- extern struct current_user current_user;
- SMB_STRUCT_STAT sb;
+ SMB_STRUCT_STAT sbuf;
int ret = -1;
errno = 0;
+ ZERO_STRUCT(sbuf);
+
+ /* Don't update the time on read-only shares */
+ /* We need this as set_filetime (which can be called on
+ close and other paths) can end up calling this function
+ without the NEED_WRITE protection. Found by :
+ Leo Weppelman <leo@wau.mis.ah.nl>
+ */
+
+ if (!CAN_WRITE(conn)) {
+ return 0;
+ }
if(SMB_VFS_UTIME(conn,fname, times) == 0)
return 0;
(as DOS does).
*/
- if(SMB_VFS_STAT(conn,fname,&sb) != 0)
- return -1;
-
/* Check if we have write access. */
- if (CAN_WRITE(conn)) {
- if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
- ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
- ((sb.st_mode & S_IWGRP) &&
- in_group(sb.st_gid,current_user.gid,
- current_user.ngroups,current_user.groups)))) {
- /* We are allowed to become root and change the filetime. */
- become_root();
- ret = SMB_VFS_UTIME(conn,fname, times);
- unbecome_root();
- }
+ if (can_write_to_file(conn, fname, &sbuf)) {
+ /* We are allowed to become root and change the filetime. */
+ become_root();
+ ret = SMB_VFS_UTIME(conn,fname, times);
+ unbecome_root();
}
return ret;
Change a filetime - possibly allowing DOS semantics.
*******************************************************************/
-BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
+BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime)
{
struct utimbuf times;