static NTSTATUS cmd_fchmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv) { int fd; mode_t mode; if (argc != 3) { printf("Usage: fchmod <fd> <mode>\n"); return NT_STATUS_OK; } fd = atoi(argv[1]); mode = atoi(argv[2]); if (fd < 0 || fd > 1024) { printf("fchmod: error=%d (file descriptor out of range)\n", EBADF); return NT_STATUS_OK; } if (vfs->files[fd] == NULL) { printf("fchmod: error=%d (invalid file descriptor)\n", EBADF); return NT_STATUS_OK; } if (SMB_VFS_FCHMOD(vfs->files[fd], fd, mode) == -1) { printf("fchmod: error=%d (%s)\n", errno, strerror(errno)); return NT_STATUS_UNSUCCESSFUL; } printf("fchmod: ok\n"); return NT_STATUS_OK; }
int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname, uint32 dosmode, const char *parent_dir, bool newfile) { int mask=0; mode_t tmp; mode_t unixmode; int ret = -1, lret = -1; uint32_t old_mode; struct timespec new_create_timespec; files_struct *fsp = NULL; bool need_close = false; NTSTATUS status; if (!CAN_WRITE(conn)) { errno = EROFS; return -1; } /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE); DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, smb_fname_str_dbg(smb_fname))); unixmode = smb_fname->st.st_ex_mode; get_acl_group_bits(conn, smb_fname->base_name, &smb_fname->st.st_ex_mode); if (S_ISDIR(smb_fname->st.st_ex_mode)) dosmode |= FILE_ATTRIBUTE_DIRECTORY; else dosmode &= ~FILE_ATTRIBUTE_DIRECTORY; new_create_timespec = smb_fname->st.st_ex_btime; old_mode = dos_mode(conn, smb_fname); if ((dosmode & FILE_ATTRIBUTE_OFFLINE) && !(old_mode & FILE_ATTRIBUTE_OFFLINE)) { lret = SMB_VFS_SET_OFFLINE(conn, smb_fname); if (lret == -1) { if (errno == ENOTSUP) { DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for " "%s/%s is not supported.\n", parent_dir, smb_fname_str_dbg(smb_fname))); } else { DEBUG(0, ("An error occurred while setting " "FILE_ATTRIBUTE_OFFLINE for " "%s/%s: %s", parent_dir, smb_fname_str_dbg(smb_fname), strerror(errno))); } } } dosmode &= ~FILE_ATTRIBUTE_OFFLINE; old_mode &= ~FILE_ATTRIBUTE_OFFLINE; smb_fname->st.st_ex_btime = new_create_timespec; /* Store the DOS attributes in an EA by preference. */ if (lp_store_dos_attributes(SNUM(conn))) { /* * Don't fall back to using UNIX modes. Finally * follow the smb.conf manpage. */ if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) { return -1; } if (!newfile) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } smb_fname->st.st_ex_mode = unixmode; return 0; } unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir); /* preserve the file type bits */ mask |= S_IFMT; /* preserve the s bits */ mask |= (S_ISUID | S_ISGID); /* preserve the t bit */ #ifdef S_ISVTX mask |= S_ISVTX; #endif /* possibly preserve the x bits */ if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; unixmode |= (smb_fname->st.st_ex_mode & mask); /* if we previously had any r bits set then leave them alone */ if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); unixmode |= tmp; } /* if we previously had any w bits set then leave them alone whilst adding in the new w bits, if the new mode is not rdonly */ if (!IS_DOS_READONLY(dosmode)) { unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } /* * From the chmod 2 man page: * * "If the calling process is not privileged, and the group of the file * does not match the effective group ID of the process or one of its * supplementary group IDs, the S_ISGID bit will be turned off, but * this will not cause an error to be returned." * * Simply refuse to do the chmod in this case. */ if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) && geteuid() != sec_initial_uid() && !current_user_in_group(conn, smb_fname->st.st_ex_gid)) { DEBUG(3,("file_set_dosmode: setgid bit cannot be " "set for directory %s\n", smb_fname_str_dbg(smb_fname))); errno = EPERM; return -1; } ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode); if (ret == 0) { if(!newfile || (lret != -1)) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } smb_fname->st.st_ex_mode = unixmode; return 0; } if((errno != EPERM) && (errno != EACCES)) return -1; if(!lp_dos_filemode(SNUM(conn))) return -1; /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ if (!can_write_to_file(conn, smb_fname)) { errno = EACCES; return -1; } /* * We need to get an open file handle to do the * metadata operation under root. */ status = get_file_handle_for_metadata(conn, smb_fname, &fsp, &need_close); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return -1; } become_root(); ret = SMB_VFS_FCHMOD(fsp, unixmode); unbecome_root(); if (need_close) { close_file(NULL, fsp, NORMAL_CLOSE); } if (!newfile) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, smb_fname->base_name); } if (ret == 0) { smb_fname->st.st_ex_mode = unixmode; } return( ret ); }
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 tmp; 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 == NULL) { SET_STAT_INVALID(st1); st = &st1; } if (!VALID_STAT(*st)) { if (SMB_VFS_STAT(conn,fname,st)) return(-1); } unixmode = st->st_mode; get_acl_group_bits(conn, fname, &st->st_mode); if (S_ISDIR(st->st_mode)) dosmode |= aDIR; else dosmode &= ~aDIR; if (dos_mode(conn,fname,st) == dosmode) { st->st_mode = unixmode; return(0); } /* Store the DOS attributes in an EA by preference. */ if (set_ea_dos_attribute(conn, fname, st, dosmode)) { st->st_mode = unixmode; return 0; } unixmode = unix_mode(conn,dosmode,fname, parent_dir); /* preserve the s bits */ mask |= (S_ISUID | S_ISGID); /* preserve the t bit */ #ifdef S_ISVTX mask |= S_ISVTX; #endif /* possibly preserve the x bits */ if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; unixmode |= (st->st_mode & mask); /* if we previously had any r bits set then leave them alone */ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); unixmode |= tmp; } /* if we previously had any w bits set then leave them alone whilst adding in the new w bits, if the new mode is not rdonly */ if (!IS_DOS_READONLY(dosmode)) { unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); st->st_mode = unixmode; return 0; } if((errno != EPERM) && (errno != EACCES)) return -1; if(!lp_dos_filemode(SNUM(conn))) return -1; /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ if (CAN_WRITE(conn)) { /* * We need to open the file with write access whilst * still in our current user context. This ensures we * are not violating security in doing the fchmod. * This file open does *not* break any oplocks we are * holding. We need to review this.... may need to * break batch oplocks open by others. JRA. */ 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->fh->fd, unixmode); unbecome_root(); close_file_fchmod(fsp); notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); if (ret == 0) { st->st_mode = unixmode; } } return( ret ); }