001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.hdfs;
019    
020    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT;
021    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_KEY;
022    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_DEFAULT;
023    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY;
024    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_DEFAULT;
025    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY;
026    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_RETRIES_DEFAULT;
027    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_RETRIES_KEY;
028    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_CACHED_CONN_RETRY_DEFAULT;
029    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_CACHED_CONN_RETRY_KEY;
030    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT;
031    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
032    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT;
033    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY;
034    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT;
035    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY;
036    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_DEFAULT;
037    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_KEY;
038    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_READ_PREFETCH_SIZE_KEY;
039    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_WINDOW_BASE;
040    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_CAPACITY_DEFAULT;
041    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_CAPACITY_KEY;
042    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_TIMEOUT_KEY;
043    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_LEGACY_BLOCKREADER;
044    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_LEGACY_BLOCKREADER_DEFAULT;
045    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_WRITE_PACKET_SIZE_DEFAULT;
046    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_WRITE_PACKET_SIZE_KEY;
047    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY;
048    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_REPLICATION_DEFAULT;
049    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_REPLICATION_KEY;
050    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME;
051    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME_DEFAULT;
052    
053    import java.io.BufferedOutputStream;
054    import java.io.DataInputStream;
055    import java.io.DataOutputStream;
056    import java.io.FileNotFoundException;
057    import java.io.IOException;
058    import java.io.InputStream;
059    import java.io.OutputStream;
060    import java.net.InetAddress;
061    import java.net.InetSocketAddress;
062    import java.net.NetworkInterface;
063    import java.net.Socket;
064    import java.net.SocketException;
065    import java.net.SocketAddress;
066    import java.net.URI;
067    import java.net.UnknownHostException;
068    import java.util.ArrayList;
069    import java.util.Collections;
070    import java.util.EnumSet;
071    import java.util.HashMap;
072    import java.util.LinkedHashMap;
073    import java.util.List;
074    import java.util.Map;
075    import java.util.Random;
076    
077    import javax.net.SocketFactory;
078    
079    import org.apache.commons.logging.Log;
080    import org.apache.commons.logging.LogFactory;
081    import org.apache.hadoop.classification.InterfaceAudience;
082    import org.apache.hadoop.conf.Configuration;
083    import org.apache.hadoop.fs.BlockLocation;
084    import org.apache.hadoop.fs.BlockStorageLocation;
085    import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
086    import org.apache.hadoop.fs.ContentSummary;
087    import org.apache.hadoop.fs.CreateFlag;
088    import org.apache.hadoop.fs.FileAlreadyExistsException;
089    import org.apache.hadoop.fs.FileSystem;
090    import org.apache.hadoop.fs.FsServerDefaults;
091    import org.apache.hadoop.fs.FsStatus;
092    import org.apache.hadoop.fs.HdfsBlockLocation;
093    import org.apache.hadoop.fs.InvalidPathException;
094    import org.apache.hadoop.fs.MD5MD5CRC32CastagnoliFileChecksum;
095    import org.apache.hadoop.fs.MD5MD5CRC32FileChecksum;
096    import org.apache.hadoop.fs.MD5MD5CRC32GzipFileChecksum;
097    import org.apache.hadoop.fs.Options;
098    import org.apache.hadoop.fs.Options.ChecksumOpt;
099    import org.apache.hadoop.fs.ParentNotDirectoryException;
100    import org.apache.hadoop.fs.Path;
101    import org.apache.hadoop.fs.UnresolvedLinkException;
102    import org.apache.hadoop.fs.VolumeId;
103    import org.apache.hadoop.fs.permission.FsPermission;
104    import org.apache.hadoop.hdfs.client.HdfsDataInputStream;
105    import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
106    import org.apache.hadoop.hdfs.protocol.ClientProtocol;
107    import org.apache.hadoop.hdfs.protocol.CorruptFileBlocks;
108    import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException;
109    import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
110    import org.apache.hadoop.hdfs.protocol.DirectoryListing;
111    import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
112    import org.apache.hadoop.hdfs.protocol.HdfsBlocksMetadata;
113    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
114    import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
115    import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
116    import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
117    import org.apache.hadoop.hdfs.protocol.HdfsProtoUtil;
118    import org.apache.hadoop.hdfs.protocol.LocatedBlock;
119    import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
120    import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException;
121    import org.apache.hadoop.hdfs.protocol.UnresolvedPathException;
122    import org.apache.hadoop.hdfs.protocol.datatransfer.DataTransferEncryptor;
123    import org.apache.hadoop.hdfs.protocol.datatransfer.IOStreamPair;
124    import org.apache.hadoop.hdfs.protocol.datatransfer.Op;
125    import org.apache.hadoop.hdfs.protocol.datatransfer.ReplaceDatanodeOnFailure;
126    import org.apache.hadoop.hdfs.protocol.datatransfer.Sender;
127    import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.BlockOpResponseProto;
128    import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.OpBlockChecksumResponseProto;
129    import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.Status;
130    import org.apache.hadoop.hdfs.security.token.block.DataEncryptionKey;
131    import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
132    import org.apache.hadoop.hdfs.security.token.block.InvalidBlockTokenException;
133    import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
134    import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
135    import org.apache.hadoop.hdfs.server.namenode.NameNode;
136    import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
137    import org.apache.hadoop.io.DataOutputBuffer;
138    import org.apache.hadoop.io.EnumSetWritable;
139    import org.apache.hadoop.io.IOUtils;
140    import org.apache.hadoop.io.MD5Hash;
141    import org.apache.hadoop.io.Text;
142    import org.apache.hadoop.ipc.Client;
143    import org.apache.hadoop.ipc.RPC;
144    import org.apache.hadoop.ipc.RemoteException;
145    import org.apache.hadoop.net.DNS;
146    import org.apache.hadoop.net.NetUtils;
147    import org.apache.hadoop.security.AccessControlException;
148    import org.apache.hadoop.security.UserGroupInformation;
149    import org.apache.hadoop.security.token.SecretManager.InvalidToken;
150    import org.apache.hadoop.security.token.Token;
151    import org.apache.hadoop.security.token.TokenRenewer;
152    import org.apache.hadoop.util.DataChecksum;
153    import org.apache.hadoop.util.Progressable;
154    import org.apache.hadoop.util.Time;
155    
156    import com.google.common.annotations.VisibleForTesting;
157    import com.google.common.base.Joiner;
158    import com.google.common.base.Preconditions;
159    import com.google.common.net.InetAddresses;
160    
161    /********************************************************
162     * DFSClient can connect to a Hadoop Filesystem and 
163     * perform basic file tasks.  It uses the ClientProtocol
164     * to communicate with a NameNode daemon, and connects 
165     * directly to DataNodes to read/write block data.
166     *
167     * Hadoop DFS users should obtain an instance of 
168     * DistributedFileSystem, which uses DFSClient to handle
169     * filesystem tasks.
170     *
171     ********************************************************/
172    @InterfaceAudience.Private
173    public class DFSClient implements java.io.Closeable {
174      public static final Log LOG = LogFactory.getLog(DFSClient.class);
175      public static final long SERVER_DEFAULTS_VALIDITY_PERIOD = 60 * 60 * 1000L; // 1 hour
176      static final int TCP_WINDOW_SIZE = 128 * 1024; // 128 KB
177      final ClientProtocol namenode;
178      /* The service used for delegation tokens */
179      private Text dtService;
180    
181      final UserGroupInformation ugi;
182      volatile boolean clientRunning = true;
183      volatile long lastLeaseRenewal;
184      private volatile FsServerDefaults serverDefaults;
185      private volatile long serverDefaultsLastUpdate;
186      final String clientName;
187      Configuration conf;
188      SocketFactory socketFactory;
189      final ReplaceDatanodeOnFailure dtpReplaceDatanodeOnFailure;
190      final FileSystem.Statistics stats;
191      final int hdfsTimeout;    // timeout value for a DFS operation.
192      private final String authority;
193      final SocketCache socketCache;
194      final Conf dfsClientConf;
195      private Random r = new Random();
196      private SocketAddress[] localInterfaceAddrs;
197      private DataEncryptionKey encryptionKey;
198    
199      /**
200       * DFSClient configuration 
201       */
202      static class Conf {
203        final int maxFailoverAttempts;
204        final int failoverSleepBaseMillis;
205        final int failoverSleepMaxMillis;
206        final int maxBlockAcquireFailures;
207        final int confTime;
208        final int ioBufferSize;
209        final ChecksumOpt defaultChecksumOpt;
210        final int writePacketSize;
211        final int socketTimeout;
212        final int socketCacheCapacity;
213        /** Wait time window (in msec) if BlockMissingException is caught */
214        final int timeWindow;
215        final int nCachedConnRetry;
216        final int nBlockWriteRetry;
217        final int nBlockWriteLocateFollowingRetry;
218        final long defaultBlockSize;
219        final long prefetchSize;
220        final short defaultReplication;
221        final String taskId;
222        final FsPermission uMask;
223        final boolean useLegacyBlockReader;
224        final boolean connectToDnViaHostname;
225        final boolean getHdfsBlocksMetadataEnabled;
226        final int getFileBlockStorageLocationsNumThreads;
227        final int getFileBlockStorageLocationsTimeout;
228    
229        Conf(Configuration conf) {
230          maxFailoverAttempts = conf.getInt(
231              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY,
232              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT);
233          failoverSleepBaseMillis = conf.getInt(
234              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY,
235              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT);
236          failoverSleepMaxMillis = conf.getInt(
237              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY,
238              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT);
239    
240          maxBlockAcquireFailures = conf.getInt(
241              DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_KEY,
242              DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_DEFAULT);
243          confTime = conf.getInt(DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY,
244              HdfsServerConstants.WRITE_TIMEOUT);
245          ioBufferSize = conf.getInt(
246              CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_KEY,
247              CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_DEFAULT);
248          defaultChecksumOpt = getChecksumOptFromConf(conf);
249          socketTimeout = conf.getInt(DFS_CLIENT_SOCKET_TIMEOUT_KEY,
250              HdfsServerConstants.READ_TIMEOUT);
251          /** dfs.write.packet.size is an internal config variable */
252          writePacketSize = conf.getInt(DFS_CLIENT_WRITE_PACKET_SIZE_KEY,
253              DFS_CLIENT_WRITE_PACKET_SIZE_DEFAULT);
254          defaultBlockSize = conf.getLongBytes(DFS_BLOCK_SIZE_KEY,
255              DFS_BLOCK_SIZE_DEFAULT);
256          defaultReplication = (short) conf.getInt(
257              DFS_REPLICATION_KEY, DFS_REPLICATION_DEFAULT);
258          taskId = conf.get("mapreduce.task.attempt.id", "NONMAPREDUCE");
259          socketCacheCapacity = conf.getInt(DFS_CLIENT_SOCKET_CACHE_CAPACITY_KEY,
260              DFS_CLIENT_SOCKET_CACHE_CAPACITY_DEFAULT);
261          prefetchSize = conf.getLong(DFS_CLIENT_READ_PREFETCH_SIZE_KEY,
262              10 * defaultBlockSize);
263          timeWindow = conf
264              .getInt(DFS_CLIENT_RETRY_WINDOW_BASE, 3000);
265          nCachedConnRetry = conf.getInt(DFS_CLIENT_CACHED_CONN_RETRY_KEY,
266              DFS_CLIENT_CACHED_CONN_RETRY_DEFAULT);
267          nBlockWriteRetry = conf.getInt(DFS_CLIENT_BLOCK_WRITE_RETRIES_KEY,
268              DFS_CLIENT_BLOCK_WRITE_RETRIES_DEFAULT);
269          nBlockWriteLocateFollowingRetry = conf
270              .getInt(DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY,
271                  DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_DEFAULT);
272          uMask = FsPermission.getUMask(conf);
273          useLegacyBlockReader = conf.getBoolean(
274              DFS_CLIENT_USE_LEGACY_BLOCKREADER,
275              DFS_CLIENT_USE_LEGACY_BLOCKREADER_DEFAULT);
276          connectToDnViaHostname = conf.getBoolean(DFS_CLIENT_USE_DN_HOSTNAME,
277              DFS_CLIENT_USE_DN_HOSTNAME_DEFAULT);
278          getHdfsBlocksMetadataEnabled = conf.getBoolean(
279              DFSConfigKeys.DFS_HDFS_BLOCKS_METADATA_ENABLED, 
280              DFSConfigKeys.DFS_HDFS_BLOCKS_METADATA_ENABLED_DEFAULT);
281          getFileBlockStorageLocationsNumThreads = conf.getInt(
282              DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_NUM_THREADS,
283              DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_NUM_THREADS_DEFAULT);
284          getFileBlockStorageLocationsTimeout = conf.getInt(
285              DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_TIMEOUT,
286              DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_TIMEOUT_DEFAULT);
287        }
288    
289        private DataChecksum.Type getChecksumType(Configuration conf) {
290          final String checksum = conf.get(
291              DFSConfigKeys.DFS_CHECKSUM_TYPE_KEY,
292              DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT);
293          try {
294            return DataChecksum.Type.valueOf(checksum);
295          } catch(IllegalArgumentException iae) {
296            LOG.warn("Bad checksum type: " + checksum + ". Using default "
297                + DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT);
298            return DataChecksum.Type.valueOf(
299                DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT); 
300          }
301        }
302    
303        // Construct a checksum option from conf
304        private ChecksumOpt getChecksumOptFromConf(Configuration conf) {
305          DataChecksum.Type type = getChecksumType(conf);
306          int bytesPerChecksum = conf.getInt(DFS_BYTES_PER_CHECKSUM_KEY,
307              DFS_BYTES_PER_CHECKSUM_DEFAULT);
308          return new ChecksumOpt(type, bytesPerChecksum);
309        }
310    
311        // create a DataChecksum with the default option.
312        private DataChecksum createChecksum() throws IOException {
313          return createChecksum(null);
314        }
315    
316        private DataChecksum createChecksum(ChecksumOpt userOpt) 
317            throws IOException {
318          // Fill in any missing field with the default.
319          ChecksumOpt myOpt = ChecksumOpt.processChecksumOpt(
320              defaultChecksumOpt, userOpt);
321          DataChecksum dataChecksum = DataChecksum.newDataChecksum(
322              myOpt.getChecksumType(),
323              myOpt.getBytesPerChecksum());
324          if (dataChecksum == null) {
325            throw new IOException("Invalid checksum type specified: "
326                + myOpt.getChecksumType().name());
327          }
328          return dataChecksum;
329        }
330      }
331     
332      Conf getConf() {
333        return dfsClientConf;
334      }
335      
336      /**
337       * A map from file names to {@link DFSOutputStream} objects
338       * that are currently being written by this client.
339       * Note that a file can only be written by a single client.
340       */
341      private final Map<String, DFSOutputStream> filesBeingWritten
342          = new HashMap<String, DFSOutputStream>();
343    
344      private boolean shortCircuitLocalReads;
345      
346      /**
347       * Same as this(NameNode.getAddress(conf), conf);
348       * @see #DFSClient(InetSocketAddress, Configuration)
349       * @deprecated Deprecated at 0.21
350       */
351      @Deprecated
352      public DFSClient(Configuration conf) throws IOException {
353        this(NameNode.getAddress(conf), conf);
354      }
355      
356      public DFSClient(InetSocketAddress address, Configuration conf) throws IOException {
357        this(NameNode.getUri(address), conf);
358      }
359    
360      /**
361       * Same as this(nameNodeUri, conf, null);
362       * @see #DFSClient(InetSocketAddress, Configuration, org.apache.hadoop.fs.FileSystem.Statistics)
363       */
364      public DFSClient(URI nameNodeUri, Configuration conf
365          ) throws IOException {
366        this(nameNodeUri, conf, null);
367      }
368    
369      /**
370       * Same as this(nameNodeUri, null, conf, stats);
371       * @see #DFSClient(InetSocketAddress, ClientProtocol, Configuration, org.apache.hadoop.fs.FileSystem.Statistics) 
372       */
373      public DFSClient(URI nameNodeUri, Configuration conf,
374                       FileSystem.Statistics stats)
375        throws IOException {
376        this(nameNodeUri, null, conf, stats);
377      }
378      
379      /** 
380       * Create a new DFSClient connected to the given nameNodeUri or rpcNamenode.
381       * Exactly one of nameNodeUri or rpcNamenode must be null.
382       */
383      DFSClient(URI nameNodeUri, ClientProtocol rpcNamenode,
384          Configuration conf, FileSystem.Statistics stats)
385        throws IOException {
386        // Copy only the required DFSClient configuration
387        this.dfsClientConf = new Conf(conf);
388        this.conf = conf;
389        this.stats = stats;
390        this.socketFactory = NetUtils.getSocketFactory(conf, ClientProtocol.class);
391        this.dtpReplaceDatanodeOnFailure = ReplaceDatanodeOnFailure.get(conf);
392    
393        // The hdfsTimeout is currently the same as the ipc timeout 
394        this.hdfsTimeout = Client.getTimeout(conf);
395        this.ugi = UserGroupInformation.getCurrentUser();
396        
397        this.authority = nameNodeUri == null? "null": nameNodeUri.getAuthority();
398        this.clientName = "DFSClient_" + dfsClientConf.taskId + "_" + 
399            DFSUtil.getRandom().nextInt()  + "_" + Thread.currentThread().getId();
400        
401        if (rpcNamenode != null) {
402          // This case is used for testing.
403          Preconditions.checkArgument(nameNodeUri == null);
404          this.namenode = rpcNamenode;
405          dtService = null;
406        } else {
407          Preconditions.checkArgument(nameNodeUri != null,
408              "null URI");
409          NameNodeProxies.ProxyAndInfo<ClientProtocol> proxyInfo =
410            NameNodeProxies.createProxy(conf, nameNodeUri, ClientProtocol.class);
411          this.dtService = proxyInfo.getDelegationTokenService();
412          this.namenode = proxyInfo.getProxy();
413        }
414    
415        // read directly from the block file if configured.
416        this.shortCircuitLocalReads = conf.getBoolean(
417            DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_KEY,
418            DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_DEFAULT);
419        if (LOG.isDebugEnabled()) {
420          LOG.debug("Short circuit read is " + shortCircuitLocalReads);
421        }
422        String localInterfaces[] =
423          conf.getTrimmedStrings(DFSConfigKeys.DFS_CLIENT_LOCAL_INTERFACES);
424        localInterfaceAddrs = getLocalInterfaceAddrs(localInterfaces);
425        if (LOG.isDebugEnabled() && 0 != localInterfaces.length) {
426          LOG.debug("Using local interfaces [" +
427          Joiner.on(',').join(localInterfaces)+ "] with addresses [" +
428          Joiner.on(',').join(localInterfaceAddrs) + "]");
429        }
430        
431        this.socketCache = new SocketCache(dfsClientConf.socketCacheCapacity);
432      }
433    
434      /**
435       * Return the socket addresses to use with each configured
436       * local interface. Local interfaces may be specified by IP
437       * address, IP address range using CIDR notation, interface
438       * name (e.g. eth0) or sub-interface name (e.g. eth0:0).
439       * The socket addresses consist of the IPs for the interfaces
440       * and the ephemeral port (port 0). If an IP, IP range, or
441       * interface name matches an interface with sub-interfaces
442       * only the IP of the interface is used. Sub-interfaces can
443       * be used by specifying them explicitly (by IP or name).
444       * 
445       * @return SocketAddresses for the configured local interfaces,
446       *    or an empty array if none are configured
447       * @throws UnknownHostException if a given interface name is invalid
448       */
449      private static SocketAddress[] getLocalInterfaceAddrs(
450          String interfaceNames[]) throws UnknownHostException {
451        List<SocketAddress> localAddrs = new ArrayList<SocketAddress>();
452        for (String interfaceName : interfaceNames) {
453          if (InetAddresses.isInetAddress(interfaceName)) {
454            localAddrs.add(new InetSocketAddress(interfaceName, 0));
455          } else if (NetUtils.isValidSubnet(interfaceName)) {
456            for (InetAddress addr : NetUtils.getIPs(interfaceName, false)) {
457              localAddrs.add(new InetSocketAddress(addr, 0));
458            }
459          } else {
460            for (String ip : DNS.getIPs(interfaceName, false)) {
461              localAddrs.add(new InetSocketAddress(ip, 0));
462            }
463          }
464        }
465        return localAddrs.toArray(new SocketAddress[localAddrs.size()]);
466      }
467    
468      /**
469       * Select one of the configured local interfaces at random. We use a random
470       * interface because other policies like round-robin are less effective
471       * given that we cache connections to datanodes.
472       *
473       * @return one of the local interface addresses at random, or null if no
474       *    local interfaces are configured
475       */
476      SocketAddress getRandomLocalInterfaceAddr() {
477        if (localInterfaceAddrs.length == 0) {
478          return null;
479        }
480        final int idx = r.nextInt(localInterfaceAddrs.length);
481        final SocketAddress addr = localInterfaceAddrs[idx];
482        if (LOG.isDebugEnabled()) {
483          LOG.debug("Using local interface " + addr);
484        }
485        return addr;
486      }
487    
488      /**
489       * Return the number of times the client should go back to the namenode
490       * to retrieve block locations when reading.
491       */
492      int getMaxBlockAcquireFailures() {
493        return dfsClientConf.maxBlockAcquireFailures;
494      }
495    
496      /**
497       * Return the timeout that clients should use when writing to datanodes.
498       * @param numNodes the number of nodes in the pipeline.
499       */
500      int getDatanodeWriteTimeout(int numNodes) {
501        return (dfsClientConf.confTime > 0) ?
502          (dfsClientConf.confTime + HdfsServerConstants.WRITE_TIMEOUT_EXTENSION * numNodes) : 0;
503      }
504    
505      int getDatanodeReadTimeout(int numNodes) {
506        return dfsClientConf.socketTimeout > 0 ?
507            (HdfsServerConstants.READ_TIMEOUT_EXTENSION * numNodes +
508                dfsClientConf.socketTimeout) : 0;
509      }
510      
511      int getHdfsTimeout() {
512        return hdfsTimeout;
513      }
514      
515      String getClientName() {
516        return clientName;
517      }
518    
519      /**
520       * @return whether the client should use hostnames instead of IPs
521       *    when connecting to DataNodes
522       */
523      boolean connectToDnViaHostname() {
524        return dfsClientConf.connectToDnViaHostname;
525      }
526    
527      void checkOpen() throws IOException {
528        if (!clientRunning) {
529          IOException result = new IOException("Filesystem closed");
530          throw result;
531        }
532      }
533    
534      /** Return the lease renewer instance. The renewer thread won't start
535       *  until the first output stream is created. The same instance will
536       *  be returned until all output streams are closed.
537       */
538      public LeaseRenewer getLeaseRenewer() throws IOException {
539          return LeaseRenewer.getInstance(authority, ugi, this);
540      }
541    
542      /** Get a lease and start automatic renewal */
543      private void beginFileLease(final String src, final DFSOutputStream out) 
544          throws IOException {
545        getLeaseRenewer().put(src, out, this);
546      }
547    
548      /** Stop renewal of lease for the file. */
549      void endFileLease(final String src) throws IOException {
550        getLeaseRenewer().closeFile(src, this);
551      }
552        
553    
554      /** Put a file. Only called from LeaseRenewer, where proper locking is
555       *  enforced to consistently update its local dfsclients array and 
556       *  client's filesBeingWritten map.
557       */
558      void putFileBeingWritten(final String src, final DFSOutputStream out) {
559        synchronized(filesBeingWritten) {
560          filesBeingWritten.put(src, out);
561          // update the last lease renewal time only when there was no
562          // writes. once there is one write stream open, the lease renewer
563          // thread keeps it updated well with in anyone's expiration time.
564          if (lastLeaseRenewal == 0) {
565            updateLastLeaseRenewal();
566          }
567        }
568      }
569    
570      /** Remove a file. Only called from LeaseRenewer. */
571      void removeFileBeingWritten(final String src) {
572        synchronized(filesBeingWritten) {
573          filesBeingWritten.remove(src);
574          if (filesBeingWritten.isEmpty()) {
575            lastLeaseRenewal = 0;
576          }
577        }
578      }
579    
580      /** Is file-being-written map empty? */
581      boolean isFilesBeingWrittenEmpty() {
582        synchronized(filesBeingWritten) {
583          return filesBeingWritten.isEmpty();
584        }
585      }
586      
587      /** @return true if the client is running */
588      boolean isClientRunning() {
589        return clientRunning;
590      }
591    
592      long getLastLeaseRenewal() {
593        return lastLeaseRenewal;
594      }
595    
596      void updateLastLeaseRenewal() {
597        synchronized(filesBeingWritten) {
598          if (filesBeingWritten.isEmpty()) {
599            return;
600          }
601          lastLeaseRenewal = Time.now();
602        }
603      }
604    
605      /**
606       * Renew leases.
607       * @return true if lease was renewed. May return false if this
608       * client has been closed or has no files open.
609       **/
610      boolean renewLease() throws IOException {
611        if (clientRunning && !isFilesBeingWrittenEmpty()) {
612          try {
613            namenode.renewLease(clientName);
614            updateLastLeaseRenewal();
615            return true;
616          } catch (IOException e) {
617            // Abort if the lease has already expired. 
618            final long elapsed = Time.now() - getLastLeaseRenewal();
619            if (elapsed > HdfsConstants.LEASE_SOFTLIMIT_PERIOD) {
620              LOG.warn("Failed to renew lease for " + clientName + " for "
621                  + (elapsed/1000) + " seconds (>= soft-limit ="
622                  + (HdfsConstants.LEASE_SOFTLIMIT_PERIOD/1000) + " seconds.) "
623                  + "Closing all files being written ...", e);
624              closeAllFilesBeingWritten(true);
625            } else {
626              // Let the lease renewer handle it and retry.
627              throw e;
628            }
629          }
630        }
631        return false;
632      }
633      
634      /**
635       * Close connections the Namenode.
636       */
637      void closeConnectionToNamenode() {
638        RPC.stopProxy(namenode);
639      }
640      
641      /** Abort and release resources held.  Ignore all errors. */
642      void abort() {
643        clientRunning = false;
644        closeAllFilesBeingWritten(true);
645        socketCache.clear();
646    
647        try {
648          // remove reference to this client and stop the renewer,
649          // if there is no more clients under the renewer.
650          getLeaseRenewer().closeClient(this);
651        } catch (IOException ioe) {
652           LOG.info("Exception occurred while aborting the client. " + ioe);
653        }
654        closeConnectionToNamenode();
655      }
656    
657      /** Close/abort all files being written. */
658      private void closeAllFilesBeingWritten(final boolean abort) {
659        for(;;) {
660          final String src;
661          final DFSOutputStream out;
662          synchronized(filesBeingWritten) {
663            if (filesBeingWritten.isEmpty()) {
664              return;
665            }
666            src = filesBeingWritten.keySet().iterator().next();
667            out = filesBeingWritten.remove(src);
668          }
669          if (out != null) {
670            try {
671              if (abort) {
672                out.abort();
673              } else {
674                out.close();
675              }
676            } catch(IOException ie) {
677              LOG.error("Failed to " + (abort? "abort": "close") + " file " + src,
678                  ie);
679            }
680          }
681        }
682      }
683    
684      /**
685       * Close the file system, abandoning all of the leases and files being
686       * created and close connections to the namenode.
687       */
688      @Override
689      public synchronized void close() throws IOException {
690        if(clientRunning) {
691          closeAllFilesBeingWritten(false);
692          socketCache.clear();
693          clientRunning = false;
694          getLeaseRenewer().closeClient(this);
695          // close connections to the namenode
696          closeConnectionToNamenode();
697        }
698      }
699    
700      /**
701       * Get the default block size for this cluster
702       * @return the default block size in bytes
703       */
704      public long getDefaultBlockSize() {
705        return dfsClientConf.defaultBlockSize;
706      }
707        
708      /**
709       * @see ClientProtocol#getPreferredBlockSize(String)
710       */
711      public long getBlockSize(String f) throws IOException {
712        try {
713          return namenode.getPreferredBlockSize(f);
714        } catch (IOException ie) {
715          LOG.warn("Problem getting block size", ie);
716          throw ie;
717        }
718      }
719    
720      /**
721       * Get server default values for a number of configuration params.
722       * @see ClientProtocol#getServerDefaults()
723       */
724      public FsServerDefaults getServerDefaults() throws IOException {
725        long now = Time.now();
726        if (now - serverDefaultsLastUpdate > SERVER_DEFAULTS_VALIDITY_PERIOD) {
727          serverDefaults = namenode.getServerDefaults();
728          serverDefaultsLastUpdate = now;
729        }
730        return serverDefaults;
731      }
732      
733      /**
734       * Get a canonical token service name for this client's tokens.  Null should
735       * be returned if the client is not using tokens.
736       * @return the token service for the client
737       */
738      @InterfaceAudience.LimitedPrivate( { "HDFS" }) 
739      public String getCanonicalServiceName() {
740        return (dtService != null) ? dtService.toString() : null;
741      }
742      
743      /**
744       * @see ClientProtocol#getDelegationToken(Text)
745       */
746      public Token<DelegationTokenIdentifier> getDelegationToken(Text renewer)
747          throws IOException {
748        assert dtService != null;
749        Token<DelegationTokenIdentifier> token =
750          namenode.getDelegationToken(renewer);
751        token.setService(this.dtService);
752    
753        LOG.info("Created " + DelegationTokenIdentifier.stringifyToken(token));
754        return token;
755      }
756    
757      /**
758       * Renew a delegation token
759       * @param token the token to renew
760       * @return the new expiration time
761       * @throws InvalidToken
762       * @throws IOException
763       * @deprecated Use Token.renew instead.
764       */
765      public long renewDelegationToken(Token<DelegationTokenIdentifier> token)
766          throws InvalidToken, IOException {
767        LOG.info("Renewing " + DelegationTokenIdentifier.stringifyToken(token));
768        try {
769          return token.renew(conf);
770        } catch (InterruptedException ie) {                                       
771          throw new RuntimeException("caught interrupted", ie);
772        } catch (RemoteException re) {
773          throw re.unwrapRemoteException(InvalidToken.class,
774                                         AccessControlException.class);
775        }
776      }
777    
778      /**
779       * Get {@link BlockReader} for short circuited local reads.
780       */
781      static BlockReader getLocalBlockReader(Configuration conf,
782          String src, ExtendedBlock blk, Token<BlockTokenIdentifier> accessToken,
783          DatanodeInfo chosenNode, int socketTimeout, long offsetIntoBlock,
784          boolean connectToDnViaHostname) throws InvalidToken, IOException {
785        try {
786          return BlockReaderLocal.newBlockReader(conf, src, blk, accessToken,
787              chosenNode, socketTimeout, offsetIntoBlock, blk.getNumBytes()
788                  - offsetIntoBlock, connectToDnViaHostname);
789        } catch (RemoteException re) {
790          throw re.unwrapRemoteException(InvalidToken.class,
791              AccessControlException.class);
792        }
793      }
794      
795      private static Map<String, Boolean> localAddrMap = Collections
796          .synchronizedMap(new HashMap<String, Boolean>());
797      
798      private static boolean isLocalAddress(InetSocketAddress targetAddr) {
799        InetAddress addr = targetAddr.getAddress();
800        Boolean cached = localAddrMap.get(addr.getHostAddress());
801        if (cached != null) {
802          if (LOG.isTraceEnabled()) {
803            LOG.trace("Address " + targetAddr +
804                      (cached ? " is local" : " is not local"));
805          }
806          return cached;
807        }
808    
809        // Check if the address is any local or loop back
810        boolean local = addr.isAnyLocalAddress() || addr.isLoopbackAddress();
811    
812        // Check if the address is defined on any interface
813        if (!local) {
814          try {
815            local = NetworkInterface.getByInetAddress(addr) != null;
816          } catch (SocketException e) {
817            local = false;
818          }
819        }
820        if (LOG.isTraceEnabled()) {
821          LOG.trace("Address " + targetAddr +
822                    (local ? " is local" : " is not local"));
823        }
824        localAddrMap.put(addr.getHostAddress(), local);
825        return local;
826      }
827      
828      /**
829       * Should the block access token be refetched on an exception
830       * 
831       * @param ex Exception received
832       * @param targetAddr Target datanode address from where exception was received
833       * @return true if block access token has expired or invalid and it should be
834       *         refetched
835       */
836      private static boolean tokenRefetchNeeded(IOException ex,
837          InetSocketAddress targetAddr) {
838        /*
839         * Get a new access token and retry. Retry is needed in 2 cases. 1) When
840         * both NN and DN re-started while DFSClient holding a cached access token.
841         * 2) In the case that NN fails to update its access key at pre-set interval
842         * (by a wide margin) and subsequently restarts. In this case, DN
843         * re-registers itself with NN and receives a new access key, but DN will
844         * delete the old access key from its memory since it's considered expired
845         * based on the estimated expiration date.
846         */
847        if (ex instanceof InvalidBlockTokenException || ex instanceof InvalidToken) {
848          LOG.info("Access token was invalid when connecting to " + targetAddr
849              + " : " + ex);
850          return true;
851        }
852        return false;
853      }
854      
855      /**
856       * Cancel a delegation token
857       * @param token the token to cancel
858       * @throws InvalidToken
859       * @throws IOException
860       * @deprecated Use Token.cancel instead.
861       */
862      public void cancelDelegationToken(Token<DelegationTokenIdentifier> token)
863          throws InvalidToken, IOException {
864        LOG.info("Cancelling " + DelegationTokenIdentifier.stringifyToken(token));
865        try {
866          token.cancel(conf);
867         } catch (InterruptedException ie) {                                       
868          throw new RuntimeException("caught interrupted", ie);
869        } catch (RemoteException re) {
870          throw re.unwrapRemoteException(InvalidToken.class,
871                                         AccessControlException.class);
872        }
873      }
874      
875      @InterfaceAudience.Private
876      public static class Renewer extends TokenRenewer {
877        
878        static {
879          //Ensure that HDFS Configuration files are loaded before trying to use
880          // the renewer.
881          HdfsConfiguration.init();
882        }
883        
884        @Override
885        public boolean handleKind(Text kind) {
886          return DelegationTokenIdentifier.HDFS_DELEGATION_KIND.equals(kind);
887        }
888    
889        @SuppressWarnings("unchecked")
890        @Override
891        public long renew(Token<?> token, Configuration conf) throws IOException {
892          Token<DelegationTokenIdentifier> delToken = 
893            (Token<DelegationTokenIdentifier>) token;
894          ClientProtocol nn = getNNProxy(delToken, conf);
895          try {
896            return nn.renewDelegationToken(delToken);
897          } catch (RemoteException re) {
898            throw re.unwrapRemoteException(InvalidToken.class, 
899                                           AccessControlException.class);
900          }
901        }
902    
903        @SuppressWarnings("unchecked")
904        @Override
905        public void cancel(Token<?> token, Configuration conf) throws IOException {
906          Token<DelegationTokenIdentifier> delToken = 
907              (Token<DelegationTokenIdentifier>) token;
908          LOG.info("Cancelling " + 
909                   DelegationTokenIdentifier.stringifyToken(delToken));
910          ClientProtocol nn = getNNProxy(delToken, conf);
911          try {
912            nn.cancelDelegationToken(delToken);
913          } catch (RemoteException re) {
914            throw re.unwrapRemoteException(InvalidToken.class,
915                AccessControlException.class);
916          }
917        }
918        
919        private static ClientProtocol getNNProxy(
920            Token<DelegationTokenIdentifier> token, Configuration conf)
921            throws IOException {
922          URI uri = HAUtil.getServiceUriFromToken(token);
923          if (HAUtil.isTokenForLogicalUri(token) &&
924              !HAUtil.isLogicalUri(conf, uri)) {
925            // If the token is for a logical nameservice, but the configuration
926            // we have disagrees about that, we can't actually renew it.
927            // This can be the case in MR, for example, if the RM doesn't
928            // have all of the HA clusters configured in its configuration.
929            throw new IOException("Unable to map logical nameservice URI '" +
930                uri + "' to a NameNode. Local configuration does not have " +
931                "a failover proxy provider configured.");
932          }
933          
934          NameNodeProxies.ProxyAndInfo<ClientProtocol> info =
935            NameNodeProxies.createProxy(conf, uri, ClientProtocol.class);
936          assert info.getDelegationTokenService().equals(token.getService()) :
937            "Returned service '" + info.getDelegationTokenService().toString() +
938            "' doesn't match expected service '" +
939            token.getService().toString() + "'";
940            
941          return info.getProxy();
942        }
943    
944        @Override
945        public boolean isManaged(Token<?> token) throws IOException {
946          return true;
947        }
948        
949      }
950    
951      /**
952       * Report corrupt blocks that were discovered by the client.
953       * @see ClientProtocol#reportBadBlocks(LocatedBlock[])
954       */
955      public void reportBadBlocks(LocatedBlock[] blocks) throws IOException {
956        namenode.reportBadBlocks(blocks);
957      }
958      
959      public short getDefaultReplication() {
960        return dfsClientConf.defaultReplication;
961      }
962      
963      /*
964       * This is just a wrapper around callGetBlockLocations, but non-static so that
965       * we can stub it out for tests.
966       */
967      @VisibleForTesting
968      public LocatedBlocks getLocatedBlocks(String src, long start, long length)
969          throws IOException {
970        return callGetBlockLocations(namenode, src, start, length);
971      }
972    
973      /**
974       * @see ClientProtocol#getBlockLocations(String, long, long)
975       */
976      static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,
977          String src, long start, long length) 
978          throws IOException {
979        try {
980          return namenode.getBlockLocations(src, start, length);
981        } catch(RemoteException re) {
982          throw re.unwrapRemoteException(AccessControlException.class,
983                                         FileNotFoundException.class,
984                                         UnresolvedPathException.class);
985        }
986      }
987    
988      /**
989       * Recover a file's lease
990       * @param src a file's path
991       * @return true if the file is already closed
992       * @throws IOException
993       */
994      boolean recoverLease(String src) throws IOException {
995        checkOpen();
996    
997        try {
998          return namenode.recoverLease(src, clientName);
999        } catch (RemoteException re) {
1000          throw re.unwrapRemoteException(FileNotFoundException.class,
1001                                         AccessControlException.class);
1002        }
1003      }
1004    
1005      /**
1006       * Get block location info about file
1007       * 
1008       * getBlockLocations() returns a list of hostnames that store 
1009       * data for a specific file region.  It returns a set of hostnames
1010       * for every block within the indicated region.
1011       *
1012       * This function is very useful when writing code that considers
1013       * data-placement when performing operations.  For example, the
1014       * MapReduce system tries to schedule tasks on the same machines
1015       * as the data-block the task processes. 
1016       */
1017      public BlockLocation[] getBlockLocations(String src, long start, 
1018        long length) throws IOException, UnresolvedLinkException {
1019        LocatedBlocks blocks = getLocatedBlocks(src, start, length);
1020        BlockLocation[] locations =  DFSUtil.locatedBlocks2Locations(blocks);
1021        HdfsBlockLocation[] hdfsLocations = new HdfsBlockLocation[locations.length];
1022        for (int i = 0; i < locations.length; i++) {
1023          hdfsLocations[i] = new HdfsBlockLocation(locations[i], blocks.get(i));
1024        }
1025        return hdfsLocations;
1026      }
1027      
1028      /**
1029       * Get block location information about a list of {@link HdfsBlockLocation}.
1030       * Used by {@link DistributedFileSystem#getFileBlockStorageLocations(List)} to
1031       * get {@link BlockStorageLocation}s for blocks returned by
1032       * {@link DistributedFileSystem#getFileBlockLocations(org.apache.hadoop.fs.FileStatus, long, long)}
1033       * .
1034       * 
1035       * This is done by making a round of RPCs to the associated datanodes, asking
1036       * the volume of each block replica. The returned array of
1037       * {@link BlockStorageLocation} expose this information as a
1038       * {@link VolumeId}.
1039       * 
1040       * @param blockLocations
1041       *          target blocks on which to query volume location information
1042       * @return volumeBlockLocations original block array augmented with additional
1043       *         volume location information for each replica.
1044       */
1045      public BlockStorageLocation[] getBlockStorageLocations(
1046          List<BlockLocation> blockLocations) throws IOException,
1047          UnsupportedOperationException, InvalidBlockTokenException {
1048        if (!getConf().getHdfsBlocksMetadataEnabled) {
1049          throw new UnsupportedOperationException("Datanode-side support for " +
1050              "getVolumeBlockLocations() must also be enabled in the client " +
1051              "configuration.");
1052        }
1053        // Downcast blockLocations and fetch out required LocatedBlock(s)
1054        List<LocatedBlock> blocks = new ArrayList<LocatedBlock>();
1055        for (BlockLocation loc : blockLocations) {
1056          if (!(loc instanceof HdfsBlockLocation)) {
1057            throw new ClassCastException("DFSClient#getVolumeBlockLocations " +
1058                "expected to be passed HdfsBlockLocations");
1059          }
1060          HdfsBlockLocation hdfsLoc = (HdfsBlockLocation) loc;
1061          blocks.add(hdfsLoc.getLocatedBlock());
1062        }
1063        
1064        // Re-group the LocatedBlocks to be grouped by datanodes, with the values
1065        // a list of the LocatedBlocks on the datanode.
1066        Map<DatanodeInfo, List<LocatedBlock>> datanodeBlocks = 
1067            new LinkedHashMap<DatanodeInfo, List<LocatedBlock>>();
1068        for (LocatedBlock b : blocks) {
1069          for (DatanodeInfo info : b.getLocations()) {
1070            if (!datanodeBlocks.containsKey(info)) {
1071              datanodeBlocks.put(info, new ArrayList<LocatedBlock>());
1072            }
1073            List<LocatedBlock> l = datanodeBlocks.get(info);
1074            l.add(b);
1075          }
1076        }
1077            
1078        // Make RPCs to the datanodes to get volume locations for its replicas
1079        List<HdfsBlocksMetadata> metadatas = BlockStorageLocationUtil
1080            .queryDatanodesForHdfsBlocksMetadata(conf, datanodeBlocks,
1081                getConf().getFileBlockStorageLocationsNumThreads,
1082                getConf().getFileBlockStorageLocationsTimeout,
1083                getConf().connectToDnViaHostname);
1084        
1085        // Regroup the returned VolumeId metadata to again be grouped by
1086        // LocatedBlock rather than by datanode
1087        Map<LocatedBlock, List<VolumeId>> blockVolumeIds = BlockStorageLocationUtil
1088            .associateVolumeIdsWithBlocks(blocks, datanodeBlocks, metadatas);
1089        
1090        // Combine original BlockLocations with new VolumeId information
1091        BlockStorageLocation[] volumeBlockLocations = BlockStorageLocationUtil
1092            .convertToVolumeBlockLocations(blocks, blockVolumeIds);
1093    
1094        return volumeBlockLocations;
1095      }
1096      
1097      public DFSInputStream open(String src) 
1098          throws IOException, UnresolvedLinkException {
1099        return open(src, dfsClientConf.ioBufferSize, true, null);
1100      }
1101    
1102      /**
1103       * Create an input stream that obtains a nodelist from the
1104       * namenode, and then reads from all the right places.  Creates
1105       * inner subclass of InputStream that does the right out-of-band
1106       * work.
1107       * @deprecated Use {@link #open(String, int, boolean)} instead.
1108       */
1109      @Deprecated
1110      public DFSInputStream open(String src, int buffersize, boolean verifyChecksum,
1111                                 FileSystem.Statistics stats)
1112          throws IOException, UnresolvedLinkException {
1113        return open(src, buffersize, verifyChecksum);
1114      }
1115      
1116    
1117      /**
1118       * Create an input stream that obtains a nodelist from the
1119       * namenode, and then reads from all the right places.  Creates
1120       * inner subclass of InputStream that does the right out-of-band
1121       * work.
1122       */
1123      public DFSInputStream open(String src, int buffersize, boolean verifyChecksum)
1124          throws IOException, UnresolvedLinkException {
1125        checkOpen();
1126        //    Get block info from namenode
1127        return new DFSInputStream(this, src, buffersize, verifyChecksum);
1128      }
1129    
1130      /**
1131       * Get the namenode associated with this DFSClient object
1132       * @return the namenode associated with this DFSClient object
1133       */
1134      public ClientProtocol getNamenode() {
1135        return namenode;
1136      }
1137      
1138      /**
1139       * Call {@link #create(String, boolean, short, long, Progressable)} with
1140       * default <code>replication</code> and <code>blockSize<code> and null <code>
1141       * progress</code>.
1142       */
1143      public OutputStream create(String src, boolean overwrite) 
1144          throws IOException {
1145        return create(src, overwrite, dfsClientConf.defaultReplication,
1146            dfsClientConf.defaultBlockSize, null);
1147      }
1148        
1149      /**
1150       * Call {@link #create(String, boolean, short, long, Progressable)} with
1151       * default <code>replication</code> and <code>blockSize<code>.
1152       */
1153      public OutputStream create(String src, 
1154                                 boolean overwrite,
1155                                 Progressable progress) throws IOException {
1156        return create(src, overwrite, dfsClientConf.defaultReplication,
1157            dfsClientConf.defaultBlockSize, progress);
1158      }
1159        
1160      /**
1161       * Call {@link #create(String, boolean, short, long, Progressable)} with
1162       * null <code>progress</code>.
1163       */
1164      public OutputStream create(String src, 
1165                                 boolean overwrite, 
1166                                 short replication,
1167                                 long blockSize) throws IOException {
1168        return create(src, overwrite, replication, blockSize, null);
1169      }
1170    
1171      /**
1172       * Call {@link #create(String, boolean, short, long, Progressable, int)}
1173       * with default bufferSize.
1174       */
1175      public OutputStream create(String src, boolean overwrite, short replication,
1176          long blockSize, Progressable progress) throws IOException {
1177        return create(src, overwrite, replication, blockSize, progress,
1178            dfsClientConf.ioBufferSize);
1179      }
1180    
1181      /**
1182       * Call {@link #create(String, FsPermission, EnumSet, short, long, 
1183       * Progressable, int)} with default <code>permission</code>
1184       * {@link FsPermission#getDefault()}.
1185       * 
1186       * @param src File name
1187       * @param overwrite overwrite an existing file if true
1188       * @param replication replication factor for the file
1189       * @param blockSize maximum block size
1190       * @param progress interface for reporting client progress
1191       * @param buffersize underlying buffersize
1192       * 
1193       * @return output stream
1194       */
1195      public OutputStream create(String src,
1196                                 boolean overwrite,
1197                                 short replication,
1198                                 long blockSize,
1199                                 Progressable progress,
1200                                 int buffersize)
1201          throws IOException {
1202        return create(src, FsPermission.getDefault(),
1203            overwrite ? EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE)
1204                : EnumSet.of(CreateFlag.CREATE), replication, blockSize, progress,
1205            buffersize, null);
1206      }
1207    
1208      /**
1209       * Call {@link #create(String, FsPermission, EnumSet, boolean, short, 
1210       * long, Progressable, int, ChecksumOpt)} with <code>createParent</code>
1211       *  set to true.
1212       */
1213      public DFSOutputStream create(String src, 
1214                                 FsPermission permission,
1215                                 EnumSet<CreateFlag> flag, 
1216                                 short replication,
1217                                 long blockSize,
1218                                 Progressable progress,
1219                                 int buffersize,
1220                                 ChecksumOpt checksumOpt)
1221          throws IOException {
1222        return create(src, permission, flag, true,
1223            replication, blockSize, progress, buffersize, checksumOpt);
1224      }
1225    
1226      /**
1227       * Create a new dfs file with the specified block replication 
1228       * with write-progress reporting and return an output stream for writing
1229       * into the file.  
1230       * 
1231       * @param src File name
1232       * @param permission The permission of the directory being created.
1233       *          If null, use default permission {@link FsPermission#getDefault()}
1234       * @param flag indicates create a new file or create/overwrite an
1235       *          existing file or append to an existing file
1236       * @param createParent create missing parent directory if true
1237       * @param replication block replication
1238       * @param blockSize maximum block size
1239       * @param progress interface for reporting client progress
1240       * @param buffersize underlying buffer size 
1241       * @param checksumOpt checksum options
1242       * 
1243       * @return output stream
1244       * 
1245       * @see ClientProtocol#create(String, FsPermission, String, EnumSetWritable,
1246       * boolean, short, long) for detailed description of exceptions thrown
1247       */
1248      public DFSOutputStream create(String src, 
1249                                 FsPermission permission,
1250                                 EnumSet<CreateFlag> flag, 
1251                                 boolean createParent,
1252                                 short replication,
1253                                 long blockSize,
1254                                 Progressable progress,
1255                                 int buffersize,
1256                                 ChecksumOpt checksumOpt) throws IOException {
1257        checkOpen();
1258        if (permission == null) {
1259          permission = FsPermission.getDefault();
1260        }
1261        FsPermission masked = permission.applyUMask(dfsClientConf.uMask);
1262        if(LOG.isDebugEnabled()) {
1263          LOG.debug(src + ": masked=" + masked);
1264        }
1265        final DFSOutputStream result = DFSOutputStream.newStreamForCreate(this,
1266            src, masked, flag, createParent, replication, blockSize, progress,
1267            buffersize, dfsClientConf.createChecksum(checksumOpt));
1268        beginFileLease(src, result);
1269        return result;
1270      }
1271      
1272      /**
1273       * Append to an existing file if {@link CreateFlag#APPEND} is present
1274       */
1275      private DFSOutputStream primitiveAppend(String src, EnumSet<CreateFlag> flag,
1276          int buffersize, Progressable progress) throws IOException {
1277        if (flag.contains(CreateFlag.APPEND)) {
1278          HdfsFileStatus stat = getFileInfo(src);
1279          if (stat == null) { // No file to append to
1280            // New file needs to be created if create option is present
1281            if (!flag.contains(CreateFlag.CREATE)) {
1282              throw new FileNotFoundException("failed to append to non-existent file "
1283                  + src + " on client " + clientName);
1284            }
1285            return null;
1286          }
1287          return callAppend(stat, src, buffersize, progress);
1288        }
1289        return null;
1290      }
1291      
1292      /**
1293       * Same as {{@link #create(String, FsPermission, EnumSet, short, long,
1294       *  Progressable, int)} except that the permission
1295       *  is absolute (ie has already been masked with umask.
1296       */
1297      public DFSOutputStream primitiveCreate(String src, 
1298                                 FsPermission absPermission,
1299                                 EnumSet<CreateFlag> flag,
1300                                 boolean createParent,
1301                                 short replication,
1302                                 long blockSize,
1303                                 Progressable progress,
1304                                 int buffersize,
1305                                 ChecksumOpt checksumOpt)
1306          throws IOException, UnresolvedLinkException {
1307        checkOpen();
1308        CreateFlag.validate(flag);
1309        DFSOutputStream result = primitiveAppend(src, flag, buffersize, progress);
1310        if (result == null) {
1311          DataChecksum checksum = dfsClientConf.createChecksum(checksumOpt);
1312          result = DFSOutputStream.newStreamForCreate(this, src, absPermission,
1313              flag, createParent, replication, blockSize, progress, buffersize,
1314              checksum);
1315        }
1316        beginFileLease(src, result);
1317        return result;
1318      }
1319      
1320      /**
1321       * Creates a symbolic link.
1322       * 
1323       * @see ClientProtocol#createSymlink(String, String,FsPermission, boolean) 
1324       */
1325      public void createSymlink(String target, String link, boolean createParent)
1326          throws IOException {
1327        try {
1328          FsPermission dirPerm = 
1329              FsPermission.getDefault().applyUMask(dfsClientConf.uMask); 
1330          namenode.createSymlink(target, link, dirPerm, createParent);
1331        } catch (RemoteException re) {
1332          throw re.unwrapRemoteException(AccessControlException.class,
1333                                         FileAlreadyExistsException.class, 
1334                                         FileNotFoundException.class,
1335                                         ParentNotDirectoryException.class,
1336                                         NSQuotaExceededException.class, 
1337                                         DSQuotaExceededException.class,
1338                                         UnresolvedPathException.class);
1339        }
1340      }
1341    
1342      /**
1343       * Resolve the *first* symlink, if any, in the path.
1344       * 
1345       * @see ClientProtocol#getLinkTarget(String)
1346       */
1347      public String getLinkTarget(String path) throws IOException { 
1348        checkOpen();
1349        try {
1350          return namenode.getLinkTarget(path);
1351        } catch (RemoteException re) {
1352          throw re.unwrapRemoteException(AccessControlException.class,
1353                                         FileNotFoundException.class);
1354        }
1355      }
1356    
1357      /** Method to get stream returned by append call */
1358      private DFSOutputStream callAppend(HdfsFileStatus stat, String src,
1359          int buffersize, Progressable progress) throws IOException {
1360        LocatedBlock lastBlock = null;
1361        try {
1362          lastBlock = namenode.append(src, clientName);
1363        } catch(RemoteException re) {
1364          throw re.unwrapRemoteException(AccessControlException.class,
1365                                         FileNotFoundException.class,
1366                                         SafeModeException.class,
1367                                         DSQuotaExceededException.class,
1368                                         UnsupportedOperationException.class,
1369                                         UnresolvedPathException.class);
1370        }
1371        return DFSOutputStream.newStreamForAppend(this, src, buffersize, progress,
1372            lastBlock, stat, dfsClientConf.createChecksum());
1373      }
1374      
1375      /**
1376       * Append to an existing HDFS file.  
1377       * 
1378       * @param src file name
1379       * @param buffersize buffer size
1380       * @param progress for reporting write-progress; null is acceptable.
1381       * @param statistics file system statistics; null is acceptable.
1382       * @return an output stream for writing into the file
1383       * 
1384       * @see ClientProtocol#append(String, String) 
1385       */
1386      public HdfsDataOutputStream append(final String src, final int buffersize,
1387          final Progressable progress, final FileSystem.Statistics statistics
1388          ) throws IOException {
1389        final DFSOutputStream out = append(src, buffersize, progress);
1390        return new HdfsDataOutputStream(out, statistics, out.getInitialLen());
1391      }
1392    
1393      private DFSOutputStream append(String src, int buffersize, Progressable progress) 
1394          throws IOException {
1395        checkOpen();
1396        HdfsFileStatus stat = getFileInfo(src);
1397        if (stat == null) { // No file found
1398          throw new FileNotFoundException("failed to append to non-existent file "
1399              + src + " on client " + clientName);
1400        }
1401        final DFSOutputStream result = callAppend(stat, src, buffersize, progress);
1402        beginFileLease(src, result);
1403        return result;
1404      }
1405    
1406      /**
1407       * Set replication for an existing file.
1408       * @param src file name
1409       * @param replication
1410       * 
1411       * @see ClientProtocol#setReplication(String, short)
1412       */
1413      public boolean setReplication(String src, short replication)
1414          throws IOException {
1415        try {
1416          return namenode.setReplication(src, replication);
1417        } catch(RemoteException re) {
1418          throw re.unwrapRemoteException(AccessControlException.class,
1419                                         FileNotFoundException.class,
1420                                         SafeModeException.class,
1421                                         DSQuotaExceededException.class,
1422                                         UnresolvedPathException.class);
1423        }
1424      }
1425    
1426      /**
1427       * Rename file or directory.
1428       * @see ClientProtocol#rename(String, String)
1429       * @deprecated Use {@link #rename(String, String, Options.Rename...)} instead.
1430       */
1431      @Deprecated
1432      public boolean rename(String src, String dst) throws IOException {
1433        checkOpen();
1434        try {
1435          return namenode.rename(src, dst);
1436        } catch(RemoteException re) {
1437          throw re.unwrapRemoteException(AccessControlException.class,
1438                                         NSQuotaExceededException.class,
1439                                         DSQuotaExceededException.class,
1440                                         UnresolvedPathException.class);
1441        }
1442      }
1443    
1444      /**
1445       * Move blocks from src to trg and delete src
1446       * See {@link ClientProtocol#concat(String, String [])}. 
1447       */
1448      public void concat(String trg, String [] srcs) throws IOException {
1449        checkOpen();
1450        try {
1451          namenode.concat(trg, srcs);
1452        } catch(RemoteException re) {
1453          throw re.unwrapRemoteException(AccessControlException.class,
1454                                         UnresolvedPathException.class);
1455        }
1456      }
1457      /**
1458       * Rename file or directory.
1459       * @see ClientProtocol#rename2(String, String, Options.Rename...)
1460       */
1461      public void rename(String src, String dst, Options.Rename... options)
1462          throws IOException {
1463        checkOpen();
1464        try {
1465          namenode.rename2(src, dst, options);
1466        } catch(RemoteException re) {
1467          throw re.unwrapRemoteException(AccessControlException.class,
1468                                         DSQuotaExceededException.class,
1469                                         FileAlreadyExistsException.class,
1470                                         FileNotFoundException.class,
1471                                         ParentNotDirectoryException.class,
1472                                         SafeModeException.class,
1473                                         NSQuotaExceededException.class,
1474                                         UnresolvedPathException.class);
1475        }
1476      }
1477      /**
1478       * Delete file or directory.
1479       * See {@link ClientProtocol#delete(String)}. 
1480       */
1481      @Deprecated
1482      public boolean delete(String src) throws IOException {
1483        checkOpen();
1484        return namenode.delete(src, true);
1485      }
1486    
1487      /**
1488       * delete file or directory.
1489       * delete contents of the directory if non empty and recursive 
1490       * set to true
1491       *
1492       * @see ClientProtocol#delete(String, boolean)
1493       */
1494      public boolean delete(String src, boolean recursive) throws IOException {
1495        checkOpen();
1496        try {
1497          return namenode.delete(src, recursive);
1498        } catch(RemoteException re) {
1499          throw re.unwrapRemoteException(AccessControlException.class,
1500                                         FileNotFoundException.class,
1501                                         SafeModeException.class,
1502                                         UnresolvedPathException.class);
1503        }
1504      }
1505      
1506      /** Implemented using getFileInfo(src)
1507       */
1508      public boolean exists(String src) throws IOException {
1509        checkOpen();
1510        return getFileInfo(src) != null;
1511      }
1512    
1513      /**
1514       * Get a partial listing of the indicated directory
1515       * No block locations need to be fetched
1516       */
1517      public DirectoryListing listPaths(String src,  byte[] startAfter)
1518        throws IOException {
1519        return listPaths(src, startAfter, false);
1520      }
1521      
1522      /**
1523       * Get a partial listing of the indicated directory
1524       *
1525       * Recommend to use HdfsFileStatus.EMPTY_NAME as startAfter
1526       * if the application wants to fetch a listing starting from
1527       * the first entry in the directory
1528       *
1529       * @see ClientProtocol#getListing(String, byte[], boolean)
1530       */
1531      public DirectoryListing listPaths(String src,  byte[] startAfter,
1532          boolean needLocation) 
1533        throws IOException {
1534        checkOpen();
1535        try {
1536          return namenode.getListing(src, startAfter, needLocation);
1537        } catch(RemoteException re) {
1538          throw re.unwrapRemoteException(AccessControlException.class,
1539                                         FileNotFoundException.class,
1540                                         UnresolvedPathException.class);
1541        }
1542      }
1543    
1544      /**
1545       * Get the file info for a specific file or directory.
1546       * @param src The string representation of the path to the file
1547       * @return object containing information regarding the file
1548       *         or null if file not found
1549       *         
1550       * @see ClientProtocol#getFileInfo(String) for description of exceptions
1551       */
1552      public HdfsFileStatus getFileInfo(String src) throws IOException {
1553        checkOpen();
1554        try {
1555          return namenode.getFileInfo(src);
1556        } catch(RemoteException re) {
1557          throw re.unwrapRemoteException(AccessControlException.class,
1558                                         FileNotFoundException.class,
1559                                         UnresolvedPathException.class);
1560        }
1561      }
1562    
1563      /**
1564       * Get the file info for a specific file or directory. If src
1565       * refers to a symlink then the FileStatus of the link is returned.
1566       * @param src path to a file or directory.
1567       * 
1568       * For description of exceptions thrown 
1569       * @see ClientProtocol#getFileLinkInfo(String)
1570       */
1571      public HdfsFileStatus getFileLinkInfo(String src) throws IOException {
1572        checkOpen();
1573        try {
1574          return namenode.getFileLinkInfo(src);
1575        } catch(RemoteException re) {
1576          throw re.unwrapRemoteException(AccessControlException.class,
1577                                         UnresolvedPathException.class);
1578         }
1579       }
1580    
1581      /**
1582       * Get the checksum of a file.
1583       * @param src The file path
1584       * @return The checksum 
1585       * @see DistributedFileSystem#getFileChecksum(Path)
1586       */
1587      public MD5MD5CRC32FileChecksum getFileChecksum(String src) throws IOException {
1588        checkOpen();
1589        return getFileChecksum(src, namenode, socketFactory,
1590            dfsClientConf.socketTimeout, getDataEncryptionKey(),
1591            dfsClientConf.connectToDnViaHostname);
1592      }
1593      
1594      @InterfaceAudience.Private
1595      public void clearDataEncryptionKey() {
1596        LOG.debug("Clearing encryption key");
1597        synchronized (this) {
1598          encryptionKey = null;
1599        }
1600      }
1601      
1602      /**
1603       * @return true if data sent between this client and DNs should be encrypted,
1604       *         false otherwise.
1605       * @throws IOException in the event of error communicating with the NN
1606       */
1607      boolean shouldEncryptData() throws IOException {
1608        FsServerDefaults d = getServerDefaults();
1609        return d == null ? false : d.getEncryptDataTransfer();
1610      }
1611      
1612      @InterfaceAudience.Private
1613      public DataEncryptionKey getDataEncryptionKey()
1614          throws IOException {
1615        if (shouldEncryptData()) {
1616          synchronized (this) {
1617            if (encryptionKey == null ||
1618                (encryptionKey != null &&
1619                 encryptionKey.expiryDate < Time.now())) {
1620              LOG.debug("Getting new encryption token from NN");
1621              encryptionKey = namenode.getDataEncryptionKey();
1622            }
1623            return encryptionKey;
1624          }
1625        } else {
1626          return null;
1627        }
1628      }
1629    
1630      /**
1631       * Get the checksum of a file.
1632       * @param src The file path
1633       * @return The checksum 
1634       */
1635      public static MD5MD5CRC32FileChecksum getFileChecksum(String src,
1636          ClientProtocol namenode, SocketFactory socketFactory, int socketTimeout,
1637          DataEncryptionKey encryptionKey, boolean connectToDnViaHostname)
1638          throws IOException {
1639        //get all block locations
1640        LocatedBlocks blockLocations = callGetBlockLocations(namenode, src, 0, Long.MAX_VALUE);
1641        if (null == blockLocations) {
1642          throw new FileNotFoundException("File does not exist: " + src);
1643        }
1644        List<LocatedBlock> locatedblocks = blockLocations.getLocatedBlocks();
1645        final DataOutputBuffer md5out = new DataOutputBuffer();
1646        int bytesPerCRC = -1;
1647        DataChecksum.Type crcType = DataChecksum.Type.DEFAULT;
1648        long crcPerBlock = 0;
1649        boolean refetchBlocks = false;
1650        int lastRetriedIndex = -1;
1651    
1652        //get block checksum for each block
1653        for(int i = 0; i < locatedblocks.size(); i++) {
1654          if (refetchBlocks) {  // refetch to get fresh tokens
1655            blockLocations = callGetBlockLocations(namenode, src, 0, Long.MAX_VALUE);
1656            if (null == blockLocations) {
1657              throw new FileNotFoundException("File does not exist: " + src);
1658            }
1659            locatedblocks = blockLocations.getLocatedBlocks();
1660            refetchBlocks = false;
1661          }
1662          LocatedBlock lb = locatedblocks.get(i);
1663          final ExtendedBlock block = lb.getBlock();
1664          final DatanodeInfo[] datanodes = lb.getLocations();
1665          
1666          //try each datanode location of the block
1667          final int timeout = 3000 * datanodes.length + socketTimeout;
1668          boolean done = false;
1669          for(int j = 0; !done && j < datanodes.length; j++) {
1670            Socket sock = null;
1671            DataOutputStream out = null;
1672            DataInputStream in = null;
1673            
1674            try {
1675              //connect to a datanode
1676              sock = socketFactory.createSocket();
1677              String dnAddr = datanodes[j].getXferAddr(connectToDnViaHostname);
1678              if (LOG.isDebugEnabled()) {
1679                LOG.debug("Connecting to datanode " + dnAddr);
1680              }
1681              NetUtils.connect(sock, NetUtils.createSocketAddr(dnAddr), timeout);
1682              sock.setSoTimeout(timeout);
1683    
1684              OutputStream unbufOut = NetUtils.getOutputStream(sock);
1685              InputStream unbufIn = NetUtils.getInputStream(sock);
1686              if (encryptionKey != null) {
1687                IOStreamPair encryptedStreams =
1688                    DataTransferEncryptor.getEncryptedStreams(
1689                        unbufOut, unbufIn, encryptionKey);
1690                unbufOut = encryptedStreams.out;
1691                unbufIn = encryptedStreams.in;
1692              }
1693              out = new DataOutputStream(new BufferedOutputStream(unbufOut,
1694                  HdfsConstants.SMALL_BUFFER_SIZE));
1695              in = new DataInputStream(unbufIn);
1696    
1697              if (LOG.isDebugEnabled()) {
1698                LOG.debug("write to " + datanodes[j] + ": "
1699                    + Op.BLOCK_CHECKSUM + ", block=" + block);
1700              }
1701              // get block MD5
1702              new Sender(out).blockChecksum(block, lb.getBlockToken());
1703    
1704              final BlockOpResponseProto reply =
1705                BlockOpResponseProto.parseFrom(HdfsProtoUtil.vintPrefixed(in));
1706    
1707              if (reply.getStatus() != Status.SUCCESS) {
1708                if (reply.getStatus() == Status.ERROR_ACCESS_TOKEN
1709                    && i > lastRetriedIndex) {
1710                  if (LOG.isDebugEnabled()) {
1711                    LOG.debug("Got access token error in response to OP_BLOCK_CHECKSUM "
1712                        + "for file " + src + " for block " + block
1713                        + " from datanode " + datanodes[j]
1714                        + ". Will retry the block once.");
1715                  }
1716                  lastRetriedIndex = i;
1717                  done = true; // actually it's not done; but we'll retry
1718                  i--; // repeat at i-th block
1719                  refetchBlocks = true;
1720                  break;
1721                } else {
1722                  throw new IOException("Bad response " + reply + " for block "
1723                      + block + " from datanode " + datanodes[j]);
1724                }
1725              }
1726              
1727              OpBlockChecksumResponseProto checksumData =
1728                reply.getChecksumResponse();
1729    
1730              //read byte-per-checksum
1731              final int bpc = checksumData.getBytesPerCrc();
1732              if (i == 0) { //first block
1733                bytesPerCRC = bpc;
1734              }
1735              else if (bpc != bytesPerCRC) {
1736                throw new IOException("Byte-per-checksum not matched: bpc=" + bpc
1737                    + " but bytesPerCRC=" + bytesPerCRC);
1738              }
1739              
1740              //read crc-per-block
1741              final long cpb = checksumData.getCrcPerBlock();
1742              if (locatedblocks.size() > 1 && i == 0) {
1743                crcPerBlock = cpb;
1744              }
1745    
1746              //read md5
1747              final MD5Hash md5 = new MD5Hash(
1748                  checksumData.getMd5().toByteArray());
1749              md5.write(md5out);
1750              
1751              // read crc-type
1752              final DataChecksum.Type ct = HdfsProtoUtil.
1753                  fromProto(checksumData.getCrcType());
1754              if (i == 0) { // first block
1755                crcType = ct;
1756              } else if (crcType != DataChecksum.Type.MIXED
1757                  && crcType != ct) {
1758                // if crc types are mixed in a file
1759                crcType = DataChecksum.Type.MIXED;
1760              }
1761    
1762              done = true;
1763    
1764              if (LOG.isDebugEnabled()) {
1765                if (i == 0) {
1766                  LOG.debug("set bytesPerCRC=" + bytesPerCRC
1767                      + ", crcPerBlock=" + crcPerBlock);
1768                }
1769                LOG.debug("got reply from " + datanodes[j] + ": md5=" + md5);
1770              }
1771            } catch (IOException ie) {
1772              LOG.warn("src=" + src + ", datanodes["+j+"]=" + datanodes[j], ie);
1773            } finally {
1774              IOUtils.closeStream(in);
1775              IOUtils.closeStream(out);
1776              IOUtils.closeSocket(sock);        
1777            }
1778          }
1779    
1780          if (!done) {
1781            throw new IOException("Fail to get block MD5 for " + block);
1782          }
1783        }
1784    
1785        //compute file MD5
1786        final MD5Hash fileMD5 = MD5Hash.digest(md5out.getData()); 
1787        switch (crcType) {
1788          case CRC32:
1789            return new MD5MD5CRC32GzipFileChecksum(bytesPerCRC,
1790                crcPerBlock, fileMD5);
1791          case CRC32C:
1792            return new MD5MD5CRC32CastagnoliFileChecksum(bytesPerCRC,
1793                crcPerBlock, fileMD5);
1794          default:
1795            // we should never get here since the validity was checked
1796            // when getCrcType() was called above.
1797            return null;
1798        }
1799      }
1800    
1801      /**
1802       * Set permissions to a file or directory.
1803       * @param src path name.
1804       * @param permission
1805       * 
1806       * @see ClientProtocol#setPermission(String, FsPermission)
1807       */
1808      public void setPermission(String src, FsPermission permission)
1809          throws IOException {
1810        checkOpen();
1811        try {
1812          namenode.setPermission(src, permission);
1813        } catch(RemoteException re) {
1814          throw re.unwrapRemoteException(AccessControlException.class,
1815                                         FileNotFoundException.class,
1816                                         SafeModeException.class,
1817                                         UnresolvedPathException.class);
1818        }
1819      }
1820    
1821      /**
1822       * Set file or directory owner.
1823       * @param src path name.
1824       * @param username user id.
1825       * @param groupname user group.
1826       * 
1827       * @see ClientProtocol#setOwner(String, String, String)
1828       */
1829      public void setOwner(String src, String username, String groupname)
1830          throws IOException {
1831        checkOpen();
1832        try {
1833          namenode.setOwner(src, username, groupname);
1834        } catch(RemoteException re) {
1835          throw re.unwrapRemoteException(AccessControlException.class,
1836                                         FileNotFoundException.class,
1837                                         SafeModeException.class,
1838                                         UnresolvedPathException.class);                                   
1839        }
1840      }
1841    
1842      /**
1843       * @see ClientProtocol#getStats()
1844       */
1845      public FsStatus getDiskStatus() throws IOException {
1846        long rawNums[] = namenode.getStats();
1847        return new FsStatus(rawNums[0], rawNums[1], rawNums[2]);
1848      }
1849    
1850      /**
1851       * Returns count of blocks with no good replicas left. Normally should be 
1852       * zero.
1853       * @throws IOException
1854       */ 
1855      public long getMissingBlocksCount() throws IOException {
1856        return namenode.getStats()[ClientProtocol.GET_STATS_MISSING_BLOCKS_IDX];
1857      }
1858      
1859      /**
1860       * Returns count of blocks with one of more replica missing.
1861       * @throws IOException
1862       */ 
1863      public long getUnderReplicatedBlocksCount() throws IOException {
1864        return namenode.getStats()[ClientProtocol.GET_STATS_UNDER_REPLICATED_IDX];
1865      }
1866      
1867      /**
1868       * Returns count of blocks with at least one replica marked corrupt. 
1869       * @throws IOException
1870       */ 
1871      public long getCorruptBlocksCount() throws IOException {
1872        return namenode.getStats()[ClientProtocol.GET_STATS_CORRUPT_BLOCKS_IDX];
1873      }
1874      
1875      /**
1876       * @return a list in which each entry describes a corrupt file/block
1877       * @throws IOException
1878       */
1879      public CorruptFileBlocks listCorruptFileBlocks(String path,
1880                                                     String cookie)
1881        throws IOException {
1882        return namenode.listCorruptFileBlocks(path, cookie);
1883      }
1884    
1885      public DatanodeInfo[] datanodeReport(DatanodeReportType type)
1886      throws IOException {
1887        return namenode.getDatanodeReport(type);
1888      }
1889        
1890      /**
1891       * Enter, leave or get safe mode.
1892       * 
1893       * @see ClientProtocol#setSafeMode(HdfsConstants.SafeModeAction)
1894       */
1895      public boolean setSafeMode(SafeModeAction action) throws IOException {
1896        return namenode.setSafeMode(action);
1897      }
1898    
1899      /**
1900       * Save namespace image.
1901       * 
1902       * @see ClientProtocol#saveNamespace()
1903       */
1904      void saveNamespace() throws AccessControlException, IOException {
1905        try {
1906          namenode.saveNamespace();
1907        } catch(RemoteException re) {
1908          throw re.unwrapRemoteException(AccessControlException.class);
1909        }
1910      }
1911    
1912      /**
1913       * Rolls the edit log on the active NameNode.
1914       * @return the txid of the new log segment 
1915       *
1916       * @see ClientProtocol#rollEdits()
1917       */
1918      long rollEdits() throws AccessControlException, IOException {
1919        try {
1920          return namenode.rollEdits();
1921        } catch(RemoteException re) {
1922          throw re.unwrapRemoteException(AccessControlException.class);
1923        }
1924      }
1925      
1926      /**
1927       * enable/disable restore failed storage.
1928       * 
1929       * @see ClientProtocol#restoreFailedStorage(String arg)
1930       */
1931      boolean restoreFailedStorage(String arg)
1932          throws AccessControlException, IOException{
1933        return namenode.restoreFailedStorage(arg);
1934      }
1935    
1936      /**
1937       * Refresh the hosts and exclude files.  (Rereads them.)
1938       * See {@link ClientProtocol#refreshNodes()} 
1939       * for more details.
1940       * 
1941       * @see ClientProtocol#refreshNodes()
1942       */
1943      public void refreshNodes() throws IOException {
1944        namenode.refreshNodes();
1945      }
1946    
1947      /**
1948       * Dumps DFS data structures into specified file.
1949       * 
1950       * @see ClientProtocol#metaSave(String)
1951       */
1952      public void metaSave(String pathname) throws IOException {
1953        namenode.metaSave(pathname);
1954      }
1955    
1956      /**
1957       * Requests the namenode to tell all datanodes to use a new, non-persistent
1958       * bandwidth value for dfs.balance.bandwidthPerSec.
1959       * See {@link ClientProtocol#setBalancerBandwidth(long)} 
1960       * for more details.
1961       * 
1962       * @see ClientProtocol#setBalancerBandwidth(long)
1963       */
1964      public void setBalancerBandwidth(long bandwidth) throws IOException {
1965        namenode.setBalancerBandwidth(bandwidth);
1966      }
1967        
1968      /**
1969       * @see ClientProtocol#finalizeUpgrade()
1970       */
1971      public void finalizeUpgrade() throws IOException {
1972        namenode.finalizeUpgrade();
1973      }
1974    
1975      /**
1976       */
1977      @Deprecated
1978      public boolean mkdirs(String src) throws IOException {
1979        return mkdirs(src, null, true);
1980      }
1981    
1982      /**
1983       * Create a directory (or hierarchy of directories) with the given
1984       * name and permission.
1985       *
1986       * @param src The path of the directory being created
1987       * @param permission The permission of the directory being created.
1988       * If permission == null, use {@link FsPermission#getDefault()}.
1989       * @param createParent create missing parent directory if true
1990       * 
1991       * @return True if the operation success.
1992       * 
1993       * @see ClientProtocol#mkdirs(String, FsPermission, boolean)
1994       */
1995      public boolean mkdirs(String src, FsPermission permission,
1996          boolean createParent) throws IOException {
1997        if (permission == null) {
1998          permission = FsPermission.getDefault();
1999        }
2000        FsPermission masked = permission.applyUMask(dfsClientConf.uMask);
2001        return primitiveMkdir(src, masked, createParent);
2002      }
2003    
2004      /**
2005       * Same {{@link #mkdirs(String, FsPermission, boolean)} except
2006       * that the permissions has already been masked against umask.
2007       */
2008      public boolean primitiveMkdir(String src, FsPermission absPermission)
2009        throws IOException {
2010        return primitiveMkdir(src, absPermission, true);
2011      }
2012    
2013      /**
2014       * Same {{@link #mkdirs(String, FsPermission, boolean)} except
2015       * that the permissions has already been masked against umask.
2016       */
2017      public boolean primitiveMkdir(String src, FsPermission absPermission, 
2018        boolean createParent)
2019        throws IOException {
2020        checkOpen();
2021        if (absPermission == null) {
2022          absPermission = 
2023            FsPermission.getDefault().applyUMask(dfsClientConf.uMask);
2024        } 
2025    
2026        if(LOG.isDebugEnabled()) {
2027          LOG.debug(src + ": masked=" + absPermission);
2028        }
2029        try {
2030          return namenode.mkdirs(src, absPermission, createParent);
2031        } catch(RemoteException re) {
2032          throw re.unwrapRemoteException(AccessControlException.class,
2033                                         InvalidPathException.class,
2034                                         FileAlreadyExistsException.class,
2035                                         FileNotFoundException.class,
2036                                         ParentNotDirectoryException.class,
2037                                         SafeModeException.class,
2038                                         NSQuotaExceededException.class,
2039                                         DSQuotaExceededException.class,
2040                                         UnresolvedPathException.class);
2041        }
2042      }
2043      
2044      /**
2045       * Get {@link ContentSummary} rooted at the specified directory.
2046       * @param path The string representation of the path
2047       * 
2048       * @see ClientProtocol#getContentSummary(String)
2049       */
2050      ContentSummary getContentSummary(String src) throws IOException {
2051        try {
2052          return namenode.getContentSummary(src);
2053        } catch(RemoteException re) {
2054          throw re.unwrapRemoteException(AccessControlException.class,
2055                                         FileNotFoundException.class,
2056                                         UnresolvedPathException.class);
2057        }
2058      }
2059    
2060      /**
2061       * Sets or resets quotas for a directory.
2062       * @see ClientProtocol#setQuota(String, long, long)
2063       */
2064      void setQuota(String src, long namespaceQuota, long diskspaceQuota) 
2065          throws IOException {
2066        // sanity check
2067        if ((namespaceQuota <= 0 && namespaceQuota != HdfsConstants.QUOTA_DONT_SET &&
2068             namespaceQuota != HdfsConstants.QUOTA_RESET) ||
2069            (diskspaceQuota <= 0 && diskspaceQuota != HdfsConstants.QUOTA_DONT_SET &&
2070             diskspaceQuota != HdfsConstants.QUOTA_RESET)) {
2071          throw new IllegalArgumentException("Invalid values for quota : " +
2072                                             namespaceQuota + " and " + 
2073                                             diskspaceQuota);
2074                                             
2075        }
2076        try {
2077          namenode.setQuota(src, namespaceQuota, diskspaceQuota);
2078        } catch(RemoteException re) {
2079          throw re.unwrapRemoteException(AccessControlException.class,
2080                                         FileNotFoundException.class,
2081                                         NSQuotaExceededException.class,
2082                                         DSQuotaExceededException.class,
2083                                         UnresolvedPathException.class);
2084        }
2085      }
2086    
2087      /**
2088       * set the modification and access time of a file
2089       * 
2090       * @see ClientProtocol#setTimes(String, long, long)
2091       */
2092      public void setTimes(String src, long mtime, long atime) throws IOException {
2093        checkOpen();
2094        try {
2095          namenode.setTimes(src, mtime, atime);
2096        } catch(RemoteException re) {
2097          throw re.unwrapRemoteException(AccessControlException.class,
2098                                         FileNotFoundException.class,
2099                                         UnresolvedPathException.class);
2100        }
2101      }
2102    
2103      /**
2104       * @deprecated use {@link HdfsDataInputStream} instead.
2105       */
2106      @Deprecated
2107      public static class DFSDataInputStream extends HdfsDataInputStream {
2108    
2109        public DFSDataInputStream(DFSInputStream in) throws IOException {
2110          super(in);
2111        }
2112      }
2113      
2114      boolean shouldTryShortCircuitRead(InetSocketAddress targetAddr) {
2115        return shortCircuitLocalReads && isLocalAddress(targetAddr);
2116      }
2117    
2118      void reportChecksumFailure(String file, ExtendedBlock blk, DatanodeInfo dn) {
2119        DatanodeInfo [] dnArr = { dn };
2120        LocatedBlock [] lblocks = { new LocatedBlock(blk, dnArr) };
2121        reportChecksumFailure(file, lblocks);
2122      }
2123        
2124      // just reports checksum failure and ignores any exception during the report.
2125      void reportChecksumFailure(String file, LocatedBlock lblocks[]) {
2126        try {
2127          reportBadBlocks(lblocks);
2128        } catch (IOException ie) {
2129          LOG.info("Found corruption while reading " + file
2130              + ".  Error repairing corrupt blocks.  Bad blocks remain.", ie);
2131        }
2132      }
2133    
2134      @Override
2135      public String toString() {
2136        return getClass().getSimpleName() + "[clientName=" + clientName
2137            + ", ugi=" + ugi + "]"; 
2138      }
2139    
2140      void disableShortCircuit() {
2141        shortCircuitLocalReads = false;
2142      }
2143    }