1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <strsafe.h> 24 #include <stdio.h> 25 #include <time.h> 26 27 #include "nfs41_ops.h" 28 #include "from_kernel.h" 29 #include "upcall.h" 30 #include "util.h" 31 #include "daemon_debug.h" 32 33 34 /* windows volume queries want size in 'units', so we have to 35 * convert the nfs space_* attributes from bytes to units */ 36 #define SECTORS_PER_UNIT 8 37 #define BYTES_PER_SECTOR 512 38 #define BYTES_PER_UNIT (SECTORS_PER_UNIT * BYTES_PER_SECTOR) 39 40 #define TO_UNITS(bytes) (bytes / BYTES_PER_UNIT) 41 42 #define VOLUME_CACHE_EXPIRATION 20 43 44 45 /* NFS41_VOLUME_QUERY */ 46 static int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) 47 { 48 int status; 49 volume_upcall_args *args = &upcall->args.volume; 50 51 status = safe_read(&buffer, &length, &args->query, sizeof(FS_INFORMATION_CLASS)); 52 if (status) goto out; 53 54 dprintf(1, "parsing NFS41_VOLUME_QUERY: query=%d\n", args->query); 55 out: 56 return status; 57 } 58 59 static int get_volume_size_info( 60 IN nfs41_open_state *state, 61 IN const char *query, 62 OUT OPTIONAL PLONGLONG total_out, 63 OUT OPTIONAL PLONGLONG user_out, 64 OUT OPTIONAL PLONGLONG avail_out) 65 { 66 nfs41_file_info info = { 0 }; 67 nfs41_superblock *superblock = state->file.fh.superblock; 68 int status = ERROR_NOT_FOUND; 69 70 AcquireSRWLockShared(&superblock->lock); 71 /* check superblock for cached attributes */ 72 if (time(NULL) <= superblock->cache_expiration) { 73 info.space_total = superblock->space_total; 74 info.space_avail = superblock->space_avail; 75 info.space_free = superblock->space_free; 76 status = NO_ERROR; 77 78 dprintf(2, "%s cached: %llu user, %llu free of %llu total\n", 79 query, info.space_avail, info.space_free, info.space_total); 80 } 81 ReleaseSRWLockShared(&superblock->lock); 82 83 if (status) { 84 bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL | 85 FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } }; 86 87 /* query the space_ attributes of the filesystem */ 88 status = nfs41_getattr(state->session, &state->file, 89 &attr_request, &info); 90 if (status) { 91 eprintf("nfs41_getattr() failed with %s\n", 92 nfs_error_string(status)); 93 status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); 94 goto out; 95 } 96 97 AcquireSRWLockExclusive(&superblock->lock); 98 superblock->space_total = info.space_total; 99 superblock->space_avail = info.space_avail; 100 superblock->space_free = info.space_free; 101 superblock->cache_expiration = time(NULL) + VOLUME_CACHE_EXPIRATION; 102 ReleaseSRWLockExclusive(&superblock->lock); 103 104 dprintf(2, "%s: %llu user, %llu free of %llu total\n", 105 query, info.space_avail, info.space_free, info.space_total); 106 } 107 108 if (total_out) *total_out = TO_UNITS(info.space_total); 109 if (user_out) *user_out = TO_UNITS(info.space_avail); 110 if (avail_out) *avail_out = TO_UNITS(info.space_free); 111 out: 112 return status; 113 } 114 115 static int handle_volume(nfs41_upcall *upcall) 116 { 117 volume_upcall_args *args = &upcall->args.volume; 118 int status = NO_ERROR; 119 120 switch (args->query) { 121 case FileFsSizeInformation: 122 args->len = sizeof(args->info.size); 123 args->info.size.SectorsPerAllocationUnit = SECTORS_PER_UNIT; 124 args->info.size.BytesPerSector = BYTES_PER_SECTOR; 125 126 status = get_volume_size_info(upcall->state_ref, 127 "FileFsSizeInformation", 128 &args->info.size.TotalAllocationUnits.QuadPart, 129 &args->info.size.AvailableAllocationUnits.QuadPart, 130 NULL); 131 break; 132 133 case FileFsFullSizeInformation: 134 args->len = sizeof(args->info.fullsize); 135 args->info.fullsize.SectorsPerAllocationUnit = SECTORS_PER_UNIT; 136 args->info.fullsize.BytesPerSector = BYTES_PER_SECTOR; 137 138 status = get_volume_size_info(upcall->state_ref, 139 "FileFsFullSizeInformation", 140 &args->info.fullsize.TotalAllocationUnits.QuadPart, 141 &args->info.fullsize.CallerAvailableAllocationUnits.QuadPart, 142 &args->info.fullsize.ActualAvailableAllocationUnits.QuadPart); 143 break; 144 145 case FileFsAttributeInformation: 146 args->len = sizeof(args->info.attribute); 147 nfs41_superblock_fs_attributes(upcall->state_ref->file.fh.superblock, 148 &args->info.attribute); 149 break; 150 151 default: 152 eprintf("unhandled fs query class %d\n", args->query); 153 status = ERROR_INVALID_PARAMETER; 154 break; 155 } 156 return status; 157 } 158 159 static int marshall_volume(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall) 160 { 161 int status; 162 volume_upcall_args *args = &upcall->args.volume; 163 164 status = safe_write(&buffer, length, &args->len, sizeof(args->len)); 165 if (status) goto out; 166 status = safe_write(&buffer, length, &args->info, args->len); 167 out: 168 return status; 169 } 170 171 172 const nfs41_upcall_op nfs41_op_volume = { 173 parse_volume, 174 handle_volume, 175 marshall_volume 176 }; 177